@c15t/cli 2.0.0-rc.4 → 2.0.0-rc.5

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.
Files changed (224) hide show
  1. package/README.md +43 -14
  2. package/dist/{153.mjs → 642.mjs} +45 -43
  3. package/dist/index.mjs +3113 -349
  4. package/{dist → dist-types}/actions/show-help-menu.d.ts +0 -1
  5. package/dist-types/auth/base-url.d.ts +7 -0
  6. package/{dist → dist-types}/auth/config-store.d.ts +0 -1
  7. package/{dist → dist-types}/auth/device-flow.d.ts +0 -1
  8. package/{dist → dist-types}/auth/index.d.ts +1 -1
  9. package/{dist → dist-types}/auth/types.d.ts +0 -1
  10. package/{dist → dist-types}/commands/auth/index.d.ts +0 -1
  11. package/dist-types/commands/codemods/active-ui-api.d.ts +41 -0
  12. package/dist-types/commands/codemods/component-renames.d.ts +41 -0
  13. package/dist-types/commands/codemods/gdpr-types-to-consent-categories.d.ts +41 -0
  14. package/dist-types/commands/codemods/ignore-geo-location-to-overrides.d.ts +41 -0
  15. package/dist-types/commands/codemods/index.d.ts +28 -0
  16. package/dist-types/commands/codemods/mode-c15t-to-hosted.d.ts +43 -0
  17. package/dist-types/commands/codemods/offline-add-policy-packs.d.ts +21 -0
  18. package/dist-types/commands/codemods/react-options-to-top-level.d.ts +41 -0
  19. package/dist-types/commands/codemods/tracking-blocker-to-network-blocker.d.ts +41 -0
  20. package/dist-types/commands/codemods/translations-to-i18n.d.ts +51 -0
  21. package/dist-types/commands/codemods/versioning.d.ts +49 -0
  22. package/{dist → dist-types}/commands/generate/index.d.ts +0 -1
  23. package/{dist → dist-types}/commands/generate/options/shared/backend-options.d.ts +0 -1
  24. package/{dist → dist-types}/commands/generate/options/shared/dev-tools.d.ts +0 -1
  25. package/{dist → dist-types}/commands/generate/options/shared/frontend-ui-options.d.ts +0 -1
  26. package/{dist → dist-types}/commands/generate/options/shared/scripts.d.ts +0 -1
  27. package/{dist → dist-types}/commands/generate/options/shared/ssr.d.ts +0 -1
  28. package/{dist → dist-types}/commands/generate/options/types.d.ts +0 -1
  29. package/{dist → dist-types}/commands/generate/options/utils/dependencies.d.ts +0 -1
  30. package/{dist → dist-types}/commands/generate/options/utils/generate-files.d.ts +2 -2
  31. package/{dist → dist-types}/commands/generate/preflight.d.ts +0 -1
  32. package/{dist → dist-types}/commands/generate/prompts/expanded-theme.d.ts +0 -1
  33. package/{dist → dist-types}/commands/generate/prompts/index.d.ts +0 -2
  34. package/{dist → dist-types}/commands/generate/prompts/mode-select.d.ts +1 -7
  35. package/{dist → dist-types}/commands/generate/prompts/scripts.d.ts +0 -1
  36. package/{dist → dist-types}/commands/generate/prompts/theme.d.ts +0 -1
  37. package/{dist → dist-types}/commands/generate/prompts/ui-style.d.ts +0 -1
  38. package/{dist → dist-types}/commands/generate/summary.d.ts +0 -1
  39. package/{dist → dist-types}/commands/generate/templates/config.d.ts +0 -1
  40. package/{dist → dist-types}/commands/generate/templates/css.d.ts +0 -1
  41. package/{dist → dist-types}/commands/generate/templates/env.d.ts +0 -1
  42. package/{dist → dist-types}/commands/generate/templates/index.d.ts +0 -1
  43. package/{dist → dist-types}/commands/generate/templates/layout.d.ts +0 -1
  44. package/{dist → dist-types}/commands/generate/templates/next/app/layout.d.ts +0 -1
  45. package/{dist → dist-types}/commands/generate/templates/next/index.d.ts +0 -1
  46. package/{dist → dist-types}/commands/generate/templates/next/pages/layout.d.ts +0 -1
  47. package/{dist → dist-types}/commands/generate/templates/next-config.d.ts +1 -4
  48. package/{dist → dist-types}/commands/generate/templates/shared/components.d.ts +2 -3
  49. package/{dist → dist-types}/commands/generate/templates/shared/directory.d.ts +0 -1
  50. package/{dist → dist-types}/commands/generate/templates/shared/expanded-components.d.ts +0 -1
  51. package/{dist → dist-types}/commands/generate/templates/shared/framework-config.d.ts +0 -1
  52. package/{dist → dist-types}/commands/generate/templates/shared/layout-pipeline.d.ts +0 -1
  53. package/{dist → dist-types}/commands/generate/templates/shared/module-specifier.d.ts +0 -1
  54. package/{dist → dist-types}/commands/generate/templates/shared/options.d.ts +5 -6
  55. package/{dist → dist-types}/commands/generate/templates/shared/scripts.d.ts +0 -1
  56. package/{dist → dist-types}/commands/generate/templates/shared/server-components.d.ts +0 -1
  57. package/{dist → dist-types}/commands/index.d.ts +1 -1
  58. package/{dist → dist-types}/commands/instances/index.d.ts +0 -1
  59. package/{dist → dist-types}/commands/self-host/index.d.ts +0 -1
  60. package/{dist → dist-types}/commands/self-host/migrate/ensure-backend-config.d.ts +0 -1
  61. package/{dist → dist-types}/commands/self-host/migrate/index.d.ts +0 -1
  62. package/{dist → dist-types}/commands/self-host/migrate/migrator-result.d.ts +1 -2
  63. package/{dist → dist-types}/commands/self-host/migrate/orm-result.d.ts +1 -2
  64. package/{dist → dist-types}/commands/self-host/migrate/read-config.d.ts +1 -2
  65. package/{dist → dist-types}/commands/skills.d.ts +0 -1
  66. package/{dist → dist-types}/components/intro.d.ts +0 -1
  67. package/{dist → dist-types}/constants.d.ts +19 -9
  68. package/{dist → dist-types}/context/creator.d.ts +0 -1
  69. package/{dist → dist-types}/context/error-handlers.d.ts +0 -1
  70. package/{dist → dist-types}/context/file-system.d.ts +0 -1
  71. package/{dist → dist-types}/context/framework-detection.d.ts +0 -1
  72. package/{dist → dist-types}/context/package-manager-detection.d.ts +0 -1
  73. package/{dist → dist-types}/context/parser.d.ts +0 -1
  74. package/{dist → dist-types}/context/types.d.ts +1 -2
  75. package/{dist → dist-types}/context/user-interaction.d.ts +0 -1
  76. package/dist-types/control-plane/client.d.ts +56 -0
  77. package/dist-types/control-plane/index.d.ts +5 -0
  78. package/dist-types/control-plane/types.d.ts +73 -0
  79. package/{dist → dist-types}/core/context.d.ts +0 -1
  80. package/{dist → dist-types}/core/errors.d.ts +5 -11
  81. package/{dist → dist-types}/core/index.d.ts +0 -1
  82. package/{dist → dist-types}/core/logger.d.ts +0 -1
  83. package/{dist → dist-types}/core/parser.d.ts +0 -1
  84. package/{dist → dist-types}/core/telemetry.d.ts +0 -1
  85. package/{dist → dist-types}/detection/framework.d.ts +0 -1
  86. package/{dist → dist-types}/detection/index.d.ts +0 -1
  87. package/{dist → dist-types}/detection/layout.d.ts +0 -1
  88. package/{dist → dist-types}/detection/package-manager.d.ts +0 -1
  89. package/{dist → dist-types}/index.d.ts +0 -1
  90. package/{dist → dist-types}/machines/generate/actions.d.ts +0 -1
  91. package/{dist → dist-types}/machines/generate/actors/dependencies.d.ts +0 -1
  92. package/{dist → dist-types}/machines/generate/actors/file-generation.d.ts +1 -2
  93. package/{dist → dist-types}/machines/generate/actors/preflight.d.ts +0 -1
  94. package/{dist → dist-types}/machines/generate/actors/prompts.d.ts +8 -13
  95. package/{dist → dist-types}/machines/generate/guards.d.ts +4 -5
  96. package/{dist → dist-types}/machines/generate/machine.d.ts +10 -23
  97. package/{dist → dist-types}/machines/generate/runner.d.ts +0 -1
  98. package/{dist → dist-types}/machines/generate/types.d.ts +4 -3
  99. package/{dist → dist-types}/machines/index.d.ts +1 -2
  100. package/{dist → dist-types}/machines/persistence.d.ts +0 -1
  101. package/{dist → dist-types}/machines/telemetry-plugin.d.ts +0 -1
  102. package/{dist → dist-types}/machines/types.d.ts +0 -1
  103. package/{dist → dist-types}/types.d.ts +6 -3
  104. package/{dist → dist-types}/utils/capitalize-first-letter.d.ts +0 -1
  105. package/{dist → dist-types}/utils/formatter.d.ts +0 -1
  106. package/{dist → dist-types}/utils/fs.d.ts +0 -1
  107. package/{dist → dist-types}/utils/index.d.ts +0 -1
  108. package/{dist → dist-types}/utils/logger.d.ts +1 -2
  109. package/{dist → dist-types}/utils/spinner.d.ts +0 -1
  110. package/{dist → dist-types}/utils/telemetry.d.ts +1 -2
  111. package/{dist → dist-types}/utils/validation.d.ts +0 -1
  112. package/package.json +7 -5
  113. package/readme.json +22 -8
  114. package/dist/actions/show-help-menu.d.ts.map +0 -1
  115. package/dist/auth/config-store.d.ts.map +0 -1
  116. package/dist/auth/device-flow.d.ts.map +0 -1
  117. package/dist/auth/index.d.ts.map +0 -1
  118. package/dist/auth/types.d.ts.map +0 -1
  119. package/dist/commands/auth/index.d.ts.map +0 -1
  120. package/dist/commands/generate/index.d.ts.map +0 -1
  121. package/dist/commands/generate/options/c15t-mode.d.ts +0 -27
  122. package/dist/commands/generate/options/c15t-mode.d.ts.map +0 -1
  123. package/dist/commands/generate/options/custom-mode.d.ts +0 -26
  124. package/dist/commands/generate/options/custom-mode.d.ts.map +0 -1
  125. package/dist/commands/generate/options/offline-mode.d.ts +0 -26
  126. package/dist/commands/generate/options/offline-mode.d.ts.map +0 -1
  127. package/dist/commands/generate/options/self-hosted-mode.d.ts +0 -19
  128. package/dist/commands/generate/options/self-hosted-mode.d.ts.map +0 -1
  129. package/dist/commands/generate/options/shared/backend-options.d.ts.map +0 -1
  130. package/dist/commands/generate/options/shared/dev-tools.d.ts.map +0 -1
  131. package/dist/commands/generate/options/shared/frontend-ui-options.d.ts.map +0 -1
  132. package/dist/commands/generate/options/shared/index.d.ts +0 -9
  133. package/dist/commands/generate/options/shared/index.d.ts.map +0 -1
  134. package/dist/commands/generate/options/shared/scripts.d.ts.map +0 -1
  135. package/dist/commands/generate/options/shared/ssr.d.ts.map +0 -1
  136. package/dist/commands/generate/options/types.d.ts.map +0 -1
  137. package/dist/commands/generate/options/utils/dependencies.d.ts.map +0 -1
  138. package/dist/commands/generate/options/utils/generate-files.d.ts.map +0 -1
  139. package/dist/commands/generate/preflight.d.ts.map +0 -1
  140. package/dist/commands/generate/prompts/expanded-theme.d.ts.map +0 -1
  141. package/dist/commands/generate/prompts/index.d.ts.map +0 -1
  142. package/dist/commands/generate/prompts/instance.d.ts +0 -33
  143. package/dist/commands/generate/prompts/instance.d.ts.map +0 -1
  144. package/dist/commands/generate/prompts/mode-select.d.ts.map +0 -1
  145. package/dist/commands/generate/prompts/scripts.d.ts.map +0 -1
  146. package/dist/commands/generate/prompts/theme.d.ts.map +0 -1
  147. package/dist/commands/generate/prompts/ui-style.d.ts.map +0 -1
  148. package/dist/commands/generate/summary.d.ts.map +0 -1
  149. package/dist/commands/generate/templates/config.d.ts.map +0 -1
  150. package/dist/commands/generate/templates/css.d.ts.map +0 -1
  151. package/dist/commands/generate/templates/env.d.ts.map +0 -1
  152. package/dist/commands/generate/templates/index.d.ts.map +0 -1
  153. package/dist/commands/generate/templates/layout.d.ts.map +0 -1
  154. package/dist/commands/generate/templates/next/app/layout.d.ts.map +0 -1
  155. package/dist/commands/generate/templates/next/index.d.ts.map +0 -1
  156. package/dist/commands/generate/templates/next/pages/layout.d.ts.map +0 -1
  157. package/dist/commands/generate/templates/next-config.d.ts.map +0 -1
  158. package/dist/commands/generate/templates/shared/components.d.ts.map +0 -1
  159. package/dist/commands/generate/templates/shared/directory.d.ts.map +0 -1
  160. package/dist/commands/generate/templates/shared/expanded-components.d.ts.map +0 -1
  161. package/dist/commands/generate/templates/shared/framework-config.d.ts.map +0 -1
  162. package/dist/commands/generate/templates/shared/layout-pipeline.d.ts.map +0 -1
  163. package/dist/commands/generate/templates/shared/module-specifier.d.ts.map +0 -1
  164. package/dist/commands/generate/templates/shared/options.d.ts.map +0 -1
  165. package/dist/commands/generate/templates/shared/scripts.d.ts.map +0 -1
  166. package/dist/commands/generate/templates/shared/server-components.d.ts.map +0 -1
  167. package/dist/commands/index.d.ts.map +0 -1
  168. package/dist/commands/instances/index.d.ts.map +0 -1
  169. package/dist/commands/self-host/index.d.ts.map +0 -1
  170. package/dist/commands/self-host/migrate/ensure-backend-config.d.ts.map +0 -1
  171. package/dist/commands/self-host/migrate/index.d.ts.map +0 -1
  172. package/dist/commands/self-host/migrate/migrator-result.d.ts.map +0 -1
  173. package/dist/commands/self-host/migrate/orm-result.d.ts.map +0 -1
  174. package/dist/commands/self-host/migrate/read-config.d.ts.map +0 -1
  175. package/dist/commands/skills.d.ts.map +0 -1
  176. package/dist/components/intro.d.ts.map +0 -1
  177. package/dist/constants.d.ts.map +0 -1
  178. package/dist/context/creator.d.ts.map +0 -1
  179. package/dist/context/error-handlers.d.ts.map +0 -1
  180. package/dist/context/file-system.d.ts.map +0 -1
  181. package/dist/context/framework-detection.d.ts.map +0 -1
  182. package/dist/context/package-manager-detection.d.ts.map +0 -1
  183. package/dist/context/parser.d.ts.map +0 -1
  184. package/dist/context/types.d.ts.map +0 -1
  185. package/dist/context/user-interaction.d.ts.map +0 -1
  186. package/dist/core/context.d.ts.map +0 -1
  187. package/dist/core/errors.d.ts.map +0 -1
  188. package/dist/core/index.d.ts.map +0 -1
  189. package/dist/core/logger.d.ts.map +0 -1
  190. package/dist/core/parser.d.ts.map +0 -1
  191. package/dist/core/telemetry.d.ts.map +0 -1
  192. package/dist/detection/framework.d.ts.map +0 -1
  193. package/dist/detection/index.d.ts.map +0 -1
  194. package/dist/detection/layout.d.ts.map +0 -1
  195. package/dist/detection/package-manager.d.ts.map +0 -1
  196. package/dist/index.d.ts.map +0 -1
  197. package/dist/machines/generate/actions.d.ts.map +0 -1
  198. package/dist/machines/generate/actors/dependencies.d.ts.map +0 -1
  199. package/dist/machines/generate/actors/file-generation.d.ts.map +0 -1
  200. package/dist/machines/generate/actors/preflight.d.ts.map +0 -1
  201. package/dist/machines/generate/actors/prompts.d.ts.map +0 -1
  202. package/dist/machines/generate/guards.d.ts.map +0 -1
  203. package/dist/machines/generate/machine.d.ts.map +0 -1
  204. package/dist/machines/generate/runner.d.ts.map +0 -1
  205. package/dist/machines/generate/types.d.ts.map +0 -1
  206. package/dist/machines/index.d.ts.map +0 -1
  207. package/dist/machines/persistence.d.ts.map +0 -1
  208. package/dist/machines/telemetry-plugin.d.ts.map +0 -1
  209. package/dist/machines/types.d.ts.map +0 -1
  210. package/dist/mcp/client.d.ts +0 -61
  211. package/dist/mcp/client.d.ts.map +0 -1
  212. package/dist/mcp/index.d.ts +0 -6
  213. package/dist/mcp/index.d.ts.map +0 -1
  214. package/dist/mcp/types.d.ts +0 -84
  215. package/dist/mcp/types.d.ts.map +0 -1
  216. package/dist/types.d.ts.map +0 -1
  217. package/dist/utils/capitalize-first-letter.d.ts.map +0 -1
  218. package/dist/utils/formatter.d.ts.map +0 -1
  219. package/dist/utils/fs.d.ts.map +0 -1
  220. package/dist/utils/index.d.ts.map +0 -1
  221. package/dist/utils/logger.d.ts.map +0 -1
  222. package/dist/utils/spinner.d.ts.map +0 -1
  223. package/dist/utils/telemetry.d.ts.map +0 -1
  224. package/dist/utils/validation.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import * as __rspack_external__clack_prompts_3cae1695 from "@clack/prompts";
3
3
  import * as __rspack_external_node_fs_promises_153e37e0 from "node:fs/promises";
4
+ import * as __rspack_external_node_os_74b4b876 from "node:os";
4
5
  import * as __rspack_external_node_path_c5b9b54f from "node:path";
5
6
  import * as __rspack_external_picocolors from "picocolors";
7
+ import * as __rspack_external_ts_morph_07c7ce60 from "ts-morph";
6
8
  import "dotenv/config";
7
9
  import * as __rspack_external_open from "open";
8
10
  import * as __rspack_external_xstate from "xstate";
9
11
  import * as __rspack_external_node_crypto_9ba42079 from "node:crypto";
10
- import * as __rspack_external_node_os_74b4b876 from "node:os";
11
12
  import * as __rspack_external_posthog_node_1b07bdf4 from "posthog-node";
12
13
  import * as __rspack_external_node_child_process_27f17141 from "node:child_process";
13
14
  import * as __rspack_external_node_events_0a6aefe7 from "node:events";
@@ -19,21 +20,127 @@ import * as __rspack_external_figlet from "figlet";
19
20
  import * as __rspack_external_fs_extra_ce68a66b from "fs-extra";
20
21
  import * as __rspack_external_package_manager_detector_detect_94d6a9ae from "package-manager-detector/detect";
21
22
  var __webpack_modules__ = {
23
+ "./src/auth/config-store.ts" (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
24
+ __webpack_require__.d(__webpack_exports__, {
25
+ getAccessToken: ()=>getAccessToken,
26
+ Ex: ()=>storeTokens,
27
+ IM: ()=>getAuthState,
28
+ _g: ()=>setSelectedInstanceId
29
+ });
30
+ var promises_ = __webpack_require__("node:fs/promises");
31
+ var external_node_os_ = __webpack_require__("node:os");
32
+ var external_node_path_ = __webpack_require__("node:path");
33
+ var constants = __webpack_require__("./src/constants.ts");
34
+ function getConfigDir() {
35
+ return external_node_path_["default"].join(external_node_os_["default"].homedir(), constants.R7.CONFIG_DIR);
36
+ }
37
+ function getConfigPath() {
38
+ return external_node_path_["default"].join(getConfigDir(), constants.R7.CONFIG_FILE);
39
+ }
40
+ async function ensureConfigDir() {
41
+ const configDir = getConfigDir();
42
+ await promises_["default"].mkdir(configDir, {
43
+ recursive: true
44
+ });
45
+ }
46
+ async function loadConfig() {
47
+ try {
48
+ const configPath = getConfigPath();
49
+ const content = await promises_["default"].readFile(configPath, 'utf-8');
50
+ const config = JSON.parse(content);
51
+ if (!config.accessToken) return null;
52
+ return config;
53
+ } catch (error) {
54
+ return null;
55
+ }
56
+ }
57
+ async function saveConfig(config) {
58
+ await ensureConfigDir();
59
+ const configPath = getConfigPath();
60
+ const content = JSON.stringify(config, null, 2);
61
+ await promises_["default"].writeFile(configPath, content, {
62
+ mode: 384
63
+ });
64
+ }
65
+ async function updateConfig(updates) {
66
+ const existing = await loadConfig();
67
+ if (!existing) return null;
68
+ const updated = {
69
+ ...existing,
70
+ ...updates
71
+ };
72
+ await saveConfig(updated);
73
+ return updated;
74
+ }
75
+ function isTokenExpired(config) {
76
+ if (!config.expiresAt) return false;
77
+ const buffer = 300000;
78
+ return Date.now() > config.expiresAt - buffer;
79
+ }
80
+ async function getAuthState() {
81
+ const config = await loadConfig();
82
+ if (!config) return {
83
+ isLoggedIn: false,
84
+ config: null,
85
+ isExpired: false
86
+ };
87
+ return {
88
+ isLoggedIn: true,
89
+ config,
90
+ isExpired: isTokenExpired(config)
91
+ };
92
+ }
93
+ async function getAccessToken() {
94
+ const config = await loadConfig();
95
+ if (!config || isTokenExpired(config)) return null;
96
+ return config.accessToken;
97
+ }
98
+ async function setSelectedInstanceId(instanceId) {
99
+ await updateConfig({
100
+ selectedInstanceId: instanceId
101
+ });
102
+ }
103
+ async function storeTokens(accessToken, options) {
104
+ const config = {
105
+ accessToken,
106
+ refreshToken: options?.refreshToken,
107
+ expiresAt: options?.expiresIn ? Date.now() + 1000 * options.expiresIn : void 0,
108
+ email: options?.email,
109
+ lastLogin: Date.now()
110
+ };
111
+ const existing = await loadConfig();
112
+ if (existing?.selectedInstanceId) config.selectedInstanceId = existing.selectedInstanceId;
113
+ await saveConfig(config);
114
+ }
115
+ },
22
116
  "./src/constants.ts" (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
23
117
  __webpack_require__.d(__webpack_exports__, {
24
118
  $V: ()=>STORAGE_MODES,
119
+ Aw: ()=>CLI_INFO,
25
120
  Lk: ()=>LAYOUT_PATTERNS,
26
121
  QL: ()=>REGEX,
122
+ R7: ()=>PATHS,
27
123
  Vj: ()=>PAGES_APP_PATTERNS,
124
+ fT: ()=>TIMEOUTS,
125
+ h3: ()=>ENV_VARS,
28
126
  tl: ()=>URLS
29
127
  });
30
128
  const URLS = {
31
129
  CONSENT_IO: 'https://consent.io',
32
- DOCS: 'https://c15t.dev/docs',
130
+ DOCS: 'https://v2.c15t.com/docs',
33
131
  GITHUB: 'https://github.com/c15t/c15t',
34
- DISCORD: 'https://c15t.dev/discord',
35
- API_DOCS: 'https://c15t.dev/docs/api',
36
- CLI_DOCS: 'https://c15t.dev/docs/cli'
132
+ DISCORD: 'https://v2.c15t.com/discord',
133
+ API_DOCS: 'https://v2.c15t.com/docs/api',
134
+ CLI_DOCS: 'https://v2.c15t.com/docs/cli',
135
+ CHANGELOG: 'https://v2.c15t.com/changelog'
136
+ };
137
+ const PATHS = {
138
+ CONFIG_DIR: '.c15t',
139
+ CONFIG_FILE: 'config.json',
140
+ PROJECT_CONFIG: 'c15t.config.ts',
141
+ PROJECT_CONFIG_JS: 'c15t.config.js',
142
+ ENV_FILE: '.env',
143
+ ENV_LOCAL: '.env.local'
37
144
  };
38
145
  const REGEX = {
39
146
  URL: /^https?:\/\/.+/,
@@ -42,7 +149,28 @@ var __webpack_modules__ = {
42
149
  SEMVER: /^\d+\.\d+\.\d+(-[\w.]+)?$/,
43
150
  PACKAGE_NAME: /^(@[\w-]+\/)?[\w-]+$/
44
151
  };
152
+ const CLI_INFO = {
153
+ NAME: 'c15t',
154
+ BIN: 'c15t',
155
+ CONTROL_PLANE_CLIENT_NAME: 'c15t-cli',
156
+ VERSION: '2.0.0'
157
+ };
158
+ const TIMEOUTS = {
159
+ DEVICE_FLOW_POLL_INTERVAL: 5,
160
+ DEVICE_FLOW_EXPIRY: 900,
161
+ HTTP_REQUEST: 10000,
162
+ CONTROL_PLANE_CONNECTION: 30000
163
+ };
164
+ const ENV_VARS = {
165
+ V2: 'V2',
166
+ TELEMETRY_DISABLED: 'C15T_TELEMETRY_DISABLED',
167
+ CONSENT_URL: 'CONSENT_URL',
168
+ BACKEND_URL: 'C15T_URL',
169
+ API_KEY: 'C15T_API_KEY',
170
+ DEBUG: 'C15T_DEBUG'
171
+ };
45
172
  const STORAGE_MODES = {
173
+ HOSTED: 'hosted',
46
174
  C15T: 'c15t',
47
175
  OFFLINE: 'offline',
48
176
  SELF_HOSTED: 'self-hosted',
@@ -175,113 +303,1925 @@ var __webpack_modules__ = {
175
303
  return extendedLogger;
176
304
  };
177
305
  },
178
- "@clack/prompts" (module) {
179
- module.exports = __rspack_external__clack_prompts_3cae1695;
306
+ "@clack/prompts" (module) {
307
+ module.exports = __rspack_external__clack_prompts_3cae1695;
308
+ },
309
+ "node:fs/promises" (module) {
310
+ module.exports = __rspack_external_node_fs_promises_153e37e0;
311
+ },
312
+ "node:os" (module) {
313
+ module.exports = __rspack_external_node_os_74b4b876;
314
+ },
315
+ "node:path" (module) {
316
+ module.exports = __rspack_external_node_path_c5b9b54f;
317
+ },
318
+ picocolors (module) {
319
+ module.exports = __rspack_external_picocolors;
320
+ },
321
+ "ts-morph" (module) {
322
+ module.exports = __rspack_external_ts_morph_07c7ce60;
323
+ }
324
+ };
325
+ var __webpack_module_cache__ = {};
326
+ function __webpack_require__(moduleId) {
327
+ var cachedModule = __webpack_module_cache__[moduleId];
328
+ if (void 0 !== cachedModule) return cachedModule.exports;
329
+ var module = __webpack_module_cache__[moduleId] = {
330
+ exports: {}
331
+ };
332
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
333
+ return module.exports;
334
+ }
335
+ __webpack_require__.m = __webpack_modules__;
336
+ (()=>{
337
+ __webpack_require__.d = (exports, definition)=>{
338
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
339
+ enumerable: true,
340
+ get: definition[key]
341
+ });
342
+ };
343
+ })();
344
+ (()=>{
345
+ __webpack_require__.f = {};
346
+ __webpack_require__.e = (chunkId)=>Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key)=>{
347
+ __webpack_require__.f[key](chunkId, promises);
348
+ return promises;
349
+ }, []));
350
+ })();
351
+ (()=>{
352
+ __webpack_require__.u = (chunkId)=>"" + chunkId + ".mjs";
353
+ })();
354
+ (()=>{
355
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
356
+ })();
357
+ (()=>{
358
+ var installedChunks = {
359
+ 410: 0
360
+ };
361
+ var installChunk = (data)=>{
362
+ var __rspack_esm_ids = data.__rspack_esm_ids;
363
+ var __webpack_modules__ = data.__webpack_modules__;
364
+ var __rspack_esm_runtime = data.__rspack_esm_runtime;
365
+ var moduleId, chunkId, i = 0;
366
+ for(moduleId in __webpack_modules__)if (__webpack_require__.o(__webpack_modules__, moduleId)) __webpack_require__.m[moduleId] = __webpack_modules__[moduleId];
367
+ if (__rspack_esm_runtime) __rspack_esm_runtime(__webpack_require__);
368
+ for(; i < __rspack_esm_ids.length; i++){
369
+ chunkId = __rspack_esm_ids[i];
370
+ if (__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) installedChunks[chunkId][0]();
371
+ installedChunks[__rspack_esm_ids[i]] = 0;
372
+ }
373
+ };
374
+ __webpack_require__.f.j = function(chunkId, promises) {
375
+ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : void 0;
376
+ if (0 !== installedChunkData) if (installedChunkData) promises.push(installedChunkData[1]);
377
+ else {
378
+ var promise = import("./" + __webpack_require__.u(chunkId)).then(installChunk, (e)=>{
379
+ if (0 !== installedChunks[chunkId]) installedChunks[chunkId] = void 0;
380
+ throw e;
381
+ });
382
+ var promise = Promise.race([
383
+ promise,
384
+ new Promise((resolve)=>{
385
+ installedChunkData = installedChunks[chunkId] = [
386
+ resolve
387
+ ];
388
+ })
389
+ ]);
390
+ promises.push(installedChunkData[1] = promise);
391
+ }
392
+ };
393
+ })();
394
+ var prompts_ = __webpack_require__("@clack/prompts");
395
+ var external_picocolors_ = __webpack_require__("picocolors");
396
+ function showHelpMenu(context, version, commands, flags) {
397
+ const { logger } = context;
398
+ logger.debug('Displaying help menu using command and flag structures.');
399
+ const commandColumnWidth = Math.max(...commands.map((cmd)=>cmd.name.length), 10) + 2;
400
+ const commandLines = commands.map((cmd)=>` ${cmd.name.padEnd(commandColumnWidth)}${cmd.description}`).join('\n');
401
+ const flagDisplays = flags.map((flag)=>{
402
+ const names = flag.names.join(', ');
403
+ const valuePlaceholder = flag.expectsValue ? ' <value>' : '';
404
+ return names + valuePlaceholder;
405
+ });
406
+ const optionColumnWidth = Math.max(...flagDisplays.map((flag)=>flag.length), 20) + 2;
407
+ const optionLines = flags.map((flag, index)=>{
408
+ const display = flagDisplays[index] ?? '';
409
+ return ` ${display.padEnd(optionColumnWidth)}${flag.description}`;
410
+ }).join('\n');
411
+ const helpContent = `c15t CLI version ${version}
412
+
413
+ Available Commands:
414
+ ${commandLines}
415
+
416
+ Options:
417
+ ${optionLines}
418
+
419
+ Run a command directly (e.g., ${external_picocolors_["default"].cyan('c15t setup')}) or select one interactively when no command is provided.
420
+
421
+ For more help, visit: https://v2.c15t.com`;
422
+ logger.debug('Help menu content generated.');
423
+ logger.note(helpContent, 'Usage');
424
+ }
425
+ var promises_ = __webpack_require__("node:fs/promises");
426
+ var external_node_path_ = __webpack_require__("node:path");
427
+ var external_ts_morph_ = __webpack_require__("ts-morph");
428
+ const SUPPORTED_EXTENSIONS = new Set([
429
+ '.ts',
430
+ '.tsx',
431
+ '.js',
432
+ '.jsx'
433
+ ]);
434
+ const IGNORED_DIRS = new Set([
435
+ '.git',
436
+ '.next',
437
+ '.turbo',
438
+ 'coverage',
439
+ 'dist',
440
+ 'build',
441
+ 'node_modules',
442
+ 'out'
443
+ ]);
444
+ const BOOLEAN_STATE_PROPERTIES = {
445
+ showPopup: 'banner',
446
+ isPrivacyDialogOpen: 'dialog'
447
+ };
448
+ const LEGACY_SETTER_NAMES = {
449
+ setShowPopup: 'banner',
450
+ setIsPrivacyDialogOpen: 'dialog'
451
+ };
452
+ function getBindingPropertyName(element) {
453
+ const propertyNameNode = element.getPropertyNameNode();
454
+ if (propertyNameNode) {
455
+ if (external_ts_morph_.Node.isIdentifier(propertyNameNode)) return propertyNameNode.getText();
456
+ if (external_ts_morph_.Node.isStringLiteral(propertyNameNode)) return propertyNameNode.getLiteralText();
457
+ return;
458
+ }
459
+ const nameNode = element.getNameNode();
460
+ if (!external_ts_morph_.Node.isIdentifier(nameNode)) return;
461
+ return nameNode.getText();
462
+ }
463
+ function mapBooleanExpressionToUi(expressionText, activeValue) {
464
+ const trimmed = expressionText?.trim();
465
+ if (!trimmed || 'true' === trimmed) return `'${activeValue}'`;
466
+ if ('false' === trimmed) return "'none'";
467
+ return `${trimmed} ? '${activeValue}' : 'none'`;
468
+ }
469
+ function mapShowPopupCall(callee, args) {
470
+ const uiArgument = mapBooleanExpressionToUi(args[0], 'banner');
471
+ const forceArg = args[1]?.trim();
472
+ if (!forceArg || 'false' === forceArg || 'undefined' === forceArg) return {
473
+ text: `${callee}(${uiArgument})`,
474
+ operations: 1
475
+ };
476
+ if ('true' === forceArg) return {
477
+ text: `${callee}(${uiArgument}, { force: true })`,
478
+ operations: 1
479
+ };
480
+ return {
481
+ text: `${callee}(${uiArgument}, ${forceArg} ? { force: true } : undefined)`,
482
+ operations: 1
483
+ };
484
+ }
485
+ function mapPrivacyDialogCall(callee, args) {
486
+ const uiArgument = mapBooleanExpressionToUi(args[0], 'dialog');
487
+ return `${callee}(${uiArgument})`;
488
+ }
489
+ function transformSourceFile(sourceFile) {
490
+ let operations = 0;
491
+ const summaries = [];
492
+ const booleanAliases = new Map();
493
+ const setterAliases = new Map();
494
+ const bindingElements = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.BindingElement);
495
+ for (const element of bindingElements){
496
+ const propertyName = getBindingPropertyName(element);
497
+ if (!propertyName) continue;
498
+ const localNameNode = element.getNameNode();
499
+ if (!external_ts_morph_.Node.isIdentifier(localNameNode)) continue;
500
+ const localName = localNameNode.getText();
501
+ if (propertyName in BOOLEAN_STATE_PROPERTIES) {
502
+ const stateValue = BOOLEAN_STATE_PROPERTIES[propertyName];
503
+ booleanAliases.set(localName, stateValue);
504
+ const propertyNameNode = element.getPropertyNameNode();
505
+ if (propertyNameNode) propertyNameNode.replaceWithText('activeUI');
506
+ else {
507
+ const initializerText = element.getInitializer()?.getText();
508
+ let replacement = `activeUI: ${localName}`;
509
+ if (initializerText) replacement += ` = ${initializerText}`;
510
+ element.replaceWithText(replacement);
511
+ }
512
+ operations += 1;
513
+ summaries.push(`${propertyName} -> activeUI`);
514
+ continue;
515
+ }
516
+ if (propertyName in LEGACY_SETTER_NAMES) {
517
+ setterAliases.set(localName, propertyName);
518
+ const propertyNameNode = element.getPropertyNameNode();
519
+ if (propertyNameNode) propertyNameNode.replaceWithText('setActiveUI');
520
+ else {
521
+ const initializerText = element.getInitializer()?.getText();
522
+ let replacement = `setActiveUI: ${localName}`;
523
+ if (initializerText) replacement += ` = ${initializerText}`;
524
+ element.replaceWithText(replacement);
525
+ }
526
+ operations += 1;
527
+ summaries.push(`${propertyName} -> setActiveUI alias`);
528
+ }
529
+ }
530
+ const callExpressions = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.CallExpression);
531
+ for (const callExpression of callExpressions){
532
+ const expression = callExpression.getExpression();
533
+ const args = callExpression.getArguments().map((argument)=>argument.getText());
534
+ if (external_ts_morph_.Node.isPropertyAccessExpression(expression)) {
535
+ const methodName = expression.getName();
536
+ if (!(methodName in LEGACY_SETTER_NAMES)) continue;
537
+ const receiver = expression.getExpression().getText();
538
+ const callee = `${receiver}.setActiveUI`;
539
+ let replacement = '';
540
+ if ('setShowPopup' === methodName) {
541
+ replacement = mapShowPopupCall(callee, args).text;
542
+ summaries.push('setShowPopup(...) -> setActiveUI(...)');
543
+ } else {
544
+ replacement = mapPrivacyDialogCall(callee, args);
545
+ summaries.push('setIsPrivacyDialogOpen(...) -> setActiveUI(...)');
546
+ }
547
+ callExpression.replaceWithText(replacement);
548
+ operations += 1;
549
+ continue;
550
+ }
551
+ if (!external_ts_morph_.Node.isIdentifier(expression)) continue;
552
+ const calleeName = expression.getText();
553
+ const aliasKind = setterAliases.get(calleeName);
554
+ if (aliasKind) {
555
+ if ('setShowPopup' === aliasKind) {
556
+ const mapped = mapShowPopupCall(calleeName, args);
557
+ callExpression.replaceWithText(mapped.text);
558
+ summaries.push('setShowPopup alias call -> setActiveUI args');
559
+ } else {
560
+ callExpression.replaceWithText(mapPrivacyDialogCall(calleeName, args));
561
+ summaries.push('setIsPrivacyDialogOpen alias call -> setActiveUI args');
562
+ }
563
+ operations += 1;
564
+ }
565
+ }
566
+ const propertyAccesses = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAccessExpression);
567
+ for (const propertyAccess of propertyAccesses){
568
+ const propertyName = propertyAccess.getName();
569
+ if (!(propertyName in BOOLEAN_STATE_PROPERTIES)) continue;
570
+ const parent = propertyAccess.getParent();
571
+ if (external_ts_morph_.Node.isBinaryExpression(parent) && parent.getLeft() === propertyAccess) continue;
572
+ const receiver = propertyAccess.getExpression().getText();
573
+ const activeValue = BOOLEAN_STATE_PROPERTIES[propertyName];
574
+ propertyAccess.replaceWithText(`(${receiver}.activeUI === '${activeValue}')`);
575
+ operations += 1;
576
+ summaries.push(`${propertyName} state check -> activeUI comparison`);
577
+ }
578
+ const identifiers = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.Identifier);
579
+ for (const identifier of identifiers){
580
+ if (identifier.wasForgotten()) continue;
581
+ const identifierText = identifier.getText();
582
+ const aliasTarget = booleanAliases.get(identifierText);
583
+ if (!aliasTarget) continue;
584
+ const parent = identifier.getParent();
585
+ if (!external_ts_morph_.Node.isBindingElement(parent) || parent.getNameNode() !== identifier) {
586
+ if (!external_ts_morph_.Node.isPropertyAccessExpression(parent) || parent.getNameNode() !== identifier) {
587
+ if (!external_ts_morph_.Node.isPropertyAssignment(parent) || parent.getNameNode() !== identifier) {
588
+ if (external_ts_morph_.Node.isShorthandPropertyAssignment(parent)) {
589
+ const name = identifierText;
590
+ parent.replaceWithText(`${name}: (${name} === '${aliasTarget}')`);
591
+ operations += 1;
592
+ summaries.push(`${name} shorthand -> activeUI comparison`);
593
+ continue;
594
+ }
595
+ identifier.replaceWithText(`(${identifierText} === '${aliasTarget}')`);
596
+ operations += 1;
597
+ summaries.push(`${identifierText} usage -> activeUI comparison`);
598
+ }
599
+ }
600
+ }
601
+ }
602
+ return {
603
+ changed: operations > 0,
604
+ operations,
605
+ summaries: [
606
+ ...new Set(summaries)
607
+ ]
608
+ };
609
+ }
610
+ async function collectSourceFiles(rootDir) {
611
+ const files = [];
612
+ async function walk(currentDir) {
613
+ const entries = await (0, promises_.readdir)(currentDir, {
614
+ withFileTypes: true
615
+ });
616
+ for (const entry of entries){
617
+ if (entry.isSymbolicLink()) continue;
618
+ if (entry.isDirectory()) {
619
+ if (IGNORED_DIRS.has(entry.name)) continue;
620
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
621
+ continue;
622
+ }
623
+ if (!entry.isFile()) continue;
624
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
625
+ if (SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
626
+ }
627
+ }
628
+ await walk(rootDir);
629
+ return files;
630
+ }
631
+ async function runActiveUiApiCodemod(options) {
632
+ const project = new external_ts_morph_.Project({
633
+ skipAddingFilesFromTsConfig: true,
634
+ compilerOptions: {
635
+ allowJs: true
636
+ }
637
+ });
638
+ const filePaths = await collectSourceFiles(options.projectRoot);
639
+ const changedFiles = [];
640
+ const errors = [];
641
+ for (const filePath of filePaths)try {
642
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
643
+ if (!sourceFile) continue;
644
+ const result = transformSourceFile(sourceFile);
645
+ if (!result.changed) continue;
646
+ changedFiles.push({
647
+ filePath,
648
+ operations: result.operations,
649
+ summaries: result.summaries
650
+ });
651
+ if (!options.dryRun) await sourceFile.save();
652
+ } catch (error) {
653
+ errors.push({
654
+ filePath,
655
+ error: error instanceof Error ? error.message : String(error)
656
+ });
657
+ }
658
+ return {
659
+ totalFiles: filePaths.length,
660
+ changedFiles,
661
+ errors
662
+ };
663
+ }
664
+ const component_renames_SUPPORTED_EXTENSIONS = new Set([
665
+ '.ts',
666
+ '.tsx',
667
+ '.js',
668
+ '.jsx'
669
+ ]);
670
+ const component_renames_IGNORED_DIRS = new Set([
671
+ '.git',
672
+ '.next',
673
+ '.turbo',
674
+ 'coverage',
675
+ 'dist',
676
+ 'build',
677
+ 'node_modules',
678
+ 'out'
679
+ ]);
680
+ const C15T_REACT_PACKAGES = new Set([
681
+ '@c15t/react',
682
+ '@c15t/nextjs'
683
+ ]);
684
+ const RENAME_MAP = {
685
+ CookieBanner: 'ConsentBanner',
686
+ ConsentManagerDialog: 'ConsentDialog',
687
+ ConsentManagerWidget: 'ConsentWidget',
688
+ CookieBannerProps: 'ConsentBannerProps',
689
+ ConsentManagerDialogProps: 'ConsentDialogProps',
690
+ ConsentManagerWidgetProps: 'ConsentWidgetProps'
691
+ };
692
+ function hasLegacyC15tComponentImport(sourceFile) {
693
+ for (const importDeclaration of sourceFile.getImportDeclarations()){
694
+ const specifier = importDeclaration.getModuleSpecifierValue();
695
+ if (C15T_REACT_PACKAGES.has(specifier)) for (const namedImport of importDeclaration.getNamedImports()){
696
+ const importedName = namedImport.getNameNode().getText();
697
+ if (importedName in RENAME_MAP) return true;
698
+ }
699
+ }
700
+ return false;
701
+ }
702
+ function component_renames_transformSourceFile(sourceFile) {
703
+ if (!hasLegacyC15tComponentImport(sourceFile)) return {
704
+ changed: false,
705
+ operations: 0,
706
+ summaries: []
707
+ };
708
+ let operations = 0;
709
+ const summaries = [];
710
+ const identifiers = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.Identifier);
711
+ for (const identifier of identifiers){
712
+ const identifierText = identifier.getText();
713
+ if (!(identifierText in RENAME_MAP)) continue;
714
+ const replacement = RENAME_MAP[identifierText];
715
+ const parent = identifier.getParent();
716
+ if (external_ts_morph_.Node.isPropertyAssignment(parent) && parent.getNameNode() === identifier) continue;
717
+ if (!external_ts_morph_.Node.isPropertyAccessExpression(parent) || parent.getNameNode() !== identifier) {
718
+ if (!external_ts_morph_.Node.isImportSpecifier(parent) || parent.getAliasNode() !== identifier) {
719
+ if (!external_ts_morph_.Node.isShorthandPropertyAssignment(parent) || parent.getNameNode() !== identifier) {
720
+ identifier.replaceWithText(replacement);
721
+ operations += 1;
722
+ summaries.push(`${identifierText} -> ${replacement}`);
723
+ }
724
+ }
725
+ }
726
+ }
727
+ return {
728
+ changed: operations > 0,
729
+ operations,
730
+ summaries: [
731
+ ...new Set(summaries)
732
+ ]
733
+ };
734
+ }
735
+ async function component_renames_collectSourceFiles(rootDir) {
736
+ const files = [];
737
+ async function walk(currentDir) {
738
+ const entries = await (0, promises_.readdir)(currentDir, {
739
+ withFileTypes: true
740
+ });
741
+ for (const entry of entries){
742
+ if (entry.isSymbolicLink()) continue;
743
+ if (entry.isDirectory()) {
744
+ if (component_renames_IGNORED_DIRS.has(entry.name)) continue;
745
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
746
+ continue;
747
+ }
748
+ if (!entry.isFile()) continue;
749
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
750
+ if (component_renames_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
751
+ }
752
+ }
753
+ await walk(rootDir);
754
+ return files;
755
+ }
756
+ async function runComponentRenamesCodemod(options) {
757
+ const project = new external_ts_morph_.Project({
758
+ skipAddingFilesFromTsConfig: true,
759
+ compilerOptions: {
760
+ allowJs: true
761
+ }
762
+ });
763
+ const filePaths = await component_renames_collectSourceFiles(options.projectRoot);
764
+ const changedFiles = [];
765
+ const errors = [];
766
+ for (const filePath of filePaths)try {
767
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
768
+ if (!sourceFile) continue;
769
+ const result = component_renames_transformSourceFile(sourceFile);
770
+ if (!result.changed) continue;
771
+ changedFiles.push({
772
+ filePath,
773
+ operations: result.operations,
774
+ summaries: result.summaries
775
+ });
776
+ if (!options.dryRun) await sourceFile.save();
777
+ } catch (error) {
778
+ errors.push({
779
+ filePath,
780
+ error: error instanceof Error ? error.message : String(error)
781
+ });
782
+ }
783
+ return {
784
+ totalFiles: filePaths.length,
785
+ changedFiles,
786
+ errors
787
+ };
788
+ }
789
+ const gdpr_types_to_consent_categories_SUPPORTED_EXTENSIONS = new Set([
790
+ '.ts',
791
+ '.tsx',
792
+ '.js',
793
+ '.jsx'
794
+ ]);
795
+ const gdpr_types_to_consent_categories_IGNORED_DIRS = new Set([
796
+ '.git',
797
+ '.next',
798
+ '.turbo',
799
+ 'coverage',
800
+ 'dist',
801
+ 'build',
802
+ 'node_modules',
803
+ 'out'
804
+ ]);
805
+ const LEGACY_KEYS = new Set([
806
+ 'gdprTypes',
807
+ 'initialGDPRTypes'
808
+ ]);
809
+ const NEXT_KEY = 'consentCategories';
810
+ function getPropertyName(property) {
811
+ const rawName = property.getNameNode().getText().trim();
812
+ return rawName.replace(/^['"]|['"]$/g, '');
813
+ }
814
+ function objectHasConsentCategories(objectLiteral) {
815
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
816
+ if (getPropertyName(property) === NEXT_KEY) return true;
817
+ }
818
+ return false;
819
+ }
820
+ function gdpr_types_to_consent_categories_getBindingPropertyName(element) {
821
+ const propertyNameNode = element.getPropertyNameNode();
822
+ if (propertyNameNode) {
823
+ if (external_ts_morph_.Node.isIdentifier(propertyNameNode)) return propertyNameNode.getText();
824
+ if (external_ts_morph_.Node.isStringLiteral(propertyNameNode)) return propertyNameNode.getLiteralText();
825
+ return;
826
+ }
827
+ const nameNode = element.getNameNode();
828
+ if (!external_ts_morph_.Node.isIdentifier(nameNode)) return;
829
+ return nameNode.getText();
830
+ }
831
+ function gdpr_types_to_consent_categories_transformSourceFile(sourceFile) {
832
+ let operations = 0;
833
+ const summaries = [];
834
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
835
+ for (const property of propertyAssignments){
836
+ const name = getPropertyName(property);
837
+ if (!LEGACY_KEYS.has(name)) continue;
838
+ const parent = property.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
839
+ if (!(parent && objectHasConsentCategories(parent))) {
840
+ property.getNameNode().replaceWithText(NEXT_KEY);
841
+ operations += 1;
842
+ summaries.push(`${name} -> ${NEXT_KEY}`);
843
+ }
844
+ }
845
+ const shorthandAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.ShorthandPropertyAssignment);
846
+ for (const shorthand of shorthandAssignments){
847
+ const name = shorthand.getNameNode().getText();
848
+ if (!LEGACY_KEYS.has(name)) continue;
849
+ const parent = shorthand.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
850
+ if (!(parent && objectHasConsentCategories(parent))) {
851
+ shorthand.replaceWithText(`${NEXT_KEY}: ${name}`);
852
+ operations += 1;
853
+ summaries.push(`${name} shorthand -> ${NEXT_KEY}`);
854
+ }
855
+ }
856
+ const bindingElements = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.BindingElement);
857
+ for (const element of bindingElements){
858
+ const propertyName = gdpr_types_to_consent_categories_getBindingPropertyName(element);
859
+ if (!propertyName || !LEGACY_KEYS.has(propertyName)) continue;
860
+ const propertyNameNode = element.getPropertyNameNode();
861
+ if (propertyNameNode) propertyNameNode.replaceWithText(NEXT_KEY);
862
+ else {
863
+ const nameNode = element.getNameNode();
864
+ if (!external_ts_morph_.Node.isIdentifier(nameNode)) continue;
865
+ const localName = nameNode.getText();
866
+ const initializerText = element.getInitializer()?.getText();
867
+ let replacement = `${NEXT_KEY}: ${localName}`;
868
+ if (initializerText) replacement += ` = ${initializerText}`;
869
+ element.replaceWithText(replacement);
870
+ }
871
+ operations += 1;
872
+ summaries.push(`${propertyName} destructuring -> ${NEXT_KEY}`);
873
+ }
874
+ const propertyAccesses = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAccessExpression);
875
+ for (const propertyAccess of propertyAccesses){
876
+ const name = propertyAccess.getName();
877
+ if (!LEGACY_KEYS.has(name)) continue;
878
+ const expressionText = propertyAccess.getExpression().getText();
879
+ propertyAccess.replaceWithText(`${expressionText}.${NEXT_KEY}`);
880
+ operations += 1;
881
+ summaries.push(`${name} access -> ${NEXT_KEY}`);
882
+ }
883
+ return {
884
+ changed: operations > 0,
885
+ operations,
886
+ summaries: [
887
+ ...new Set(summaries)
888
+ ]
889
+ };
890
+ }
891
+ async function gdpr_types_to_consent_categories_collectSourceFiles(rootDir) {
892
+ const files = [];
893
+ async function walk(currentDir) {
894
+ const entries = await (0, promises_.readdir)(currentDir, {
895
+ withFileTypes: true
896
+ });
897
+ for (const entry of entries){
898
+ if (entry.isSymbolicLink()) continue;
899
+ if (entry.isDirectory()) {
900
+ if (gdpr_types_to_consent_categories_IGNORED_DIRS.has(entry.name)) continue;
901
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
902
+ continue;
903
+ }
904
+ if (!entry.isFile()) continue;
905
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
906
+ if (gdpr_types_to_consent_categories_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
907
+ }
908
+ }
909
+ await walk(rootDir);
910
+ return files;
911
+ }
912
+ async function runGdprTypesToConsentCategoriesCodemod(options) {
913
+ const project = new external_ts_morph_.Project({
914
+ skipAddingFilesFromTsConfig: true,
915
+ compilerOptions: {
916
+ allowJs: true
917
+ }
918
+ });
919
+ const filePaths = await gdpr_types_to_consent_categories_collectSourceFiles(options.projectRoot);
920
+ const changedFiles = [];
921
+ const errors = [];
922
+ for (const filePath of filePaths)try {
923
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
924
+ if (!sourceFile) continue;
925
+ const result = gdpr_types_to_consent_categories_transformSourceFile(sourceFile);
926
+ if (!result.changed) continue;
927
+ changedFiles.push({
928
+ filePath,
929
+ operations: result.operations,
930
+ summaries: result.summaries
931
+ });
932
+ if (!options.dryRun) await sourceFile.save();
933
+ } catch (error) {
934
+ errors.push({
935
+ filePath,
936
+ error: error instanceof Error ? error.message : String(error)
937
+ });
938
+ }
939
+ return {
940
+ totalFiles: filePaths.length,
941
+ changedFiles,
942
+ errors
943
+ };
944
+ }
945
+ const ignore_geo_location_to_overrides_SUPPORTED_EXTENSIONS = new Set([
946
+ '.ts',
947
+ '.tsx',
948
+ '.js',
949
+ '.jsx'
950
+ ]);
951
+ const ignore_geo_location_to_overrides_IGNORED_DIRS = new Set([
952
+ '.git',
953
+ '.next',
954
+ '.turbo',
955
+ 'coverage',
956
+ 'dist',
957
+ 'build',
958
+ 'node_modules',
959
+ 'out'
960
+ ]);
961
+ const LEGACY_KEY = 'ignoreGeoLocation';
962
+ const ignore_geo_location_to_overrides_NEXT_KEY = 'overrides';
963
+ const DEFAULT_COUNTRY_CODE = 'DE';
964
+ function ignore_geo_location_to_overrides_getPropertyName(property) {
965
+ const rawName = property.getNameNode().getText().trim();
966
+ return rawName.replace(/^['"]|['"]$/g, '');
967
+ }
968
+ function getProperty(objectLiteral, name) {
969
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
970
+ if (ignore_geo_location_to_overrides_getPropertyName(property) === name) return property;
971
+ }
972
+ }
973
+ function objectHasCountry(objectLiteral) {
974
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
975
+ if ('country' === ignore_geo_location_to_overrides_getPropertyName(property)) return true;
976
+ }
977
+ return false;
978
+ }
979
+ function mergeCountryIntoOverridesExpression(overridesExpressionText, ignoreExpressionText) {
980
+ if ('true' === ignoreExpressionText) return `{ ...(${overridesExpressionText}), country: (${overridesExpressionText})?.country ?? '${DEFAULT_COUNTRY_CODE}' }`;
981
+ if ('false' === ignoreExpressionText) return overridesExpressionText;
982
+ return `${ignoreExpressionText} ? { ...(${overridesExpressionText}), country: (${overridesExpressionText})?.country ?? '${DEFAULT_COUNTRY_CODE}' } : (${overridesExpressionText})`;
983
+ }
984
+ function ignore_geo_location_to_overrides_transformSourceFile(sourceFile) {
985
+ let operations = 0;
986
+ const summaries = [];
987
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
988
+ for (const property of propertyAssignments){
989
+ if (property.wasForgotten() || ignore_geo_location_to_overrides_getPropertyName(property) !== LEGACY_KEY) continue;
990
+ const parentObject = property.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
991
+ if (!parentObject) continue;
992
+ const ignoreExpressionText = property.getInitializer()?.getText()?.trim() ?? 'true';
993
+ const overridesProperty = getProperty(parentObject, ignore_geo_location_to_overrides_NEXT_KEY);
994
+ if (!overridesProperty) {
995
+ if ('false' === ignoreExpressionText) {
996
+ property.remove();
997
+ operations += 1;
998
+ summaries.push('removed ignoreGeoLocation: false');
999
+ continue;
1000
+ }
1001
+ property.getNameNode().replaceWithText(ignore_geo_location_to_overrides_NEXT_KEY);
1002
+ if ('true' === ignoreExpressionText) {
1003
+ property.setInitializer(`{ country: '${DEFAULT_COUNTRY_CODE}' }`);
1004
+ summaries.push('ignoreGeoLocation -> overrides.country');
1005
+ } else {
1006
+ property.setInitializer(`${ignoreExpressionText} ? { country: '${DEFAULT_COUNTRY_CODE}' } : undefined`);
1007
+ summaries.push('ignoreGeoLocation expression -> conditional overrides.country');
1008
+ }
1009
+ operations += 1;
1010
+ continue;
1011
+ }
1012
+ const overridesInitializer = overridesProperty.getInitializer();
1013
+ const overridesObject = overridesInitializer?.asKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1014
+ if (overridesObject) {
1015
+ if (!objectHasCountry(overridesObject)) {
1016
+ if ('true' === ignoreExpressionText) {
1017
+ overridesObject.addPropertyAssignment({
1018
+ name: 'country',
1019
+ initializer: `'${DEFAULT_COUNTRY_CODE}'`
1020
+ });
1021
+ summaries.push('merged ignoreGeoLocation into overrides.country');
1022
+ operations += 1;
1023
+ } else if ('false' !== ignoreExpressionText) {
1024
+ overridesObject.addPropertyAssignment({
1025
+ name: 'country',
1026
+ initializer: `${ignoreExpressionText} ? '${DEFAULT_COUNTRY_CODE}' : undefined`
1027
+ });
1028
+ summaries.push('merged ignoreGeoLocation expression into overrides.country');
1029
+ operations += 1;
1030
+ }
1031
+ }
1032
+ } else if (overridesInitializer) {
1033
+ const mergedExpression = mergeCountryIntoOverridesExpression(overridesInitializer.getText().trim(), ignoreExpressionText);
1034
+ if (mergedExpression !== overridesInitializer.getText().trim()) {
1035
+ overridesProperty.setInitializer(mergedExpression);
1036
+ summaries.push('merged ignoreGeoLocation into existing overrides expression');
1037
+ operations += 1;
1038
+ }
1039
+ }
1040
+ property.remove();
1041
+ operations += 1;
1042
+ summaries.push('removed ignoreGeoLocation property');
1043
+ }
1044
+ return {
1045
+ changed: operations > 0,
1046
+ operations,
1047
+ summaries: [
1048
+ ...new Set(summaries)
1049
+ ]
1050
+ };
1051
+ }
1052
+ async function ignore_geo_location_to_overrides_collectSourceFiles(rootDir) {
1053
+ const files = [];
1054
+ async function walk(currentDir) {
1055
+ const entries = await (0, promises_.readdir)(currentDir, {
1056
+ withFileTypes: true
1057
+ });
1058
+ for (const entry of entries){
1059
+ if (entry.isSymbolicLink()) continue;
1060
+ if (entry.isDirectory()) {
1061
+ if (ignore_geo_location_to_overrides_IGNORED_DIRS.has(entry.name)) continue;
1062
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
1063
+ continue;
1064
+ }
1065
+ if (!entry.isFile()) continue;
1066
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
1067
+ if (ignore_geo_location_to_overrides_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
1068
+ }
1069
+ }
1070
+ await walk(rootDir);
1071
+ return files;
1072
+ }
1073
+ async function runIgnoreGeoLocationToOverridesCodemod(options) {
1074
+ const project = new external_ts_morph_.Project({
1075
+ skipAddingFilesFromTsConfig: true,
1076
+ compilerOptions: {
1077
+ allowJs: true
1078
+ }
1079
+ });
1080
+ const filePaths = await ignore_geo_location_to_overrides_collectSourceFiles(options.projectRoot);
1081
+ const changedFiles = [];
1082
+ const errors = [];
1083
+ for (const filePath of filePaths)try {
1084
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
1085
+ if (!sourceFile) continue;
1086
+ const result = ignore_geo_location_to_overrides_transformSourceFile(sourceFile);
1087
+ if (!result.changed) continue;
1088
+ changedFiles.push({
1089
+ filePath,
1090
+ operations: result.operations,
1091
+ summaries: result.summaries
1092
+ });
1093
+ if (!options.dryRun) await sourceFile.save();
1094
+ } catch (error) {
1095
+ errors.push({
1096
+ filePath,
1097
+ error: error instanceof Error ? error.message : String(error)
1098
+ });
1099
+ }
1100
+ return {
1101
+ totalFiles: filePaths.length,
1102
+ changedFiles,
1103
+ errors
1104
+ };
1105
+ }
1106
+ const mode_c15t_to_hosted_SUPPORTED_EXTENSIONS = new Set([
1107
+ '.ts',
1108
+ '.tsx',
1109
+ '.js',
1110
+ '.jsx'
1111
+ ]);
1112
+ const mode_c15t_to_hosted_IGNORED_DIRS = new Set([
1113
+ '.git',
1114
+ '.next',
1115
+ '.turbo',
1116
+ 'coverage',
1117
+ 'dist',
1118
+ 'build',
1119
+ 'node_modules',
1120
+ 'out'
1121
+ ]);
1122
+ function mode_c15t_to_hosted_getPropertyName(property) {
1123
+ const rawName = property.getNameNode().getText().trim();
1124
+ return rawName.replace(/^['"]|['"]$/g, '');
1125
+ }
1126
+ function mode_c15t_to_hosted_transformSourceFile(sourceFile) {
1127
+ let operations = 0;
1128
+ const summaries = [];
1129
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
1130
+ for (const property of propertyAssignments){
1131
+ if ('mode' !== mode_c15t_to_hosted_getPropertyName(property)) continue;
1132
+ const initializer = property.getInitializerIfKind(external_ts_morph_.SyntaxKind.StringLiteral);
1133
+ if (initializer) {
1134
+ if ('c15t' === initializer.getLiteralValue()) {
1135
+ initializer.setLiteralValue('hosted');
1136
+ operations += 1;
1137
+ summaries.push("mode 'c15t' -> 'hosted'");
1138
+ }
1139
+ }
1140
+ }
1141
+ return {
1142
+ changed: operations > 0,
1143
+ operations,
1144
+ summaries: [
1145
+ ...new Set(summaries)
1146
+ ]
1147
+ };
1148
+ }
1149
+ async function mode_c15t_to_hosted_collectSourceFiles(rootDir) {
1150
+ const files = [];
1151
+ async function walk(currentDir) {
1152
+ const entries = await (0, promises_.readdir)(currentDir, {
1153
+ withFileTypes: true
1154
+ });
1155
+ for (const entry of entries){
1156
+ if (entry.isSymbolicLink()) continue;
1157
+ if (entry.isDirectory()) {
1158
+ if (mode_c15t_to_hosted_IGNORED_DIRS.has(entry.name)) continue;
1159
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
1160
+ continue;
1161
+ }
1162
+ if (!entry.isFile()) continue;
1163
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
1164
+ if (mode_c15t_to_hosted_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
1165
+ }
1166
+ }
1167
+ await walk(rootDir);
1168
+ return files;
1169
+ }
1170
+ async function runC15tModeToHostedCodemod(options) {
1171
+ const project = new external_ts_morph_.Project({
1172
+ skipAddingFilesFromTsConfig: true,
1173
+ compilerOptions: {
1174
+ allowJs: true
1175
+ }
1176
+ });
1177
+ const filePaths = await mode_c15t_to_hosted_collectSourceFiles(options.projectRoot);
1178
+ const changedFiles = [];
1179
+ const errors = [];
1180
+ for (const filePath of filePaths)try {
1181
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
1182
+ if (!sourceFile) continue;
1183
+ const result = mode_c15t_to_hosted_transformSourceFile(sourceFile);
1184
+ if (!result.changed) continue;
1185
+ changedFiles.push({
1186
+ filePath,
1187
+ operations: result.operations,
1188
+ summaries: result.summaries
1189
+ });
1190
+ if (!options.dryRun) await sourceFile.save();
1191
+ } catch (error) {
1192
+ errors.push({
1193
+ filePath,
1194
+ error: error instanceof Error ? error.message : String(error)
1195
+ });
1196
+ }
1197
+ return {
1198
+ totalFiles: filePaths.length,
1199
+ changedFiles,
1200
+ errors
1201
+ };
1202
+ }
1203
+ const offline_add_policy_packs_SUPPORTED_EXTENSIONS = new Set([
1204
+ '.ts',
1205
+ '.tsx',
1206
+ '.js',
1207
+ '.jsx'
1208
+ ]);
1209
+ const offline_add_policy_packs_IGNORED_DIRS = new Set([
1210
+ '.git',
1211
+ '.next',
1212
+ '.turbo',
1213
+ 'coverage',
1214
+ 'dist',
1215
+ 'build',
1216
+ 'node_modules',
1217
+ 'out'
1218
+ ]);
1219
+ function offline_add_policy_packs_getPropertyName(property) {
1220
+ const rawName = property.getNameNode().getText().trim();
1221
+ return rawName.replace(/^['"]|['"]$/g, '');
1222
+ }
1223
+ function offline_add_policy_packs_getProperty(objectLiteral, name) {
1224
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
1225
+ if (offline_add_policy_packs_getPropertyName(property) === name) return property;
1226
+ }
1227
+ }
1228
+ const STARTER_POLICY_PACK = '[\n\t\t\tpolicyPackPresets.europeOptIn(),\n\t\t\tpolicyPackPresets.californiaOptOut(),\n\t\t\tpolicyPackPresets.worldNoBanner(),\n\t\t]';
1229
+ function offline_add_policy_packs_transformSourceFile(sourceFile) {
1230
+ let operations = 0;
1231
+ const summaries = [];
1232
+ let needsImport = false;
1233
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
1234
+ for (const property of propertyAssignments){
1235
+ if (property.wasForgotten() || 'mode' !== offline_add_policy_packs_getPropertyName(property)) continue;
1236
+ const initializer = property.getInitializerIfKind(external_ts_morph_.SyntaxKind.StringLiteral);
1237
+ if (!initializer || 'offline' !== initializer.getLiteralValue()) continue;
1238
+ const configObject = property.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1239
+ if (!configObject) continue;
1240
+ const offlinePolicyProperty = offline_add_policy_packs_getProperty(configObject, 'offlinePolicy');
1241
+ if (offlinePolicyProperty) {
1242
+ const offlinePolicyObject = offlinePolicyProperty.getInitializerIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1243
+ if (offlinePolicyObject && offline_add_policy_packs_getProperty(offlinePolicyObject, 'policyPacks')) continue;
1244
+ }
1245
+ if (offlinePolicyProperty) {
1246
+ const offlinePolicyObject = offlinePolicyProperty.getInitializerIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1247
+ if (offlinePolicyObject) {
1248
+ offlinePolicyObject.addPropertyAssignment({
1249
+ name: 'policyPacks',
1250
+ initializer: STARTER_POLICY_PACK
1251
+ });
1252
+ operations += 1;
1253
+ needsImport = true;
1254
+ summaries.push('added policyPacks: starter presets');
1255
+ }
1256
+ } else {
1257
+ configObject.addPropertyAssignment({
1258
+ name: 'offlinePolicy',
1259
+ initializer: `{\n\t\tpolicyPacks: ${STARTER_POLICY_PACK},\n\t}`
1260
+ });
1261
+ operations += 1;
1262
+ needsImport = true;
1263
+ summaries.push('added offlinePolicy.policyPacks with starter presets');
1264
+ }
1265
+ }
1266
+ if (needsImport) {
1267
+ const existingImports = sourceFile.getImportDeclarations();
1268
+ const alreadyImported = existingImports.some((decl)=>{
1269
+ const namedImports = decl.getNamedImports();
1270
+ return namedImports.some((ni)=>'policyPackPresets' === ni.getName());
1271
+ });
1272
+ if (!alreadyImported) {
1273
+ const c15tImport = existingImports.find((decl)=>{
1274
+ const moduleSpecifier = decl.getModuleSpecifierValue();
1275
+ return 'c15t' === moduleSpecifier || '@c15t/react' === moduleSpecifier || '@c15t/nextjs' === moduleSpecifier;
1276
+ });
1277
+ if (c15tImport) {
1278
+ c15tImport.addNamedImport('policyPackPresets');
1279
+ summaries.push('added policyPackPresets to existing import');
1280
+ } else {
1281
+ sourceFile.addImportDeclaration({
1282
+ namedImports: [
1283
+ 'policyPackPresets'
1284
+ ],
1285
+ moduleSpecifier: 'c15t'
1286
+ });
1287
+ summaries.push("added import { policyPackPresets } from 'c15t'");
1288
+ }
1289
+ operations += 1;
1290
+ }
1291
+ }
1292
+ return {
1293
+ changed: operations > 0,
1294
+ operations,
1295
+ summaries: [
1296
+ ...new Set(summaries)
1297
+ ]
1298
+ };
1299
+ }
1300
+ async function offline_add_policy_packs_collectSourceFiles(rootDir) {
1301
+ const files = [];
1302
+ async function walk(currentDir) {
1303
+ const entries = await (0, promises_.readdir)(currentDir, {
1304
+ withFileTypes: true
1305
+ });
1306
+ for (const entry of entries){
1307
+ if (entry.isSymbolicLink()) continue;
1308
+ if (entry.isDirectory()) {
1309
+ if (offline_add_policy_packs_IGNORED_DIRS.has(entry.name)) continue;
1310
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
1311
+ continue;
1312
+ }
1313
+ if (!entry.isFile()) continue;
1314
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
1315
+ if (offline_add_policy_packs_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
1316
+ }
1317
+ }
1318
+ await walk(rootDir);
1319
+ return files;
1320
+ }
1321
+ async function runOfflineAddPolicyPacksCodemod(options) {
1322
+ const project = new external_ts_morph_.Project({
1323
+ skipAddingFilesFromTsConfig: true,
1324
+ compilerOptions: {
1325
+ allowJs: true
1326
+ }
1327
+ });
1328
+ const filePaths = await offline_add_policy_packs_collectSourceFiles(options.projectRoot);
1329
+ const changedFiles = [];
1330
+ const errors = [];
1331
+ for (const filePath of filePaths)try {
1332
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
1333
+ if (!sourceFile) continue;
1334
+ const result = offline_add_policy_packs_transformSourceFile(sourceFile);
1335
+ if (!result.changed) continue;
1336
+ changedFiles.push({
1337
+ filePath,
1338
+ operations: result.operations,
1339
+ summaries: result.summaries
1340
+ });
1341
+ if (!options.dryRun) await sourceFile.save();
1342
+ } catch (error) {
1343
+ errors.push({
1344
+ filePath,
1345
+ error: error instanceof Error ? error.message : String(error)
1346
+ });
1347
+ }
1348
+ return {
1349
+ totalFiles: filePaths.length,
1350
+ changedFiles,
1351
+ errors
1352
+ };
1353
+ }
1354
+ const react_options_to_top_level_SUPPORTED_EXTENSIONS = new Set([
1355
+ '.ts',
1356
+ '.tsx',
1357
+ '.js',
1358
+ '.jsx'
1359
+ ]);
1360
+ const react_options_to_top_level_IGNORED_DIRS = new Set([
1361
+ '.git',
1362
+ '.next',
1363
+ '.turbo',
1364
+ 'coverage',
1365
+ 'dist',
1366
+ 'build',
1367
+ 'node_modules',
1368
+ 'out'
1369
+ ]);
1370
+ const UI_OPTION_KEYS = [
1371
+ 'theme',
1372
+ 'colorScheme',
1373
+ 'disableAnimation'
1374
+ ];
1375
+ function react_options_to_top_level_getPropertyName(property) {
1376
+ const rawName = property.getNameNode().getText().trim();
1377
+ return rawName.replace(/^['"]|['"]$/g, '');
1378
+ }
1379
+ function react_options_to_top_level_getProperty(objectLiteral, name) {
1380
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
1381
+ if (react_options_to_top_level_getPropertyName(property) === name) return property;
1382
+ }
1383
+ }
1384
+ function react_options_to_top_level_transformSourceFile(sourceFile) {
1385
+ let operations = 0;
1386
+ const summaries = [];
1387
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
1388
+ for (const property of propertyAssignments){
1389
+ if (property.wasForgotten()) continue;
1390
+ if ('react' !== react_options_to_top_level_getPropertyName(property)) continue;
1391
+ const parentObject = property.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1392
+ if (!parentObject) continue;
1393
+ const reactObject = property.getInitializerIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1394
+ if (reactObject) {
1395
+ for (const key of UI_OPTION_KEYS){
1396
+ const nestedProperty = react_options_to_top_level_getProperty(reactObject, key);
1397
+ if (!nestedProperty) continue;
1398
+ const initializerText = nestedProperty.getInitializer()?.getText();
1399
+ if (initializerText) {
1400
+ if (react_options_to_top_level_getProperty(parentObject, key)) summaries.push(`removed duplicate react.${key}`);
1401
+ else {
1402
+ parentObject.addPropertyAssignment({
1403
+ name: key,
1404
+ initializer: initializerText
1405
+ });
1406
+ summaries.push(`react.${key} -> ${key}`);
1407
+ }
1408
+ nestedProperty.remove();
1409
+ operations += 1;
1410
+ }
1411
+ }
1412
+ if (0 === reactObject.getProperties().length) {
1413
+ property.remove();
1414
+ operations += 1;
1415
+ summaries.push('removed empty react options object');
1416
+ }
1417
+ }
1418
+ }
1419
+ return {
1420
+ changed: operations > 0,
1421
+ operations,
1422
+ summaries: [
1423
+ ...new Set(summaries)
1424
+ ]
1425
+ };
1426
+ }
1427
+ async function react_options_to_top_level_collectSourceFiles(rootDir) {
1428
+ const files = [];
1429
+ async function walk(currentDir) {
1430
+ const entries = await (0, promises_.readdir)(currentDir, {
1431
+ withFileTypes: true
1432
+ });
1433
+ for (const entry of entries){
1434
+ if (entry.isSymbolicLink()) continue;
1435
+ if (entry.isDirectory()) {
1436
+ if (react_options_to_top_level_IGNORED_DIRS.has(entry.name)) continue;
1437
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
1438
+ continue;
1439
+ }
1440
+ if (!entry.isFile()) continue;
1441
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
1442
+ if (react_options_to_top_level_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
1443
+ }
1444
+ }
1445
+ await walk(rootDir);
1446
+ return files;
1447
+ }
1448
+ async function runReactOptionsToTopLevelCodemod(options) {
1449
+ const project = new external_ts_morph_.Project({
1450
+ skipAddingFilesFromTsConfig: true,
1451
+ compilerOptions: {
1452
+ allowJs: true
1453
+ }
1454
+ });
1455
+ const filePaths = await react_options_to_top_level_collectSourceFiles(options.projectRoot);
1456
+ const changedFiles = [];
1457
+ const errors = [];
1458
+ for (const filePath of filePaths)try {
1459
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
1460
+ if (!sourceFile) continue;
1461
+ const result = react_options_to_top_level_transformSourceFile(sourceFile);
1462
+ if (!result.changed) continue;
1463
+ changedFiles.push({
1464
+ filePath,
1465
+ operations: result.operations,
1466
+ summaries: result.summaries
1467
+ });
1468
+ if (!options.dryRun) await sourceFile.save();
1469
+ } catch (error) {
1470
+ errors.push({
1471
+ filePath,
1472
+ error: error instanceof Error ? error.message : String(error)
1473
+ });
1474
+ }
1475
+ return {
1476
+ totalFiles: filePaths.length,
1477
+ changedFiles,
1478
+ errors
1479
+ };
1480
+ }
1481
+ const tracking_blocker_to_network_blocker_SUPPORTED_EXTENSIONS = new Set([
1482
+ '.ts',
1483
+ '.tsx',
1484
+ '.js',
1485
+ '.jsx'
1486
+ ]);
1487
+ const tracking_blocker_to_network_blocker_IGNORED_DIRS = new Set([
1488
+ '.git',
1489
+ '.next',
1490
+ '.turbo',
1491
+ 'coverage',
1492
+ 'dist',
1493
+ 'build',
1494
+ 'node_modules',
1495
+ 'out'
1496
+ ]);
1497
+ const LEGACY_CONFIG_KEY = 'trackingBlockerConfig';
1498
+ const NEXT_CONFIG_KEY = 'networkBlocker';
1499
+ const LEGACY_TYPE_NAME = 'TrackingBlockerConfig';
1500
+ const NEXT_TYPE_NAME = 'NetworkBlockerConfig';
1501
+ const C15T_PACKAGES = new Set([
1502
+ 'c15t',
1503
+ '@c15t/react',
1504
+ '@c15t/nextjs'
1505
+ ]);
1506
+ function tracking_blocker_to_network_blocker_getPropertyName(property) {
1507
+ const rawName = property.getNameNode().getText().trim();
1508
+ return rawName.replace(/^['"]|['"]$/g, '');
1509
+ }
1510
+ function tracking_blocker_to_network_blocker_getProperty(objectLiteral, name) {
1511
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
1512
+ if (tracking_blocker_to_network_blocker_getPropertyName(property) === name) return property;
1513
+ }
1514
+ }
1515
+ function invertExpression(expressionText) {
1516
+ const trimmed = expressionText.trim();
1517
+ if ('true' === trimmed) return 'false';
1518
+ if ('false' === trimmed) return 'true';
1519
+ if (trimmed.startsWith('!')) return trimmed.slice(1).trim();
1520
+ return `!(${trimmed})`;
1521
+ }
1522
+ function getObjectPropertyKeyText(property) {
1523
+ if (!external_ts_morph_.Node.isPropertyAssignment(property)) return;
1524
+ const nameNode = property.getNameNode();
1525
+ if (external_ts_morph_.Node.isIdentifier(nameNode)) return nameNode.getText();
1526
+ if (external_ts_morph_.Node.isStringLiteral(nameNode)) return nameNode.getLiteralText();
1527
+ const raw = nameNode.getText();
1528
+ return raw.replace(/^['"]|['"]$/g, '');
1529
+ }
1530
+ function buildRulesExpression(domainConsentMapInitializer) {
1531
+ const domainMapObject = domainConsentMapInitializer.asKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1532
+ if (!domainMapObject) {
1533
+ const expressionText = domainConsentMapInitializer.getText();
1534
+ return `Object.entries(${expressionText}).map(([domain, category]) => ({ domain, category }))`;
1535
+ }
1536
+ const entries = [];
1537
+ for (const property of domainMapObject.getProperties()){
1538
+ if (!external_ts_morph_.Node.isPropertyAssignment(property)) continue;
1539
+ const key = getObjectPropertyKeyText(property);
1540
+ const value = property.getInitializer()?.getText();
1541
+ if (key && value) entries.push(`{ domain: '${key}', category: ${value} }`);
1542
+ }
1543
+ if (0 === entries.length) {
1544
+ const expressionText = domainConsentMapInitializer.getText();
1545
+ return `Object.entries(${expressionText}).map(([domain, category]) => ({ domain, category }))`;
1546
+ }
1547
+ return `[${entries.join(', ')}]`;
1548
+ }
1549
+ function migrateTrackingBlockerObject(trackingObject) {
1550
+ const disableAutomaticBlockingProperty = tracking_blocker_to_network_blocker_getProperty(trackingObject, 'disableAutomaticBlocking');
1551
+ const domainConsentMapProperty = tracking_blocker_to_network_blocker_getProperty(trackingObject, 'domainConsentMap');
1552
+ const enabledLine = disableAutomaticBlockingProperty ? `enabled: ${invertExpression(disableAutomaticBlockingProperty.getInitializer()?.getText() ?? 'false')},` : '';
1553
+ const rulesExpression = domainConsentMapProperty?.getInitializer() ? buildRulesExpression(domainConsentMapProperty.getInitializer()) : '[]';
1554
+ const lines = [
1555
+ `rules: ${rulesExpression},`
1556
+ ];
1557
+ if (enabledLine) lines.unshift(enabledLine);
1558
+ return `{
1559
+ ${lines.join('\n\t\t')}
1560
+ }`;
1561
+ }
1562
+ function tracking_blocker_to_network_blocker_getBindingPropertyName(element) {
1563
+ const propertyNameNode = element.getPropertyNameNode();
1564
+ if (propertyNameNode) {
1565
+ if (external_ts_morph_.Node.isIdentifier(propertyNameNode)) return propertyNameNode.getText();
1566
+ if (external_ts_morph_.Node.isStringLiteral(propertyNameNode)) return propertyNameNode.getLiteralText();
1567
+ return;
1568
+ }
1569
+ const nameNode = element.getNameNode();
1570
+ if (!external_ts_morph_.Node.isIdentifier(nameNode)) return;
1571
+ return nameNode.getText();
1572
+ }
1573
+ function tracking_blocker_to_network_blocker_transformSourceFile(sourceFile) {
1574
+ let operations = 0;
1575
+ const summaries = [];
1576
+ let hasC15tTrackingTypeImport = false;
1577
+ const imports = sourceFile.getImportDeclarations();
1578
+ for (const importDeclaration of imports)if (C15T_PACKAGES.has(importDeclaration.getModuleSpecifierValue())) {
1579
+ for (const namedImport of importDeclaration.getNamedImports())if (namedImport.getNameNode().getText() === LEGACY_TYPE_NAME) {
1580
+ namedImport.getNameNode().replaceWithText(NEXT_TYPE_NAME);
1581
+ hasC15tTrackingTypeImport = true;
1582
+ operations += 1;
1583
+ summaries.push(`${LEGACY_TYPE_NAME} -> ${NEXT_TYPE_NAME}`);
1584
+ }
1585
+ }
1586
+ if (hasC15tTrackingTypeImport) {
1587
+ const identifiers = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.Identifier);
1588
+ for (const identifier of identifiers){
1589
+ if (identifier.getText() !== LEGACY_TYPE_NAME) continue;
1590
+ const parent = identifier.getParent();
1591
+ if (!external_ts_morph_.Node.isImportSpecifier(parent) || parent.getNameNode() !== identifier) {
1592
+ identifier.replaceWithText(NEXT_TYPE_NAME);
1593
+ operations += 1;
1594
+ summaries.push(`${LEGACY_TYPE_NAME} references -> ${NEXT_TYPE_NAME}`);
1595
+ }
1596
+ }
1597
+ }
1598
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
1599
+ for (const property of propertyAssignments){
1600
+ if (property.wasForgotten()) continue;
1601
+ if (tracking_blocker_to_network_blocker_getPropertyName(property) !== LEGACY_CONFIG_KEY) continue;
1602
+ const parentObject = property.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1603
+ if (!parentObject) continue;
1604
+ if (tracking_blocker_to_network_blocker_getProperty(parentObject, NEXT_CONFIG_KEY)) continue;
1605
+ const initializer = property.getInitializer();
1606
+ const trackingObject = initializer?.asKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1607
+ if (trackingObject) {
1608
+ property.getNameNode().replaceWithText(NEXT_CONFIG_KEY);
1609
+ property.setInitializer(migrateTrackingBlockerObject(trackingObject));
1610
+ operations += 1;
1611
+ summaries.push('trackingBlockerConfig object -> networkBlocker rules/enabled');
1612
+ continue;
1613
+ }
1614
+ property.getNameNode().replaceWithText(NEXT_CONFIG_KEY);
1615
+ operations += 1;
1616
+ summaries.push('trackingBlockerConfig -> networkBlocker');
1617
+ }
1618
+ const shorthandAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.ShorthandPropertyAssignment);
1619
+ for (const shorthand of shorthandAssignments){
1620
+ const name = shorthand.getNameNode().getText();
1621
+ if (name !== LEGACY_CONFIG_KEY) continue;
1622
+ const parent = shorthand.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1623
+ if (!(parent && tracking_blocker_to_network_blocker_getProperty(parent, NEXT_CONFIG_KEY))) {
1624
+ shorthand.replaceWithText(`${NEXT_CONFIG_KEY}: ${name}`);
1625
+ operations += 1;
1626
+ summaries.push('trackingBlockerConfig shorthand -> networkBlocker');
1627
+ }
1628
+ }
1629
+ const bindingElements = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.BindingElement);
1630
+ for (const element of bindingElements){
1631
+ const propertyName = tracking_blocker_to_network_blocker_getBindingPropertyName(element);
1632
+ if (propertyName !== LEGACY_CONFIG_KEY) continue;
1633
+ const propertyNameNode = element.getPropertyNameNode();
1634
+ if (propertyNameNode) propertyNameNode.replaceWithText(NEXT_CONFIG_KEY);
1635
+ else {
1636
+ const nameNode = element.getNameNode();
1637
+ if (!external_ts_morph_.Node.isIdentifier(nameNode)) continue;
1638
+ const localName = nameNode.getText();
1639
+ const initializerText = element.getInitializer()?.getText();
1640
+ let replacement = `${NEXT_CONFIG_KEY}: ${localName}`;
1641
+ if (initializerText) replacement += ` = ${initializerText}`;
1642
+ element.replaceWithText(replacement);
1643
+ }
1644
+ operations += 1;
1645
+ summaries.push('trackingBlockerConfig destructuring -> networkBlocker');
1646
+ }
1647
+ const propertyAccesses = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAccessExpression);
1648
+ for (const propertyAccess of propertyAccesses){
1649
+ if (propertyAccess.getName() !== LEGACY_CONFIG_KEY) continue;
1650
+ const expressionText = propertyAccess.getExpression().getText();
1651
+ propertyAccess.replaceWithText(`${expressionText}.${NEXT_CONFIG_KEY}`);
1652
+ operations += 1;
1653
+ summaries.push('trackingBlockerConfig access -> networkBlocker');
1654
+ }
1655
+ return {
1656
+ changed: operations > 0,
1657
+ operations,
1658
+ summaries: [
1659
+ ...new Set(summaries)
1660
+ ]
1661
+ };
1662
+ }
1663
+ async function tracking_blocker_to_network_blocker_collectSourceFiles(rootDir) {
1664
+ const files = [];
1665
+ async function walk(currentDir) {
1666
+ const entries = await (0, promises_.readdir)(currentDir, {
1667
+ withFileTypes: true
1668
+ });
1669
+ for (const entry of entries){
1670
+ if (entry.isSymbolicLink()) continue;
1671
+ if (entry.isDirectory()) {
1672
+ if (tracking_blocker_to_network_blocker_IGNORED_DIRS.has(entry.name)) continue;
1673
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
1674
+ continue;
1675
+ }
1676
+ if (!entry.isFile()) continue;
1677
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
1678
+ if (tracking_blocker_to_network_blocker_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
1679
+ }
1680
+ }
1681
+ await walk(rootDir);
1682
+ return files;
1683
+ }
1684
+ async function runTrackingBlockerToNetworkBlockerCodemod(options) {
1685
+ const project = new external_ts_morph_.Project({
1686
+ skipAddingFilesFromTsConfig: true,
1687
+ compilerOptions: {
1688
+ allowJs: true
1689
+ }
1690
+ });
1691
+ const filePaths = await tracking_blocker_to_network_blocker_collectSourceFiles(options.projectRoot);
1692
+ const changedFiles = [];
1693
+ const errors = [];
1694
+ for (const filePath of filePaths)try {
1695
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
1696
+ if (!sourceFile) continue;
1697
+ const result = tracking_blocker_to_network_blocker_transformSourceFile(sourceFile);
1698
+ if (!result.changed) continue;
1699
+ changedFiles.push({
1700
+ filePath,
1701
+ operations: result.operations,
1702
+ summaries: result.summaries
1703
+ });
1704
+ if (!options.dryRun) await sourceFile.save();
1705
+ } catch (error) {
1706
+ errors.push({
1707
+ filePath,
1708
+ error: error instanceof Error ? error.message : String(error)
1709
+ });
1710
+ }
1711
+ return {
1712
+ totalFiles: filePaths.length,
1713
+ changedFiles,
1714
+ errors
1715
+ };
1716
+ }
1717
+ const translations_to_i18n_SUPPORTED_EXTENSIONS = new Set([
1718
+ '.ts',
1719
+ '.tsx',
1720
+ '.js',
1721
+ '.jsx'
1722
+ ]);
1723
+ const translations_to_i18n_IGNORED_DIRS = new Set([
1724
+ '.git',
1725
+ '.next',
1726
+ '.turbo',
1727
+ 'coverage',
1728
+ 'dist',
1729
+ 'build',
1730
+ 'node_modules',
1731
+ 'out'
1732
+ ]);
1733
+ function translations_to_i18n_getPropertyName(property) {
1734
+ const rawName = property.getNameNode().getText().trim();
1735
+ return rawName.replace(/^['"]|['"]$/g, '');
1736
+ }
1737
+ function translations_to_i18n_getProperty(objectLiteral, name) {
1738
+ for (const property of objectLiteral.getProperties())if (external_ts_morph_.Node.isPropertyAssignment(property)) {
1739
+ if (translations_to_i18n_getPropertyName(property) === name) return property;
1740
+ }
1741
+ }
1742
+ function hasProperty(objectLiteral, name) {
1743
+ return Boolean(translations_to_i18n_getProperty(objectLiteral, name));
1744
+ }
1745
+ function renameProperty(property, nextName) {
1746
+ property.getNameNode().replaceWithText(nextName);
1747
+ }
1748
+ function isLegacyTranslationConfigObject(objectLiteral) {
1749
+ const hasDefaultLanguage = hasProperty(objectLiteral, 'defaultLanguage');
1750
+ const hasDisableAutoLanguageSwitch = hasProperty(objectLiteral, 'disableAutoLanguageSwitch');
1751
+ const hasTranslations = hasProperty(objectLiteral, 'translations');
1752
+ const hasLanguage = hasProperty(objectLiteral, 'language');
1753
+ const hasI18n = hasProperty(objectLiteral, 'i18n');
1754
+ const hasMessages = hasProperty(objectLiteral, 'messages');
1755
+ const hasLocale = hasProperty(objectLiteral, 'locale');
1756
+ if (hasDefaultLanguage || hasDisableAutoLanguageSwitch) return true;
1757
+ if (hasTranslations && !hasLanguage && !hasI18n && !hasMessages && !hasLocale) return true;
1758
+ return false;
1759
+ }
1760
+ function translations_to_i18n_invertExpression(expressionText) {
1761
+ const trimmed = expressionText.trim();
1762
+ if ('true' === trimmed) return 'false';
1763
+ if ('false' === trimmed) return 'true';
1764
+ if (trimmed.startsWith('!')) return trimmed.slice(1).trim();
1765
+ return `!(${trimmed})`;
1766
+ }
1767
+ function transformLegacyConfigObject(objectLiteral) {
1768
+ let operations = 0;
1769
+ const summaries = [];
1770
+ const translationsProperty = translations_to_i18n_getProperty(objectLiteral, 'translations');
1771
+ if (translationsProperty && !hasProperty(objectLiteral, 'messages')) {
1772
+ renameProperty(translationsProperty, 'messages');
1773
+ operations += 1;
1774
+ summaries.push('translations -> messages');
1775
+ }
1776
+ const defaultLanguageProperty = translations_to_i18n_getProperty(objectLiteral, 'defaultLanguage');
1777
+ if (defaultLanguageProperty && !hasProperty(objectLiteral, 'locale')) {
1778
+ renameProperty(defaultLanguageProperty, 'locale');
1779
+ operations += 1;
1780
+ summaries.push('defaultLanguage -> locale');
1781
+ }
1782
+ const disableAutoSwitchProperty = translations_to_i18n_getProperty(objectLiteral, 'disableAutoLanguageSwitch');
1783
+ if (disableAutoSwitchProperty && !hasProperty(objectLiteral, 'detectBrowserLanguage')) {
1784
+ const initializer = disableAutoSwitchProperty.getInitializer();
1785
+ const expressionText = initializer?.getText() ?? 'false';
1786
+ renameProperty(disableAutoSwitchProperty, 'detectBrowserLanguage');
1787
+ disableAutoSwitchProperty.setInitializer(translations_to_i18n_invertExpression(expressionText));
1788
+ operations += 1;
1789
+ summaries.push('disableAutoLanguageSwitch -> detectBrowserLanguage');
1790
+ }
1791
+ return {
1792
+ changed: operations > 0,
1793
+ operations,
1794
+ summaries
1795
+ };
1796
+ }
1797
+ function translations_to_i18n_transformSourceFile(sourceFile) {
1798
+ let operations = 0;
1799
+ const summaries = [];
1800
+ const propertyAssignments = sourceFile.getDescendantsOfKind(external_ts_morph_.SyntaxKind.PropertyAssignment);
1801
+ for (const property of propertyAssignments){
1802
+ if ('translations' !== translations_to_i18n_getPropertyName(property)) continue;
1803
+ const parentObject = property.getParentIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1804
+ if (!parentObject) continue;
1805
+ const initializer = property.getInitializerIfKind(external_ts_morph_.SyntaxKind.ObjectLiteralExpression);
1806
+ if (!initializer || !isLegacyTranslationConfigObject(initializer)) continue;
1807
+ if (hasProperty(parentObject, 'i18n')) continue;
1808
+ renameProperty(property, 'i18n');
1809
+ operations += 1;
1810
+ summaries.push('options.translations -> options.i18n');
1811
+ const result = transformLegacyConfigObject(initializer);
1812
+ operations += result.operations;
1813
+ summaries.push(...result.summaries);
1814
+ }
1815
+ return {
1816
+ changed: operations > 0,
1817
+ operations,
1818
+ summaries: [
1819
+ ...new Set(summaries)
1820
+ ]
1821
+ };
1822
+ }
1823
+ async function translations_to_i18n_collectSourceFiles(rootDir) {
1824
+ const files = [];
1825
+ async function walk(currentDir) {
1826
+ const entries = await (0, promises_.readdir)(currentDir, {
1827
+ withFileTypes: true
1828
+ });
1829
+ for (const entry of entries){
1830
+ if (entry.isDirectory()) {
1831
+ if (translations_to_i18n_IGNORED_DIRS.has(entry.name)) continue;
1832
+ await walk((0, external_node_path_.join)(currentDir, entry.name));
1833
+ continue;
1834
+ }
1835
+ if (!entry.isFile()) continue;
1836
+ const extension = (0, external_node_path_.extname)(entry.name).toLowerCase();
1837
+ if (translations_to_i18n_SUPPORTED_EXTENSIONS.has(extension)) files.push((0, external_node_path_.join)(currentDir, entry.name));
1838
+ }
1839
+ }
1840
+ await walk(rootDir);
1841
+ return files;
1842
+ }
1843
+ async function runTranslationsToI18nCodemod(options) {
1844
+ const project = new external_ts_morph_.Project({
1845
+ skipAddingFilesFromTsConfig: true,
1846
+ compilerOptions: {
1847
+ allowJs: true
1848
+ }
1849
+ });
1850
+ const filePaths = await translations_to_i18n_collectSourceFiles(options.projectRoot);
1851
+ const changedFiles = [];
1852
+ const errors = [];
1853
+ for (const filePath of filePaths)try {
1854
+ const sourceFile = project.addSourceFileAtPathIfExists(filePath);
1855
+ if (!sourceFile) continue;
1856
+ const result = translations_to_i18n_transformSourceFile(sourceFile);
1857
+ if (!result.changed) continue;
1858
+ changedFiles.push({
1859
+ filePath,
1860
+ operations: result.operations,
1861
+ summaries: result.summaries
1862
+ });
1863
+ if (!options.dryRun) await sourceFile.save();
1864
+ } catch (error) {
1865
+ errors.push({
1866
+ filePath,
1867
+ error: error instanceof Error ? error.message : String(error)
1868
+ });
1869
+ }
1870
+ return {
1871
+ totalFiles: filePaths.length,
1872
+ changedFiles,
1873
+ errors
1874
+ };
1875
+ }
1876
+ const C15T_PACKAGE_PREFIX = '@c15t/';
1877
+ function parseVersion(raw) {
1878
+ const match = raw.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
1879
+ if (!match) return null;
1880
+ const [, majorPart, minorPart, patchPart, preReleasePart] = match;
1881
+ if (!majorPart || !minorPart || !patchPart) return null;
1882
+ const major = Number.parseInt(majorPart, 10);
1883
+ const minor = Number.parseInt(minorPart, 10);
1884
+ const patch = Number.parseInt(patchPart, 10);
1885
+ const preRelease = preReleasePart ? preReleasePart.split('.') : [];
1886
+ return {
1887
+ major,
1888
+ minor,
1889
+ patch,
1890
+ preRelease
1891
+ };
1892
+ }
1893
+ function extractVersionFromSpecifier(specifier) {
1894
+ const match = specifier.match(/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)/);
1895
+ if (!match) return null;
1896
+ const [, version] = match;
1897
+ return version ?? null;
1898
+ }
1899
+ function isNumericSegment(value) {
1900
+ return /^\d+$/.test(value);
1901
+ }
1902
+ function comparePreRelease(a, b) {
1903
+ if (0 === a.length && 0 === b.length) return 0;
1904
+ if (0 === a.length) return 1;
1905
+ if (0 === b.length) return -1;
1906
+ const maxLength = Math.max(a.length, b.length);
1907
+ for(let index = 0; index < maxLength; index += 1){
1908
+ const aValue = a[index];
1909
+ const bValue = b[index];
1910
+ if (void 0 === aValue) return -1;
1911
+ if (void 0 === bValue) return 1;
1912
+ const aNumeric = isNumericSegment(aValue);
1913
+ const bNumeric = isNumericSegment(bValue);
1914
+ if (aNumeric && bNumeric) {
1915
+ const aNumber = Number.parseInt(aValue, 10);
1916
+ const bNumber = Number.parseInt(bValue, 10);
1917
+ if (aNumber !== bNumber) return aNumber > bNumber ? 1 : -1;
1918
+ continue;
1919
+ }
1920
+ if (aNumeric && !bNumeric) return -1;
1921
+ if (!aNumeric && bNumeric) return 1;
1922
+ if (aValue !== bValue) return aValue > bValue ? 1 : -1;
1923
+ }
1924
+ return 0;
1925
+ }
1926
+ function compareVersions(a, b) {
1927
+ const parsedA = parseVersion(a);
1928
+ const parsedB = parseVersion(b);
1929
+ if (!parsedA || !parsedB) return null;
1930
+ if (parsedA.major !== parsedB.major) return parsedA.major > parsedB.major ? 1 : -1;
1931
+ if (parsedA.minor !== parsedB.minor) return parsedA.minor > parsedB.minor ? 1 : -1;
1932
+ if (parsedA.patch !== parsedB.patch) return parsedA.patch > parsedB.patch ? 1 : -1;
1933
+ return comparePreRelease(parsedA.preRelease, parsedB.preRelease);
1934
+ }
1935
+ function satisfiesComparator(version, comparator) {
1936
+ const match = comparator.trim().match(/^(<=|>=|<|>|=)?\s*v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/);
1937
+ if (!match) return false;
1938
+ const [, operatorRaw, target] = match;
1939
+ if (!target) return false;
1940
+ const operator = operatorRaw ?? '=';
1941
+ const comparison = compareVersions(version, target);
1942
+ if (null === comparison) return false;
1943
+ switch(operator){
1944
+ case '<':
1945
+ return comparison < 0;
1946
+ case '<=':
1947
+ return comparison <= 0;
1948
+ case '>':
1949
+ return comparison > 0;
1950
+ case '>=':
1951
+ return comparison >= 0;
1952
+ default:
1953
+ return 0 === comparison;
1954
+ }
1955
+ }
1956
+ function satisfiesSimpleRange(version, range) {
1957
+ const comparators = range.trim().split(/\s+/).filter(Boolean);
1958
+ if (0 === comparators.length) return true;
1959
+ return comparators.every((comparator)=>satisfiesComparator(version, comparator));
1960
+ }
1961
+ function isCodemodApplicableForVersion(version, metadata) {
1962
+ if (!version) return true;
1963
+ if (metadata.fromRange && !satisfiesSimpleRange(version, metadata.fromRange)) return false;
1964
+ if (metadata.toRange && satisfiesSimpleRange(version, metadata.toRange)) return false;
1965
+ return true;
1966
+ }
1967
+ function detectInstalledC15tVersionFromPackageJson(manifest) {
1968
+ const dependencyGroups = [
1969
+ manifest.dependencies,
1970
+ manifest.devDependencies,
1971
+ manifest.peerDependencies,
1972
+ manifest.optionalDependencies
1973
+ ];
1974
+ const versions = [];
1975
+ for (const dependencies of dependencyGroups)if (dependencies) for (const [packageName, specifier] of Object.entries(dependencies)){
1976
+ if ('c15t' !== packageName && !packageName.startsWith(C15T_PACKAGE_PREFIX)) continue;
1977
+ const extracted = extractVersionFromSpecifier(specifier);
1978
+ if (extracted) {
1979
+ if (parseVersion(extracted)) versions.push(extracted);
1980
+ }
1981
+ }
1982
+ if (0 === versions.length) return null;
1983
+ const firstVersion = versions.at(0);
1984
+ if (!firstVersion) return null;
1985
+ let selected = firstVersion;
1986
+ for (const current of versions.slice(1)){
1987
+ const comparison = compareVersions(current, selected);
1988
+ if (null !== comparison && comparison < 0) selected = current;
1989
+ }
1990
+ return selected;
1991
+ }
1992
+ async function detectInstalledC15tVersion(projectRoot) {
1993
+ const manifestPath = (0, external_node_path_.join)(projectRoot, 'package.json');
1994
+ try {
1995
+ let content;
1996
+ const bunRuntime = globalThis.Bun;
1997
+ if (bunRuntime) content = await bunRuntime.file(manifestPath).text();
1998
+ else {
1999
+ const fs = await import("node:fs/promises");
2000
+ content = await fs.readFile(manifestPath, 'utf-8');
2001
+ }
2002
+ const parsed = JSON.parse(content);
2003
+ return detectInstalledC15tVersionFromPackageJson(parsed);
2004
+ } catch {
2005
+ return null;
2006
+ }
2007
+ }
2008
+ function logCodemodResult(context, result, dryRun) {
2009
+ const { logger } = context;
2010
+ if (0 === result.changedFiles.length) {
2011
+ logger.info(`No files needed updates (scanned ${result.totalFiles} source files).`);
2012
+ for (const error of result.errors)logger.warn(`Skipped ${error.filePath}: ${error.error}`);
2013
+ return;
2014
+ }
2015
+ let actionPrefix = 'Applied';
2016
+ if (dryRun) actionPrefix = 'Dry run';
2017
+ logger.success(`${actionPrefix}: updated ${result.changedFiles.length} file(s) out of ${result.totalFiles} scanned.`);
2018
+ for (const file of result.changedFiles){
2019
+ let summary = '';
2020
+ if (file.summaries.length > 0) summary = `: ${file.summaries.join(', ')}`;
2021
+ logger.info(`- ${file.filePath} (${file.operations} changes${summary})`);
2022
+ }
2023
+ for (const error of result.errors)logger.warn(`Skipped ${error.filePath}: ${error.error}`);
2024
+ }
2025
+ const codemods = [
2026
+ {
2027
+ id: 'active-ui-api',
2028
+ label: 'showPopup API -> activeUI API',
2029
+ hint: 'Migrates showPopup/isPrivacyDialogOpen and setter usage to activeUI.',
2030
+ run: async (context, dryRun)=>{
2031
+ const { projectRoot } = context;
2032
+ const result = await runActiveUiApiCodemod({
2033
+ projectRoot,
2034
+ dryRun
2035
+ });
2036
+ logCodemodResult(context, result, dryRun);
2037
+ },
2038
+ versioning: {
2039
+ fromRange: '<2.0.0',
2040
+ toRange: '>=2.0.0'
2041
+ }
2042
+ },
2043
+ {
2044
+ id: 'component-renames',
2045
+ label: 'legacy component names -> v2 names',
2046
+ hint: 'Renames CookieBanner/ConsentManagerDialog/ConsentManagerWidget.',
2047
+ run: async (context, dryRun)=>{
2048
+ const { projectRoot } = context;
2049
+ const result = await runComponentRenamesCodemod({
2050
+ projectRoot,
2051
+ dryRun
2052
+ });
2053
+ logCodemodResult(context, result, dryRun);
2054
+ },
2055
+ versioning: {
2056
+ fromRange: '<2.0.0',
2057
+ toRange: '>=2.0.0'
2058
+ }
180
2059
  },
181
- "node:fs/promises" (module) {
182
- module.exports = __rspack_external_node_fs_promises_153e37e0;
2060
+ {
2061
+ id: 'gdpr-types-to-consent-categories',
2062
+ label: 'gdprTypes -> consentCategories',
2063
+ hint: 'Migrates gdprTypes/initialGDPRTypes to consentCategories.',
2064
+ run: async (context, dryRun)=>{
2065
+ const { projectRoot } = context;
2066
+ const result = await runGdprTypesToConsentCategoriesCodemod({
2067
+ projectRoot,
2068
+ dryRun
2069
+ });
2070
+ logCodemodResult(context, result, dryRun);
2071
+ },
2072
+ versioning: {
2073
+ fromRange: '<2.0.0',
2074
+ toRange: '>=2.0.0'
2075
+ }
183
2076
  },
184
- "node:path" (module) {
185
- module.exports = __rspack_external_node_path_c5b9b54f;
2077
+ {
2078
+ id: 'ignore-geo-location-to-overrides',
2079
+ label: 'ignoreGeoLocation -> overrides',
2080
+ hint: "Migrates ignoreGeoLocation to overrides (forces country='DE').",
2081
+ run: async (context, dryRun)=>{
2082
+ const { projectRoot } = context;
2083
+ const result = await runIgnoreGeoLocationToOverridesCodemod({
2084
+ projectRoot,
2085
+ dryRun
2086
+ });
2087
+ logCodemodResult(context, result, dryRun);
2088
+ },
2089
+ versioning: {
2090
+ fromRange: '<2.0.0',
2091
+ toRange: '>=2.0.0'
2092
+ }
186
2093
  },
187
- picocolors (module) {
188
- module.exports = __rspack_external_picocolors;
189
- }
190
- };
191
- var __webpack_module_cache__ = {};
192
- function __webpack_require__(moduleId) {
193
- var cachedModule = __webpack_module_cache__[moduleId];
194
- if (void 0 !== cachedModule) return cachedModule.exports;
195
- var module = __webpack_module_cache__[moduleId] = {
196
- exports: {}
197
- };
198
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
199
- return module.exports;
200
- }
201
- __webpack_require__.m = __webpack_modules__;
202
- (()=>{
203
- __webpack_require__.d = (exports, definition)=>{
204
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
205
- enumerable: true,
206
- get: definition[key]
207
- });
208
- };
209
- })();
210
- (()=>{
211
- __webpack_require__.f = {};
212
- __webpack_require__.e = (chunkId)=>Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key)=>{
213
- __webpack_require__.f[key](chunkId, promises);
214
- return promises;
215
- }, []));
216
- })();
217
- (()=>{
218
- __webpack_require__.u = (chunkId)=>"" + chunkId + ".mjs";
219
- })();
220
- (()=>{
221
- __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
222
- })();
223
- (()=>{
224
- var installedChunks = {
225
- 410: 0
226
- };
227
- var installChunk = (data)=>{
228
- var __rspack_esm_ids = data.__rspack_esm_ids;
229
- var __webpack_modules__ = data.__webpack_modules__;
230
- var __rspack_esm_runtime = data.__rspack_esm_runtime;
231
- var moduleId, chunkId, i = 0;
232
- for(moduleId in __webpack_modules__)if (__webpack_require__.o(__webpack_modules__, moduleId)) __webpack_require__.m[moduleId] = __webpack_modules__[moduleId];
233
- if (__rspack_esm_runtime) __rspack_esm_runtime(__webpack_require__);
234
- for(; i < __rspack_esm_ids.length; i++){
235
- chunkId = __rspack_esm_ids[i];
236
- if (__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) installedChunks[chunkId][0]();
237
- installedChunks[__rspack_esm_ids[i]] = 0;
2094
+ {
2095
+ id: 'mode-c15t-to-hosted',
2096
+ label: "mode: 'c15t' -> 'hosted'",
2097
+ hint: "Migrates legacy mode values from 'c15t' to 'hosted'.",
2098
+ run: async (context, dryRun)=>{
2099
+ const { projectRoot } = context;
2100
+ const result = await runC15tModeToHostedCodemod({
2101
+ projectRoot,
2102
+ dryRun
2103
+ });
2104
+ logCodemodResult(context, result, dryRun);
2105
+ },
2106
+ versioning: {
2107
+ fromRange: '<2.0.0',
2108
+ toRange: '>=2.0.0'
238
2109
  }
239
- };
240
- __webpack_require__.f.j = function(chunkId, promises) {
241
- var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : void 0;
242
- if (0 !== installedChunkData) if (installedChunkData) promises.push(installedChunkData[1]);
243
- else {
244
- var promise = import("./" + __webpack_require__.u(chunkId)).then(installChunk, (e)=>{
245
- if (0 !== installedChunks[chunkId]) installedChunks[chunkId] = void 0;
246
- throw e;
2110
+ },
2111
+ {
2112
+ id: 'offline-add-policy-packs',
2113
+ label: 'offline mode -> add policy packs',
2114
+ hint: 'Adds starter policyPackPresets to offline configs missing policies.',
2115
+ run: async (context, dryRun)=>{
2116
+ const { projectRoot } = context;
2117
+ const result = await runOfflineAddPolicyPacksCodemod({
2118
+ projectRoot,
2119
+ dryRun
247
2120
  });
248
- var promise = Promise.race([
249
- promise,
250
- new Promise((resolve)=>{
251
- installedChunkData = installedChunks[chunkId] = [
252
- resolve
253
- ];
254
- })
255
- ]);
256
- promises.push(installedChunkData[1] = promise);
2121
+ logCodemodResult(context, result, dryRun);
2122
+ },
2123
+ versioning: {
2124
+ fromRange: '<2.0.0',
2125
+ toRange: '>=2.0.0'
257
2126
  }
258
- };
259
- })();
260
- var prompts_ = __webpack_require__("@clack/prompts");
261
- var external_picocolors_ = __webpack_require__("picocolors");
262
- function showHelpMenu(context, version, commands, flags) {
263
- const { logger } = context;
264
- logger.debug('Displaying help menu using command and flag structures.');
265
- const commandLines = commands.map((cmd)=>` ${cmd.name.padEnd(10)} ${cmd.description}`).join('\n');
266
- const optionLines = flags.map((flag)=>{
267
- const names = flag.names.join(', ');
268
- const valuePlaceholder = flag.expectsValue ? ' <value>' : '';
269
- return ` ${(names + valuePlaceholder).padEnd(20)} ${flag.description}`;
270
- }).join('\n');
271
- const helpContent = `c15t CLI version ${version}
272
-
273
- Available Commands:
274
- ${commandLines}
275
-
276
- Options:
277
- ${optionLines}
278
-
279
- Run a command directly (e.g., ${external_picocolors_["default"].cyan('c15t generate')}) or select one interactively when no command is provided.
280
-
281
- For more help, visit: https://c15t.dev`;
282
- logger.debug('Help menu content generated.');
283
- logger.note(helpContent, 'Usage');
2127
+ },
2128
+ {
2129
+ id: 'react-options-to-top-level',
2130
+ label: 'react options -> top-level options',
2131
+ hint: 'Lifts react.theme/colorScheme/disableAnimation to top-level.',
2132
+ run: async (context, dryRun)=>{
2133
+ const { projectRoot } = context;
2134
+ const result = await runReactOptionsToTopLevelCodemod({
2135
+ projectRoot,
2136
+ dryRun
2137
+ });
2138
+ logCodemodResult(context, result, dryRun);
2139
+ },
2140
+ versioning: {
2141
+ fromRange: '<2.0.0',
2142
+ toRange: '>=2.0.0'
2143
+ }
2144
+ },
2145
+ {
2146
+ id: 'tracking-blocker-to-network-blocker',
2147
+ label: 'trackingBlockerConfig -> networkBlocker',
2148
+ hint: 'Migrates tracking blocker config to network blocker rules.',
2149
+ run: async (context, dryRun)=>{
2150
+ const { projectRoot } = context;
2151
+ const result = await runTrackingBlockerToNetworkBlockerCodemod({
2152
+ projectRoot,
2153
+ dryRun
2154
+ });
2155
+ logCodemodResult(context, result, dryRun);
2156
+ },
2157
+ versioning: {
2158
+ fromRange: '<2.0.0',
2159
+ toRange: '>=2.0.0'
2160
+ }
2161
+ },
2162
+ {
2163
+ id: 'translations-to-i18n',
2164
+ label: 'translations -> i18n',
2165
+ hint: 'Migrates legacy translation config keys to the v2 i18n shape.',
2166
+ run: async (context, dryRun)=>{
2167
+ const { projectRoot } = context;
2168
+ const result = await runTranslationsToI18nCodemod({
2169
+ projectRoot,
2170
+ dryRun
2171
+ });
2172
+ logCodemodResult(context, result, dryRun);
2173
+ },
2174
+ versioning: {
2175
+ fromRange: '<2.0.0',
2176
+ toRange: '>=2.0.0'
2177
+ }
2178
+ }
2179
+ ];
2180
+ async function runCodemods(context) {
2181
+ const { logger, commandArgs, projectRoot } = context;
2182
+ const dryRun = commandArgs.includes('--dry-run');
2183
+ const installedVersion = await detectInstalledC15tVersion(projectRoot);
2184
+ if (installedVersion) logger.info(`Detected c15t version ${installedVersion}.`);
2185
+ else logger.warn('Could not detect c15t version from package.json. Showing all codemods.');
2186
+ const availableCodemods = codemods.filter((codemod)=>isCodemodApplicableForVersion(installedVersion, codemod.versioning ?? {}));
2187
+ if (0 === availableCodemods.length) {
2188
+ if (installedVersion) logger.info(`No codemods are applicable for detected c15t version ${installedVersion}.`);
2189
+ else logger.info('No codemods available.');
2190
+ return;
2191
+ }
2192
+ const selected = await prompts_.multiselect({
2193
+ message: 'Select codemods to run (space to toggle, enter to confirm):',
2194
+ options: availableCodemods.map((codemod)=>({
2195
+ value: codemod.id,
2196
+ label: codemod.label,
2197
+ hint: codemod.hint
2198
+ })),
2199
+ required: false
2200
+ });
2201
+ if (prompts_.isCancel(selected)) return void logger.warn('Codemod execution cancelled.');
2202
+ const selectedCodemods = selected;
2203
+ if (!selectedCodemods.length) return void logger.info('No codemods selected.');
2204
+ let dryRunSuffix = '';
2205
+ if (dryRun) dryRunSuffix = ' in dry-run mode';
2206
+ logger.info(`Running ${selectedCodemods.length} codemod(s)${dryRunSuffix}...`);
2207
+ for (const codemodId of selectedCodemods){
2208
+ const codemod = availableCodemods.find((item)=>item.id === codemodId);
2209
+ if (!codemod) {
2210
+ logger.warn(`Unknown codemod selected: ${codemodId}`);
2211
+ continue;
2212
+ }
2213
+ logger.info(`Running: ${codemod.label}`);
2214
+ await codemod.run(context, dryRun);
2215
+ }
284
2216
  }
2217
+ const codemodsCommand = {
2218
+ name: 'codemods',
2219
+ label: 'Codemods',
2220
+ hint: 'Run migration codemods',
2221
+ description: 'Run project codemods (for example translations -> i18n migration).',
2222
+ action: runCodemods
2223
+ };
2224
+ var external_node_os_ = __webpack_require__("node:os");
285
2225
  const TELEMETRY_DISABLED_ENV = 'C15T_TELEMETRY_DISABLED';
286
2226
  const TelemetryEventName = {
287
2227
  CLI_INVOKED: 'cli.invoked',
@@ -485,7 +2425,7 @@ class Telemetry {
485
2425
  }
486
2426
  }
487
2427
  generateAnonymousId() {
488
- const machineId = __rspack_external_node_crypto_9ba42079["default"].createHash('sha256').update(__rspack_external_node_os_74b4b876["default"].hostname() + __rspack_external_node_os_74b4b876["default"].platform() + __rspack_external_node_os_74b4b876["default"].arch() + __rspack_external_node_os_74b4b876["default"].totalmem()).digest('hex');
2428
+ const machineId = __rspack_external_node_crypto_9ba42079["default"].createHash('sha256').update(external_node_os_["default"].hostname() + external_node_os_["default"].platform() + external_node_os_["default"].arch() + external_node_os_["default"].totalmem()).digest('hex');
489
2429
  return machineId;
490
2430
  }
491
2431
  flushSync() {
@@ -501,7 +2441,6 @@ class Telemetry {
501
2441
  function createTelemetry(options) {
502
2442
  return new Telemetry(options);
503
2443
  }
504
- var external_node_path_ = __webpack_require__("node:path");
505
2444
  const DEFAULT_PERSIST_FILENAME = '.c15t-state.json';
506
2445
  function getPersistPath(projectRoot) {
507
2446
  return external_node_path_["default"].join(projectRoot, DEFAULT_PERSIST_FILENAME);
@@ -666,6 +2605,198 @@ function createDebugSubscriber(machineId, logger) {
666
2605
  }
667
2606
  };
668
2607
  }
2608
+ var constants = __webpack_require__("./src/constants.ts");
2609
+ const ERROR_CATALOG = {
2610
+ AUTH_FAILED: {
2611
+ code: 'AUTH_FAILED',
2612
+ message: 'Authentication failed',
2613
+ hint: 'Try running `c15t login` again',
2614
+ docs: `${constants.tl.CLI_DOCS}/auth`
2615
+ },
2616
+ AUTH_EXPIRED: {
2617
+ code: 'AUTH_EXPIRED',
2618
+ message: 'Session expired',
2619
+ hint: 'Run `c15t login` to refresh your session'
2620
+ },
2621
+ AUTH_NOT_LOGGED_IN: {
2622
+ code: 'AUTH_NOT_LOGGED_IN',
2623
+ message: 'Not logged in',
2624
+ hint: 'Run `c15t login` to authenticate'
2625
+ },
2626
+ AUTH_TOKEN_INVALID: {
2627
+ code: 'AUTH_TOKEN_INVALID',
2628
+ message: 'Invalid authentication token',
2629
+ hint: 'Try logging out with `c15t logout` and logging in again'
2630
+ },
2631
+ DEVICE_FLOW_TIMEOUT: {
2632
+ code: 'DEVICE_FLOW_TIMEOUT',
2633
+ message: 'Authentication timed out',
2634
+ hint: 'The login request expired. Run `c15t login` to try again'
2635
+ },
2636
+ DEVICE_FLOW_DENIED: {
2637
+ code: 'DEVICE_FLOW_DENIED',
2638
+ message: 'Authentication denied',
2639
+ hint: 'The login request was denied. Run `c15t login` to try again'
2640
+ },
2641
+ DEVICE_FLOW_PENDING: {
2642
+ code: 'DEVICE_FLOW_PENDING',
2643
+ message: 'Waiting for authentication',
2644
+ hint: 'Please complete the login in your browser'
2645
+ },
2646
+ NOT_A_PROJECT: {
2647
+ code: 'NOT_A_PROJECT',
2648
+ message: 'No package.json found',
2649
+ hint: 'Make sure you are in a JavaScript/TypeScript project directory'
2650
+ },
2651
+ FRAMEWORK_NOT_DETECTED: {
2652
+ code: 'FRAMEWORK_NOT_DETECTED',
2653
+ message: 'Could not detect framework',
2654
+ hint: 'Supported frameworks: Next.js, React, Remix, Vite',
2655
+ docs: `${constants.tl.CLI_DOCS}/frameworks`
2656
+ },
2657
+ LAYOUT_NOT_FOUND: {
2658
+ code: 'LAYOUT_NOT_FOUND',
2659
+ message: 'Could not find layout file',
2660
+ hint: 'Make sure you have app/layout.tsx or pages/_app.tsx'
2661
+ },
2662
+ CONFIG_EXISTS: {
2663
+ code: 'CONFIG_EXISTS',
2664
+ message: 'c15t configuration already exists',
2665
+ hint: 'Use --force to overwrite existing configuration'
2666
+ },
2667
+ CONFIG_NOT_FOUND: {
2668
+ code: 'CONFIG_NOT_FOUND',
2669
+ message: 'c15t configuration not found',
2670
+ hint: 'Run `c15t generate` to create a configuration'
2671
+ },
2672
+ CONFIG_INVALID: {
2673
+ code: 'CONFIG_INVALID',
2674
+ message: 'Invalid c15t configuration',
2675
+ hint: 'Check your c15t.config.ts file for errors',
2676
+ docs: `${constants.tl.DOCS}/configuration`
2677
+ },
2678
+ NETWORK_ERROR: {
2679
+ code: 'NETWORK_ERROR',
2680
+ message: 'Network request failed',
2681
+ hint: 'Check your internet connection'
2682
+ },
2683
+ CONTROL_PLANE_CONNECTION_FAILED: {
2684
+ code: 'CONTROL_PLANE_CONNECTION_FAILED',
2685
+ message: 'Could not connect to consent.io',
2686
+ hint: `Check if ${constants.tl.CONSENT_IO} is accessible`
2687
+ },
2688
+ API_ERROR: {
2689
+ code: 'API_ERROR',
2690
+ message: 'API request failed',
2691
+ hint: 'Check the error details and try again'
2692
+ },
2693
+ URL_INVALID: {
2694
+ code: 'URL_INVALID',
2695
+ message: 'Invalid URL format',
2696
+ hint: 'Expected format: https://your-instance.c15t.dev'
2697
+ },
2698
+ INSTANCE_NOT_FOUND: {
2699
+ code: 'INSTANCE_NOT_FOUND',
2700
+ message: 'Instance not found',
2701
+ hint: 'Run `c15t instances list` to see available instances'
2702
+ },
2703
+ INSTANCE_NAME_INVALID: {
2704
+ code: 'INSTANCE_NAME_INVALID',
2705
+ message: 'Invalid instance name',
2706
+ hint: 'Instance names must be alphanumeric with hyphens'
2707
+ },
2708
+ FILE_NOT_FOUND: {
2709
+ code: 'FILE_NOT_FOUND',
2710
+ message: 'File not found'
2711
+ },
2712
+ FILE_READ_ERROR: {
2713
+ code: 'FILE_READ_ERROR',
2714
+ message: 'Could not read file',
2715
+ hint: 'Check file permissions'
2716
+ },
2717
+ FILE_WRITE_ERROR: {
2718
+ code: 'FILE_WRITE_ERROR',
2719
+ message: 'Could not write file',
2720
+ hint: 'Check file permissions and disk space'
2721
+ },
2722
+ DIRECTORY_NOT_FOUND: {
2723
+ code: 'DIRECTORY_NOT_FOUND',
2724
+ message: 'Directory not found'
2725
+ },
2726
+ COMMAND_NOT_FOUND: {
2727
+ code: 'COMMAND_NOT_FOUND',
2728
+ message: 'Unknown command',
2729
+ hint: 'Run `c15t --help` to see available commands'
2730
+ },
2731
+ SUBCOMMAND_REQUIRED: {
2732
+ code: 'SUBCOMMAND_REQUIRED',
2733
+ message: 'Subcommand required',
2734
+ hint: 'Run the command with --help to see available subcommands'
2735
+ },
2736
+ FLAG_VALUE_REQUIRED: {
2737
+ code: 'FLAG_VALUE_REQUIRED',
2738
+ message: 'Flag requires a value'
2739
+ },
2740
+ INSTALL_FAILED: {
2741
+ code: 'INSTALL_FAILED',
2742
+ message: 'Package installation failed',
2743
+ hint: 'Try running the install command manually'
2744
+ },
2745
+ PACKAGE_MANAGER_NOT_FOUND: {
2746
+ code: 'PACKAGE_MANAGER_NOT_FOUND',
2747
+ message: 'Could not detect package manager',
2748
+ hint: 'Make sure npm, yarn, pnpm, or bun is installed'
2749
+ },
2750
+ MIGRATION_FAILED: {
2751
+ code: 'MIGRATION_FAILED',
2752
+ message: 'Database migration failed',
2753
+ hint: 'Check the error details and database connection'
2754
+ },
2755
+ MIGRATION_CONFIG_MISSING: {
2756
+ code: 'MIGRATION_CONFIG_MISSING',
2757
+ message: 'Migration configuration missing',
2758
+ hint: 'Make sure your c15t.config.ts includes database configuration'
2759
+ },
2760
+ UNKNOWN_ERROR: {
2761
+ code: 'UNKNOWN_ERROR',
2762
+ message: 'An unexpected error occurred',
2763
+ hint: `Please report this issue at ${constants.tl.GITHUB}/issues`
2764
+ },
2765
+ CANCELLED: {
2766
+ code: 'CANCELLED',
2767
+ message: 'Operation cancelled'
2768
+ }
2769
+ };
2770
+ class CliError extends Error {
2771
+ code;
2772
+ context;
2773
+ entry;
2774
+ constructor(code, context){
2775
+ const entry = ERROR_CATALOG[code];
2776
+ super(entry.message);
2777
+ this.name = 'CliError';
2778
+ this.code = code;
2779
+ this.context = context;
2780
+ this.entry = entry;
2781
+ if (Error.captureStackTrace) Error.captureStackTrace(this, CliError);
2782
+ }
2783
+ display(logger) {
2784
+ const entry = this.entry;
2785
+ let message = entry.message;
2786
+ if (this.context?.details) message += `: ${this.context.details}`;
2787
+ logger.error(message);
2788
+ if ('hint' in entry && entry.hint) logger.info(`Hint: ${entry.hint}`);
2789
+ if ('docs' in entry && entry.docs) logger.info(`Docs: ${entry.docs}`);
2790
+ }
2791
+ static from(error, fallbackCode = 'UNKNOWN_ERROR') {
2792
+ if (error instanceof CliError) return error;
2793
+ const message = error instanceof Error ? error.message : String(error);
2794
+ return new CliError(fallbackCode, {
2795
+ details: message,
2796
+ originalError: error
2797
+ });
2798
+ }
2799
+ }
669
2800
  async function runPackageManagerInstall(projectRoot, dependencies, packageManager) {
670
2801
  if (0 === dependencies.length) return;
671
2802
  let command;
@@ -838,9 +2969,8 @@ const fileGenerationActor = (0, __rspack_external_xstate.fromPromise)(async ({ i
838
2969
  nextConfigPath: null,
839
2970
  envPath: null
840
2971
  };
841
- const { projectRoot, framework, cwd, logger } = cliContext;
842
- framework.pkg;
843
- const { generateFiles } = await __webpack_require__.e("153").then(__webpack_require__.bind(__webpack_require__, "./src/commands/generate/options/utils/generate-files.ts"));
2972
+ const { projectRoot, logger } = cliContext;
2973
+ const { generateFiles } = await __webpack_require__.e("642").then(__webpack_require__.bind(__webpack_require__, "./src/commands/generate/options/utils/generate-files.ts"));
844
2974
  const spinnerMock = {
845
2975
  start: (msg)=>logger.debug(`[spinner] ${msg}`),
846
2976
  stop: (msg)=>logger.debug(`[spinner] ${msg}`),
@@ -868,7 +2998,7 @@ const fileGenerationActor = (0, __rspack_external_xstate.fromPromise)(async ({ i
868
2998
  }
869
2999
  const generateResult = await generateFiles({
870
3000
  context: cliContext,
871
- mode: mode,
3001
+ mode,
872
3002
  spinner: spinnerMock,
873
3003
  backendURL: backendURL ?? void 0,
874
3004
  useEnvFile,
@@ -938,29 +3068,6 @@ async function preflight_fileExists(filePath) {
938
3068
  return false;
939
3069
  }
940
3070
  }
941
- async function checkGitStatus(projectRoot) {
942
- try {
943
- const gitDir = external_node_path_["default"].join(projectRoot, '.git');
944
- const isGitRepo = await preflight_fileExists(gitDir);
945
- if (!isGitRepo) return {
946
- name: 'Git',
947
- status: 'warn',
948
- message: 'Not a git repository',
949
- hint: 'Consider initializing git to track changes'
950
- };
951
- return {
952
- name: 'Git',
953
- status: 'pass',
954
- message: 'Git repository detected'
955
- };
956
- } catch {
957
- return {
958
- name: 'Git',
959
- status: 'warn',
960
- message: 'Could not check git status'
961
- };
962
- }
963
- }
964
3071
  async function runPreflightChecks(context) {
965
3072
  const { projectRoot, framework, packageManager, logger } = context;
966
3073
  const checks = [];
@@ -991,16 +3098,6 @@ async function runPreflightChecks(context) {
991
3098
  message: packageManager.name ? `Using ${packageManager.name}` : 'No package manager detected',
992
3099
  hint: packageManager.name ? void 0 : 'Will default to npm'
993
3100
  });
994
- const gitCheck = await checkGitStatus(projectRoot);
995
- checks.push(gitCheck);
996
- const configPath = external_node_path_["default"].join(projectRoot, 'c15t.config.ts');
997
- const hasExistingConfig = await preflight_fileExists(configPath);
998
- checks.push({
999
- name: 'Existing config',
1000
- status: hasExistingConfig ? 'warn' : 'pass',
1001
- message: hasExistingConfig ? 'c15t.config.ts already exists' : 'No existing configuration',
1002
- hint: hasExistingConfig ? 'Existing config will be overwritten' : void 0
1003
- });
1004
3101
  const hasFailures = checks.some((c)=>'fail' === c.status);
1005
3102
  return {
1006
3103
  passed: !hasFailures,
@@ -1057,14 +3154,248 @@ function displayPreflightFailure(context, checks) {
1057
3154
  lines.push(` ${external_picocolors_["default"].red('•')} ${check.message}`);
1058
3155
  if (check.hint) lines.push(` ${check.hint}`);
1059
3156
  }
1060
- lines.push('');
1061
- lines.push('Please fix the issues above and try again.');
1062
- logger.message(lines.join('\n'));
3157
+ lines.push('');
3158
+ lines.push('Please fix the issues above and try again.');
3159
+ logger.message(lines.join('\n'));
3160
+ }
3161
+ function getControlPlaneBaseUrl() {
3162
+ const envValue = process.env.CONSENT_URL?.trim();
3163
+ if (!envValue) return constants.tl.CONSENT_IO;
3164
+ return envValue.replace(/\/+$/, '');
3165
+ }
3166
+ var config_store = __webpack_require__("./src/auth/config-store.ts");
3167
+ function getEndpoints(baseUrl) {
3168
+ return {
3169
+ deviceCodeV1Endpoint: `${baseUrl}/api/v1/auth/device/code`,
3170
+ deviceTokenV1Endpoint: `${baseUrl}/api/v1/auth/device/token`,
3171
+ deviceAuthorizationEndpoint: `${baseUrl}/oauth/device/code`,
3172
+ tokenEndpoint: `${baseUrl}/oauth/token`
3173
+ };
3174
+ }
3175
+ function isApiSuccessPayload(payload) {
3176
+ return 'object' == typeof payload && null !== payload && 'success' in payload && true === payload.success && 'data' in payload;
3177
+ }
3178
+ function normalizeDeviceCodeResponse(payload) {
3179
+ if (payload && 'object' == typeof payload && 'device_code' in payload && 'user_code' in payload && 'verification_uri' in payload) return payload;
3180
+ if (payload && 'object' == typeof payload && 'deviceCode' in payload && 'userCode' in payload && 'verificationUri' in payload) {
3181
+ const v1 = payload;
3182
+ return {
3183
+ device_code: v1.deviceCode,
3184
+ user_code: v1.userCode,
3185
+ verification_uri: v1.verificationUri,
3186
+ verification_uri_complete: v1.verificationUriComplete,
3187
+ expires_in: v1.expiresIn,
3188
+ interval: v1.interval
3189
+ };
3190
+ }
3191
+ throw new CliError('AUTH_FAILED', {
3192
+ details: 'Invalid device code response'
3193
+ });
3194
+ }
3195
+ function normalizeTokenResponse(payload) {
3196
+ if (payload && 'object' == typeof payload && 'access_token' in payload && 'token_type' in payload) return payload;
3197
+ if (payload && 'object' == typeof payload && 'accessToken' in payload && 'tokenType' in payload) {
3198
+ const v1 = payload;
3199
+ return {
3200
+ access_token: v1.accessToken,
3201
+ token_type: v1.tokenType,
3202
+ expires_in: v1.expiresIn,
3203
+ refresh_token: v1.refreshToken,
3204
+ scope: v1.scope
3205
+ };
3206
+ }
3207
+ throw new CliError('AUTH_FAILED', {
3208
+ details: 'Invalid token response'
3209
+ });
3210
+ }
3211
+ async function parseJsonSafe(response) {
3212
+ try {
3213
+ return await response.json();
3214
+ } catch {
3215
+ return null;
3216
+ }
3217
+ }
3218
+ function toDeviceFlowErrorFromV1(response, payload) {
3219
+ if (!payload || 'object' != typeof payload) return null;
3220
+ const apiError = payload;
3221
+ const errorCode = apiError.error?.code;
3222
+ const details = apiError.error?.details && 'object' == typeof apiError.error.details ? apiError.error.details : void 0;
3223
+ const status = details?.status;
3224
+ if ('authorization_pending' === status) return {
3225
+ error: 'authorization_pending',
3226
+ error_description: apiError.error?.message ?? 'Authorization pending'
3227
+ };
3228
+ if ('used' === status) return {
3229
+ error: 'access_denied',
3230
+ error_description: apiError.error?.message ?? 'Device code already used. Start a new login flow.'
3231
+ };
3232
+ if ('expired' === status || 401 === response.status) return {
3233
+ error: 'expired_token',
3234
+ error_description: apiError.error?.message ?? 'Device code expired'
3235
+ };
3236
+ if (409 === response.status) return {
3237
+ error: 'authorization_pending',
3238
+ error_description: apiError.error?.message ?? 'Authorization pending'
3239
+ };
3240
+ if ('FORBIDDEN' === errorCode || 403 === response.status) return {
3241
+ error: 'access_denied',
3242
+ error_description: apiError.error?.message ?? 'Authorization denied'
3243
+ };
3244
+ return null;
3245
+ }
3246
+ async function initiateDeviceFlow(baseUrl = constants.tl.CONSENT_IO) {
3247
+ const endpoints = getEndpoints(baseUrl);
3248
+ {
3249
+ const v1Response = await fetch(endpoints.deviceCodeV1Endpoint, {
3250
+ method: 'POST',
3251
+ headers: {
3252
+ 'Content-Type': 'application/json'
3253
+ },
3254
+ body: '{}'
3255
+ });
3256
+ if (v1Response.ok) {
3257
+ const payload = await parseJsonSafe(v1Response);
3258
+ const data = isApiSuccessPayload(payload) ? payload.data : payload;
3259
+ return normalizeDeviceCodeResponse(data);
3260
+ }
3261
+ if (404 !== v1Response.status) {
3262
+ const payload = await parseJsonSafe(v1Response);
3263
+ const message = payload && 'object' == typeof payload && 'error' in payload && payload.error?.message ? payload.error?.message : 'Request failed';
3264
+ throw new CliError('AUTH_FAILED', {
3265
+ details: `Device authorization failed: ${v1Response.status} ${message}`
3266
+ });
3267
+ }
3268
+ }
3269
+ const response = await fetch(endpoints.deviceAuthorizationEndpoint, {
3270
+ method: 'POST',
3271
+ headers: {
3272
+ 'Content-Type': 'application/x-www-form-urlencoded'
3273
+ },
3274
+ body: new URLSearchParams({
3275
+ client_id: 'c15t-cli',
3276
+ scope: 'instances:read instances:write'
3277
+ })
3278
+ });
3279
+ if (!response.ok) {
3280
+ const text = await response.text();
3281
+ throw new CliError('AUTH_FAILED', {
3282
+ details: `Device authorization failed: ${response.status} ${text}`
3283
+ });
3284
+ }
3285
+ const payload = await parseJsonSafe(response);
3286
+ const data = normalizeDeviceCodeResponse(payload);
3287
+ if (!data.device_code || !data.user_code || !data.verification_uri) throw new CliError('AUTH_FAILED', {
3288
+ details: 'Invalid device code response'
3289
+ });
3290
+ return data;
3291
+ }
3292
+ async function pollForToken(baseUrl, deviceCode, interval = constants.fT.DEVICE_FLOW_POLL_INTERVAL, expiresIn = constants.fT.DEVICE_FLOW_EXPIRY) {
3293
+ const endpoints = getEndpoints(baseUrl);
3294
+ const startTime = Date.now();
3295
+ const expiryTime = startTime + 1000 * expiresIn;
3296
+ let currentInterval = 1000 * interval;
3297
+ let useLegacyOAuthEndpoints = false;
3298
+ while(Date.now() < expiryTime){
3299
+ await sleep(currentInterval);
3300
+ try {
3301
+ if (!useLegacyOAuthEndpoints) {
3302
+ const v1Response = await fetch(endpoints.deviceTokenV1Endpoint, {
3303
+ method: 'POST',
3304
+ headers: {
3305
+ 'Content-Type': 'application/json'
3306
+ },
3307
+ body: JSON.stringify({
3308
+ deviceCode
3309
+ })
3310
+ });
3311
+ if (v1Response.ok) {
3312
+ const payload = await parseJsonSafe(v1Response);
3313
+ const data = isApiSuccessPayload(payload) ? payload.data : payload;
3314
+ return normalizeTokenResponse(data);
3315
+ }
3316
+ if (404 === v1Response.status) useLegacyOAuthEndpoints = true;
3317
+ else {
3318
+ const payload = await parseJsonSafe(v1Response);
3319
+ const mappedError = toDeviceFlowErrorFromV1(v1Response, payload);
3320
+ if (mappedError) switch(mappedError.error){
3321
+ case 'authorization_pending':
3322
+ continue;
3323
+ case 'expired_token':
3324
+ throw new CliError('DEVICE_FLOW_TIMEOUT', {
3325
+ details: mappedError.error_description
3326
+ });
3327
+ case 'access_denied':
3328
+ throw new CliError('DEVICE_FLOW_DENIED', {
3329
+ details: mappedError.error_description
3330
+ });
3331
+ }
3332
+ const message = payload && 'object' == typeof payload && 'error' in payload && payload.error?.message ? payload.error?.message : 'Request failed';
3333
+ throw new CliError('AUTH_FAILED', {
3334
+ details: `Token request failed: ${v1Response.status} ${message}`
3335
+ });
3336
+ }
3337
+ }
3338
+ const response = await fetch(endpoints.tokenEndpoint, {
3339
+ method: 'POST',
3340
+ headers: {
3341
+ 'Content-Type': 'application/x-www-form-urlencoded'
3342
+ },
3343
+ body: new URLSearchParams({
3344
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
3345
+ device_code: deviceCode,
3346
+ client_id: 'c15t-cli'
3347
+ })
3348
+ });
3349
+ if (response.ok) {
3350
+ const payload = await parseJsonSafe(response);
3351
+ const token = normalizeTokenResponse(payload);
3352
+ return token;
3353
+ }
3354
+ const payload = await parseJsonSafe(response);
3355
+ const error = payload;
3356
+ switch(error.error){
3357
+ case 'authorization_pending':
3358
+ continue;
3359
+ case 'slow_down':
3360
+ currentInterval += 5000;
3361
+ continue;
3362
+ case 'access_denied':
3363
+ throw new CliError('DEVICE_FLOW_DENIED', {
3364
+ details: error.error_description
3365
+ });
3366
+ case 'expired_token':
3367
+ throw new CliError('DEVICE_FLOW_TIMEOUT', {
3368
+ details: 'The device code has expired'
3369
+ });
3370
+ default:
3371
+ throw new CliError('AUTH_FAILED', {
3372
+ details: error.error_description || `Unknown error: ${error.error}`
3373
+ });
3374
+ }
3375
+ } catch (error) {
3376
+ if (error instanceof CliError) throw error;
3377
+ }
3378
+ }
3379
+ throw new CliError('DEVICE_FLOW_TIMEOUT');
3380
+ }
3381
+ function formatUserCode(userCode) {
3382
+ if (userCode.includes('-')) return userCode;
3383
+ const midpoint = Math.ceil(userCode.length / 2);
3384
+ return `${userCode.slice(0, midpoint)}-${userCode.slice(midpoint)}`;
3385
+ }
3386
+ function getVerificationUrl(response) {
3387
+ if (response.verification_uri_complete) return response.verification_uri_complete;
3388
+ const url = new URL(response.verification_uri);
3389
+ url.searchParams.set('user_code', response.user_code);
3390
+ return url.toString();
3391
+ }
3392
+ function sleep(ms) {
3393
+ return new Promise((resolve)=>setTimeout(resolve, ms));
1063
3394
  }
1064
3395
  async function getDevToolsOption({ context, handleCancel, onCancel }) {
1065
3396
  const isReactProject = '@c15t/react' === context.framework.pkg || '@c15t/nextjs' === context.framework.pkg;
1066
3397
  context.logger.info("c15t DevTools helps you inspect consent state, scripts, and location overrides during development.");
1067
- context.logger.info('Learn more: https://c15t.com/docs/dev-tools/overview');
3398
+ context.logger.info('Learn more: https://v2.c15t.com/docs/dev-tools/overview');
1068
3399
  const enableDevTools = await prompts_.select({
1069
3400
  message: 'Install and enable c15t DevTools?',
1070
3401
  options: [
@@ -1095,7 +3426,7 @@ async function getSSROption({ context, handleCancel, onCancel }) {
1095
3426
  context.logger.info('SSR consent prefetch starts data loading on the server for faster banner visibility.');
1096
3427
  context.logger.info('Tradeoff: this uses Next.js headers() and makes the route dynamic (not fully static).');
1097
3428
  context.logger.info('On slow backends or cross-region setups, SSR can increase TTFB. Measure both TTFB and banner visibility.');
1098
- context.logger.info('Learn more: https://c15t.com/docs/frameworks/nextjs/ssr');
3429
+ context.logger.info('Learn more: https://v2.c15t.com/docs/frameworks/nextjs/ssr');
1099
3430
  const enableSSR = await prompts_.select({
1100
3431
  message: 'Enable SSR consent prefetch? (faster first banner visibility, dynamic route)',
1101
3432
  options: [
@@ -1122,6 +3453,205 @@ async function getSSROption({ context, handleCancel, onCancel }) {
1122
3453
  }
1123
3454
  return enableSSR;
1124
3455
  }
3456
+ function extractAwsRegion(value) {
3457
+ if (!value) return;
3458
+ const match = value.match(/[a-z]{2}-[a-z]+-\d/);
3459
+ return match?.[0];
3460
+ }
3461
+ function client_isApiSuccessPayload(payload) {
3462
+ return 'object' == typeof payload && null !== payload && 'success' in payload && true === payload.success && 'data' in payload;
3463
+ }
3464
+ async function client_parseJsonSafe(response) {
3465
+ try {
3466
+ return await response.json();
3467
+ } catch {
3468
+ return null;
3469
+ }
3470
+ }
3471
+ function mapControlPlaneInstance(raw) {
3472
+ const regionFromObject = 'string' == typeof raw.region ? raw.region : raw.region?.id ?? raw.region?.slug ?? raw.region?.code;
3473
+ const region = regionFromObject ?? raw.regionId ?? raw.regionSlug ?? extractAwsRegion(raw.backendURL) ?? extractAwsRegion(raw.dashboardURL);
3474
+ return {
3475
+ id: raw.instanceId,
3476
+ name: raw.instanceName,
3477
+ organizationSlug: raw.organizationSlug,
3478
+ region,
3479
+ url: raw.backendURL ?? raw.dashboardURL ?? '',
3480
+ createdAt: new Date(0).toISOString(),
3481
+ status: raw.backendURL ? 'active' : 'pending'
3482
+ };
3483
+ }
3484
+ class ControlPlaneClient {
3485
+ config;
3486
+ connected = false;
3487
+ constructor(config){
3488
+ this.config = {
3489
+ clientName: constants.Aw.CONTROL_PLANE_CLIENT_NAME,
3490
+ clientVersion: constants.Aw.VERSION,
3491
+ timeout: constants.fT.CONTROL_PLANE_CONNECTION,
3492
+ ...config
3493
+ };
3494
+ }
3495
+ async connect() {
3496
+ this.connected = true;
3497
+ return {
3498
+ connected: true,
3499
+ capabilities: {
3500
+ tools: [
3501
+ 'listInstances',
3502
+ 'createInstance',
3503
+ 'getInstance'
3504
+ ]
3505
+ }
3506
+ };
3507
+ }
3508
+ async close() {
3509
+ this.connected = false;
3510
+ }
3511
+ isConnected() {
3512
+ return this.connected;
3513
+ }
3514
+ ensureConnected() {
3515
+ if (!this.connected) throw new CliError('CONTROL_PLANE_CONNECTION_FAILED', {
3516
+ details: 'Not connected to control plane'
3517
+ });
3518
+ }
3519
+ buildUrl(path) {
3520
+ return `${this.config.baseUrl}/api/v1${path}`;
3521
+ }
3522
+ async request(path, init) {
3523
+ this.ensureConnected();
3524
+ const response = await fetch(this.buildUrl(path), {
3525
+ method: init?.method ?? 'GET',
3526
+ headers: {
3527
+ Authorization: `Bearer ${this.config.accessToken}`,
3528
+ 'Content-Type': 'application/json'
3529
+ },
3530
+ body: init?.body === void 0 ? void 0 : JSON.stringify(init.body)
3531
+ });
3532
+ const payload = await client_parseJsonSafe(response);
3533
+ if (response.ok && client_isApiSuccessPayload(payload)) return payload.data;
3534
+ const apiError = payload;
3535
+ const errorCode = apiError?.error?.code;
3536
+ const errorMessage = apiError?.error?.message ?? 'Request failed';
3537
+ if (401 === response.status) throw new CliError('AUTH_TOKEN_INVALID', {
3538
+ details: `${response.status} ${errorMessage}`
3539
+ });
3540
+ throw new CliError('API_ERROR', {
3541
+ details: `${response.status} ${errorCode ? `${errorCode}: ` : ''}${errorMessage}`
3542
+ });
3543
+ }
3544
+ async listOrganizations() {
3545
+ return this.request('/consent/organizations');
3546
+ }
3547
+ async listRegions() {
3548
+ return this.request('/consent/regions');
3549
+ }
3550
+ async listInstances() {
3551
+ const raw = await this.request('/consent/instances');
3552
+ return raw.map(mapControlPlaneInstance);
3553
+ }
3554
+ async getInstance(id) {
3555
+ const instances = await this.listInstances();
3556
+ const instance = instances.find((item)=>item.id === id);
3557
+ if (!instance) throw new CliError('INSTANCE_NOT_FOUND', {
3558
+ details: `Instance not found: ${id}`
3559
+ });
3560
+ return instance;
3561
+ }
3562
+ async createInstance(request) {
3563
+ const { organizationSlug, region, trustedOrigins } = request.config;
3564
+ if (!organizationSlug || !region) throw new CliError('API_ERROR', {
3565
+ details: 'organizationSlug and region are required'
3566
+ });
3567
+ const instance = await this.request('/consent/instances', {
3568
+ method: 'POST',
3569
+ body: {
3570
+ organizationSlug,
3571
+ name: request.name,
3572
+ region,
3573
+ production: false,
3574
+ trustedOrigins: trustedOrigins ?? [],
3575
+ useV2: true
3576
+ }
3577
+ });
3578
+ return mapControlPlaneInstance(instance);
3579
+ }
3580
+ async deleteInstance(id) {
3581
+ await this.request(`/consent/instances/${encodeURIComponent(id)}`, {
3582
+ method: 'DELETE'
3583
+ });
3584
+ }
3585
+ }
3586
+ async function createControlPlaneClient(accessToken, baseUrl = constants.tl.CONSENT_IO) {
3587
+ const client = new ControlPlaneClient({
3588
+ baseUrl,
3589
+ accessToken
3590
+ });
3591
+ const state = await client.connect();
3592
+ if (!state.connected) throw new CliError('CONTROL_PLANE_CONNECTION_FAILED', {
3593
+ details: state.error
3594
+ });
3595
+ return client;
3596
+ }
3597
+ async function createControlPlaneClientFromConfig(baseUrl = constants.tl.CONSENT_IO) {
3598
+ const { getAccessToken } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "./src/auth/config-store.ts"));
3599
+ const accessToken = await getAccessToken();
3600
+ if (!accessToken) return null;
3601
+ return createControlPlaneClient(accessToken, baseUrl);
3602
+ }
3603
+ external_picocolors_["default"].green, external_picocolors_["default"].red, external_picocolors_["default"].yellow, external_picocolors_["default"].blue, external_picocolors_["default"].dim, external_picocolors_["default"].cyan, external_picocolors_["default"].bold, external_picocolors_["default"].underline;
3604
+ function createTaskSpinner(initialMessage) {
3605
+ const spinner = prompts_.spinner();
3606
+ let isRunning = false;
3607
+ return {
3608
+ start (message) {
3609
+ if (!isRunning) {
3610
+ spinner.start(message || initialMessage || 'Processing...');
3611
+ isRunning = true;
3612
+ }
3613
+ },
3614
+ stop (message) {
3615
+ if (isRunning) {
3616
+ spinner.stop(message || 'Done');
3617
+ isRunning = false;
3618
+ }
3619
+ },
3620
+ message (message) {
3621
+ if (isRunning) spinner.message(message);
3622
+ },
3623
+ success (message) {
3624
+ if (isRunning) {
3625
+ spinner.stop(external_picocolors_["default"].green('✓') + ' ' + message);
3626
+ isRunning = false;
3627
+ }
3628
+ },
3629
+ error (message) {
3630
+ if (isRunning) {
3631
+ spinner.stop(external_picocolors_["default"].red('✗') + ' ' + message);
3632
+ isRunning = false;
3633
+ }
3634
+ }
3635
+ };
3636
+ }
3637
+ function isValidUrl(value) {
3638
+ return constants.QL.URL.test(value);
3639
+ }
3640
+ function isValidC15tUrl(value) {
3641
+ return constants.QL.C15T_URL.test(value);
3642
+ }
3643
+ function isValidInstanceName(value) {
3644
+ return /^[a-z][a-z0-9-]{2,62}$/.test(value) && !value.includes('--');
3645
+ }
3646
+ function createValidator(validate, errorMessage) {
3647
+ return (value)=>{
3648
+ if (!validate(value)) return errorMessage;
3649
+ };
3650
+ }
3651
+ createValidator(isValidUrl, 'Please enter a valid URL (e.g., https://example.com)');
3652
+ createValidator(isValidC15tUrl, 'Please enter a valid c15t URL (e.g., https://my-app.c15t.dev)');
3653
+ const validateInstanceName = createValidator(isValidInstanceName, 'Instance name must be 3-63 lowercase alphanumeric characters with hyphens');
3654
+ createValidator((value)=>value.trim().length > 0, 'This field is required');
1125
3655
  function isCancel(value) {
1126
3656
  return prompts_.isCancel(value);
1127
3657
  }
@@ -1131,29 +3661,37 @@ class PromptCancelledError extends Error {
1131
3661
  this.name = 'PromptCancelledError';
1132
3662
  }
1133
3663
  }
3664
+ function formatInstanceLabel(instance) {
3665
+ if (instance.organizationSlug) return `${instance.organizationSlug}/${instance.name}`;
3666
+ return instance.name;
3667
+ }
3668
+ function formatInstanceRegion(instance) {
3669
+ return `(${instance.region ?? 'unknown'})`;
3670
+ }
3671
+ function isV2ModeEnabled() {
3672
+ return '1' === process.env[constants.h3.V2];
3673
+ }
1134
3674
  const modeSelectionActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
3675
+ let initialMode = input.initialMode;
3676
+ if ('c15t' === initialMode || 'self-hosted' === initialMode) initialMode = 'hosted';
3677
+ if (void 0 === initialMode) initialMode = 'hosted';
1135
3678
  const result = await prompts_.select({
1136
3679
  message: 'How would you like to store consent decisions?',
1137
- initialValue: input.initialMode ?? 'c15t',
3680
+ initialValue: initialMode,
1138
3681
  options: [
1139
3682
  {
1140
- value: 'c15t',
1141
- label: 'Hosted c15t (consent.io)',
1142
- hint: 'Recommended: Fully managed service'
3683
+ value: 'hosted',
3684
+ label: 'Hosted',
3685
+ hint: 'consent.io or self-hosted backend URL'
1143
3686
  },
1144
3687
  {
1145
3688
  value: 'offline',
1146
- label: 'Offline Mode',
3689
+ label: 'Offline',
1147
3690
  hint: 'Store in browser, no backend needed'
1148
3691
  },
1149
- {
1150
- value: 'self-hosted',
1151
- label: 'Self-Hosted',
1152
- hint: 'Run your own c15t backend'
1153
- },
1154
3692
  {
1155
3693
  value: 'custom',
1156
- label: 'Custom Implementation',
3694
+ label: 'Custom',
1157
3695
  hint: 'Full control over storage logic'
1158
3696
  }
1159
3697
  ]
@@ -1163,61 +3701,272 @@ const modeSelectionActor = (0, __rspack_external_xstate.fromPromise)(async ({ in
1163
3701
  mode: result
1164
3702
  };
1165
3703
  });
1166
- const accountCreationActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
1167
- const { cliContext } = input;
1168
- const needsAccount = await prompts_.confirm({
1169
- message: 'Do you need to create a consent.io account?',
1170
- initialValue: true
1171
- });
1172
- if (isCancel(needsAccount)) throw new PromptCancelledError('account_creation');
1173
- if (!needsAccount) return {
1174
- needsAccount: false,
1175
- browserOpened: false
1176
- };
1177
- prompts_.note(`We'll open your browser to create a consent.io account and set up your instance.\nFollow these steps:\n1. Sign up for a consent.io account\n2. Create a new instance in the dashboard\n3. Configure your trusted origins (domains that can connect)\n4. Copy the provided backendURL (e.g., https://your-instance.c15t.dev)`, 'consent.io Setup');
1178
- const shouldOpen = await prompts_.confirm({
1179
- message: 'Open browser to sign up for consent.io?',
1180
- initialValue: true
1181
- });
1182
- if (isCancel(shouldOpen)) throw new PromptCancelledError('browser_open');
1183
- let browserOpened = false;
1184
- if (shouldOpen) try {
1185
- const open = (await import("open")).default;
1186
- await open('https://consent.io/dashboard/register?ref=cli');
1187
- browserOpened = true;
1188
- const enterPressed = await prompts_.text({
1189
- message: 'Press Enter once you have created your instance and have the backendURL'
1190
- });
1191
- if (isCancel(enterPressed)) throw new PromptCancelledError('url_input_wait');
1192
- } catch (error) {
1193
- if (error instanceof PromptCancelledError) throw error;
1194
- cliContext.logger.warn('Failed to open browser automatically. Please visit https://consent.io/dashboard/register manually.');
1195
- }
1196
- return {
1197
- needsAccount: true,
1198
- browserOpened
1199
- };
1200
- });
1201
- const backendURLActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
1202
- const { initialURL, isC15tMode } = input;
1203
- const placeholder = isC15tMode ? 'https://your-instance.c15t.dev' : 'https://your-backend.example.com/api/c15t';
3704
+ async function promptBackendURL(input) {
1204
3705
  const result = await prompts_.text({
1205
- message: isC15tMode ? 'Enter your consent.io instance URL:' : 'Enter your self-hosted backend URL:',
1206
- placeholder,
1207
- initialValue: initialURL,
3706
+ message: input.message,
3707
+ placeholder: input.placeholder,
3708
+ initialValue: input.initialURL,
1208
3709
  validate: (value)=>{
1209
- if (!value || '' === value) return 'URL is required';
3710
+ if (!value || '' === value.trim()) return 'URL is required';
1210
3711
  try {
1211
- const url = new URL(value);
1212
- if (isC15tMode && !url.hostname.endsWith('.c15t.dev')) return 'Please enter a valid *.c15t.dev URL';
3712
+ new URL(value);
1213
3713
  } catch {
1214
3714
  return 'Please enter a valid URL';
1215
3715
  }
1216
3716
  }
1217
3717
  });
1218
- if (isCancel(result)) throw new PromptCancelledError('backend_url');
3718
+ if (isCancel(result)) throw new PromptCancelledError(input.stage);
3719
+ return result;
3720
+ }
3721
+ async function runConsentLogin(cliContext) {
3722
+ const baseUrl = getControlPlaneBaseUrl();
3723
+ const authState = await (0, config_store.IM)();
3724
+ let useExistingSession = false;
3725
+ if (authState.isLoggedIn && !authState.isExpired) {
3726
+ const keepCurrentSession = await prompts_.confirm({
3727
+ message: 'You are already signed in. Use your existing session?',
3728
+ initialValue: true
3729
+ });
3730
+ if (isCancel(keepCurrentSession)) throw new PromptCancelledError('consent_existing_session');
3731
+ if (keepCurrentSession) useExistingSession = true;
3732
+ }
3733
+ if (useExistingSession) return;
3734
+ const deviceSpinner = createTaskSpinner('Requesting device code...');
3735
+ deviceSpinner.start();
3736
+ try {
3737
+ const deviceCode = await initiateDeviceFlow(baseUrl);
3738
+ deviceSpinner.success('Device code received');
3739
+ const userCode = formatUserCode(deviceCode.user_code);
3740
+ const verificationUrl = getVerificationUrl(deviceCode);
3741
+ cliContext.logger.message('');
3742
+ cliContext.logger.note(`Your code: ${external_picocolors_["default"].bold(external_picocolors_["default"].cyan(userCode))}\n\nThis code will expire in ${Math.floor(deviceCode.expires_in / 60)} minutes.`, 'Verification Code');
3743
+ cliContext.logger.message('');
3744
+ cliContext.logger.message(`Open this URL to continue: ${external_picocolors_["default"].underline(verificationUrl)}`);
3745
+ cliContext.logger.message('');
3746
+ const shouldOpen = await prompts_.confirm({
3747
+ message: 'Open the verification page in your browser?',
3748
+ initialValue: true
3749
+ });
3750
+ if (isCancel(shouldOpen)) throw new PromptCancelledError('consent_open_verification');
3751
+ if (shouldOpen) try {
3752
+ const open = (await import("open")).default;
3753
+ await open(verificationUrl);
3754
+ } catch {
3755
+ cliContext.logger.warn(`Could not open browser automatically. Visit ${verificationUrl} manually.`);
3756
+ }
3757
+ const authSpinner = createTaskSpinner('Waiting for authorization...');
3758
+ authSpinner.start();
3759
+ try {
3760
+ const token = await pollForToken(baseUrl, deviceCode.device_code, deviceCode.interval, deviceCode.expires_in);
3761
+ authSpinner.success('Authorization received');
3762
+ await (0, config_store.Ex)(token.access_token, {
3763
+ refreshToken: token.refresh_token,
3764
+ expiresIn: token.expires_in
3765
+ });
3766
+ } catch (error) {
3767
+ authSpinner.error('Authorization failed');
3768
+ throw error;
3769
+ }
3770
+ } catch (error) {
3771
+ deviceSpinner.stop();
3772
+ throw error;
3773
+ }
3774
+ }
3775
+ async function createInstanceInteractively(client, cliContext) {
3776
+ const preloadSpinner = createTaskSpinner('Loading organizations and regions...');
3777
+ preloadSpinner.start();
3778
+ let organizations;
3779
+ let regions;
3780
+ try {
3781
+ [organizations, regions] = await Promise.all([
3782
+ client.listOrganizations(),
3783
+ client.listRegions()
3784
+ ]);
3785
+ } finally{
3786
+ preloadSpinner.stop();
3787
+ }
3788
+ if (0 === organizations.length) throw new CliError('API_ERROR', {
3789
+ details: 'No organizations available for this account'
3790
+ });
3791
+ if (0 === regions.length) throw new CliError('API_ERROR', {
3792
+ details: 'No provisioning regions available'
3793
+ });
3794
+ const orgSelection = await prompts_.select({
3795
+ message: 'Select organization:',
3796
+ options: organizations.map((org)=>({
3797
+ value: org.organizationSlug,
3798
+ label: org.organizationName,
3799
+ hint: `${org.organizationSlug} • ${org.role}`
3800
+ })),
3801
+ initialValue: organizations[0]?.organizationSlug
3802
+ });
3803
+ if (isCancel(orgSelection)) throw new PromptCancelledError('instance_create_org_slug');
3804
+ const v2Regions = regions.filter((region)=>'v2' === region.family);
3805
+ if (0 === v2Regions.length) throw new CliError('API_ERROR', {
3806
+ details: 'No v2 provisioning regions available'
3807
+ });
3808
+ const regionSelection = await prompts_.select({
3809
+ message: 'Select V2 region:',
3810
+ options: v2Regions.map((region)=>({
3811
+ value: region.id,
3812
+ label: region.id,
3813
+ hint: region.label
3814
+ })),
3815
+ initialValue: v2Regions.find((region)=>'us-east-1' === region.id)?.id
3816
+ });
3817
+ if (isCancel(regionSelection)) throw new PromptCancelledError('instance_create_region');
3818
+ const slugInput = await prompts_.text({
3819
+ message: 'New instance slug:',
3820
+ placeholder: 'my-app',
3821
+ validate: (value)=>validateInstanceName(value.trim())
3822
+ });
3823
+ if (isCancel(slugInput)) throw new PromptCancelledError('instance_create_name');
3824
+ const slug = slugInput.trim();
3825
+ const createSpinner = createTaskSpinner(`Creating instance "${slug}"...`);
3826
+ createSpinner.start();
3827
+ try {
3828
+ const instance = await client.createInstance({
3829
+ name: slug,
3830
+ config: {
3831
+ organizationSlug: orgSelection,
3832
+ region: regionSelection
3833
+ }
3834
+ });
3835
+ createSpinner.success('Instance created');
3836
+ cliContext.logger.info('Created as a v2 development instance. Enable production mode in the dashboard when you are ready.');
3837
+ return instance;
3838
+ } catch (error) {
3839
+ createSpinner.error('Failed to create instance');
3840
+ throw error;
3841
+ }
3842
+ }
3843
+ async function selectOrCreateInstance(cliContext) {
3844
+ const baseUrl = getControlPlaneBaseUrl();
3845
+ const listSpinner = createTaskSpinner('Fetching your consent.io instances...');
3846
+ listSpinner.start();
3847
+ const client = await createControlPlaneClientFromConfig(baseUrl);
3848
+ if (!client) {
3849
+ listSpinner.stop();
3850
+ throw new CliError('AUTH_NOT_LOGGED_IN');
3851
+ }
3852
+ try {
3853
+ const instances = await client.listInstances();
3854
+ listSpinner.stop();
3855
+ if (0 === instances.length) {
3856
+ cliContext.logger.info('No instances found. Creating a new instance for this project.');
3857
+ return await createInstanceInteractively(client, cliContext);
3858
+ }
3859
+ const selectedId = await prompts_.select({
3860
+ message: 'Select an instance to use:',
3861
+ options: [
3862
+ ...instances.map((instance)=>({
3863
+ value: instance.id,
3864
+ label: formatInstanceLabel(instance),
3865
+ hint: formatInstanceRegion(instance)
3866
+ })),
3867
+ {
3868
+ value: '__create__',
3869
+ label: 'Create new instance',
3870
+ hint: 'Provision a new consent.io instance now'
3871
+ }
3872
+ ]
3873
+ });
3874
+ if (isCancel(selectedId)) throw new PromptCancelledError('instance_select');
3875
+ if ('__create__' === selectedId) return await createInstanceInteractively(client, cliContext);
3876
+ const selected = instances.find((instance)=>instance.id === selectedId);
3877
+ if (!selected) throw new CliError('INSTANCE_NOT_FOUND');
3878
+ return selected;
3879
+ } catch (error) {
3880
+ listSpinner.stop();
3881
+ throw error;
3882
+ } finally{
3883
+ await client.close();
3884
+ }
3885
+ }
3886
+ const hostedModeActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
3887
+ const { cliContext, initialURL, preselectedProvider } = input;
3888
+ let provider = preselectedProvider ?? null;
3889
+ if (!provider) {
3890
+ const providerSelection = await prompts_.select({
3891
+ message: 'Choose your hosted backend option:',
3892
+ options: [
3893
+ {
3894
+ value: 'consent.io',
3895
+ label: 'consent.io (Recommended)',
3896
+ hint: 'Managed infrastucture'
3897
+ },
3898
+ {
3899
+ value: 'self-hosted',
3900
+ label: 'Self-hosted',
3901
+ hint: 'Use your own deployed c15t backend'
3902
+ }
3903
+ ],
3904
+ initialValue: 'consent.io'
3905
+ });
3906
+ if (isCancel(providerSelection)) throw new PromptCancelledError('hosted_provider');
3907
+ provider = providerSelection;
3908
+ }
3909
+ if ('self-hosted' === provider) {
3910
+ const url = await promptBackendURL({
3911
+ message: 'Enter your self-hosted backend URL:',
3912
+ placeholder: 'https://your-backend.example.com/api/c15t',
3913
+ initialURL,
3914
+ stage: 'self_hosted_backend_url'
3915
+ });
3916
+ return {
3917
+ url,
3918
+ provider
3919
+ };
3920
+ }
3921
+ if (!isV2ModeEnabled()) {
3922
+ cliContext.logger.info('consent.io sign-in is currently disabled. Set V2=1 to enable sign-in and instance selection.');
3923
+ const url = await promptBackendURL({
3924
+ message: 'Enter your consent.io instance URL:',
3925
+ placeholder: 'https://your-instance.c15t.dev',
3926
+ initialURL,
3927
+ stage: 'consent_manual_url'
3928
+ });
3929
+ return {
3930
+ url,
3931
+ provider: 'consent.io'
3932
+ };
3933
+ }
3934
+ const setupMethod = await prompts_.select({
3935
+ message: 'How do you want to configure consent.io?',
3936
+ options: [
3937
+ {
3938
+ value: 'sign-in',
3939
+ label: 'Sign in and pick an instance',
3940
+ hint: 'List existing instances or create a new one'
3941
+ },
3942
+ {
3943
+ value: 'manual-url',
3944
+ label: 'Enter instance URL manually',
3945
+ hint: 'Use an existing backend URL'
3946
+ }
3947
+ ],
3948
+ initialValue: 'sign-in'
3949
+ });
3950
+ if (isCancel(setupMethod)) throw new PromptCancelledError('consent_setup_method');
3951
+ if ('manual-url' === setupMethod) {
3952
+ const url = await promptBackendURL({
3953
+ message: 'Enter your consent.io instance URL:',
3954
+ placeholder: 'https://your-instance.c15t.dev',
3955
+ initialURL,
3956
+ stage: 'consent_manual_url'
3957
+ });
3958
+ return {
3959
+ url,
3960
+ provider: 'consent.io'
3961
+ };
3962
+ }
3963
+ await runConsentLogin(cliContext);
3964
+ const instance = await selectOrCreateInstance(cliContext);
3965
+ await (0, config_store._g)(instance.id);
3966
+ cliContext.logger.info(`Using instance ${external_picocolors_["default"].cyan(instance.name)} (${external_picocolors_["default"].dim(instance.id)})`);
1219
3967
  return {
1220
- url: result
3968
+ url: instance.url,
3969
+ provider: 'consent.io'
1221
3970
  };
1222
3971
  });
1223
3972
  const backendOptionsActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
@@ -1268,7 +4017,7 @@ const frontendOptionsActor = (0, __rspack_external_xstate.fromPromise)(async ({
1268
4017
  });
1269
4018
  }
1270
4019
  cliContext.logger.info('Choose how you want your consent UI components generated.');
1271
- cliContext.logger.info('Learn more: https://c15t.com/docs/frameworks/nextjs/customization');
4020
+ cliContext.logger.info('Learn more: https://v2.c15t.com/docs/frameworks/nextjs/customization');
1272
4021
  const styleResult = await prompts_.select({
1273
4022
  message: 'UI component style:',
1274
4023
  options: [
@@ -1543,8 +4292,8 @@ function preflightFailed({ context }) {
1543
4292
  function hasModeArg({ context }) {
1544
4293
  return null !== context.modeArg;
1545
4294
  }
1546
- function guards_isC15tMode({ context }) {
1547
- return 'c15t' === context.selectedMode;
4295
+ function isHostedMode({ context }) {
4296
+ return 'hosted' === context.selectedMode || 'c15t' === context.selectedMode || 'self-hosted' === context.selectedMode;
1548
4297
  }
1549
4298
  function isOfflineMode({ context }) {
1550
4299
  return 'offline' === context.selectedMode;
@@ -1556,7 +4305,7 @@ function isCustomMode({ context }) {
1556
4305
  return 'custom' === context.selectedMode;
1557
4306
  }
1558
4307
  function modeRequiresBackend({ context }) {
1559
- return 'c15t' === context.selectedMode || 'self-hosted' === context.selectedMode;
4308
+ return 'hosted' === context.selectedMode || 'c15t' === context.selectedMode || 'self-hosted' === context.selectedMode;
1560
4309
  }
1561
4310
  function modeNoBackend({ context }) {
1562
4311
  return 'offline' === context.selectedMode || 'custom' === context.selectedMode;
@@ -1601,7 +4350,9 @@ function needsCleanup({ context }) {
1601
4350
  return !context.cleanupDone && (context.filesCreated.length > 0 || context.filesModified.length > 0);
1602
4351
  }
1603
4352
  function shouldPromptSSR({ context }) {
1604
- return context.framework?.pkg === '@c15t/nextjs' && ('c15t' === context.selectedMode || 'self-hosted' === context.selectedMode);
4353
+ return context.framework?.pkg === '@c15t/nextjs' && modeRequiresBackend({
4354
+ context
4355
+ });
1605
4356
  }
1606
4357
  function shouldPromptUIStyle({ context }) {
1607
4358
  return context.framework?.pkg === '@c15t/nextjs' || context.framework?.pkg === '@c15t/react';
@@ -1610,7 +4361,7 @@ const guards = {
1610
4361
  preflightPassed,
1611
4362
  preflightFailed,
1612
4363
  hasModeArg,
1613
- isC15tMode: guards_isC15tMode,
4364
+ isHostedMode,
1614
4365
  isOfflineMode,
1615
4366
  isSelfHostedMode,
1616
4367
  isCustomMode,
@@ -1642,6 +4393,7 @@ function createInitialContext(cliContext, modeArg) {
1642
4393
  preflightChecks: [],
1643
4394
  selectedMode: null,
1644
4395
  modeArg: modeArg ?? null,
4396
+ hostedProvider: null,
1645
4397
  backendURL: null,
1646
4398
  useEnvFile: true,
1647
4399
  proxyNextjs: true,
@@ -1665,6 +4417,15 @@ function createInitialContext(cliContext, modeArg) {
1665
4417
  stateHistory: []
1666
4418
  };
1667
4419
  }
4420
+ function normalizeSelectedMode(mode) {
4421
+ if ('c15t' === mode || 'self-hosted' === mode) return 'hosted';
4422
+ return mode ?? null;
4423
+ }
4424
+ function getHostedProviderFromMode(mode) {
4425
+ if ('self-hosted' === mode) return 'self-hosted';
4426
+ if ('c15t' === mode) return 'consent.io';
4427
+ return null;
4428
+ }
1668
4429
  const generateMachine = (0, __rspack_external_xstate.setup)({
1669
4430
  types: {
1670
4431
  context: {},
@@ -1675,8 +4436,7 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
1675
4436
  actors: {
1676
4437
  preflight: preflightActor,
1677
4438
  modeSelection: modeSelectionActor,
1678
- accountCreation: accountCreationActor,
1679
- backendURL: backendURLActor,
4439
+ hostedMode: hostedModeActor,
1680
4440
  backendOptions: backendOptionsActor,
1681
4441
  frontendOptions: frontendOptionsActor,
1682
4442
  scriptsOption: scriptsOptionActor,
@@ -1776,7 +4536,8 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
1776
4536
  guard: 'hasModeArg',
1777
4537
  target: 'routeToMode',
1778
4538
  actions: (0, __rspack_external_xstate.assign)({
1779
- selectedMode: ({ context })=>context.modeArg
4539
+ selectedMode: ({ context })=>normalizeSelectedMode(context.modeArg),
4540
+ hostedProvider: ({ context })=>getHostedProviderFromMode(context.modeArg)
1780
4541
  })
1781
4542
  }
1782
4543
  ],
@@ -1786,7 +4547,8 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
1786
4547
  onDone: {
1787
4548
  target: 'routeToMode',
1788
4549
  actions: (0, __rspack_external_xstate.assign)({
1789
- selectedMode: ({ event })=>event.output.mode
4550
+ selectedMode: ({ event })=>normalizeSelectedMode(event.output.mode),
4551
+ hostedProvider: null
1790
4552
  })
1791
4553
  },
1792
4554
  onError: {
@@ -1800,17 +4562,13 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
1800
4562
  routeToMode: {
1801
4563
  always: [
1802
4564
  {
1803
- guard: 'isC15tMode',
1804
- target: 'c15tMode'
4565
+ guard: 'isHostedMode',
4566
+ target: 'hostedMode'
1805
4567
  },
1806
4568
  {
1807
4569
  guard: 'isOfflineMode',
1808
4570
  target: 'offlineMode'
1809
4571
  },
1810
- {
1811
- guard: 'isSelfHostedMode',
1812
- target: 'selfHostedMode'
1813
- },
1814
4572
  {
1815
4573
  guard: 'isCustomMode',
1816
4574
  target: 'customMode'
@@ -1820,69 +4578,48 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
1820
4578
  }
1821
4579
  ]
1822
4580
  },
1823
- c15tMode: {
1824
- initial: 'accountCreation',
1825
- states: {
1826
- accountCreation: {
1827
- invoke: {
1828
- src: 'accountCreation',
1829
- input: ({ context })=>({
1830
- cliContext: context.cliContext
1831
- }),
1832
- onDone: 'backendURL',
1833
- onError: {
1834
- target: '#generate.cancelling',
1835
- actions: (0, __rspack_external_xstate.assign)({
1836
- cancelReason: 'Account creation cancelled'
1837
- })
1838
- }
1839
- }
1840
- },
1841
- backendURL: {
1842
- invoke: {
1843
- src: 'backendURL',
1844
- input: ()=>({
1845
- isC15tMode: true
1846
- }),
1847
- onDone: {
1848
- target: '#generate.backendOptions',
1849
- actions: (0, __rspack_external_xstate.assign)({
1850
- backendURL: ({ event })=>event.output.url
1851
- })
1852
- },
1853
- onError: {
1854
- target: '#generate.cancelling',
1855
- actions: (0, __rspack_external_xstate.assign)({
1856
- cancelReason: 'Backend URL entry cancelled'
1857
- })
1858
- }
1859
- }
1860
- }
1861
- }
1862
- },
1863
- offlineMode: {
1864
- always: 'frontendOptions'
1865
- },
1866
- selfHostedMode: {
4581
+ hostedMode: {
1867
4582
  invoke: {
1868
- src: 'backendURL',
1869
- input: ()=>({
1870
- isC15tMode: false
4583
+ src: 'hostedMode',
4584
+ input: ({ context })=>({
4585
+ cliContext: context.cliContext,
4586
+ initialURL: context.backendURL ?? void 0,
4587
+ preselectedProvider: context.hostedProvider
1871
4588
  }),
1872
4589
  onDone: {
1873
4590
  target: 'backendOptions',
1874
4591
  actions: (0, __rspack_external_xstate.assign)({
1875
- backendURL: ({ event })=>event.output.url
4592
+ backendURL: ({ event })=>event.output.url,
4593
+ hostedProvider: ({ event })=>event.output.provider
1876
4594
  })
1877
4595
  },
1878
- onError: {
1879
- target: 'cancelling',
1880
- actions: (0, __rspack_external_xstate.assign)({
1881
- cancelReason: 'Backend URL entry cancelled'
1882
- })
1883
- }
4596
+ onError: [
4597
+ {
4598
+ guard: ({ event })=>event.error instanceof PromptCancelledError,
4599
+ target: 'cancelling',
4600
+ actions: (0, __rspack_external_xstate.assign)({
4601
+ cancelReason: 'Hosted setup cancelled'
4602
+ })
4603
+ },
4604
+ {
4605
+ target: 'error',
4606
+ actions: (0, __rspack_external_xstate.assign)({
4607
+ errors: ({ context, event })=>[
4608
+ ...context.errors,
4609
+ {
4610
+ state: 'hostedMode',
4611
+ error: event.error,
4612
+ timestamp: Date.now()
4613
+ }
4614
+ ]
4615
+ })
4616
+ }
4617
+ ]
1884
4618
  }
1885
4619
  },
4620
+ offlineMode: {
4621
+ always: 'frontendOptions'
4622
+ },
1886
4623
  customMode: {
1887
4624
  always: 'frontendOptions'
1888
4625
  },
@@ -1913,7 +4650,7 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
1913
4650
  src: 'frontendOptions',
1914
4651
  input: ({ context })=>({
1915
4652
  cliContext: context.cliContext,
1916
- hasBackend: 'c15t' === context.selectedMode || 'self-hosted' === context.selectedMode
4653
+ hasBackend: 'hosted' === context.selectedMode
1917
4654
  }),
1918
4655
  onDone: {
1919
4656
  target: "scriptsOptions",
@@ -2079,9 +4816,9 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
2079
4816
  entry: ({ context })=>{
2080
4817
  if (!context.cliContext) return;
2081
4818
  const { logger, packageManager } = context.cliContext;
2082
- if ('self-hosted' === context.selectedMode) {
4819
+ if ('hosted' === context.selectedMode && 'self-hosted' === context.hostedProvider) {
2083
4820
  logger.info('Setup your backend with the c15t docs:');
2084
- logger.info('https://c15t.com/docs/self-host/v2');
4821
+ logger.info('https://v2.c15t.com/docs/self-host/v2');
2085
4822
  } else if ('custom' === context.selectedMode) logger.info('Configuration Complete! Implement your custom endpoint handlers.');
2086
4823
  if (context.installConfirmed && !context.installSucceeded) logger.warn('Dependency installation failed. Please check errors and install manually.');
2087
4824
  else if (!context.installConfirmed && context.dependenciesToAdd.length > 0) {
@@ -2127,7 +4864,9 @@ const generateMachine = (0, __rspack_external_xstate.setup)({
2127
4864
  error: {
2128
4865
  entry: ({ context })=>{
2129
4866
  const lastError = context.errors[context.errors.length - 1];
2130
- context.cliContext?.logger.error(`Error: ${lastError?.error?.message ?? 'Unknown error'}`);
4867
+ const error = lastError?.error;
4868
+ const details = error instanceof CliError && 'string' == typeof error.context?.details ? error.context.details : void 0;
4869
+ context.cliContext?.logger.error(`Error: ${error?.message ?? 'Unknown error'}${details ? `: ${details}` : ''}`);
2131
4870
  },
2132
4871
  always: [
2133
4872
  {
@@ -2232,7 +4971,7 @@ async function runGenerateMachine(options) {
2232
4971
  const finalState = String(finalSnapshot.value);
2233
4972
  const duration = Date.now() - startTime;
2234
4973
  clearSnapshot(persistPath).catch(()=>{});
2235
- const success = 'complete' === finalState || 'done' === finalSnapshot.status;
4974
+ const success = 'complete' === finalState;
2236
4975
  telemetry.trackEvent(TelemetryEventName.ONBOARDING_COMPLETED, {
2237
4976
  success,
2238
4977
  selectedMode: finalContext.selectedMode ?? void 0,
@@ -2251,9 +4990,14 @@ async function runGenerateMachine(options) {
2251
4990
  });
2252
4991
  });
2253
4992
  }
4993
+ function normalizeModeArg(mode) {
4994
+ if (!mode || mode.startsWith('-')) return;
4995
+ const validModes = new Set(Object.values(constants.$V));
4996
+ return validModes.has(mode) ? mode : void 0;
4997
+ }
2254
4998
  async function generateAction(context) {
2255
4999
  const { logger, commandArgs, flags } = context;
2256
- const modeArg = commandArgs[0];
5000
+ const modeArg = normalizeModeArg(commandArgs[0]);
2257
5001
  const resume = true === flags.resume;
2258
5002
  const debug = true === flags.debug || 'debug' === flags.logger;
2259
5003
  logger.debug('Starting generate command with state machine...');
@@ -2269,6 +5013,7 @@ async function generateAction(context) {
2269
5013
  });
2270
5014
  if (!result.success) {
2271
5015
  if (result.errors.length > 0) process.exitCode = 1;
5016
+ return;
2272
5017
  }
2273
5018
  } catch (error) {
2274
5019
  logger.error(`Generate command failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -2390,7 +5135,6 @@ async function installDependencies({ context, dependenciesToAdd, handleCancel, a
2390
5135
  };
2391
5136
  }
2392
5137
  }
2393
- var promises_ = __webpack_require__("node:fs/promises");
2394
5138
  const ADAPTER_LABELS = {
2395
5139
  kyselyAdapter: 'kysely',
2396
5140
  drizzleAdapter: 'drizzle',
@@ -2823,16 +5567,10 @@ async function migrate(context) {
2823
5567
  else await handleMigrationResult(context, result);
2824
5568
  }
2825
5569
  const subcommands = [
2826
- {
2827
- name: 'generate',
2828
- label: 'Generate',
2829
- hint: 'Generate code for your c15t project',
2830
- action: generate
2831
- },
2832
5570
  {
2833
5571
  name: 'migrate',
2834
- label: 'Migrate',
2835
- hint: 'Run database migrations',
5572
+ label: 'Migrate database',
5573
+ hint: 'Run latest database migrations',
2836
5574
  action: migrate
2837
5575
  }
2838
5576
  ];
@@ -2843,15 +5581,13 @@ async function selfHost(context) {
2843
5581
  const [subcommand] = commandArgs;
2844
5582
  if (subcommand) {
2845
5583
  switch(subcommand){
2846
- case 'generate':
2847
- await generate(context, 'self-hosted');
2848
- break;
2849
5584
  case 'migrate':
2850
5585
  await migrate(context);
2851
5586
  break;
2852
5587
  default:
2853
5588
  logger.error(`Unknown self-host subcommand: ${subcommand}`);
2854
- logger.info('Available subcommands: generate, migrate');
5589
+ logger.info('Available subcommands: migrate');
5590
+ logger.info('Usage: c15t self-host <migrate>');
2855
5591
  telemetry.trackEvent(TelemetryEventName.SELF_HOST_COMPLETED, {
2856
5592
  success: false,
2857
5593
  reason: 'unknown_subcommand'
@@ -2872,16 +5608,16 @@ async function selfHost(context) {
2872
5608
  promptOptions.push({
2873
5609
  value: 'exit',
2874
5610
  label: 'Exit',
2875
- hint: 'Return to main menu'
5611
+ hint: 'Close the CLI'
2876
5612
  });
2877
5613
  const selectedSubcommandName = await prompts_.select({
2878
- message: (0, utils_logger.$e)('info', 'Which self-host command would you like to run?'),
5614
+ message: (0, utils_logger.$e)('info', 'Which self-host task would you like to run?'),
2879
5615
  options: promptOptions
2880
5616
  });
2881
- if (prompts_.isCancel(selectedSubcommandName) || 'exit' === selectedSubcommandName) {
2882
- logger.debug('Interactive selection cancelled or exit chosen.');
5617
+ if (prompts_.isCancel(selectedSubcommandName)) {
5618
+ logger.debug('Self-host interactive selection cancelled.');
2883
5619
  telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
2884
- action: prompts_.isCancel(selectedSubcommandName) ? 'cancelled' : 'exit',
5620
+ action: 'cancelled',
2885
5621
  context: 'self-host'
2886
5622
  });
2887
5623
  error.handleCancel('Operation cancelled.', {
@@ -2890,11 +5626,19 @@ async function selfHost(context) {
2890
5626
  });
2891
5627
  return;
2892
5628
  }
5629
+ if ('exit' === selectedSubcommandName) {
5630
+ logger.debug('Self-host interactive selection exited.');
5631
+ telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
5632
+ action: 'exit',
5633
+ context: 'self-host'
5634
+ });
5635
+ logger.outro('Exited self-host menu.');
5636
+ return;
5637
+ }
2893
5638
  const selectedSubcommand = subcommands.find((cmd)=>cmd.name === selectedSubcommandName);
2894
5639
  if (selectedSubcommand) {
2895
5640
  logger.debug(`User selected subcommand: ${selectedSubcommand.name}`);
2896
- if ('generate' === selectedSubcommand.name) await generate(context, 'self-hosted');
2897
- else await selectedSubcommand.action(context);
5641
+ await selectedSubcommand.action(context);
2898
5642
  } else {
2899
5643
  logger.error(`Unknown subcommand: ${selectedSubcommandName}`);
2900
5644
  telemetry.trackEvent(TelemetryEventName.SELF_HOST_COMPLETED, {
@@ -2989,8 +5733,7 @@ async function displayIntro(context, version) {
2989
5733
  });
2990
5734
  logger.message(coloredLines.join('\n'));
2991
5735
  }
2992
- var constants = __webpack_require__("./src/constants.ts");
2993
- function createErrorHandlers(context) {
5736
+ function error_handlers_createErrorHandlers(context) {
2994
5737
  const { logger, telemetry } = context;
2995
5738
  return {
2996
5739
  handleError: (error, message)=>{
@@ -3329,7 +6072,7 @@ async function createCliContext(rawArgs, cwd, commands) {
3329
6072
  cwd
3330
6073
  };
3331
6074
  const context = baseContext;
3332
- context.error = createErrorHandlers(context);
6075
+ context.error = error_handlers_createErrorHandlers(context);
3333
6076
  const userInteraction = createUserInteraction(context);
3334
6077
  context.confirm = userInteraction.confirm;
3335
6078
  context.fs = createFileSystem(context);
@@ -3369,26 +6112,49 @@ async function createCliContext(rawArgs, cwd, commands) {
3369
6112
  }
3370
6113
  const src_commands = [
3371
6114
  {
3372
- name: 'generate',
3373
- label: 'Generate (Recommended)',
3374
- hint: 'Add c15t to your project',
3375
- description: 'Setup your c15t project',
6115
+ name: 'setup',
6116
+ label: 'Setup (Recommended)',
6117
+ hint: 'Set up c15t in your project',
6118
+ description: 'Set up c15t in your project.',
3376
6119
  action: (context)=>generate(context)
3377
6120
  },
3378
- {
3379
- name: 'self-host',
3380
- label: 'Self Host',
3381
- hint: 'Host c15t backend on your own infra',
3382
- description: 'Commands for self-hosting c15t (generate, migrate).',
3383
- action: (context)=>selfHost(context)
3384
- },
6121
+ codemodsCommand,
3385
6122
  {
3386
6123
  name: 'skills',
3387
6124
  label: 'Skills',
3388
- hint: 'Add c15t agent skills for AI tools',
6125
+ hint: 'Install c15t agent skills for AI tooling',
3389
6126
  description: 'Install c15t skills for AI-assisted development (Claude, Cursor, etc.)',
3390
6127
  action: (context)=>installSkills(context)
3391
6128
  },
6129
+ {
6130
+ name: 'docs',
6131
+ label: 'Docs',
6132
+ hint: 'Open c15t documentation',
6133
+ description: 'Open the c15t documentation in your browser.',
6134
+ action: async (context)=>{
6135
+ const { logger } = context;
6136
+ await (0, __rspack_external_open["default"])(`${constants.tl.DOCS}?ref=cli`);
6137
+ logger.success('Documentation opened in your browser.');
6138
+ }
6139
+ },
6140
+ {
6141
+ name: 'changelog',
6142
+ label: 'Changelog',
6143
+ hint: 'Open the latest releases and changes',
6144
+ description: 'Open the c15t changelog in your browser.',
6145
+ action: async (context)=>{
6146
+ const { logger } = context;
6147
+ await (0, __rspack_external_open["default"])(constants.tl.CHANGELOG);
6148
+ logger.success('Changelog opened in your browser.');
6149
+ }
6150
+ },
6151
+ {
6152
+ name: 'self-host',
6153
+ label: 'Self-host',
6154
+ hint: 'Self-hosted backend workflow tools',
6155
+ description: 'Self-host workflow commands (migrations).',
6156
+ action: (context)=>selfHost(context)
6157
+ },
3392
6158
  {
3393
6159
  name: 'github',
3394
6160
  label: 'GitHub',
@@ -3400,17 +6166,6 @@ const src_commands = [
3400
6166
  await (0, __rspack_external_open["default"])(constants.tl.GITHUB);
3401
6167
  logger.success('Thank you for your support!');
3402
6168
  }
3403
- },
3404
- {
3405
- name: 'docs',
3406
- label: 'c15t docs',
3407
- hint: 'Open documentation',
3408
- description: 'Open the c15t documentation in your browser.',
3409
- action: async (context)=>{
3410
- const { logger } = context;
3411
- await (0, __rspack_external_open["default"])(`${constants.tl.DOCS}?ref=cli`);
3412
- logger.success('Documentation opened in your browser.');
3413
- }
3414
6169
  }
3415
6170
  ];
3416
6171
  async function main() {
@@ -3493,40 +6248,49 @@ flag or set ${external_picocolors_["default"].cyan('C15T_TELEMETRY_DISABLED=1')}
3493
6248
  }));
3494
6249
  promptOptions.push({
3495
6250
  value: 'exit',
3496
- label: 'exit',
6251
+ label: 'Exit',
3497
6252
  hint: 'Close the CLI'
3498
6253
  });
3499
6254
  const selectedCommandName = await prompts_.select({
3500
6255
  message: (0, utils_logger.$e)('info', 'Which command would you like to run?'),
3501
6256
  options: promptOptions
3502
6257
  });
3503
- if (prompts_.isCancel(selectedCommandName) || 'exit' === selectedCommandName) {
3504
- logger.debug('Interactive selection cancelled or exit chosen.');
6258
+ if (prompts_.isCancel(selectedCommandName)) {
6259
+ logger.debug('Interactive selection cancelled.');
3505
6260
  telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
3506
- action: prompts_.isCancel(selectedCommandName) ? 'cancelled' : 'exit'
6261
+ action: 'cancelled'
3507
6262
  });
3508
6263
  context.error.handleCancel('Operation cancelled.', {
3509
6264
  command: 'interactive_menu',
3510
6265
  stage: 'exit'
3511
6266
  });
6267
+ return;
6268
+ }
6269
+ if ('exit' === selectedCommandName) {
6270
+ logger.debug('Interactive selection exited.');
6271
+ telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
6272
+ action: 'exit'
6273
+ });
6274
+ logger.outro('Exited c15t CLI.');
6275
+ telemetry.flushSync();
6276
+ return;
6277
+ }
6278
+ const selectedCommand = src_commands.find((cmd)=>cmd.name === selectedCommandName);
6279
+ if (selectedCommand) {
6280
+ logger.debug(`User selected command: ${selectedCommand.name}`);
6281
+ telemetry.trackCommand(selectedCommand.name, [], flags);
6282
+ await selectedCommand.action(context);
6283
+ telemetry.trackEvent(TelemetryEventName.COMMAND_SUCCEEDED, {
6284
+ command: selectedCommand.name,
6285
+ executionTime: Date.now() - performance.now()
6286
+ });
6287
+ telemetry.flushSync();
3512
6288
  } else {
3513
- const selectedCommand = src_commands.find((cmd)=>cmd.name === selectedCommandName);
3514
- if (selectedCommand) {
3515
- logger.debug(`User selected command: ${selectedCommand.name}`);
3516
- telemetry.trackCommand(selectedCommand.name, [], flags);
3517
- await selectedCommand.action(context);
3518
- telemetry.trackEvent(TelemetryEventName.COMMAND_SUCCEEDED, {
3519
- command: selectedCommand.name,
3520
- executionTime: Date.now() - performance.now()
3521
- });
3522
- telemetry.flushSync();
3523
- } else {
3524
- telemetry.trackEvent(TelemetryEventName.COMMAND_UNKNOWN, {
3525
- unknownCommand: String(selectedCommandName)
3526
- });
3527
- telemetry.flushSync();
3528
- error.handleError(new Error(`Command '${selectedCommandName}' not found`), 'An internal error occurred');
3529
- }
6289
+ telemetry.trackEvent(TelemetryEventName.COMMAND_UNKNOWN, {
6290
+ unknownCommand: String(selectedCommandName)
6291
+ });
6292
+ telemetry.flushSync();
6293
+ error.handleError(new Error(`Command '${selectedCommandName}' not found`), 'An internal error occurred');
3530
6294
  }
3531
6295
  }
3532
6296
  logger.debug('Command execution completed');