diff --git a/site/.gitignore b/site/.gitignore
new file mode 100644
index 0000000..4c49bd7
--- /dev/null
+++ b/site/.gitignore
@@ -0,0 +1 @@
+.env
diff --git a/site/package-lock.json b/site/package-lock.json
index b5176ea..88a05a6 100644
--- a/site/package-lock.json
+++ b/site/package-lock.json
@@ -16,6 +16,7 @@
"astro-icon": "^1.0.0",
"js-yaml": "^4.1.0",
"pagefind": "^1.0.0",
+ "posthog-js": "^1.369.2",
"tailwindcss": "^4.2.2",
"yaml": "^2.8.3"
},
@@ -1235,6 +1236,252 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/api-logs": {
+ "version": "0.208.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz",
+ "integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@opentelemetry/core": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz",
+ "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/exporter-logs-otlp-http": {
+ "version": "0.208.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.208.0.tgz",
+ "integrity": "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.208.0",
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/otlp-exporter-base": "0.208.0",
+ "@opentelemetry/otlp-transformer": "0.208.0",
+ "@opentelemetry/sdk-logs": "0.208.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/otlp-exporter-base": {
+ "version": "0.208.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz",
+ "integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/otlp-transformer": "0.208.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/otlp-transformer": {
+ "version": "0.208.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz",
+ "integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.208.0",
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/resources": "2.2.0",
+ "@opentelemetry/sdk-logs": "0.208.0",
+ "@opentelemetry/sdk-metrics": "2.2.0",
+ "@opentelemetry/sdk-trace-base": "2.2.0",
+ "protobufjs": "^7.3.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+ "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/resources": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz",
+ "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.6.1",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz",
+ "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.0.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-logs": {
+ "version": "0.208.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz",
+ "integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api-logs": "0.208.0",
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/resources": "2.2.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.4.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+ "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-metrics": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz",
+ "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/resources": "2.2.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.9.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+ "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace-base": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz",
+ "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/resources": "2.2.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+ "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "2.2.0",
+ "@opentelemetry/semantic-conventions": "^1.29.0"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": ">=1.3.0 <1.10.0"
+ }
+ },
+ "node_modules/@opentelemetry/semantic-conventions": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
+ "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@oslojs/encoding": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
@@ -1332,6 +1579,82 @@
"win32"
]
},
+ "node_modules/@posthog/core": {
+ "version": "1.25.2",
+ "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.25.2.tgz",
+ "integrity": "sha512-h2FO7ut/BbfwpAXWpwdDHTzQgUo9ibDFEs6ZO+3cI3KPWQt5XwczK1OLAuPprcjm8T/jl0SH8jSFo5XdU4RbTg==",
+ "license": "MIT"
+ },
+ "node_modules/@posthog/types": {
+ "version": "1.369.2",
+ "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.369.2.tgz",
+ "integrity": "sha512-PJqkqPCFnnbCZslH2jHSvXlasRqvke6YAsYPhPALy4zy2hldor8A0O2wIlpAefEJ7fVz6wR5ZbRJzQP6nwujyw==",
+ "license": "MIT"
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/@rollup/plugin-yaml": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz",
@@ -2156,6 +2479,13 @@
"@types/node": "*"
}
},
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@@ -2707,6 +3037,17 @@
"integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==",
"license": "MIT"
},
+ "node_modules/core-js": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
+ "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
"node_modules/crossws": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
@@ -2949,6 +3290,15 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
+ "node_modules/dompurify": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
+ "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/domutils": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
@@ -3154,6 +3504,12 @@
}
}
},
+ "node_modules/fflate": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+ "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
+ "license": "MIT"
+ },
"node_modules/flattie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
@@ -3898,6 +4254,12 @@
"url": "https://github.com/sponsors/antfu"
}
},
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -5131,6 +5493,37 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/posthog-js": {
+ "version": "1.369.2",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.369.2.tgz",
+ "integrity": "sha512-pY+SvNvRp3C2XW80h/jwVLTgoruK15C6klo9bYYoO6DCK9EbcwS6YzjgxBHx1dIN0XBZM3KWJPmuaSimU65HQQ==",
+ "license": "SEE LICENSE IN LICENSE",
+ "dependencies": {
+ "@opentelemetry/api": "^1.9.0",
+ "@opentelemetry/api-logs": "^0.208.0",
+ "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
+ "@opentelemetry/resources": "^2.2.0",
+ "@opentelemetry/sdk-logs": "^0.208.0",
+ "@posthog/core": "1.25.2",
+ "@posthog/types": "1.369.2",
+ "core-js": "^3.38.1",
+ "dompurify": "^3.3.2",
+ "fflate": "^0.4.8",
+ "preact": "^10.28.2",
+ "query-selector-shadow-dom": "^1.0.1",
+ "web-vitals": "^5.1.0"
+ }
+ },
+ "node_modules/preact": {
+ "version": "10.29.1",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz",
+ "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ }
+ },
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@@ -5163,6 +5556,30 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/protobufjs": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz",
+ "integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/pump": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
@@ -5189,6 +5606,12 @@
],
"license": "MIT"
},
+ "node_modules/query-selector-shadow-dom": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz",
+ "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==",
+ "license": "MIT"
+ },
"node_modules/radix3": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
@@ -6761,6 +7184,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/web-vitals": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz",
+ "integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==",
+ "license": "Apache-2.0"
+ },
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
diff --git a/site/package.json b/site/package.json
index a52703b..ff1fe89 100644
--- a/site/package.json
+++ b/site/package.json
@@ -11,13 +11,14 @@
},
"dependencies": {
"@astrojs/sitemap": "^3.0.0",
- "@tailwindcss/vite": "^4.2.2",
"@iconify-json/lucide": "^1.2.102",
"@rollup/plugin-yaml": "^4.0.0",
+ "@tailwindcss/vite": "^4.2.2",
"astro": "^5.0.0",
"astro-icon": "^1.0.0",
"js-yaml": "^4.1.0",
"pagefind": "^1.0.0",
+ "posthog-js": "^1.369.2",
"tailwindcss": "^4.2.2",
"yaml": "^2.8.3"
},
diff --git a/site/src/components/SearchWidget.astro b/site/src/components/SearchWidget.astro
index 58dbdbe..4141e1d 100644
--- a/site/src/components/SearchWidget.astro
+++ b/site/src/components/SearchWidget.astro
@@ -41,5 +41,13 @@ const base = import.meta.env.BASE_URL.replace(/\/$/, '');
} else {
initPagefind();
}
+
+ // Track search queries via PostHog
+ searchEl.addEventListener('pagefind:search', (e: Event) => {
+ const query = (e as CustomEvent<{ query: string }>).detail?.query;
+ if (query) {
+ window.posthog?.capture('search_performed', { query });
+ }
+ });
}
diff --git a/site/src/components/posthog.astro b/site/src/components/posthog.astro
new file mode 100644
index 0000000..abc3330
--- /dev/null
+++ b/site/src/components/posthog.astro
@@ -0,0 +1,10 @@
+---
+// PostHog analytics snippet
+---
+
diff --git a/site/src/env.d.ts b/site/src/env.d.ts
new file mode 100644
index 0000000..03492f9
--- /dev/null
+++ b/site/src/env.d.ts
@@ -0,0 +1,10 @@
+///
+
+interface Window {
+ posthog?: {
+ capture: (event: string, properties?: Record) => void;
+ identify: (distinctId: string, properties?: Record) => void;
+ reset: () => void;
+ captureException: (error: unknown) => void;
+ };
+}
diff --git a/site/src/layouts/BaseLayout.astro b/site/src/layouts/BaseLayout.astro
index 463adf2..901b2c2 100644
--- a/site/src/layouts/BaseLayout.astro
+++ b/site/src/layouts/BaseLayout.astro
@@ -4,6 +4,7 @@ import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import StarToast from '../components/StarToast.astro';
import SEO from '../components/SEO.astro';
+import PostHog from '../components/posthog.astro';
import { SITE_ORIGIN, AUTHOR_NAME } from '../data/site';
interface Props {
@@ -84,6 +85,8 @@ const canonical = canonicalUrl ?? `${SITE_ORIGIN}${base}${Astro.url.pathname.rep
gtag('js', new Date());
gtag('config', 'G-GDF25KKVNL');
+
+
@@ -99,10 +102,17 @@ const canonical = canonicalUrl ?? `${SITE_ORIGIN}${base}${Astro.url.pathname.rep
diff --git a/site/src/scripts/pipe.ts b/site/src/scripts/pipe.ts
index 33fbbb5..14c8354 100644
--- a/site/src/scripts/pipe.ts
+++ b/site/src/scripts/pipe.ts
@@ -1,36 +1,4 @@
-// First-party ingest pipe — records copy events to Tinybird.
-// Naming deliberately avoids ad-blocker filter-list keywords
-// (track, analytics, telemetry, metrics, beacon, pixel, collect, stat, signal).
-
-// const PIPE_URL = 'https://api.eu-west-1.aws.tinybird.co/v0/events?name=';
-const PIPE_URL = 'https://tb.samber.dev/?name=';
-const PIPE_KEY = 'p.eyJ1IjogIjQ1MzY3NjRjLTNiY2MtNDU0My04M2ZjLWM0MDUxZGFhMGM5ZiIsICJpZCI6ICJmOWZjOGQ3Yi05ZGE1LTRiZjEtYjg4YS1mNGFlNTRkNTU3YWUiLCAiaG9zdCI6ICJhd3MtZXUtd2VzdC0xIn0.-zLRexgT8W2-derRM6jVCXzUkz54sMsiOy45WO6GglM';
-
-function uid(): string {
- if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
- const r = (Math.random() * 16) | 0;
- return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
- });
-}
-
-function getUserId(): string {
- try {
- let id = localStorage.getItem('apa_uid');
- if (!id) { id = uid(); localStorage.setItem('apa_uid', id); }
- return id;
- } catch { return 'anon'; }
-}
-
-function getSessionId(): string {
- try {
- let id = sessionStorage.getItem('apa_sid');
- if (!id) { id = uid(); sessionStorage.setItem('apa_sid', id); }
- return id;
- } catch { return 'anon'; }
-}
-
-function bumpSessionCount(): number {
+export function bumpSessionCount(): number {
try {
const n = parseInt(sessionStorage.getItem('apa_sc') ?? '0', 10);
sessionStorage.setItem('apa_sc', String(n + 1));
@@ -38,87 +6,10 @@ function bumpSessionCount(): number {
} catch { return 0; }
}
-function bumpLifetimeCount(): number {
+export function bumpLifetimeCount(): number {
try {
const n = parseInt(localStorage.getItem('apa_lc') ?? '0', 10);
localStorage.setItem('apa_lc', String(n + 1));
return n + 1;
} catch { return 0; }
}
-
-function getPageContext() {
- const path = location.pathname;
- let page_type: 'service' | 'home' | 'guide' | 'other' = 'other';
- if (path.includes('/rules/') && path.split('/').filter(Boolean).length >= 4) {
- page_type = 'service';
- } else if (path.split('/').filter(Boolean).length <= 1) {
- page_type = 'home';
- } else if (['alertmanager', 'blackbox-exporter', 'sleep-peacefully'].some((g) => path.includes(g))) {
- page_type = 'guide';
- }
- return {
- page_path: path,
- page_type,
- referrer: document.referrer || undefined,
- anchor_hash: location.hash || undefined,
- };
-}
-
-// Eagerly init IDs on module load so they exist before first copy
-getUserId();
-getSessionId();
-
-function buildPayload(name: string, data: Record): object {
- return {
- timestamp: new Date().toISOString(),
- transaction_id: uid(),
- name: "awesome_prometheus_alerts_" + name,
- user_id: getUserId(),
- session_id: getSessionId(),
- ...data,
- ...getPageContext(),
- language: navigator.language,
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
- viewport_w: window.innerWidth,
- viewport_h: window.innerHeight,
- color_scheme: document.documentElement.classList.contains('dark') ? 'dark' : 'light',
- user_agent: navigator.userAgent,
- is_bot: /bot|crawl|spider/i.test(navigator.userAgent),
- };
-}
-
-export function record(name: string, data: Record): void {
- const payload = buildPayload(name, data);
- fetch(PIPE_URL + "awesome_prometheus_alerts_" + name, {
- method: 'POST',
- body: JSON.stringify(payload),
- headers: { Authorization: `Bearer ${PIPE_KEY}` },
- keepalive: true,
- }).catch(console.error);
-}
-
-export function recordCopy(name: string, data: Record): void {
- record(name, {
- session_copy_count: bumpSessionCount(),
- lifetime_copy_count: bumpLifetimeCount(),
- ...data,
- });
-}
-
-export async function recordAndWait(name: string, data: Record): Promise {
- const payload = buildPayload(name, data);
- const ctrl = new AbortController();
- const timer = setTimeout(() => ctrl.abort(), 1500);
- try {
- await fetch(PIPE_URL + "awesome_prometheus_alerts_" + name, {
- method: 'POST',
- body: JSON.stringify(payload),
- headers: { Authorization: `Bearer ${PIPE_KEY}` },
- signal: ctrl.signal,
- });
- } catch {
- // swallow — a failed event must never break sponsor navigation
- } finally {
- clearTimeout(timer);
- }
-}
diff --git a/site/src/scripts/sponsor.ts b/site/src/scripts/sponsor.ts
index 297307e..337a0ca 100644
--- a/site/src/scripts/sponsor.ts
+++ b/site/src/scripts/sponsor.ts
@@ -1,8 +1,6 @@
-import { record, recordAndWait } from './pipe';
-
export function initSponsorClickTracking(): void {
document.querySelectorAll('a[data-sponsor-name]').forEach((a) => {
- a.addEventListener('click', async (e) => {
+ a.addEventListener('click', (e) => {
const me = e as MouseEvent;
const href = a.href;
const sponsorName = a.dataset.sponsorName!;
@@ -11,22 +9,19 @@ export function initSponsorClickTracking(): void {
// Modifier / non-primary clicks: track fire-and-forget, let browser handle navigation
if (me.button !== 0 || me.metaKey || me.ctrlKey || me.shiftKey) {
- record('sponsor_click', eventData);
+ window.posthog?.capture('sponsor_clicked', eventData);
return;
}
// Plain left-click: block navigation until event is recorded
e.preventDefault();
+ window.posthog?.capture('sponsor_clicked', eventData);
// Open blank tab now (inside user gesture) to avoid popup-blocker after await
const w = a.target === '_blank' ? window.open('', '_blank') : null;
- try {
- await recordAndWait('sponsor_click', eventData);
- } finally {
- if (w) {
- w.location.href = href;
- } else {
- window.open(href, '_blank') ?? (window.location.href = href);
- }
+ if (w) {
+ w.location.href = href;
+ } else {
+ window.open(href, '_blank') ?? (window.location.href = href);
}
});
});