@vskstudio/takt-astro 0.7.0 → 0.8.0
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 +40 -2
- package/Takt.astro +7 -1
- package/TaktRoute.astro +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -1
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Astro integration for [Takt](https://github.com/vskstudio/takt-core), privacy-fr
|
|
|
11
11
|
pnpm add @vskstudio/takt-astro @vskstudio/takt-core
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Both are peer dependencies: `astro` (`>=4`) and `@vskstudio/takt-core` (`>=0.
|
|
14
|
+
Both are peer dependencies: `astro` (`>=4`) and `@vskstudio/takt-core` (`>=0.10.0`).
|
|
15
15
|
|
|
16
16
|
## Usage
|
|
17
17
|
|
|
@@ -62,6 +62,8 @@ Both the integration and the component accept the same options, with one excepti
|
|
|
62
62
|
| `trackQuery` | `boolean` | `false` | Keep the full query string and hash instead of stripping them. Wins over `queryParams`. |
|
|
63
63
|
| `queryParams` | `string[]` | – | Allowlist of query params to keep, applied only when `trackQuery` is off. |
|
|
64
64
|
| `exclude` | `string[]` | – | Path prefixes never tracked, e.g. `['/app', '/account']` (segment-bounded, checked at send time). |
|
|
65
|
+
| `redactRoutes` | `string[]` | none | Route patterns sent in place of the real path, e.g. `['/verify/[token]']` sends `/verify/[token]` instead of `/verify/abc123`. See [Route redaction](#route-redaction). |
|
|
66
|
+
| `routeTemplates` | `boolean` | `false` | Send every page as its Astro route template (`/blog/[slug]`), read from `Astro.routePattern`. Needs Astro 5 and the route meta, see [Route redaction](#route-redaction). |
|
|
65
67
|
| `scrubUrl` | `(url: string) => string` | – | **Integration only.** Rewrite each URL before it is sent (page, referrer, and the `url` prop of outbound-link and file-download events), e.g. to strip a fragment or PII. See the note below. |
|
|
66
68
|
| `tagged` | `boolean` | `false` | Auto-track clicks on `[data-takt-event]` elements; `data-takt-prop-*` attributes become event props. |
|
|
67
69
|
| `debug` | `boolean` | `false` | Log each payload to the browser console before sending. |
|
|
@@ -81,6 +83,42 @@ Both the integration and the component accept the same options, with one excepti
|
|
|
81
83
|
|
|
82
84
|
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.
|
|
83
85
|
|
|
86
|
+
## Route redaction
|
|
87
|
+
|
|
88
|
+
Query strings are stripped by default, but path segments are sent as they are, so `/verify/abc123` leaks the token. List the sensitive routes in `redactRoutes` and every matching path is sent as its pattern, while the other pages keep their real path:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
takt({ redactRoutes: ['/verify/[token]', '/invoices/[id].pdf'] })
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The rule covers the page URL, same-origin referrers, outbound and download link destinations, and 404 paths. Patterns use Astro's syntax (`[param]`, `[...rest]`) and also accept `:param` and `*`.
|
|
95
|
+
|
|
96
|
+
To send every page as its route template instead (`/blog/hello` becomes `/blog/[slug]`), turn on `routeTemplates`. Astro knows the template on the server (`Astro.routePattern`), so it travels in a `<meta name="takt:route">` tag that the runtime reads at each pageview, including after a ClientRouter navigation. With the integration, render that tag with `<TaktRoute />` in the `<head>` of your layout:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
// astro.config.mjs
|
|
100
|
+
export default defineConfig({
|
|
101
|
+
integrations: [takt({ routeTemplates: true })],
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```astro
|
|
106
|
+
---
|
|
107
|
+
import TaktRoute from '@vskstudio/takt-astro/TaktRoute.astro'
|
|
108
|
+
---
|
|
109
|
+
<head>
|
|
110
|
+
<TaktRoute />
|
|
111
|
+
</head>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `<Takt />` component renders the tag itself when `routeTemplates` is set:
|
|
115
|
+
|
|
116
|
+
```astro
|
|
117
|
+
<Takt domain="example.com" routeTemplates />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`Astro.routePattern` exists from Astro 5. On Astro 4 no tag is rendered, so `redactRoutes` still applies and the real path is sent otherwise. This mode merges every article of a public blog into one row, so it suits private areas better; `redactRoutes` is usually the right tool for a public site.
|
|
121
|
+
|
|
84
122
|
## Custom events
|
|
85
123
|
|
|
86
124
|
`track`, `pageview`, `optOut`, `optIn` and `isOptedOut` are re-exported from core for convenience. `track` and `pageview` act on core's default instance, which only exists in the browser once the injected runtime has booted, so call them from a client-side `<script>`, not from `.astro` frontmatter (where they are silent no-ops):
|
|
@@ -161,7 +199,7 @@ From `@vskstudio/takt-astro`:
|
|
|
161
199
|
- Re-exported from core: `track`, `pageview`, `optOut`, `optIn`, `isOptedOut`, `createStats`, `PublicApiError`, `badgeUrl`, `embedUrl`
|
|
162
200
|
- Types: `TaktOptions`, plus `InitOptions`, `TrackOptions`, `BadgeOptions`, `EmbedOptions`, `BadgeVariant`, `BadgeGlyph`, `EmbedTheme`, `WidgetLang`, `StatsClient`, `StatsClientOptions`, `StatsParams`, `StatsPeriod`, `StatsDimension`, `StatsMetrics`, `StatsSummary`, `StatsPoint`, `StatsTimeseries`, `StatsBreakdownRow`, `StatsBreakdown`, `StatsRealtime` re-exported from core
|
|
163
201
|
|
|
164
|
-
Component subpaths: `@vskstudio/takt-astro/Takt.astro`, `/Badge.astro`, `/Embed.astro`.
|
|
202
|
+
Component subpaths: `@vskstudio/takt-astro/Takt.astro`, `/TaktRoute.astro`, `/Badge.astro`, `/Embed.astro`.
|
|
165
203
|
|
|
166
204
|
## License
|
|
167
205
|
|
package/Takt.astro
CHANGED
|
@@ -17,7 +17,10 @@ const config = JSON.stringify(resolveOptions(Astro.props))
|
|
|
17
17
|
.replace(/</g, '\\u003c')
|
|
18
18
|
.replace(/>/g, '\\u003e')
|
|
19
19
|
.replace(/&/g, '\\u0026')
|
|
20
|
+
|
|
21
|
+
const route = Astro.props.routeTemplates ? Astro.routePattern : undefined
|
|
20
22
|
---
|
|
23
|
+
{route && <meta name="takt:route" content={route} />}
|
|
21
24
|
<!-- Pick ONE install path — this component OR the integration — not both. -->
|
|
22
25
|
<script type="application/json" data-takt-config set:html={config}></script>
|
|
23
26
|
<!-- A bundled (non-inline) module so Astro resolves the bare core import; an
|
|
@@ -29,9 +32,12 @@ const config = JSON.stringify(resolveOptions(Astro.props))
|
|
|
29
32
|
if (el && !w.__takt) {
|
|
30
33
|
const o = JSON.parse(el.textContent || '{}')
|
|
31
34
|
w.__takt = true
|
|
35
|
+
const routeTemplate = o.routeTemplates
|
|
36
|
+
? () => document.querySelector('meta[name="takt:route"]')?.getAttribute('content') ?? null
|
|
37
|
+
: undefined
|
|
32
38
|
// auto forced off: Astro's ClientRouter runs several history ops per nav, so
|
|
33
39
|
// core's history patch would over-count. Drive pageviews from after-swap.
|
|
34
|
-
init({ ...o, auto: false })
|
|
40
|
+
init({ ...o, auto: false, routeTemplate })
|
|
35
41
|
pageview()
|
|
36
42
|
if (o.auto !== false) document.addEventListener('astro:after-swap', () => pageview())
|
|
37
43
|
}
|
package/TaktRoute.astro
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ interface TaktOptions {
|
|
|
41
41
|
queryParams?: string[];
|
|
42
42
|
/** Path prefixes never tracked, e.g. `['/app','/account']`. Segment-bounded: `'/app'` matches `'/app'` and `'/app/…'` but not `'/application'`. */
|
|
43
43
|
exclude?: string[];
|
|
44
|
+
redactRoutes?: string[];
|
|
45
|
+
routeTemplates?: boolean;
|
|
44
46
|
/**
|
|
45
47
|
* Rewrite each URL before it is sent (e.g. strip a fragment or PII).
|
|
46
48
|
* MUST be a self-contained function: it is stringified at build time and
|
package/dist/index.js
CHANGED
|
@@ -22,16 +22,23 @@ function resolveOptions(options = {}) {
|
|
|
22
22
|
trackQuery: options.trackQuery,
|
|
23
23
|
queryParams: options.queryParams,
|
|
24
24
|
exclude: options.exclude,
|
|
25
|
+
redactRoutes: options.redactRoutes,
|
|
26
|
+
routeTemplates: options.routeTemplates,
|
|
25
27
|
tagged: options.tagged,
|
|
26
28
|
debug: options.debug
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
// src/runtime.ts
|
|
33
|
+
var ROUTE_TEMPLATE_RESOLVER = `() => document.querySelector('meta[name="takt:route"]')?.getAttribute('content') ?? null`;
|
|
31
34
|
function buildRuntime(options, scrubUrl) {
|
|
32
35
|
const spa = options.auto !== false;
|
|
33
36
|
const json = JSON.stringify({ ...options, auto: false }).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
34
|
-
const
|
|
37
|
+
const functions = [
|
|
38
|
+
...scrubUrl ? [`scrubUrl: ${scrubUrl.toString()}`] : [],
|
|
39
|
+
...options.routeTemplates ? [`routeTemplate: ${ROUTE_TEMPLATE_RESOLVER}`] : []
|
|
40
|
+
];
|
|
41
|
+
const config = functions.length ? `Object.assign(${json}, { ${functions.join(", ")} })` : json;
|
|
35
42
|
return [
|
|
36
43
|
`import { init, pageview } from '@vskstudio/takt-core';`,
|
|
37
44
|
// SSR/prerender guard: this module is evaluated under Node when Astro
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vskstudio/takt-astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Astro integration for Takt privacy-friendly analytics",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VSK Studio",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"default": "./dist/index.js"
|
|
33
33
|
},
|
|
34
34
|
"./Takt.astro": "./Takt.astro",
|
|
35
|
+
"./TaktRoute.astro": "./TaktRoute.astro",
|
|
35
36
|
"./Badge.astro": "./Badge.astro",
|
|
36
37
|
"./Embed.astro": "./Embed.astro",
|
|
37
38
|
"./package.json": "./package.json"
|
|
@@ -39,31 +40,22 @@
|
|
|
39
40
|
"files": [
|
|
40
41
|
"dist",
|
|
41
42
|
"Takt.astro",
|
|
43
|
+
"TaktRoute.astro",
|
|
42
44
|
"Badge.astro",
|
|
43
45
|
"Embed.astro"
|
|
44
46
|
],
|
|
45
47
|
"engines": {
|
|
46
48
|
"node": ">=18"
|
|
47
49
|
},
|
|
48
|
-
"scripts": {
|
|
49
|
-
"build": "tsup",
|
|
50
|
-
"typecheck": "tsc --noEmit",
|
|
51
|
-
"lint": "eslint .",
|
|
52
|
-
"test": "vitest run",
|
|
53
|
-
"e2e": "playwright test",
|
|
54
|
-
"size": "size-limit",
|
|
55
|
-
"prepublishOnly": "pnpm build",
|
|
56
|
-
"release": "pnpm build && changeset publish"
|
|
57
|
-
},
|
|
58
50
|
"peerDependencies": {
|
|
59
|
-
"@vskstudio/takt-core": ">=0.
|
|
51
|
+
"@vskstudio/takt-core": ">=0.10.0",
|
|
60
52
|
"astro": ">=4"
|
|
61
53
|
},
|
|
62
54
|
"devDependencies": {
|
|
63
55
|
"@changesets/cli": "^2.31.0",
|
|
64
56
|
"@playwright/test": "^1.50.0",
|
|
65
57
|
"@size-limit/preset-small-lib": "^11.1.6",
|
|
66
|
-
"@vskstudio/takt-core": "^0.
|
|
58
|
+
"@vskstudio/takt-core": "^0.10.0",
|
|
67
59
|
"astro": "^5.0.0",
|
|
68
60
|
"eslint": "^9.0.0",
|
|
69
61
|
"size-limit": "^11.1.6",
|
|
@@ -72,5 +64,13 @@
|
|
|
72
64
|
"typescript-eslint": "^8.0.0",
|
|
73
65
|
"vitest": "^2.1.0"
|
|
74
66
|
},
|
|
75
|
-
"
|
|
76
|
-
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsup",
|
|
69
|
+
"typecheck": "tsc --noEmit",
|
|
70
|
+
"lint": "eslint .",
|
|
71
|
+
"test": "vitest run",
|
|
72
|
+
"e2e": "playwright test",
|
|
73
|
+
"size": "size-limit",
|
|
74
|
+
"release": "pnpm build && changeset publish"
|
|
75
|
+
}
|
|
76
|
+
}
|