fg-devkit 1.17.3 → 1.19.2

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.
@@ -128,6 +128,30 @@ const { data, isLoading, error } = useGetDetails(id)
128
128
  - Prefer `props: true` and typed route params in components instead of reading raw params everywhere.
129
129
  - Prefer named-route navigation for internal flows (`{ name, params, query }`) and reserve raw path pushes for simple/static redirects.
130
130
  - Preserve existing query params intentionally when updating filters, pagination, or tab state.
131
+ - Returning to a view whose filters live in the URL: call `usePreviousLocation` from `fds-vue-core` in setup. `router.push('/path')` drops that query. Browser back keeps it.
132
+ - Use `hrefFor` on `router-link` and `FdsTabsItem`, and `goTo` on back buttons, only when the target is that same view. Leave other navigations unchanged.
133
+ - `hrefFor(path)` reuses the previous history entry, including `_search`, `_page`, and filters, when its pathname matches. Otherwise it returns the path only.
134
+ - `goTo(fallback, matchPaths?)` calls `router.back()` when the previous entry matches, so scroll is kept. Otherwise it pushes the fallback path.
135
+ - Pass `matchPaths` when several paths count as the same place. Without it, only the fallback path is compared.
136
+
137
+ ```ts
138
+ import { usePreviousLocation } from 'fds-vue-core'
139
+
140
+ const { hrefFor, goTo } = usePreviousLocation()
141
+
142
+ const goBack = () => {
143
+ goTo('/services/J4', ['/services/J4', '/services'])
144
+ }
145
+ ```
146
+
147
+ ```vue
148
+ <router-link :to="hrefFor(breadcrumb.path)">
149
+ {{ breadcrumb.name }}
150
+ </router-link>
151
+ <FdsTabsItem :to="hrefFor(tab.path)" />
152
+ <FdsButtonMinor :text="t('common.back')" icon="left" @click="goBack" />
153
+ ```
154
+
131
155
  - Lazy-load route components with dynamic imports to keep initial bundles smaller.
132
156
  - Name organisation route params by domain meaning, not generic `id`:
133
157
  - top org: `:topClientNumber`
package/README.md CHANGED
@@ -183,6 +183,31 @@ export default {
183
183
  }
184
184
  ```
185
185
 
186
+ ## Piwik PRO custom dimensions
187
+
188
+ Compare the local slot map with a Piwik PRO site, then create missing dimensions or rename existing ones. Run it from a consuming repo (`bokningsverktyget-ui`, `webbportalen-ui`, `feelgood-app`, and the same layout in other apps) or from the Electron maintainer UI under **Piwik**.
189
+
190
+ The local map is `src/analytics/dimensions.ts` (`customDimensions` = event, `sessionDimensions` = session) or `src/util/piwikDimensions.ts` (`CustomDimensions` / `SessionCustomDimensions`). The site defaults to the first non-production entry in `src/config/env.ts`, such as preprod. Pass `--env production` to target production. In the Electron app, Piwik actions show one radio per distinct `piwikId`. Environments that share an id, such as localhost, development, and preprod, appear once as **preprod**.
191
+
192
+ ```bash
193
+ pnpm exec fgd piwik list
194
+ pnpm exec fgd piwik list --local
195
+ pnpm exec fgd piwik diff
196
+ pnpm exec fgd piwik add --dry-run
197
+ pnpm exec fgd piwik sync --yes
198
+ pnpm exec fgd piwik pull --env preprod --dry-run
199
+ pnpm exec fgd piwik compare --from preprod --to production
200
+ pnpm exec fgd piwik migrate --from preprod --to production --dry-run
201
+ ```
202
+
203
+ `sync` copies the local map up to the selected Piwik site: it creates missing dimensions, then renames existing ones. It does not edit the repo. `pull` copies tracking ids from that site down into the local map for keys with the same name. In the Electron app, **Sync** has From and To radios. **Local map** is the repo. A site as To writes up to Piwik. **Local map** as To writes down into the repo. Two sites migrate between environments.
204
+
205
+ `compare` checks two sites from `src/config/env.ts` against each other. Event and session dimensions match on `tracking_id`, so a different slot on the other site is still in sync. `migrate` copies names, descriptions, and missing dimensions from `--from` onto `--to`, one create at a time. It stops if Piwik assigns a different `tracking_id`. Dimensions that exist only on the target stay there.
206
+
207
+ Set `PIWIK_CLIENT_ID` and `PIWIK_CLIENT_SECRET` in the environment, `.env.local`, or `env.local`. Optional `piwik/.piwik.json` can set `account`, `websiteId`, `environment`, `dimensionsFile`, and `names` (local key to Piwik label).
208
+
209
+ Session and event slots are assigned by Piwik as the next free number in that scope. The command creates one dimension at a time and stops if the returned slot is not the local slot. Deactivating a dimension does not free the slot. Product dimensions send the local slot (1–20).
210
+
186
211
  ## Local development with pnpm link
187
212
 
188
213
  Use this flow to test local `fg-devkit` changes in a consuming repo.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "toolchain": {
3
- "packageManager": "pnpm@12.4.2",
3
+ "packageManager": "pnpm@12.8.1",
4
4
  "node": {
5
5
  "nvmrc": "22",
6
6
  "engines": ">=22.13.0",
@@ -16,9 +16,9 @@
16
16
  "@lexical/list": "0.49.0",
17
17
  "@lexical/rich-text": "0.49.0",
18
18
  "@lexical/link": "0.49.0",
19
- "@piwikpro/vue-piwik-pro": "1.7.1",
20
- "@sentry/vue": "10.74.0",
21
- "@tanstack/vue-query": "5.102.8",
19
+ "@piwikpro/vue-piwik-pro": "2.0.0",
20
+ "@sentry/vue": "11.1.0",
21
+ "@tanstack/vue-query": "5.104.0",
22
22
  "@types/leaflet": "1.9.22",
23
23
  "@unhead/vue": "2.0.5",
24
24
  "@vue-leaflet/vue-leaflet": "0.10.1",
@@ -29,25 +29,25 @@
29
29
  "chart.js": "4.5.1",
30
30
  "content-disposition-header": "0.6.0",
31
31
  "date-fns": "4.4.0",
32
- "element-plus": "2.14.5",
33
- "fds-vue-core": "8.6.6",
32
+ "element-plus": "2.14.7",
33
+ "fds-vue-core": "8.10.0",
34
34
  "imask": "7.6.1",
35
35
  "leaflet": "1.9.4",
36
36
  "lexical": "0.49.0",
37
37
  "lodash": "4.18.1",
38
38
  "lodash-es": "4.18.1",
39
39
  "markdown-it": "14.3.0",
40
- "pinia": "3.0.4",
40
+ "pinia": "4.0.3",
41
41
  "playwright": "1.63.0",
42
42
  "quill": "2.0.3",
43
43
  "randexp": "0.5.3",
44
44
  "register-service-worker": "1.7.2",
45
- "vega-embed": "7.2.0",
45
+ "vega-embed": "7.3.0",
46
46
  "vega-lite": "6.4.3",
47
47
  "vue-chartjs": "5.3.4",
48
- "vue-i18n": "11.4.10",
48
+ "vue-i18n": "11.4.12",
49
49
  "vue-quilly": "1.1.6",
50
- "vue-router": "4.6.3",
50
+ "vue-router": "5.3.1",
51
51
  "vue3-touch-keyboard": "1.3.9",
52
52
  "vuex": "4.1.0",
53
53
  "vuex-persist": "3.1.3",
@@ -58,33 +58,33 @@
58
58
  "@eslint/js": "9.39.1",
59
59
  "@intlify/unplugin-vue-i18n": "11.2.5",
60
60
  "@playwright/test": "1.63.0",
61
- "@storybook/addon-a11y": "10.6.0",
62
- "@storybook/addon-docs": "10.6.0",
63
- "@storybook/addon-vitest": "10.6.0",
64
- "@storybook/vue3": "10.6.0",
65
- "@storybook/vue3-vite": "10.6.0",
66
- "@tanstack/vue-query-devtools": "6.1.48",
61
+ "@storybook/addon-a11y": "10.6.1",
62
+ "@storybook/addon-docs": "10.6.1",
63
+ "@storybook/addon-vitest": "10.6.1",
64
+ "@storybook/vue3": "10.6.1",
65
+ "@storybook/vue3-vite": "10.6.1",
66
+ "@tanstack/vue-query-devtools": "6.3.0",
67
67
  "@tsconfig/node22": "22.0.6",
68
68
  "@types/lodash-es": "4.17.12",
69
69
  "@types/node": "22.16.5",
70
- "@vitest/browser": "4.1.10",
71
- "@vue/compiler-dom": "3.5.42",
70
+ "@vitest/browser": "5.0.3",
71
+ "@vue/compiler-dom": "3.5.43",
72
72
  "@vue/eslint-config-standard": "8.0.1",
73
- "@vue/server-renderer": "3.5.42",
74
- "@vue/test-utils": "2.5.0",
73
+ "@vue/server-renderer": "3.5.43",
74
+ "@vue/test-utils": "2.5.1",
75
75
  "arg": "5.0.2",
76
76
  "babel-eslint": "10.1.0",
77
- "eslint-plugin-storybook": "10.6.0",
77
+ "eslint-plugin-storybook": "10.6.1",
78
78
  "globals": "15.11.0",
79
- "jsdom": "26.1.0",
80
- "sass": "1.104.1",
81
- "storybook": "10.6.0",
82
- "tsx": "4.23.13",
79
+ "jsdom": "30.1.1",
80
+ "sass": "1.105.1",
81
+ "storybook": "10.6.1",
82
+ "tsx": "4.23.15",
83
83
  "vega": "6.4.0",
84
- "vite-plugin-dts": "4.5.4",
84
+ "vite-plugin-dts": "5.1.1",
85
85
  "vite-plugin-vue-devtools": "8.2.1",
86
86
  "vite-plugin-vue-inspector": "7.0.0",
87
- "vitest": "4.1.10",
87
+ "vitest": "5.0.3",
88
88
  "yaml": "2.9.1"
89
89
  }
90
90
  },
@@ -92,10 +92,10 @@
92
92
  "@tailwindcss/vite": "4.3.3",
93
93
  "@vitejs/plugin-vue": "6.0.9",
94
94
  "axios": "1.20.0",
95
- "dotenv": "16.4.5",
95
+ "dotenv": "18.0.5",
96
96
  "qrcode.vue": "3.11.0",
97
97
  "tailwindcss": "4.3.3",
98
- "vite": "7.3.5",
99
- "vue": "3.5.42"
98
+ "vite": "8.3.1",
99
+ "vue": "3.5.43"
100
100
  }
101
101
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fg-devkit",
3
- "version": "1.17.3",
3
+ "version": "1.19.2",
4
4
  "description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -63,6 +63,9 @@
63
63
  "./scripts/refresh-dependency-policy-from-repos.mjs": "./scripts/refresh-dependency-policy-from-repos.mjs",
64
64
  "./scripts/sync-scaffold-vue-deps.mjs": "./scripts/sync-scaffold-vue-deps.mjs",
65
65
  "./scripts/poeditor-common.mjs": "./scripts/poeditor-common.mjs",
66
+ "./scripts/piwik.mjs": "./scripts/piwik.mjs",
67
+ "./scripts/piwik-common.mjs": "./scripts/piwik-common.mjs",
68
+ "./scripts/piwik-dimensions.mjs": "./scripts/piwik-dimensions.mjs",
66
69
  "./scripts/poeditor-download.mjs": "./scripts/poeditor-download.mjs",
67
70
  "./scripts/poeditor-upload.mjs": "./scripts/poeditor-upload.mjs",
68
71
  "./scripts/scaffold-vue.mjs": "./scripts/scaffold-vue.mjs",
@@ -84,6 +87,8 @@
84
87
  "configs",
85
88
  "release-notes",
86
89
  "scaffold",
90
+ "!**/node_modules",
91
+ "!**/node_modules/**",
87
92
  "scripts",
88
93
  "tsconfig.json",
89
94
  ".cursor/rules/core-coding-standards.mdc"
@@ -104,26 +109,26 @@
104
109
  }
105
110
  },
106
111
  "dependencies": {
107
- "@inquirer/checkbox": "4.3.2",
112
+ "@inquirer/checkbox": "5.2.5",
108
113
  "@inquirer/confirm": "6.3.2",
109
114
  "@inquirer/input": "5.1.6",
110
115
  "@inquirer/select": "5.2.5",
111
116
  "@intlify/eslint-plugin-vue-i18n": "4.5.1",
112
- "@typescript-eslint/eslint-plugin": "8.70.0",
113
- "@typescript-eslint/parser": "8.70.0",
117
+ "@typescript-eslint/eslint-plugin": "8.71.0",
118
+ "@typescript-eslint/parser": "8.71.0",
114
119
  "@vitest/eslint-plugin": "1.6.27",
115
120
  "@vue/eslint-config-prettier": "10.2.0",
116
121
  "@vue/eslint-config-typescript": "14.9.0",
117
122
  "@vue/tsconfig": "0.9.1",
118
- "eslint": "10.10.0",
123
+ "eslint": "10.11.0",
119
124
  "eslint-plugin-prettier": "5.5.6",
120
- "eslint-plugin-vue": "10.11.0",
121
- "fallow": "2.101.0",
122
- "git-cliff": "2.13.1",
123
- "lefthook": "2.1.14",
124
- "npm-check-updates": "22.2.7",
125
- "prettier": "3.9.6",
126
- "tsx": "4.23.13",
125
+ "eslint-plugin-vue": "10.11.1",
126
+ "fallow": "3.31.0",
127
+ "git-cliff": "2.14.2",
128
+ "lefthook": "2.1.15",
129
+ "npm-check-updates": "23.1.0",
130
+ "prettier": "3.9.9",
131
+ "tsx": "4.23.15",
127
132
  "typescript": "5.9.3",
128
133
  "vue-eslint-parser": "10.4.1",
129
134
  "vue-tsc": "3.3.11"
@@ -133,8 +138,6 @@
133
138
  "czg": "1.14.0"
134
139
  },
135
140
  "scripts": {
136
- "maintainer-ui": "pnpm --dir electron/maintainer-ui start",
137
- "maintainer-ui:install": "pnpm --dir electron/maintainer-ui install",
138
141
  "fgd-ui": "pnpm --dir electron/maintainer-ui start",
139
142
  "fgd-ui:install": "pnpm --dir electron/maintainer-ui install"
140
143
  }
@@ -16,30 +16,30 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@tailwindcss/vite": "4.3.3",
19
- "@tanstack/vue-query": "5.102.8",
19
+ "@tanstack/vue-query": "5.104.0",
20
20
  "axios": "1.20.0",
21
21
  "content-disposition-header": "0.6.0",
22
22
  "date-fns": "4.4.0",
23
- "dotenv": "16.4.5",
24
- "fds-vue-core": "8.6.6",
25
- "pinia": "3.0.4",
23
+ "dotenv": "18.0.5",
24
+ "fds-vue-core": "8.10.0",
25
+ "pinia": "4.0.3",
26
26
  "qrcode.vue": "3.11.0",
27
- "vue": "3.5.42",
28
- "vue-i18n": "11.4.10",
29
- "vue-router": "4.6.3"
27
+ "vue": "3.5.43",
28
+ "vue-i18n": "11.4.12",
29
+ "vue-router": "5.3.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@intlify/unplugin-vue-i18n": "11.2.5",
33
- "@tanstack/vue-query-devtools": "6.1.48",
33
+ "@tanstack/vue-query-devtools": "6.3.0",
34
34
  "@types/node": "22.16.5",
35
- "fg-devkit": "1.17.3",
35
+ "fg-devkit": "1.19.2",
36
36
  "@vitejs/plugin-vue": "6.0.9",
37
37
  "tailwindcss": "4.3.3",
38
- "tsx": "4.23.13",
39
- "vite": "7.3.5",
38
+ "tsx": "4.23.15",
39
+ "vite": "8.3.1",
40
40
  "vite-plugin-vue-inspector": "7.0.0"
41
41
  },
42
- "packageManager": "pnpm@12.4.2",
42
+ "packageManager": "pnpm@12.8.1",
43
43
  "engines": {
44
44
  "node": ">=22.13.0"
45
45
  }
@@ -41,6 +41,7 @@
41
41
  * pnpm exec fg-devkit dead-code [fallow-args...] (same as: fallow dead-code [args...])
42
42
  * pnpm exec fg-devkit poeditor-download [args...] (download translations using poeditor/.poeditor.json + env token)
43
43
  * pnpm exec fg-devkit poeditor-upload [args...] (upload translations using poeditor/.poeditor.json + env token)
44
+ * pnpm exec fg-devkit piwik <list|diff|add|update|sync|compare|migrate> (Piwik PRO custom dimensions)
44
45
  * Interactive menu (no subcommand):
45
46
  * pnpm exec fg-devkit
46
47
  *
@@ -489,6 +490,7 @@ const DIRECT_COMMANDS = {
489
490
  'sync-scaffold-vue-deps': 'sync-scaffold-vue-deps.mjs',
490
491
  'poeditor-download': 'poeditor-download.mjs',
491
492
  'poeditor-upload': 'poeditor-upload.mjs',
493
+ piwik: 'piwik.mjs',
492
494
  }
493
495
 
494
496
  function printHelp() {
@@ -568,6 +570,7 @@ Commands:
568
570
  dead-code [fallow-args...] Run Fallow dead code analysis (shortcut for: fallow dead-code)
569
571
  poeditor-download [args...] Download locale files via PoEditor (reads poeditor/.poeditor.json by default)
570
572
  poeditor-upload [args...] Upload locale file via PoEditor (reads poeditor/.poeditor.json by default; --sync-terms deletes missing terms)
573
+ piwik <list|diff|add|update|sync|compare|migrate> Compare and sync Piwik PRO custom dimensions with the local slot map, or between environments
571
574
  scaffold-vue <target-dir> [--name …] [--slug …] [--force] …
572
575
  Copy the bundled Vue scaffold into target-dir (see script header)
573
576
  sync-scaffold-vue-deps [policy-file]
@@ -663,15 +666,19 @@ async function runInteractiveMenu() {
663
666
  name: '8) Fallow analysis',
664
667
  value: 'fallow-analysis',
665
668
  },
669
+ {
670
+ name: '9) Piwik',
671
+ value: 'piwik',
672
+ },
666
673
  ]
667
674
  if (isMaintainer) {
668
675
  groups.push({
669
- name: '9) Maintainer',
676
+ name: '10) Maintainer',
670
677
  value: 'maintainer',
671
678
  })
672
679
  }
673
680
  groups.push({
674
- name: isMaintainer ? '10) Exit' : '9) Exit',
681
+ name: isMaintainer ? '11) Exit' : '10) Exit',
675
682
  value: 'exit',
676
683
  })
677
684
 
@@ -810,6 +817,151 @@ async function runInteractiveMenu() {
810
817
  return runSelectedAction(() => runNode('poeditor-upload.mjs', uploadArgs))
811
818
  }
812
819
 
820
+ if (group === 'piwik') {
821
+ const choice = await selectPrompt({
822
+ message: 'Piwik PRO custom dimensions',
823
+ choices: [
824
+ { name: 'List dimensions in Piwik', value: 'list' },
825
+ { name: 'List local dimension map', value: 'list-local' },
826
+ { name: 'Compare local map with Piwik', value: 'diff' },
827
+ { name: 'Add missing dimensions', value: 'add' },
828
+ { name: 'Update dimension names', value: 'update' },
829
+ { name: 'Sync from one side to another', value: 'sync' },
830
+ { name: 'Compare environments', value: 'compare' },
831
+ { name: 'Migrate between environments', value: 'migrate' },
832
+ { name: 'Cancel', value: 'cancel' },
833
+ ],
834
+ default: 'diff',
835
+ loop: false,
836
+ })
837
+ if (choice === 'cancel') continue
838
+ if (choice === 'list') return runSelectedAction(() => runNode('piwik.mjs', ['list']))
839
+ if (choice === 'list-local') return runSelectedAction(() => runNode('piwik.mjs', ['list', '--local']))
840
+ if (choice === 'diff') return runSelectedAction(() => runNode('piwik.mjs', ['diff']))
841
+ if (choice === 'compare' || choice === 'migrate') {
842
+ const { defaultEnvironmentPair, listPiwikEnvironments } = await import('./piwik-common.mjs')
843
+ const environments = listPiwikEnvironments(process.cwd())
844
+ if (environments.length < 2) {
845
+ console.log('[piwik] Need two Piwik sites in src/config/env.ts.')
846
+ continue
847
+ }
848
+ const defaults = defaultEnvironmentPair(environments)
849
+ const from = await selectPrompt({
850
+ message: 'From',
851
+ choices: environments.map((site) => ({ name: site.environment, value: site.environment })),
852
+ default: defaults.from,
853
+ loop: false,
854
+ })
855
+ const toChoices = environments.filter((site) => site.environment !== from)
856
+ const to = await selectPrompt({
857
+ message: 'To',
858
+ choices: toChoices.map((site) => ({ name: site.environment, value: site.environment })),
859
+ default: toChoices.some((site) => site.environment === defaults.to) ? defaults.to : toChoices[0].environment,
860
+ loop: false,
861
+ })
862
+ if (choice === 'compare') {
863
+ return runSelectedAction(() => runNode('piwik.mjs', ['compare', '--from', from, '--to', to]))
864
+ }
865
+ const writeMode = await selectPrompt({
866
+ message: `Write dimensions from ${from} to ${to}?`,
867
+ choices: [
868
+ { name: 'Dry run', value: 'dry-run' },
869
+ { name: 'Apply', value: 'apply' },
870
+ { name: 'Cancel', value: 'cancel' },
871
+ ],
872
+ default: 'dry-run',
873
+ loop: false,
874
+ })
875
+ if (writeMode === 'cancel') continue
876
+ const migrateArgs = ['migrate', '--from', from, '--to', to]
877
+ if (writeMode === 'dry-run') migrateArgs.push('--dry-run')
878
+ if (writeMode === 'apply') {
879
+ const confirmed = await confirmPrompt({
880
+ message: `This writes custom dimensions on ${to}. tracking_id is assigned by Piwik and cannot be reused. Continue?`,
881
+ default: false,
882
+ })
883
+ if (!confirmed) continue
884
+ migrateArgs.push('--yes')
885
+ }
886
+ return runSelectedAction(() => runNode('piwik.mjs', migrateArgs))
887
+ }
888
+ if (choice === 'sync') {
889
+ const { listPiwikEnvironments } = await import('./piwik-common.mjs')
890
+ const environments = listPiwikEnvironments(process.cwd())
891
+ const endpoints = [
892
+ { name: 'Local map', value: 'local' },
893
+ ...environments.map((site) => ({ name: site.environment, value: site.environment })),
894
+ ]
895
+ const from = await selectPrompt({
896
+ message: 'From',
897
+ choices: endpoints,
898
+ default: 'local',
899
+ loop: false,
900
+ })
901
+ const toChoices = endpoints.filter((item) => item.value !== from)
902
+ if (toChoices.length === 0) {
903
+ console.log('[piwik] Need a local dimension map and a Piwik site in src/config/env.ts.')
904
+ continue
905
+ }
906
+ const defaultTo = toChoices.find((item) => item.value !== 'production' && item.value !== 'local')?.value || toChoices[0]?.value
907
+ const to = await selectPrompt({
908
+ message: 'To',
909
+ choices: toChoices,
910
+ default: defaultTo,
911
+ loop: false,
912
+ })
913
+ const writeMode = await selectPrompt({
914
+ message: from === 'local' ? `Write the local map up to ${to}?` : to === 'local' ? `Write tracking ids from ${from} into the local map?` : `Write dimensions from ${from} to ${to}?`,
915
+ choices: [
916
+ { name: 'Dry run', value: 'dry-run' },
917
+ { name: 'Apply', value: 'apply' },
918
+ { name: 'Cancel', value: 'cancel' },
919
+ ],
920
+ default: 'dry-run',
921
+ loop: false,
922
+ })
923
+ if (writeMode === 'cancel') continue
924
+ /** @type {string[]} */
925
+ let syncArgs
926
+ if (from === 'local') syncArgs = ['sync', '--env', to]
927
+ else if (to === 'local') syncArgs = ['pull', '--env', from]
928
+ else syncArgs = ['migrate', '--from', from, '--to', to]
929
+ if (writeMode === 'dry-run') syncArgs.push('--dry-run')
930
+ if (writeMode === 'apply') {
931
+ const confirmed = await confirmPrompt({
932
+ message: to === 'local' ? 'This rewrites tracking ids in the local dimension map. Continue?' : `This writes custom dimensions on ${to}. tracking_id cannot be chosen for session and event. Continue?`,
933
+ default: false,
934
+ })
935
+ if (!confirmed) continue
936
+ syncArgs.push('--yes')
937
+ }
938
+ return runSelectedAction(() => runNode('piwik.mjs', syncArgs))
939
+ }
940
+
941
+ const writeMode = await selectPrompt({
942
+ message: 'Write to Piwik?',
943
+ choices: [
944
+ { name: 'Dry run', value: 'dry-run' },
945
+ { name: 'Apply', value: 'apply' },
946
+ { name: 'Cancel', value: 'cancel' },
947
+ ],
948
+ default: 'dry-run',
949
+ loop: false,
950
+ })
951
+ if (writeMode === 'cancel') continue
952
+ const piwikArgs = [choice]
953
+ if (writeMode === 'dry-run') piwikArgs.push('--dry-run')
954
+ if (writeMode === 'apply') {
955
+ const confirmed = await confirmPrompt({
956
+ message: 'This writes custom dimensions in Piwik PRO. Slots cannot be reused after creation. Continue?',
957
+ default: false,
958
+ })
959
+ if (!confirmed) continue
960
+ piwikArgs.push('--yes')
961
+ }
962
+ return runSelectedAction(() => runNode('piwik.mjs', piwikArgs))
963
+ }
964
+
813
965
  if (group === 'dependency-policy') {
814
966
  const choice = await selectPrompt({
815
967
  message: 'Dependecy policy',