@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.
- package/README.md +43 -14
- package/dist/{153.mjs → 642.mjs} +45 -43
- package/dist/index.mjs +3113 -349
- package/{dist → dist-types}/actions/show-help-menu.d.ts +0 -1
- package/dist-types/auth/base-url.d.ts +7 -0
- package/{dist → dist-types}/auth/config-store.d.ts +0 -1
- package/{dist → dist-types}/auth/device-flow.d.ts +0 -1
- package/{dist → dist-types}/auth/index.d.ts +1 -1
- package/{dist → dist-types}/auth/types.d.ts +0 -1
- package/{dist → dist-types}/commands/auth/index.d.ts +0 -1
- package/dist-types/commands/codemods/active-ui-api.d.ts +41 -0
- package/dist-types/commands/codemods/component-renames.d.ts +41 -0
- package/dist-types/commands/codemods/gdpr-types-to-consent-categories.d.ts +41 -0
- package/dist-types/commands/codemods/ignore-geo-location-to-overrides.d.ts +41 -0
- package/dist-types/commands/codemods/index.d.ts +28 -0
- package/dist-types/commands/codemods/mode-c15t-to-hosted.d.ts +43 -0
- package/dist-types/commands/codemods/offline-add-policy-packs.d.ts +21 -0
- package/dist-types/commands/codemods/react-options-to-top-level.d.ts +41 -0
- package/dist-types/commands/codemods/tracking-blocker-to-network-blocker.d.ts +41 -0
- package/dist-types/commands/codemods/translations-to-i18n.d.ts +51 -0
- package/dist-types/commands/codemods/versioning.d.ts +49 -0
- package/{dist → dist-types}/commands/generate/index.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/shared/backend-options.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/shared/dev-tools.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/shared/frontend-ui-options.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/shared/scripts.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/shared/ssr.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/types.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/utils/dependencies.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/options/utils/generate-files.d.ts +2 -2
- package/{dist → dist-types}/commands/generate/preflight.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/prompts/expanded-theme.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/prompts/index.d.ts +0 -2
- package/{dist → dist-types}/commands/generate/prompts/mode-select.d.ts +1 -7
- package/{dist → dist-types}/commands/generate/prompts/scripts.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/prompts/theme.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/prompts/ui-style.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/summary.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/config.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/css.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/env.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/index.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/layout.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/next/app/layout.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/next/index.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/next/pages/layout.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/next-config.d.ts +1 -4
- package/{dist → dist-types}/commands/generate/templates/shared/components.d.ts +2 -3
- package/{dist → dist-types}/commands/generate/templates/shared/directory.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/shared/expanded-components.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/shared/framework-config.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/shared/layout-pipeline.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/shared/module-specifier.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/shared/options.d.ts +5 -6
- package/{dist → dist-types}/commands/generate/templates/shared/scripts.d.ts +0 -1
- package/{dist → dist-types}/commands/generate/templates/shared/server-components.d.ts +0 -1
- package/{dist → dist-types}/commands/index.d.ts +1 -1
- package/{dist → dist-types}/commands/instances/index.d.ts +0 -1
- package/{dist → dist-types}/commands/self-host/index.d.ts +0 -1
- package/{dist → dist-types}/commands/self-host/migrate/ensure-backend-config.d.ts +0 -1
- package/{dist → dist-types}/commands/self-host/migrate/index.d.ts +0 -1
- package/{dist → dist-types}/commands/self-host/migrate/migrator-result.d.ts +1 -2
- package/{dist → dist-types}/commands/self-host/migrate/orm-result.d.ts +1 -2
- package/{dist → dist-types}/commands/self-host/migrate/read-config.d.ts +1 -2
- package/{dist → dist-types}/commands/skills.d.ts +0 -1
- package/{dist → dist-types}/components/intro.d.ts +0 -1
- package/{dist → dist-types}/constants.d.ts +19 -9
- package/{dist → dist-types}/context/creator.d.ts +0 -1
- package/{dist → dist-types}/context/error-handlers.d.ts +0 -1
- package/{dist → dist-types}/context/file-system.d.ts +0 -1
- package/{dist → dist-types}/context/framework-detection.d.ts +0 -1
- package/{dist → dist-types}/context/package-manager-detection.d.ts +0 -1
- package/{dist → dist-types}/context/parser.d.ts +0 -1
- package/{dist → dist-types}/context/types.d.ts +1 -2
- package/{dist → dist-types}/context/user-interaction.d.ts +0 -1
- package/dist-types/control-plane/client.d.ts +56 -0
- package/dist-types/control-plane/index.d.ts +5 -0
- package/dist-types/control-plane/types.d.ts +73 -0
- package/{dist → dist-types}/core/context.d.ts +0 -1
- package/{dist → dist-types}/core/errors.d.ts +5 -11
- package/{dist → dist-types}/core/index.d.ts +0 -1
- package/{dist → dist-types}/core/logger.d.ts +0 -1
- package/{dist → dist-types}/core/parser.d.ts +0 -1
- package/{dist → dist-types}/core/telemetry.d.ts +0 -1
- package/{dist → dist-types}/detection/framework.d.ts +0 -1
- package/{dist → dist-types}/detection/index.d.ts +0 -1
- package/{dist → dist-types}/detection/layout.d.ts +0 -1
- package/{dist → dist-types}/detection/package-manager.d.ts +0 -1
- package/{dist → dist-types}/index.d.ts +0 -1
- package/{dist → dist-types}/machines/generate/actions.d.ts +0 -1
- package/{dist → dist-types}/machines/generate/actors/dependencies.d.ts +0 -1
- package/{dist → dist-types}/machines/generate/actors/file-generation.d.ts +1 -2
- package/{dist → dist-types}/machines/generate/actors/preflight.d.ts +0 -1
- package/{dist → dist-types}/machines/generate/actors/prompts.d.ts +8 -13
- package/{dist → dist-types}/machines/generate/guards.d.ts +4 -5
- package/{dist → dist-types}/machines/generate/machine.d.ts +10 -23
- package/{dist → dist-types}/machines/generate/runner.d.ts +0 -1
- package/{dist → dist-types}/machines/generate/types.d.ts +4 -3
- package/{dist → dist-types}/machines/index.d.ts +1 -2
- package/{dist → dist-types}/machines/persistence.d.ts +0 -1
- package/{dist → dist-types}/machines/telemetry-plugin.d.ts +0 -1
- package/{dist → dist-types}/machines/types.d.ts +0 -1
- package/{dist → dist-types}/types.d.ts +6 -3
- package/{dist → dist-types}/utils/capitalize-first-letter.d.ts +0 -1
- package/{dist → dist-types}/utils/formatter.d.ts +0 -1
- package/{dist → dist-types}/utils/fs.d.ts +0 -1
- package/{dist → dist-types}/utils/index.d.ts +0 -1
- package/{dist → dist-types}/utils/logger.d.ts +1 -2
- package/{dist → dist-types}/utils/spinner.d.ts +0 -1
- package/{dist → dist-types}/utils/telemetry.d.ts +1 -2
- package/{dist → dist-types}/utils/validation.d.ts +0 -1
- package/package.json +7 -5
- package/readme.json +22 -8
- package/dist/actions/show-help-menu.d.ts.map +0 -1
- package/dist/auth/config-store.d.ts.map +0 -1
- package/dist/auth/device-flow.d.ts.map +0 -1
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/commands/auth/index.d.ts.map +0 -1
- package/dist/commands/generate/index.d.ts.map +0 -1
- package/dist/commands/generate/options/c15t-mode.d.ts +0 -27
- package/dist/commands/generate/options/c15t-mode.d.ts.map +0 -1
- package/dist/commands/generate/options/custom-mode.d.ts +0 -26
- package/dist/commands/generate/options/custom-mode.d.ts.map +0 -1
- package/dist/commands/generate/options/offline-mode.d.ts +0 -26
- package/dist/commands/generate/options/offline-mode.d.ts.map +0 -1
- package/dist/commands/generate/options/self-hosted-mode.d.ts +0 -19
- package/dist/commands/generate/options/self-hosted-mode.d.ts.map +0 -1
- package/dist/commands/generate/options/shared/backend-options.d.ts.map +0 -1
- package/dist/commands/generate/options/shared/dev-tools.d.ts.map +0 -1
- package/dist/commands/generate/options/shared/frontend-ui-options.d.ts.map +0 -1
- package/dist/commands/generate/options/shared/index.d.ts +0 -9
- package/dist/commands/generate/options/shared/index.d.ts.map +0 -1
- package/dist/commands/generate/options/shared/scripts.d.ts.map +0 -1
- package/dist/commands/generate/options/shared/ssr.d.ts.map +0 -1
- package/dist/commands/generate/options/types.d.ts.map +0 -1
- package/dist/commands/generate/options/utils/dependencies.d.ts.map +0 -1
- package/dist/commands/generate/options/utils/generate-files.d.ts.map +0 -1
- package/dist/commands/generate/preflight.d.ts.map +0 -1
- package/dist/commands/generate/prompts/expanded-theme.d.ts.map +0 -1
- package/dist/commands/generate/prompts/index.d.ts.map +0 -1
- package/dist/commands/generate/prompts/instance.d.ts +0 -33
- package/dist/commands/generate/prompts/instance.d.ts.map +0 -1
- package/dist/commands/generate/prompts/mode-select.d.ts.map +0 -1
- package/dist/commands/generate/prompts/scripts.d.ts.map +0 -1
- package/dist/commands/generate/prompts/theme.d.ts.map +0 -1
- package/dist/commands/generate/prompts/ui-style.d.ts.map +0 -1
- package/dist/commands/generate/summary.d.ts.map +0 -1
- package/dist/commands/generate/templates/config.d.ts.map +0 -1
- package/dist/commands/generate/templates/css.d.ts.map +0 -1
- package/dist/commands/generate/templates/env.d.ts.map +0 -1
- package/dist/commands/generate/templates/index.d.ts.map +0 -1
- package/dist/commands/generate/templates/layout.d.ts.map +0 -1
- package/dist/commands/generate/templates/next/app/layout.d.ts.map +0 -1
- package/dist/commands/generate/templates/next/index.d.ts.map +0 -1
- package/dist/commands/generate/templates/next/pages/layout.d.ts.map +0 -1
- package/dist/commands/generate/templates/next-config.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/components.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/directory.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/expanded-components.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/framework-config.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/layout-pipeline.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/module-specifier.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/options.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/scripts.d.ts.map +0 -1
- package/dist/commands/generate/templates/shared/server-components.d.ts.map +0 -1
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/instances/index.d.ts.map +0 -1
- package/dist/commands/self-host/index.d.ts.map +0 -1
- package/dist/commands/self-host/migrate/ensure-backend-config.d.ts.map +0 -1
- package/dist/commands/self-host/migrate/index.d.ts.map +0 -1
- package/dist/commands/self-host/migrate/migrator-result.d.ts.map +0 -1
- package/dist/commands/self-host/migrate/orm-result.d.ts.map +0 -1
- package/dist/commands/self-host/migrate/read-config.d.ts.map +0 -1
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/components/intro.d.ts.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/context/creator.d.ts.map +0 -1
- package/dist/context/error-handlers.d.ts.map +0 -1
- package/dist/context/file-system.d.ts.map +0 -1
- package/dist/context/framework-detection.d.ts.map +0 -1
- package/dist/context/package-manager-detection.d.ts.map +0 -1
- package/dist/context/parser.d.ts.map +0 -1
- package/dist/context/types.d.ts.map +0 -1
- package/dist/context/user-interaction.d.ts.map +0 -1
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/logger.d.ts.map +0 -1
- package/dist/core/parser.d.ts.map +0 -1
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/detection/framework.d.ts.map +0 -1
- package/dist/detection/index.d.ts.map +0 -1
- package/dist/detection/layout.d.ts.map +0 -1
- package/dist/detection/package-manager.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/machines/generate/actions.d.ts.map +0 -1
- package/dist/machines/generate/actors/dependencies.d.ts.map +0 -1
- package/dist/machines/generate/actors/file-generation.d.ts.map +0 -1
- package/dist/machines/generate/actors/preflight.d.ts.map +0 -1
- package/dist/machines/generate/actors/prompts.d.ts.map +0 -1
- package/dist/machines/generate/guards.d.ts.map +0 -1
- package/dist/machines/generate/machine.d.ts.map +0 -1
- package/dist/machines/generate/runner.d.ts.map +0 -1
- package/dist/machines/generate/types.d.ts.map +0 -1
- package/dist/machines/index.d.ts.map +0 -1
- package/dist/machines/persistence.d.ts.map +0 -1
- package/dist/machines/telemetry-plugin.d.ts.map +0 -1
- package/dist/machines/types.d.ts.map +0 -1
- package/dist/mcp/client.d.ts +0 -61
- package/dist/mcp/client.d.ts.map +0 -1
- package/dist/mcp/index.d.ts +0 -6
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/types.d.ts +0 -84
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/utils/capitalize-first-letter.d.ts.map +0 -1
- package/dist/utils/formatter.d.ts.map +0 -1
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/spinner.d.ts.map +0 -1
- package/dist/utils/telemetry.d.ts.map +0 -1
- 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.
|
|
130
|
+
DOCS: 'https://v2.c15t.com/docs',
|
|
33
131
|
GITHUB: 'https://github.com/c15t/c15t',
|
|
34
|
-
DISCORD: 'https://c15t.
|
|
35
|
-
API_DOCS: 'https://c15t.
|
|
36
|
-
CLI_DOCS: 'https://c15t.
|
|
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
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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(
|
|
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,
|
|
842
|
-
|
|
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
|
|
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:
|
|
3680
|
+
initialValue: initialMode,
|
|
1138
3681
|
options: [
|
|
1139
3682
|
{
|
|
1140
|
-
value: '
|
|
1141
|
-
label: 'Hosted
|
|
1142
|
-
hint: '
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
|
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' && (
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
1804
|
-
target: '
|
|
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
|
-
|
|
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: '
|
|
1869
|
-
input: ()=>({
|
|
1870
|
-
|
|
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
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
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: '
|
|
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.
|
|
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
|
-
|
|
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
|
|
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:
|
|
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: '
|
|
5611
|
+
hint: 'Close the CLI'
|
|
2876
5612
|
});
|
|
2877
5613
|
const selectedSubcommandName = await prompts_.select({
|
|
2878
|
-
message: (0, utils_logger.$e)('info', 'Which self-host
|
|
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)
|
|
2882
|
-
logger.debug('
|
|
5617
|
+
if (prompts_.isCancel(selectedSubcommandName)) {
|
|
5618
|
+
logger.debug('Self-host interactive selection cancelled.');
|
|
2883
5619
|
telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
|
|
2884
|
-
action:
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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: '
|
|
3373
|
-
label: '
|
|
3374
|
-
hint: '
|
|
3375
|
-
description: '
|
|
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: '
|
|
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: '
|
|
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)
|
|
3504
|
-
logger.debug('Interactive selection cancelled
|
|
6258
|
+
if (prompts_.isCancel(selectedCommandName)) {
|
|
6259
|
+
logger.debug('Interactive selection cancelled.');
|
|
3505
6260
|
telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
|
|
3506
|
-
action:
|
|
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
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
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');
|