@vskstudio/takt-astro 0.4.2 → 0.5.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 CHANGED
@@ -39,7 +39,7 @@ import Takt from '@vskstudio/takt-astro/Takt.astro'
39
39
 
40
40
  ## Options
41
41
 
42
- Both the integration and the component accept the same options:
42
+ Both the integration and the component accept the same options, with one exception: `scrubUrl` is a function and works **only via the integration** (see the note below).
43
43
 
44
44
  | Option | Type | Default | Description |
45
45
  | --- | --- | --- | --- |
@@ -52,6 +52,23 @@ Both the integration and the component accept the same options:
52
52
  | `spa` | `boolean` | `true` | Track client-side navigations. |
53
53
  | `respectDnt` | `boolean` | `true` | Suppress events when Do Not Track is on. |
54
54
  | `excludeLocalhost` | `boolean` | `true` | Suppress events on localhost / private IPs. |
55
+ | `enabled` | `boolean` | `true` | Master kill switch — set `false` to suppress every event. |
56
+ | `sampleRate` | `number` | `1` | Fraction of visitors to track, `0`–`1`. |
57
+ | `trackQuery` | `boolean` | `false` | Send the full query string with pageviews instead of stripping it. |
58
+ | `queryParams` | `string[]` | – | Whitelist of query params to keep when `trackQuery` is off. |
59
+ | `scrubUrl` | `(url: string) => string` | – | **Integration only.** Rewrite each URL before it is sent (e.g. strip a fragment or PII). See the note below. |
60
+ | `tagged` | `boolean` | `false` | Auto-track elements marked with `data-takt-event`. |
61
+
62
+ > **`scrubUrl` note.** Unlike the other options, `scrubUrl` is a **function**, so it
63
+ > is supported **only via the integration**, not the `<Takt />` component. The
64
+ > integration serializes config into a build-time runtime string, so `scrubUrl` is
65
+ > stringified with `.toString()` and re-evaluated in the browser. It MUST be a
66
+ > **self-contained** function — no closure variables or outer-scope references — and
67
+ > must be **developer-controlled**: never build it from user input.
68
+ >
69
+ > The `<Takt />` component serializes its config as a JSON data island, which cannot
70
+ > carry a function — so passing `scrubUrl` to `<Takt />` **throws at build time**
71
+ > rather than silently doing nothing. Use the integration if you need it.
55
72
 
56
73
  ## View Transitions
57
74
 
package/Takt.astro CHANGED
@@ -1,10 +1,15 @@
1
1
  ---
2
2
  // Import from the built output: the published package ships dist/ + this file,
3
3
  // not src/, so a src import would break on install.
4
- import { resolveOptions, type TaktOptions } from './dist/index.js'
4
+ import { resolveOptions, assertNoScrubUrl, type TaktOptions } from './dist/index.js'
5
5
 
6
6
  type Props = TaktOptions
7
7
 
8
+ // Fail fast: scrubUrl is a function and cannot survive the JSON data island
9
+ // below, so accepting it here would be a silent no-op. The integration path
10
+ // supports it (embeds it as build-time JS); the component cannot.
11
+ assertNoScrubUrl(Astro.props)
12
+
8
13
  // Options travel as a JSON data island, NOT interpolated into JS, so a domain or
9
14
  // endpoint containing `</script>`/quotes can't break out. `<` is escaped so the
10
15
  // value can't terminate the island's <script> tag either.
package/dist/index.d.ts CHANGED
@@ -31,7 +31,41 @@ interface TaktOptions {
31
31
  respectDnt?: boolean;
32
32
  /** Suppress events on localhost and private IP ranges. Default `true`. */
33
33
  excludeLocalhost?: boolean;
34
+ /** Master kill switch — when `false`, every event is suppressed. */
35
+ enabled?: boolean;
36
+ /** Fraction of visitors to track, `0`–`1`. Defaults to tracking everyone. */
37
+ sampleRate?: number;
38
+ /** Send the full query string with pageviews instead of stripping it. */
39
+ trackQuery?: boolean;
40
+ /** Whitelist of query params to keep when `trackQuery` is off. */
41
+ queryParams?: string[];
42
+ /**
43
+ * Rewrite each URL before it is sent (e.g. strip a fragment or PII).
44
+ * MUST be a self-contained function: it is stringified at build time and
45
+ * re-evaluated in the browser, so it cannot reference closure/outer-scope
46
+ * variables. Never build it from user input.
47
+ *
48
+ * Integration-only: a function cannot survive the `<Takt />` component's
49
+ * JSON data island, so it is supported solely via the `takt()` integration in
50
+ * `astro.config`. Passing it to `<Takt />` throws — see {@link assertNoScrubUrl}.
51
+ */
52
+ scrubUrl?: (url: string) => string;
53
+ /** Auto-track elements marked with `data-takt-event`. */
54
+ tagged?: boolean;
34
55
  }
56
+ /**
57
+ * Guard the `<Takt />` component path against `scrubUrl`. The component
58
+ * serializes its config as a JSON data island (`JSON.stringify` → `JSON.parse`),
59
+ * which silently drops functions — so accepting `scrubUrl` there would be a
60
+ * silent no-op. Fail fast at build time and point to the integration, which
61
+ * embeds `scrubUrl` as build-time JS instead.
62
+ */
63
+ declare function assertNoScrubUrl(options?: TaktOptions): void;
64
+ /**
65
+ * Map user options to core's {@link InitOptions} for JSON serialization.
66
+ * `scrubUrl` is intentionally absent here — it is a function and cannot survive
67
+ * `JSON.stringify`, so it is threaded separately to {@link buildRuntime}.
68
+ */
35
69
  declare function resolveOptions(options?: TaktOptions): InitOptions;
36
70
 
37
71
  /** Pick ONE install path — this integration OR the `<Takt />` component — not both. */
@@ -42,7 +76,10 @@ declare function takt(options?: TaktOptions): AstroIntegration;
42
76
  *
43
77
  * Returned as a source string because Astro's `injectScript` and the `<Takt />`
44
78
  * component both take a raw module body.
79
+ *
80
+ * `scrubUrl` is threaded separately because it is a function: `JSON.stringify`
81
+ * would silently drop it, so it is embedded as raw JS via `.toString()` instead.
45
82
  */
46
- declare function buildRuntime(options: InitOptions): string;
83
+ declare function buildRuntime(options: InitOptions, scrubUrl?: (url: string) => string): string;
47
84
 
48
- export { type TaktOptions, buildRuntime, takt as default, resolveOptions, takt };
85
+ export { type TaktOptions, assertNoScrubUrl, buildRuntime, takt as default, resolveOptions, takt };
package/dist/index.js CHANGED
@@ -1,4 +1,11 @@
1
1
  // src/options.ts
2
+ function assertNoScrubUrl(options = {}) {
3
+ if (options.scrubUrl !== void 0) {
4
+ throw new Error(
5
+ "Takt: scrubUrl is a function and cannot be serialized into the <Takt /> component's config. Use the Takt integration in astro.config instead (it embeds scrubUrl as build-time JS)."
6
+ );
7
+ }
8
+ }
2
9
  function resolveOptions(options = {}) {
3
10
  return {
4
11
  domain: options.domain,
@@ -9,20 +16,26 @@ function resolveOptions(options = {}) {
9
16
  notFound: options.track404 ?? false,
10
17
  respectDnt: options.respectDnt ?? true,
11
18
  excludeLocalhost: options.excludeLocalhost ?? true,
12
- auto: options.spa ?? true
19
+ auto: options.spa ?? true,
20
+ enabled: options.enabled,
21
+ sampleRate: options.sampleRate,
22
+ trackQuery: options.trackQuery,
23
+ queryParams: options.queryParams,
24
+ tagged: options.tagged
13
25
  };
14
26
  }
15
27
 
16
28
  // src/runtime.ts
17
- function buildRuntime(options) {
29
+ function buildRuntime(options, scrubUrl) {
18
30
  const spa = options.auto !== false;
19
31
  const json = JSON.stringify({ ...options, auto: false }).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
32
+ const config = scrubUrl ? `Object.assign(${json}, { scrubUrl: ${scrubUrl.toString()} })` : json;
20
33
  return [
21
34
  `import { init, pageview } from '@vskstudio/takt-core';`,
22
35
  // SSR/prerender guard: this module is evaluated under Node when Astro
23
36
  // prerenders, where window/init's browser APIs are absent.
24
37
  `if (typeof window !== 'undefined' && !window.__takt) {`,
25
- ` const o = ${json};`,
38
+ ` const o = ${config};`,
26
39
  // Idempotent across both entry paths (integration + <Takt/>): the flag stops
27
40
  // a second boot from re-registering listeners or re-firing the initial view.
28
41
  ` window.__takt = true;`,
@@ -39,7 +52,7 @@ function buildRuntime(options) {
39
52
 
40
53
  // src/integration.ts
41
54
  function takt(options = {}) {
42
- const runtime = buildRuntime(resolveOptions(options));
55
+ const runtime = buildRuntime(resolveOptions(options), options.scrubUrl);
43
56
  return {
44
57
  name: "@vskstudio/takt-astro",
45
58
  hooks: {
@@ -55,6 +68,7 @@ import { track, pageview, optOut, optIn } from "@vskstudio/takt-core";
55
68
  import { createStats, PublicApiError, badgeUrl, embedUrl } from "@vskstudio/takt-core";
56
69
  export {
57
70
  PublicApiError,
71
+ assertNoScrubUrl,
58
72
  badgeUrl,
59
73
  buildRuntime,
60
74
  createStats,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vskstudio/takt-astro",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Astro integration for Takt privacy-friendly analytics",
5
5
  "license": "MIT",
6
6
  "author": "VSK Studio",
@@ -45,25 +45,15 @@
45
45
  "engines": {
46
46
  "node": ">=18"
47
47
  },
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
48
  "peerDependencies": {
59
- "@vskstudio/takt-core": ">=0.4.2",
49
+ "@vskstudio/takt-core": ">=0.5.0",
60
50
  "astro": ">=4"
61
51
  },
62
52
  "devDependencies": {
63
53
  "@changesets/cli": "^2.31.0",
64
54
  "@playwright/test": "^1.50.0",
65
55
  "@size-limit/preset-small-lib": "^11.1.6",
66
- "@vskstudio/takt-core": "^0.4.2",
56
+ "@vskstudio/takt-core": "^0.5.0",
67
57
  "astro": "^5.0.0",
68
58
  "eslint": "^9.0.0",
69
59
  "size-limit": "^11.1.6",
@@ -72,5 +62,13 @@
72
62
  "typescript-eslint": "^8.0.0",
73
63
  "vitest": "^2.1.0"
74
64
  },
75
- "packageManager": "pnpm@9.0.0"
76
- }
65
+ "scripts": {
66
+ "build": "tsup",
67
+ "typecheck": "tsc --noEmit",
68
+ "lint": "eslint .",
69
+ "test": "vitest run",
70
+ "e2e": "playwright test",
71
+ "size": "size-limit",
72
+ "release": "pnpm build && changeset publish"
73
+ }
74
+ }