@tdhftw/emdash-plugin-umami 0.1.2 → 0.1.3
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/CHANGELOG.md +7 -0
- package/README.md +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +34 -3
- package/package.json +1 -1
- package/src/admin/SettingsView.tsx +19 -5
- package/src/shared/api-types.ts +1 -0
- package/src/shared/constants.ts +1 -1
- package/src/shared/settings-types.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.3 (2026-10-28)
|
|
4
|
+
|
|
5
|
+
- New tracker setting for sites that use Astro's `<ClientRouter />`: every page change, including
|
|
6
|
+
back and forward, is counted once. Without it, Umami misses back and forward navigation, and a
|
|
7
|
+
tag with `data-astro-rerun` counts each link click twice. Needs Umami 3.2 or later.
|
|
8
|
+
- README: the setting above, and how to switch over from a tracker tag in the theme.
|
|
9
|
+
|
|
3
10
|
## 0.1.2 (2026-10-28)
|
|
4
11
|
|
|
5
12
|
First release on npm. No code changes since 0.1.1.
|
package/README.md
CHANGED
|
@@ -74,6 +74,14 @@ every public page:
|
|
|
74
74
|
templates do.
|
|
75
75
|
- **Avoid double counting.** Remove any Umami script the theme already includes, or every visit
|
|
76
76
|
is counted twice.
|
|
77
|
+
- **Astro's `<ClientRouter />`.** If your layout uses it, also turn on **The site uses Astro's
|
|
78
|
+
`<ClientRouter />`**. Pages then change without a full load: Umami on its own misses back and
|
|
79
|
+
forward, and a tracker tag with `data-astro-rerun` counts every link click twice. With the
|
|
80
|
+
setting on, the plugin turns off Umami's automatic pageviews (`data-auto-pageview="false"`, so
|
|
81
|
+
Umami 3.2 or later) and sends one pageview per page change from a small inline script. Click
|
|
82
|
+
events (`data-umami-event`) keep working.
|
|
83
|
+
- **Switching over from a theme tag.** Remove the theme's tag and turn the plugin's tracker on
|
|
84
|
+
as close together as you can: a gap loses visits, an overlap counts them twice.
|
|
77
85
|
- **Custom script URL.** Set one if you serve a renamed tracker or proxy it.
|
|
78
86
|
- **Cached pages.** If your site caches pages, cached copies change over only when their cache
|
|
79
87
|
expires.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from "astro/zod";
|
|
|
4
4
|
/** Plugin id. One URL segment: routes mount at /_emdash/api/plugins/umami/<route>. */
|
|
5
5
|
export declare const PLUGIN_ID = "umami";
|
|
6
6
|
/** Keep in sync with package.json; test/plugin.test.ts enforces it. */
|
|
7
|
-
export declare const PLUGIN_VERSION = "0.1.
|
|
7
|
+
export declare const PLUGIN_VERSION = "0.1.3";
|
|
8
8
|
type Hosting = "self-hosted" | "cloud";
|
|
9
9
|
type AuthMode = "apiKey" | "password";
|
|
10
10
|
type CloudRegion = "default" | "us" | "eu";
|
|
@@ -79,6 +79,7 @@ interface UmamiSettingsValues {
|
|
|
79
79
|
username: string | null;
|
|
80
80
|
websiteId: string | null;
|
|
81
81
|
injectScript: boolean;
|
|
82
|
+
clientRouter: boolean;
|
|
82
83
|
scriptUrl: string | null;
|
|
83
84
|
}
|
|
84
85
|
//#endregion
|
|
@@ -125,6 +126,7 @@ interface SaveSettingsInput {
|
|
|
125
126
|
password?: string | null;
|
|
126
127
|
websiteId?: string | null;
|
|
127
128
|
injectScript?: boolean;
|
|
129
|
+
clientRouter?: boolean;
|
|
128
130
|
scriptUrl?: string | null;
|
|
129
131
|
}
|
|
130
132
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ async function cached(kv, key, maxAgeMs, load, now = Date.now()) {
|
|
|
34
34
|
/** Plugin id. One URL segment: routes mount at /_emdash/api/plugins/umami/<route>. */
|
|
35
35
|
const PLUGIN_ID = "umami";
|
|
36
36
|
/** Keep in sync with package.json; test/plugin.test.ts enforces it. */
|
|
37
|
-
const PLUGIN_VERSION = "0.1.
|
|
37
|
+
const PLUGIN_VERSION = "0.1.3";
|
|
38
38
|
const PACKAGE_NAME = "@tdhftw/emdash-plugin-umami";
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region src/settings.ts
|
|
@@ -113,6 +113,12 @@ const settingsSchema = {
|
|
|
113
113
|
label: "Add the Umami tracker to public pages",
|
|
114
114
|
default: false
|
|
115
115
|
},
|
|
116
|
+
clientRouter: {
|
|
117
|
+
type: "boolean",
|
|
118
|
+
label: "The site uses Astro's <ClientRouter /> (view transitions)",
|
|
119
|
+
description: "Counts every page change, including back and forward, once.",
|
|
120
|
+
default: false
|
|
121
|
+
},
|
|
116
122
|
scriptUrl: {
|
|
117
123
|
type: "url",
|
|
118
124
|
label: "Custom tracker script URL",
|
|
@@ -160,6 +166,7 @@ async function readSettings(settings) {
|
|
|
160
166
|
password: typeof raw.password === "string" && raw.password.length > 0 ? raw.password : null,
|
|
161
167
|
websiteId: str(raw.websiteId),
|
|
162
168
|
injectScript: raw.injectScript === true,
|
|
169
|
+
clientRouter: raw.clientRouter === true,
|
|
163
170
|
scriptUrl: normalizeScriptUrl(str(raw.scriptUrl))
|
|
164
171
|
};
|
|
165
172
|
}
|
|
@@ -610,6 +617,7 @@ const saveSettingsInput = z.object({
|
|
|
610
617
|
password: secretInput,
|
|
611
618
|
websiteId: optionalText,
|
|
612
619
|
injectScript: z.boolean().optional(),
|
|
620
|
+
clientRouter: z.boolean().optional(),
|
|
613
621
|
scriptUrl: optionalUrl
|
|
614
622
|
});
|
|
615
623
|
async function saveSettings(ctx) {
|
|
@@ -629,6 +637,7 @@ async function saveSettings(ctx) {
|
|
|
629
637
|
put("password", input.password);
|
|
630
638
|
put("websiteId", input.websiteId);
|
|
631
639
|
put("injectScript", input.injectScript);
|
|
640
|
+
put("clientRouter", input.clientRouter);
|
|
632
641
|
put("scriptUrl", input.scriptUrl);
|
|
633
642
|
await Promise.all(writes);
|
|
634
643
|
if (input.baseUrl !== void 0 || input.username !== void 0 || input.password !== void 0 || input.hosting !== void 0) await ctx.kv.delete(TOKEN_KEY);
|
|
@@ -690,7 +699,16 @@ function defaultScriptUrl(hosting, baseUrl, scriptUrl) {
|
|
|
690
699
|
//#endregion
|
|
691
700
|
//#region src/tracker.ts
|
|
692
701
|
/**
|
|
693
|
-
*
|
|
702
|
+
* Pageviews for sites using Astro's <ClientRouter />. Umami's own tracking hooks pushState only,
|
|
703
|
+
* so back and forward are missed, and a re-run tracker tag counts forward navigations twice.
|
|
704
|
+
* With `data-auto-pageview="false"` this sends one pageview per `astro:page-load` instead,
|
|
705
|
+
* passing the URL explicitly because Umami's stored URL is stale after a back navigation. On a
|
|
706
|
+
* page without <ClientRouter /> (no `astro-view-transitions-enabled` meta) it tracks once on
|
|
707
|
+
* DOMContentLoaded, so a wrong setting never stops tracking.
|
|
708
|
+
*/
|
|
709
|
+
const CLIENT_ROUTER_SCRIPT = `(()=>{let prev;const send=()=>{const u=window.umami;if(!u)return false;const url=location.href;u.track((p)=>({...p,url,title:document.title,...(prev?{referrer:prev}:{})}));prev=location.pathname+location.search;return true};const track=()=>{let n=0;const t=()=>{send()||++n>100||setTimeout(t,50)};t()};document.addEventListener("astro:page-load",track);document.addEventListener("DOMContentLoaded",()=>{document.querySelector('meta[name="astro-view-transitions-enabled"]')||track()})})();`;
|
|
710
|
+
/**
|
|
711
|
+
* The Umami tracker as page fragments, or null when tracking is off or incomplete.
|
|
694
712
|
* EmDash HTML-escapes attribute values and drops on* attributes when rendering.
|
|
695
713
|
*/
|
|
696
714
|
async function trackerFragment(event, settings) {
|
|
@@ -699,7 +717,7 @@ async function trackerFragment(event, settings) {
|
|
|
699
717
|
const s = await readSettings(settings);
|
|
700
718
|
const src = defaultScriptUrl(s.hosting, s.baseUrl, s.scriptUrl);
|
|
701
719
|
if (!src || !s.websiteId || !normalizeBaseUrl(src)) return null;
|
|
702
|
-
|
|
720
|
+
const tracker = {
|
|
703
721
|
kind: "external-script",
|
|
704
722
|
placement: "head",
|
|
705
723
|
src,
|
|
@@ -707,6 +725,19 @@ async function trackerFragment(event, settings) {
|
|
|
707
725
|
attributes: { "data-website-id": s.websiteId },
|
|
708
726
|
key: "umami-tracker"
|
|
709
727
|
};
|
|
728
|
+
if (!s.clientRouter) return tracker;
|
|
729
|
+
return [{
|
|
730
|
+
...tracker,
|
|
731
|
+
attributes: {
|
|
732
|
+
...tracker.attributes,
|
|
733
|
+
"data-auto-pageview": "false"
|
|
734
|
+
}
|
|
735
|
+
}, {
|
|
736
|
+
kind: "inline-script",
|
|
737
|
+
placement: "head",
|
|
738
|
+
code: CLIENT_ROUTER_SCRIPT,
|
|
739
|
+
key: "umami-client-router"
|
|
740
|
+
}];
|
|
710
741
|
}
|
|
711
742
|
//#endregion
|
|
712
743
|
//#region src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tdhftw/emdash-plugin-umami",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Umami analytics inside the EmDash CMS admin: an Analytics page, a dashboard widget and optional tracker injection, for self-hosted Umami",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,6 +17,7 @@ interface Draft {
|
|
|
17
17
|
password: string;
|
|
18
18
|
websiteId: string;
|
|
19
19
|
injectScript: boolean;
|
|
20
|
+
clientRouter: boolean;
|
|
20
21
|
scriptUrl: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -32,6 +33,7 @@ function draftFrom(s: Settings): Draft {
|
|
|
32
33
|
password: "",
|
|
33
34
|
websiteId: v.websiteId ?? "",
|
|
34
35
|
injectScript: v.injectScript,
|
|
36
|
+
clientRouter: v.clientRouter,
|
|
35
37
|
scriptUrl: v.scriptUrl ?? "",
|
|
36
38
|
};
|
|
37
39
|
}
|
|
@@ -43,6 +45,7 @@ function toInput(d: Draft): SaveSettingsInput {
|
|
|
43
45
|
authMode: d.hosting === "cloud" ? "apiKey" : d.authMode,
|
|
44
46
|
websiteId: d.websiteId || null,
|
|
45
47
|
injectScript: d.injectScript,
|
|
48
|
+
clientRouter: d.clientRouter,
|
|
46
49
|
scriptUrl: d.scriptUrl || null,
|
|
47
50
|
};
|
|
48
51
|
if (d.hosting === "self-hosted") input.baseUrl = d.baseUrl || null;
|
|
@@ -268,11 +271,22 @@ export function SettingsView({ onSaved }: { onSaved?: () => void }) {
|
|
|
268
271
|
If your site caches pages, cached copies change over when their cache expires, not the moment you save.
|
|
269
272
|
</p>
|
|
270
273
|
{draft.injectScript ? (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
274
|
+
<>
|
|
275
|
+
<Banner
|
|
276
|
+
variant="alert"
|
|
277
|
+
title="Remove any tracker already in your theme"
|
|
278
|
+
description="If the theme also includes the Umami script, every visit is counted twice. The theme must render EmDashHead."
|
|
279
|
+
/>
|
|
280
|
+
<Switch
|
|
281
|
+
label="The site uses Astro's <ClientRouter /> (view transitions)"
|
|
282
|
+
checked={draft.clientRouter}
|
|
283
|
+
onCheckedChange={(checked) => set("clientRouter", checked)}
|
|
284
|
+
/>
|
|
285
|
+
<p className="text-sm text-kumo-subtle">
|
|
286
|
+
Turn this on if your layout includes <code><ClientRouter /></code>. Pages then change without a full
|
|
287
|
+
load, and this counts each one once, including back and forward. Needs Umami 3.2 or later.
|
|
288
|
+
</p>
|
|
289
|
+
</>
|
|
276
290
|
) : null}
|
|
277
291
|
<p className="text-sm text-kumo-subtle">
|
|
278
292
|
Tracker script: <code>{tracker ?? "set the Umami URL first"}</code>{" "}
|
package/src/shared/api-types.ts
CHANGED
package/src/shared/constants.ts
CHANGED