Skip to content

Support inline events in the main v12 script - #106

Merged
adriaandotcom merged 3 commits into
v12from
feat/inline-events
Oct 15, 2026
Merged

adriaandotcom merged 3 commits into
v12from
feat/inline-events

Conversation

@simple-analytics-ai

@simple-analytics-ai simple-analytics-ai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

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:

  • Delegate tagged clicks and form submissions from document, so dynamically inserted elements and current data-simple-event-* metadata are picked up without observers or polling. Every event carries a tag field; form events also carry the submit button label. Form field values are never read.
  • Take the submit button label only from the browser-provided event.submitter, preferring textContent and falling back to value for <input type="submit">. Internal whitespace is collapsed to single spaces before trimming and capping at 200 characters, so an icon/span button keeps a stable button_text when its markup is reindented.
  • Send inline events with a keepalive fetch GET (mode: "no-cors", credentials: "omit") so they survive navigation, falling back to the existing image pixel when fetch is missing, throws, or rejects. Native navigation, form validation, formaction overrides and SPA handlers stay in control.
  • The old beta inline-events.js helper is not detected or supported. Remove that extra script when using the built-in handlers, otherwise events are counted twice.
  • Add optional confirmed 404 events via data-detect-404="true" or sa_settings.detect404 = true. The tracker scans the page title and body text for "404", confirms with a HEAD request, and only then sends a page_404 event with the query string and fragment stripped from the URL metadata. Existing installations send no extra requests by default.
  • Compile the handlers only into builds that support events, so light.js is unaffected apart from the shared transport refactor.
  • Add test/unit/inline-events.test.js plus a test:unit script, and run the full unit suite in the BrowserStack workflow after pnpm run build.
  • Bump the handlebars build dependency from 4.7.6 to 4.7.10 and add jsdom 26.1.0 for the unit tests.

Changes:

  • 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.
  • 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.
  • Normalize inline submit button label whitespace - Requested: address scripts PR Support inline events in the main v12 script #106 review comment 4282258759 so formatting a button across multiple lines does not fragment button_text metadata.

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 a HEAD request to the customer's own origin for the current URL; the resulting page_404 metadata strips query strings and fragments. Inline events are sent with credentials: "omit", and the handlebars build 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 committed dist/ is the build date stamp and local minifier output in the untouched auto-events.js bundle, so the committed dist/latest/latest.dev.js is in sync with src/.
  • 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

  • Linked to an issue
  • Tested
  • Asked for a review

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.
Comment thread src/default.js Outdated
button.form === element &&
(button.tagName === "BUTTON" ||
(button.tagName === "INPUT" && button.type === "submit"))
? button.value || button.textContent || ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
? 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/default.js Outdated
var detect404 = function () {
if (
hasInlineHelper() ||
!/[^0-9.,]404[^0-9.,]/.test(documentElement.innerHTML)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread .github/workflows/browserstack.yml Outdated
run: pnpm run test:matrix

- name: Test inline events
run: pnpm run build && pnpm exec mocha test/unit/inline-events.test.js --exit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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):

Suggested change
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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions

Copy link
Copy Markdown
Contributor

Documentation update

_docs/55_events/00_inline-events.md

Location: Replace the whole page body (keep the front matter, but update last_modified_at), because the separate helper script is no longer required and the click/submit behaviour changed.

Copy and paste this into the documentation repository:

---
title: Inline events
category: events
permalink: /events/inline
hidden: true
last_modified_at: 2026-10-15
---

The tracker watches your page for any element with a `data-simple-event` attribute and automatically sends events to Simple Analytics when the user interacts with it. You don’t need to write any extra JavaScript calls—just add the right attributes to your HTML.

<blockquote class="red">
  <p>Warning, this feature is in beta and can change any time.</p>
</blockquote>

### Installation

Inline events are built into the tracker, so the regular snippet is all you need.

```html
<script async src="https://scripts.simpleanalyticscdn.com/latest.js"></script>

If you still load the separate inline-events.js helper, the tracker steps aside and lets the helper handle everything, so you never get duplicate events. You can remove that extra script whenever you want—the built-in handlers take over automatically.

Inline events are not included in light.js, which has no event support.

Tracking button clicks

To track a click, add data-simple-event="event_name" to any clickable element. The tracker sends the event in the background and never blocks the click, so links, downloads, target="_blank", keyboard-modified clicks and your own click handlers keep working exactly as before.

<a href="/pricing" data-simple-event="visit_pricing_page">See pricing</a>

Tracking form submissions

Put data-simple-event="form_name" on your form tag. When the user submits, the tracker sends an event with the name of the form and the text of the submit button, and then lets the browser submit the form as usual. Form field values are never collected.

<form action="/signup" data-simple-event="signup_form">
  <input name="email" type="email" />
  <input name="password" type="password" />
  <button type="submit">Sign up</button>
</form>

Adding custom metadata

Any attribute starting with data-simple-event- is included as event metadata. Dashes convert to underscores. Every event also includes a tag field with the lowercase tag name of the element.

<button
  data-simple-event="select_plan"
  data-simple-event-plan="enterprise"
  data-simple-event-referrer="homepage"
>
  Choose Enterprise
</button>

Manual event tracking

You can also fire events from code using sa_event("event_name", { key: value }). This works before or after the automatic binding.

// custom event on newsletter popup
sa_event("newsletter_signup", { source: "modal" })

404 detection

404 detection is off by default because it adds a request to your server. Turn it on with the data-detect-404 attribute:

<script
  async
  data-detect-404="true"
  src="https://scripts.simpleanalyticscdn.com/latest.js"
></script>

Or with the settings object:

<script>
  window.sa_settings = { detect404: true }
</script>

When enabled, the tracker checks for "404" in your page HTML once the DOM is ready. If it finds a match and a HEAD request confirms the page really returns status 404, it sends a page_404 event with the URL and status code. Query strings and fragments are stripped from the URL metadata.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor
Claude review checkpoint

Reviewed commit f1b14281d7b8d5054c6874e8b59e61393eb2c476. This is used to keep later automated reviews focused on changes Claude has not checked yet.

@github-actions github-actions Bot added the change: needs review Changes affecting security, data protection, or system stability. label Oct 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

SDLC label: change: needs review because the production tracker begins collecting new end-user interaction data by default on every existing install — event names, element tag, data-simple-event-* attribute values and submit-button text — and adds an opt-in 404 check that issues HEAD requests to customer origins, materially changing how visitor data is collected

Comment thread src/default.js Outdated
Comment on lines +1142 to +1144
var hasInlineHelper = function () {
return doc.querySelector('script[src*="inline-events.js"]');
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just a test helper, fine if this collides, it was beta anyway. Remove this check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/default.js
Comment thread src/default.js Outdated
var detect404 = function () {
if (
hasInlineHelper() ||
!/[^0-9.,]404[^0-9.,]/.test(documentElement.innerHTML)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we cna't use a abbrivations of documentElement.innerHTML, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/default.js Outdated
Comment on lines +1171 to +1177
metadata.button_text =
button &&
button.form === element &&
(button.tagName === "BUTTON" ||
(button.tagName === "INPUT" && button.type === "submit"))
? button.value || button.textContent || ""
: "";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this less perfect, but a lot shorter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Comment thread src/default.js Outdated
if (isForm) {
var button = event.submitter;
metadata.button_text = button
? (button.textContent || button.value || "").trim().slice(0, 200)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
? (button.textContent || button.value || "").trim().slice(0, 200)
? (button.textContent || button.value || "")
.replace(/\s+/g, " ")
.trim()
.slice(0, 200)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implement this

@simple-analytics-ai simple-analytics-ai Bot Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/default.js
@github-actions

Copy link
Copy Markdown
Contributor

Documentation update

_docs/55_events/00_inline-events.md

Location: Two corrections to the page text proposed earlier in this PR: replace the inline-events.js helper paragraph at the end of ### Installation, and replace the final paragraph of ### 404 detection. The tracker no longer detects the helper (so both would fire), and 404 detection now scans the title and body text instead of the page HTML.

Copy and paste this into the documentation repository:

<!-- End of "### Installation", replacing the paragraph about the helper -->

The tracker does not detect the old `inline-events.js` helper. If you still load that script, remove it — otherwise every tagged click and submit is counted twice.

Inline events are not included in `light.js`, which has no event support.

<!-- End of "### 404 detection", replacing the final paragraph -->

When enabled, the tracker checks the page title and the visible body text for "404" once the DOM is ready. If it finds a match and a `HEAD` request confirms the page really returns status 404, it sends a `page_404` event with the URL and status code. Query strings and fragments are stripped from the URL metadata.

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.
@simple-analytics-ai

Copy link
Copy Markdown
Contributor Author

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.

@adriaandotcom
adriaandotcom merged commit 38248a0 into v12 Oct 15, 2026
20 checks passed
@adriaandotcom
adriaandotcom deleted the feat/inline-events branch October 15, 2026 13:16
@github-actions github-actions Bot mentioned this pull request Oct 15, 2026
3 tasks done
adriaandotcom added a commit that referenced this pull request Oct 22, 2026
* 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>
adriaandotcom added a commit that referenced this pull request Oct 22, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change: needs review Changes affecting security, data protection, or system stability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant