@vskstudio/takt-astro 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,7 +53,7 @@ Both the integration and the component accept the same options:
53
53
 
54
54
  ## View Transitions
55
55
 
56
- Astro's client router navigates via `history.pushState`. Core's SPA tracking patches `pushState`, so a single pageview fires per client navigation — including View Transitions DOM swaps — while `init()` fires the one initial pageview. That single source means navigations are never double-counted. The runtime is SSR/prerender safe — it only runs in the browser.
56
+ Astro's client router runs several history operations per navigation (scroll-restoration `replaceState` plus `pushState`), so Takt does **not** rely on core's history patch here — it would over-count. Instead the runtime fires one explicit initial pageview and then one per Astro `astro:after-swap` event (including View Transitions DOM swaps and back/forward), so each navigation is counted exactly once. On a plain MPA (no client router) the script re-runs per page load, which fires the initial pageview each time. The runtime is SSR/prerender safe — it only runs in the browser.
57
57
 
58
58
  ## Custom events
59
59
 
package/Takt.astro CHANGED
@@ -1,10 +1,33 @@
1
1
  ---
2
- import { resolveOptions, type TaktOptions } from './src/options'
3
- import { buildRuntime } from './src/runtime'
2
+ // Import from the built output: the published package ships dist/ + this file,
3
+ // not src/, so a src import would break on install.
4
+ import { resolveOptions, type TaktOptions } from './dist/index.js'
4
5
 
5
6
  type Props = TaktOptions
6
7
 
7
- const runtime = buildRuntime(resolveOptions(Astro.props))
8
+ // Options travel as a JSON data island, NOT interpolated into JS, so a domain or
9
+ // endpoint containing `</script>`/quotes can't break out. `<` is escaped so the
10
+ // value can't terminate the island's <script> tag either.
11
+ const config = JSON.stringify(resolveOptions(Astro.props))
12
+ .replace(/</g, '\\u003c')
13
+ .replace(/>/g, '\\u003e')
14
+ .replace(/&/g, '\\u0026')
8
15
  ---
9
16
  <!-- Pick ONE install path — this component OR the integration — not both. -->
10
- <script is:inline type="module" set:html={runtime}></script>
17
+ <script type="application/json" data-takt-config set:html={config}></script>
18
+ <!-- A bundled (non-inline) module so Astro resolves the bare core import; an
19
+ is:inline module would leave it unresolved in the browser. -->
20
+ <script>
21
+ import { init, pageview } from '@vskstudio/takt-core'
22
+ const el = document.querySelector('script[data-takt-config]')
23
+ const w = window as unknown as { __takt?: boolean }
24
+ if (el && !w.__takt) {
25
+ const o = JSON.parse(el.textContent || '{}')
26
+ w.__takt = true
27
+ // auto forced off: Astro's ClientRouter runs several history ops per nav, so
28
+ // core's history patch would over-count. Drive pageviews from after-swap.
29
+ init({ ...o, auto: false })
30
+ pageview()
31
+ if (o.auto !== false) document.addEventListener('astro:after-swap', () => pageview())
32
+ }
33
+ </script>
package/dist/index.js CHANGED
@@ -13,23 +13,24 @@ function resolveOptions(options = {}) {
13
13
 
14
14
  // src/runtime.ts
15
15
  function buildRuntime(options) {
16
- const json = JSON.stringify(options).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
16
+ const spa = options.auto !== false;
17
+ const json = JSON.stringify({ ...options, auto: false }).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
17
18
  return [
18
- `import { init } from '@vskstudio/takt-core';`,
19
+ `import { init, pageview } from '@vskstudio/takt-core';`,
19
20
  // SSR/prerender guard: this module is evaluated under Node when Astro
20
21
  // prerenders, where window/init's browser APIs are absent.
21
22
  `if (typeof window !== 'undefined' && !window.__takt) {`,
22
23
  ` const o = ${json};`,
23
24
  // Idempotent across both entry paths (integration + <Takt/>): the flag stops
24
- // a second boot from disposing the first instance's listeners.
25
+ // a second boot from re-registering listeners or re-firing the initial view.
25
26
  ` window.__takt = true;`,
26
- // Single source of pageviews. init() enables core's SPA tracking, which
27
- // patches history.pushState/replaceState. Astro's ClientRouter navigates via
28
- // pushState and does NOT replace the history object on DOM swap, so the patch
29
- // persists and fires one pageview per View Transitions navigation. init()
30
- // also fires the initial pageview. We therefore do NOT listen to
31
- // astro:page-load — doing so would double-count every in-app navigation.
32
27
  ` init(o);`,
28
+ // One initial pageview (also covers full-page MPA loads, where the script
29
+ // re-runs per navigation and astro:after-swap never fires).
30
+ ` pageview();`,
31
+ // astro:after-swap fires exactly once per ClientRouter navigation (incl.
32
+ // back/forward), giving one pageview per nav with no double-count.
33
+ ...spa ? [` document.addEventListener('astro:after-swap', () => pageview());`] : [],
33
34
  `}`
34
35
  ].join("\n");
35
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vskstudio/takt-astro",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Astro integration for Takt privacy-friendly analytics",
5
5
  "license": "MIT",
6
6
  "author": "VSK Studio",
@@ -47,6 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@changesets/cli": "^2.31.0",
50
+ "@playwright/test": "^1.50.0",
50
51
  "@size-limit/preset-small-lib": "^11.1.6",
51
52
  "@vskstudio/takt-core": "^0.2.0",
52
53
  "astro": "^5.0.0",
@@ -62,6 +63,7 @@
62
63
  "typecheck": "tsc --noEmit",
63
64
  "lint": "eslint .",
64
65
  "test": "vitest run",
66
+ "e2e": "playwright test",
65
67
  "size": "size-limit",
66
68
  "release": "pnpm build && changeset publish"
67
69
  }