@ampless/plugin-analytics-ga4 0.2.0-alpha.9 → 0.2.0-beta.36
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.ja.md +49 -7
- package/README.md +49 -7
- package/dist/index.d.ts +7 -0
- package/dist/index.js +67 -1
- package/package.json +2 -2
package/README.ja.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
[ampless](https://github.com/heavymoons/ampless) 向け Google Analytics 4 プラグイン。
|
|
7
7
|
|
|
8
|
-
> **プレリリース /
|
|
8
|
+
> **プレリリース / ベータ版。** v1.0 までは破壊的変更が入る可能性があります。
|
|
9
9
|
|
|
10
|
-
descriptor ベースのプラグイン head 注入 API ([
|
|
10
|
+
descriptor ベースのプラグイン head 注入 API ([plugin architecture](https://github.com/heavymoons/ampless/blob/main/docs/architecture/08-plugin-architecture.ja.md)) を使って、公開ページの `<head>` に GA4 標準の 2 つのスニペットを挿入します。
|
|
11
11
|
|
|
12
12
|
1. 非同期 `gtag.js` ローダー (`https://www.googletagmanager.com/gtag/js?id=...`)
|
|
13
13
|
2. インライン `gtag('config', '<measurementId>')` のブートストラップ
|
|
@@ -17,12 +17,12 @@ descriptor ベースのプラグイン head 注入 API ([docs/tmp/plugin-extensi
|
|
|
17
17
|
## インストール
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install @ampless/plugin-analytics-ga4@
|
|
20
|
+
npm install @ampless/plugin-analytics-ga4@beta
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## 設定
|
|
24
24
|
|
|
25
|
-
`cms.config.ts`
|
|
25
|
+
`cms.config.ts` に登録し、実際の値は `/admin/plugins` から編集します。constructor の `measurementId` は bootstrap と後方互換用の optional fallback です:
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
import { defineConfig } from 'ampless'
|
|
@@ -38,8 +38,51 @@ export default defineConfig({
|
|
|
38
38
|
|
|
39
39
|
| オプション | デフォルト | 備考 |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| `measurementId` |
|
|
41
|
+
| `measurementId` | `''` | Optional fallback の GA4 計測 ID。例: `G-XXXXXXXX`。runtime では admin 管理の値が優先されます。空文字 `''` を渡すとプラグインを残したまま注入だけ無効化できる。 |
|
|
42
42
|
| `instanceId` | `'analytics-ga4'` | 生成される `<script>` 要素 id の namespace。同じサイトに複数の GA4 プロパティを入れる場合に分ける。 |
|
|
43
|
+
| `consentCategory` | `''` | オプションの同意カテゴリ slug。設定すると `window.amplessConsent.has(<この値>)` が true になるまで GA4 loader を発火しない。詳細は[同意ゲーティング](#同意ゲーティング)を参照。 |
|
|
44
|
+
|
|
45
|
+
## 同意ゲーティング
|
|
46
|
+
|
|
47
|
+
デフォルトでは GA4 loader は訪問者の同意有無にかかわらずページロードごとに発火します。訪問者が同意を付与するまで発火を遅延させるには、`consentCategory` に同意カテゴリ slug を設定し、同じ `cms.config.ts` に `@ampless/plugin-cookie-consent` を登録します:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { defineConfig } from 'ampless'
|
|
51
|
+
import cookieConsent from '@ampless/plugin-cookie-consent'
|
|
52
|
+
import analyticsGa4Plugin from '@ampless/plugin-analytics-ga4'
|
|
53
|
+
|
|
54
|
+
export default defineConfig({
|
|
55
|
+
plugins: [
|
|
56
|
+
// cookie-consent は analytics plugin より前に置く
|
|
57
|
+
cookieConsent({
|
|
58
|
+
categories: [{ id: 'analytics', label: 'アナリティクス', defaultEnabled: false }],
|
|
59
|
+
}),
|
|
60
|
+
analyticsGa4Plugin({
|
|
61
|
+
measurementId: 'G-XXXXXXXX',
|
|
62
|
+
consentCategory: 'analytics',
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`consentCategory` を設定すると plugin は **gated mode** に切り替わります: 標準 GA4 の 2 descriptor の代わりに単一のインライン script を emit し、以下の動作をします:
|
|
69
|
+
|
|
70
|
+
1. 直ちに `window.amplessConsent.has('analytics')` を確認(前回の訪問で同意が付与されて `localStorage` から復元されているケースに対応)。
|
|
71
|
+
2. false なら `window.amplessConsent.on('analytics', ...)` で同意イベントを購読して待機。
|
|
72
|
+
3. analytics plugin が cookie-consent plugin の API install より先にロードされたケースに備え、`ampless:consent-ready` イベントも購読。
|
|
73
|
+
|
|
74
|
+
**Fail-closed 契約:** `consentCategory` を設定したまま `@ampless/plugin-cookie-consent` を登録しなかった場合、`window.amplessConsent` は install されません。GA4 は**永久に発火しない**まま 5 秒後に `console.warn` が出力されます:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
[ampless:analytics-ga4] consentCategory is set but window.amplessConsent never installed.
|
|
78
|
+
Did you forget to register @ampless/plugin-cookie-consent?
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
この warning は本番環境でも発火します。設定ミスを早期に検出するためのものであり、抑制する仕組みはありません。
|
|
82
|
+
|
|
83
|
+
**プラグインの順序:** `plugins` 配列内で `@ampless/plugin-cookie-consent` を analytics plugin より前に置いてください。runtime はプラグインを順番に処理するため、cookie-consent を先に置くことで analytics の gating ロジック実行時に `window.amplessConsent` が確実に install されます。
|
|
84
|
+
|
|
85
|
+
Consent Convention および `window.amplessConsent` API の詳細は [docs/architecture/08-plugin-architecture.md](https://github.com/heavymoons/ampless/blob/main/docs/architecture/08-plugin-architecture.md) を参照してください。
|
|
43
86
|
|
|
44
87
|
## measurement ID の取得
|
|
45
88
|
|
|
@@ -51,7 +94,7 @@ export default defineConfig({
|
|
|
51
94
|
|
|
52
95
|
## 複数インスタンス
|
|
53
96
|
|
|
54
|
-
|
|
97
|
+
プラグイン契約は `instanceId` を分けた複数インスタンスをサポートします。GA4 プロパティごとに 1 インスタンスを使います:
|
|
55
98
|
|
|
56
99
|
```ts
|
|
57
100
|
plugins: [
|
|
@@ -67,5 +110,4 @@ plugins: [
|
|
|
67
110
|
## まだやらないこと
|
|
68
111
|
|
|
69
112
|
- **CSP nonce 連携** — Phase 1 ではインライン script に `nonce` を付与しません。ampless サイトに CSP enforcement を入れる段で別途 RFP として middleware → SSR → descriptor の通り道を設計します。
|
|
70
|
-
- **管理画面からの設定** — Phase 1 では `cms.config.ts` 直書きのみ。admin 管理は Phase 2。
|
|
71
113
|
- **ページ単位のイベント送信** — カスタムイベントはページコード側で `window.gtag('event', ...)` を呼んでください。
|
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
Google Analytics 4 plugin for [ampless](https://github.com/heavymoons/ampless).
|
|
7
7
|
|
|
8
|
-
> **Pre-release /
|
|
8
|
+
> **Pre-release / beta.** Breaking changes are still possible before v1.0.
|
|
9
9
|
|
|
10
|
-
Drops the two standard GA4 snippets into every public page's `<head>` through the descriptor-based plugin head injection API ([
|
|
10
|
+
Drops the two standard GA4 snippets into every public page's `<head>` through the descriptor-based plugin head injection API ([plugin architecture](https://github.com/heavymoons/ampless/blob/main/docs/architecture/08-plugin-architecture.md)):
|
|
11
11
|
|
|
12
12
|
1. The async `gtag.js` loader (`https://www.googletagmanager.com/gtag/js?id=...`).
|
|
13
13
|
2. An inline `gtag('config', '<measurementId>')` bootstrap.
|
|
@@ -17,12 +17,12 @@ No AWS data permissions are required — everything runs at request time inside
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install @ampless/plugin-analytics-ga4@
|
|
20
|
+
npm install @ampless/plugin-analytics-ga4@beta
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Configure
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Register the plugin in `cms.config.ts`, then edit the live values from `/admin/plugins`. The constructor `measurementId` is an optional fallback for bootstrap and backwards compatibility:
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
import { defineConfig } from 'ampless'
|
|
@@ -38,8 +38,51 @@ export default defineConfig({
|
|
|
38
38
|
|
|
39
39
|
| Option | Default | Notes |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| `measurementId` |
|
|
41
|
+
| `measurementId` | `''` | Optional fallback GA4 measurement ID, e.g. `G-XXXXXXXX`. The admin-managed value wins at runtime. Set to `''` to disable the plugin without removing it from `cms.config.ts`. |
|
|
42
42
|
| `instanceId` | `'analytics-ga4'` | Namespace used for the script element ids. Set distinct values when registering multiple GA4 properties on the same site. |
|
|
43
|
+
| `consentCategory` | `''` | Optional consent category slug. When set, the GA4 loader fires only after `window.amplessConsent.has(<this>)` returns true. See [Consent gating](#consent-gating) below. |
|
|
44
|
+
|
|
45
|
+
## Consent gating
|
|
46
|
+
|
|
47
|
+
By default the GA4 loader fires on every page load regardless of visitor consent. To make it fire only after the visitor has granted consent, set `consentCategory` to a category slug and register `@ampless/plugin-cookie-consent` in the same `cms.config.ts`:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { defineConfig } from 'ampless'
|
|
51
|
+
import cookieConsent from '@ampless/plugin-cookie-consent'
|
|
52
|
+
import analyticsGa4Plugin from '@ampless/plugin-analytics-ga4'
|
|
53
|
+
|
|
54
|
+
export default defineConfig({
|
|
55
|
+
plugins: [
|
|
56
|
+
// cookie-consent must appear before the analytics plugin
|
|
57
|
+
cookieConsent({
|
|
58
|
+
categories: [{ id: 'analytics', label: 'Analytics', defaultEnabled: false }],
|
|
59
|
+
}),
|
|
60
|
+
analyticsGa4Plugin({
|
|
61
|
+
measurementId: 'G-XXXXXXXX',
|
|
62
|
+
consentCategory: 'analytics',
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
When `consentCategory` is set the plugin switches to **gated mode**: instead of the two standard GA4 descriptors, it emits a single inline script that:
|
|
69
|
+
|
|
70
|
+
1. Checks `window.amplessConsent.has('analytics')` immediately (covers the case where consent was granted in a previous visit and restored from `localStorage`).
|
|
71
|
+
2. Otherwise subscribes to the consent event via `window.amplessConsent.on('analytics', ...)` and waits.
|
|
72
|
+
3. Also listens for `ampless:consent-ready` in case the analytics plugin loads before the cookie-consent plugin has installed its global API.
|
|
73
|
+
|
|
74
|
+
**Fail-closed contract:** if `consentCategory` is set but `@ampless/plugin-cookie-consent` is never registered, `window.amplessConsent` is never installed. GA4 will **never fire**, and after 5 seconds a `console.warn` appears:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
[ampless:analytics-ga4] consentCategory is set but window.amplessConsent never installed.
|
|
78
|
+
Did you forget to register @ampless/plugin-cookie-consent?
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This warning fires in production too — it is intended to help operators catch misconfiguration quickly. There is no mechanism to suppress it.
|
|
82
|
+
|
|
83
|
+
**Plugin ordering:** register `@ampless/plugin-cookie-consent` before the analytics plugin in the `plugins` array. The runtime processes plugins in order; placing cookie-consent first ensures `window.amplessConsent` is installed before the analytics gating logic runs.
|
|
84
|
+
|
|
85
|
+
For full details on the Consent Convention and the `window.amplessConsent` API see [docs/architecture/08-plugin-architecture.md](https://github.com/heavymoons/ampless/blob/main/docs/architecture/08-plugin-architecture.md).
|
|
43
86
|
|
|
44
87
|
## Getting your measurement ID
|
|
45
88
|
|
|
@@ -51,7 +94,7 @@ export default defineConfig({
|
|
|
51
94
|
|
|
52
95
|
## Multiple instances
|
|
53
96
|
|
|
54
|
-
|
|
97
|
+
The plugin contract supports multiple instances through distinct `instanceId` values. Use one instance per GA4 property:
|
|
55
98
|
|
|
56
99
|
```ts
|
|
57
100
|
plugins: [
|
|
@@ -67,5 +110,4 @@ plugins: [
|
|
|
67
110
|
## What it does not do (yet)
|
|
68
111
|
|
|
69
112
|
- **CSP nonce propagation** — Phase 1 emits the inline script without a `nonce`. ampless sites do not enforce a CSP today; once that lands, a dedicated RFP will wire `nonce` end-to-end (middleware → SSR → descriptor).
|
|
70
|
-
- **Admin UI settings** — Phase 1 reads the measurement ID from `cms.config.ts` only. Admin-managed settings come in Phase 2.
|
|
71
113
|
- **Per-route event tagging** — Send any custom events from your own page code (`window.gtag('event', ...)`).
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,13 @@ interface AnalyticsGa4Options {
|
|
|
22
22
|
* site).
|
|
23
23
|
*/
|
|
24
24
|
instanceId?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Optional consent category. When set, the analytics loader does
|
|
27
|
+
* not run until window.amplessConsent.has(consentCategory) returns
|
|
28
|
+
* true. See @ampless/plugin-cookie-consent and the Consent
|
|
29
|
+
* Convention in docs/architecture/08-plugin-architecture.md.
|
|
30
|
+
*/
|
|
31
|
+
consentCategory?: string;
|
|
25
32
|
}
|
|
26
33
|
/**
|
|
27
34
|
* Factory for the GA4 plugin. Returns a plugin manifest that emits
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { definePlugin } from "ampless";
|
|
3
3
|
function analyticsGa4Plugin(options = {}) {
|
|
4
|
-
const {
|
|
4
|
+
const {
|
|
5
|
+
measurementId = "",
|
|
6
|
+
instanceId = "analytics-ga4",
|
|
7
|
+
consentCategory = ""
|
|
8
|
+
} = options;
|
|
5
9
|
return definePlugin({
|
|
6
10
|
name: "analytics-ga4",
|
|
7
11
|
packageName: "@ampless/plugin-analytics-ga4",
|
|
@@ -30,12 +34,74 @@ function analyticsGa4Plugin(options = {}) {
|
|
|
30
34
|
pattern: "^$|^G-[A-Z0-9]+$",
|
|
31
35
|
placeholder: "G-XXXXXXXX",
|
|
32
36
|
default: measurementId
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
key: "consentCategory",
|
|
41
|
+
label: { en: "Consent category", ja: "\u540C\u610F\u30AB\u30C6\u30B4\u30EA" },
|
|
42
|
+
description: {
|
|
43
|
+
en: "Optional. When set, the analytics loader fires only after `window.amplessConsent.has(<this>)` returns true. Requires `@ampless/plugin-cookie-consent` to also be registered \u2014 fail-closed otherwise (no tracking, console warning after 5s). See the Consent Convention in the plugin author guide.",
|
|
44
|
+
ja: "\u30AA\u30D7\u30B7\u30E7\u30F3\u3002\u8A2D\u5B9A\u3059\u308B\u3068 `window.amplessConsent.has(<value>)` \u304C true \u306B\u306A\u308B\u307E\u3067 analytics loader \u3092\u767A\u706B\u3057\u307E\u305B\u3093\u3002`@ampless/plugin-cookie-consent` \u306E\u4F75\u7528\u304C\u5FC5\u9808 \u2014 \u672A\u5C0E\u5165\u6642\u306F\u5B8C\u5168\u306B\u767A\u706B\u305B\u305A 5 \u79D2\u5F8C\u306B console warning (fail-closed)\u3002\u8A73\u7D30\u306F plugin author guide \u306E Consent Convention \u7BC0\u3092\u53C2\u7167\u3002"
|
|
45
|
+
},
|
|
46
|
+
pattern: "^$|^[a-z][a-z0-9_-]*$",
|
|
47
|
+
maxLength: 32,
|
|
48
|
+
placeholder: "analytics",
|
|
49
|
+
default: consentCategory
|
|
33
50
|
}
|
|
34
51
|
]
|
|
35
52
|
},
|
|
36
53
|
publicHead(ctx) {
|
|
37
54
|
const id = (ctx.setting("measurementId") ?? "").trim();
|
|
38
55
|
if (!id) return [];
|
|
56
|
+
const category = (ctx.setting("consentCategory") ?? "").trim();
|
|
57
|
+
if (category) {
|
|
58
|
+
const loaderSrc = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(id)}`;
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
type: "inlineScript",
|
|
62
|
+
id: `ga4-gated-${instanceId}`,
|
|
63
|
+
strategy: "afterInteractive",
|
|
64
|
+
body: [
|
|
65
|
+
"(function () {",
|
|
66
|
+
" var initialized = false",
|
|
67
|
+
" function init() {",
|
|
68
|
+
" if (initialized) return",
|
|
69
|
+
" initialized = true",
|
|
70
|
+
" var s = document.createElement('script')",
|
|
71
|
+
` s.src = ${JSON.stringify(loaderSrc)}`,
|
|
72
|
+
" s.async = true",
|
|
73
|
+
" document.head.appendChild(s)",
|
|
74
|
+
" // Bind dataLayer + gtag to `window` so later page code",
|
|
75
|
+
" // (e.g. window.gtag('event', ...) in custom handlers)",
|
|
76
|
+
" // can find them. The non-gated snippet relies on a",
|
|
77
|
+
" // top-level `function gtag(){}` hoisting to global,",
|
|
78
|
+
" // which inside this IIFE would only be a local \u2014 so",
|
|
79
|
+
" // we assign explicitly and preserve any gtag that a",
|
|
80
|
+
" // user script may have installed first.",
|
|
81
|
+
" window.dataLayer = window.dataLayer || []",
|
|
82
|
+
" window.gtag = window.gtag || function () { window.dataLayer.push(arguments) }",
|
|
83
|
+
" window.gtag('js', new Date())",
|
|
84
|
+
` window.gtag('config', ${JSON.stringify(id)})`,
|
|
85
|
+
" }",
|
|
86
|
+
" function wait() {",
|
|
87
|
+
` if (window.amplessConsent.has(${JSON.stringify(category)})) init()`,
|
|
88
|
+
` else window.amplessConsent.on(${JSON.stringify(category)}, init)`,
|
|
89
|
+
" }",
|
|
90
|
+
" if (window.amplessConsent) {",
|
|
91
|
+
" wait()",
|
|
92
|
+
" } else {",
|
|
93
|
+
" window.addEventListener('ampless:consent-ready', wait, { once: true })",
|
|
94
|
+
" setTimeout(function () {",
|
|
95
|
+
" if (!window.amplessConsent) {",
|
|
96
|
+
" console.warn('[ampless:analytics-ga4] consentCategory is set but window.amplessConsent never installed. Did you forget to register @ampless/plugin-cookie-consent?')",
|
|
97
|
+
" }",
|
|
98
|
+
" }, 5000)",
|
|
99
|
+
" }",
|
|
100
|
+
"})()"
|
|
101
|
+
].join("\n")
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
}
|
|
39
105
|
return [
|
|
40
106
|
{
|
|
41
107
|
type: "script",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampless/plugin-analytics-ga4",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-beta.36",
|
|
4
4
|
"description": "Google Analytics 4 plugin for ampless",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"homepage": "https://github.com/heavymoons/ampless/tree/main/packages/plugin-analytics-ga4#readme",
|
|
27
27
|
"bugs": "https://github.com/heavymoons/ampless/issues",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"ampless": "1.0.0-
|
|
29
|
+
"ampless": "1.0.0-beta.52"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
32
|
"ampless",
|