@c15t/cli 1.2.0-beta.17 → 1.2.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE.md +595 -0
  2. package/README.md +106 -0
  3. package/dist/actions/get-config/config-extraction.d.ts +37 -0
  4. package/dist/actions/get-config/config-extraction.d.ts.map +1 -0
  5. package/dist/actions/get-config/config-validation.d.ts +7 -0
  6. package/dist/actions/get-config/config-validation.d.ts.map +1 -0
  7. package/dist/actions/get-config/constants.d.ts +13 -0
  8. package/dist/actions/get-config/constants.d.ts.map +1 -0
  9. package/dist/actions/get-config/directory-search.d.ts +6 -0
  10. package/dist/actions/get-config/directory-search.d.ts.map +1 -0
  11. package/dist/actions/get-config/jiti-options.d.ts +9 -0
  12. package/dist/actions/get-config/jiti-options.d.ts.map +1 -0
  13. package/dist/actions/get-config.d.ts +13 -0
  14. package/dist/actions/get-config.d.ts.map +1 -0
  15. package/dist/actions/load-config-and-onboard.d.ts +9 -0
  16. package/dist/actions/load-config-and-onboard.d.ts.map +1 -0
  17. package/dist/actions/show-help-menu.d.ts +11 -0
  18. package/dist/actions/show-help-menu.d.ts.map +1 -0
  19. package/dist/commands/generate/actions/handle-existing-file.d.ts +7 -0
  20. package/dist/commands/generate/actions/handle-existing-file.d.ts.map +1 -0
  21. package/dist/commands/generate/actions/handle-new-file.d.ts +7 -0
  22. package/dist/commands/generate/actions/handle-new-file.d.ts.map +1 -0
  23. package/dist/commands/generate/actions/perform-write-action.d.ts +6 -0
  24. package/dist/commands/generate/actions/perform-write-action.d.ts.map +1 -0
  25. package/dist/commands/generate/generators/drizzle.d.ts +4 -0
  26. package/dist/commands/generate/generators/drizzle.d.ts.map +1 -0
  27. package/dist/commands/generate/generators/index.d.ts +19 -0
  28. package/dist/commands/generate/generators/index.d.ts.map +1 -0
  29. package/dist/commands/generate/generators/kysely.d.ts +11 -0
  30. package/dist/commands/generate/generators/kysely.d.ts.map +1 -0
  31. package/dist/commands/generate/generators/prisma.d.ts +3 -0
  32. package/dist/commands/generate/generators/prisma.d.ts.map +1 -0
  33. package/dist/commands/generate/generators/types.d.ts +13 -0
  34. package/dist/commands/generate/generators/types.d.ts.map +1 -0
  35. package/dist/commands/generate/schema.d.ts +10 -0
  36. package/dist/commands/generate/schema.d.ts.map +1 -0
  37. package/dist/commands/generate/setup.d.ts +13 -0
  38. package/dist/commands/generate/setup.d.ts.map +1 -0
  39. package/dist/commands/generate/write.d.ts +8 -0
  40. package/dist/commands/generate/write.d.ts.map +1 -0
  41. package/dist/commands/generate.d.ts +6 -0
  42. package/dist/commands/generate.d.ts.map +1 -0
  43. package/dist/commands/migrate/execute.d.ts +7 -0
  44. package/dist/commands/migrate/execute.d.ts.map +1 -0
  45. package/dist/commands/migrate/plan.d.ts +12 -0
  46. package/dist/commands/migrate/plan.d.ts.map +1 -0
  47. package/dist/commands/migrate/setup.d.ts +12 -0
  48. package/dist/commands/migrate/setup.d.ts.map +1 -0
  49. package/dist/commands/migrate.d.ts +3 -0
  50. package/dist/commands/migrate.d.ts.map +1 -0
  51. package/dist/components/intro.d.ts +9 -0
  52. package/dist/components/intro.d.ts.map +1 -0
  53. package/dist/context/config-management.d.ts +20 -0
  54. package/dist/context/config-management.d.ts.map +1 -0
  55. package/dist/context/creator.d.ts +11 -0
  56. package/dist/context/creator.d.ts.map +1 -0
  57. package/dist/context/error-handlers.d.ts +18 -0
  58. package/dist/context/error-handlers.d.ts.map +1 -0
  59. package/dist/context/file-system.d.ts +11 -0
  60. package/dist/context/file-system.d.ts.map +1 -0
  61. package/dist/context/parser.d.ts +11 -0
  62. package/dist/context/parser.d.ts.map +1 -0
  63. package/dist/context/types.d.ts +53 -0
  64. package/dist/context/types.d.ts.map +1 -0
  65. package/dist/context/user-interaction.d.ts +14 -0
  66. package/dist/context/user-interaction.d.ts.map +1 -0
  67. package/dist/index.d.ts +4 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.mjs +2002 -993
  70. package/dist/onboarding/dependencies.d.ts +20 -0
  71. package/dist/onboarding/dependencies.d.ts.map +1 -0
  72. package/dist/onboarding/detection.d.ts +33 -0
  73. package/dist/onboarding/detection.d.ts.map +1 -0
  74. package/dist/onboarding/index.d.ts +11 -0
  75. package/dist/onboarding/index.d.ts.map +1 -0
  76. package/dist/onboarding/storage-modes/c15t-mode.d.ts +22 -0
  77. package/dist/onboarding/storage-modes/c15t-mode.d.ts.map +1 -0
  78. package/dist/onboarding/storage-modes/custom-mode.d.ts +18 -0
  79. package/dist/onboarding/storage-modes/custom-mode.d.ts.map +1 -0
  80. package/dist/onboarding/storage-modes/index.d.ts +5 -0
  81. package/dist/onboarding/storage-modes/index.d.ts.map +1 -0
  82. package/dist/onboarding/storage-modes/offline-mode.d.ts +19 -0
  83. package/dist/onboarding/storage-modes/offline-mode.d.ts.map +1 -0
  84. package/dist/onboarding/storage-modes/self-hosted-mode.d.ts +22 -0
  85. package/dist/onboarding/storage-modes/self-hosted-mode.d.ts.map +1 -0
  86. package/dist/onboarding/templates.d.ts +30 -0
  87. package/dist/onboarding/templates.d.ts.map +1 -0
  88. package/dist/onboarding.d.ts +15 -0
  89. package/dist/onboarding.d.ts.map +1 -0
  90. package/dist/utils/capitalize-first-letter.d.ts +2 -0
  91. package/dist/utils/capitalize-first-letter.d.ts.map +1 -0
  92. package/dist/utils/logger.d.ts +30 -0
  93. package/dist/utils/logger.d.ts.map +1 -0
  94. package/dist/utils/telemetry.d.ts +173 -0
  95. package/dist/utils/telemetry.d.ts.map +1 -0
  96. package/package.json +36 -31
package/dist/index.mjs CHANGED
@@ -1,1058 +1,2067 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import fs$1, { existsSync } from 'node:fs';
4
- import fs from 'node:fs/promises';
5
- import path from 'node:path';
6
- import { logger } from '@c15t/backend';
7
- import { getConsentTables, getMigrations, getAdapter } from '@c15t/backend/db';
8
- import chalk from 'chalk';
9
- import prompts from 'prompts';
10
- import yoctoSpinner from 'yocto-spinner';
11
- import { z } from 'zod';
12
- import { produceSchema } from '@mrleebo/prisma-ast';
13
- import babelPresetReact from '@babel/preset-react';
14
- import babelPresetTypescript from '@babel/preset-typescript';
15
- import { C15TError } from '@c15t/backend/error';
16
- import { loadConfig } from 'c12';
17
- import 'dotenv/config';
18
- import Crypto from 'node:crypto';
19
- import fs$2 from 'fs-extra';
20
-
21
- function convertToSnakeCase(str) {
22
- if (str === void 0 || str === null) {
23
- return "";
24
- }
25
- return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
2
+ import * as __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__ from "@clack/prompts";
3
+ import "dotenv/config";
4
+ import * as __WEBPACK_EXTERNAL_MODULE_open__ from "open";
5
+ import * as __WEBPACK_EXTERNAL_MODULE_picocolors__ from "picocolors";
6
+ import * as __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__ from "node:fs/promises";
7
+ import * as __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__ from "node:path";
8
+ import * as __WEBPACK_EXTERNAL_MODULE_c12__ from "c12";
9
+ import * as __WEBPACK_EXTERNAL_MODULE__doubletie_logger_91c58a8f__ from "@doubletie/logger";
10
+ import * as __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__ from "node:crypto";
11
+ import * as __WEBPACK_EXTERNAL_MODULE_node_os_74b4b876__ from "node:os";
12
+ import * as __WEBPACK_EXTERNAL_MODULE_posthog_node_1b07bdf4__ from "posthog-node";
13
+ import * as __WEBPACK_EXTERNAL_MODULE_node_child_process_27f17141__ from "node:child_process";
14
+ import * as __WEBPACK_EXTERNAL_MODULE_node_events_0a6aefe7__ from "node:events";
15
+ import * as __WEBPACK_EXTERNAL_MODULE_package_manager_detector_detect_94d6a9ae__ from "package-manager-detector/detect";
16
+ import * as __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__ from "node:fs";
17
+ import * as __WEBPACK_EXTERNAL_MODULE__c15t_backend_pkgs_db_adapters_cee37d0f__ from "@c15t/backend/pkgs/db-adapters";
18
+ import * as __WEBPACK_EXTERNAL_MODULE__c15t_backend_pkgs_migrations_80b6e3bd__ from "@c15t/backend/pkgs/migrations";
19
+ import * as __WEBPACK_EXTERNAL_MODULE_figlet__ from "figlet";
20
+ import * as __WEBPACK_EXTERNAL_MODULE_fs_extra_ce68a66b__ from "fs-extra";
21
+ function isC15TOptions(obj) {
22
+ return 'object' == typeof obj && null !== obj && 'appName' in obj;
26
23
  }
27
- const generateDrizzleSchema = async ({
28
- options,
29
- file,
30
- adapter
31
- }) => {
32
- const tables = getConsentTables(options);
33
- const filePath = file || "./auth-schema.ts";
34
- const databaseType = adapter.options?.provider;
35
- const usePlural = adapter.options?.usePlural;
36
- const timestampAndBoolean = databaseType !== "sqlite" ? "timestamp, boolean" : "";
37
- const int = databaseType === "mysql" ? "int" : "integer";
38
- const hasBigint = Object.values(tables).some(
39
- (table) => Object.values(table.fields).some(
40
- (field) => "bigint" in field && field.bigint
41
- )
42
- );
43
- const bigint = databaseType !== "sqlite" ? "bigint" : "";
44
- const text = databaseType === "mysql" ? "varchar, text" : "text";
45
- const jsonType = ["mysql", "pg"].includes(databaseType || "") ? ", json" : "";
46
- let code = `import { ${databaseType}Table, ${text}, ${int}${hasBigint ? `, ${bigint}` : ""}, ${timestampAndBoolean}${jsonType} } from "drizzle-orm/${databaseType}-core";`;
47
- const fileExist = existsSync(filePath);
48
- let isFirstTable = true;
49
- for (const table in tables) {
50
- if (Object.prototype.hasOwnProperty.call(tables, table)) {
51
- let getMySQLStringType = function(field, name) {
52
- if (field.unique) {
53
- return `varchar('${name}', { length: 255 })`;
54
- }
55
- if (field.references) {
56
- return `varchar('${name}', { length: 36 })`;
24
+ function isClientOptions(obj) {
25
+ return 'object' == typeof obj && null !== obj && ('mode' in obj || 'backendURL' in obj);
26
+ }
27
+ function tryGetFunctionResult(fn) {
28
+ if ('function' == typeof fn) try {
29
+ return fn();
30
+ } catch (error) {
31
+ console.warn('Error executing config function:', error);
32
+ }
33
+ return null;
34
+ }
35
+ function extractOptionsFromConfig(config) {
36
+ if (config.c15tConfig && isClientOptions(config.c15tConfig)) return config.c15tConfig;
37
+ if (config.c15tOptions && isClientOptions(config.c15tOptions)) return config.c15tOptions;
38
+ if (isClientOptions(config)) return config;
39
+ if (isC15TOptions(config.c15t)) return config.c15t;
40
+ if ('function' == typeof config.c15t) {
41
+ const result = tryGetFunctionResult(config.c15t);
42
+ if (isC15TOptions(result)) return result;
43
+ }
44
+ if (isC15TOptions(config.default)) return config.default;
45
+ if ('function' == typeof config.default) {
46
+ const result = tryGetFunctionResult(config.default);
47
+ if (isC15TOptions(result)) return result;
48
+ }
49
+ if (isC15TOptions(config.c15tInstance)) return config.c15tInstance;
50
+ if ('function' == typeof config.c15tInstance) {
51
+ const result = tryGetFunctionResult(config.c15tInstance);
52
+ if (isC15TOptions(result)) return result;
53
+ }
54
+ if (isC15TOptions(config.consent)) return config.consent;
55
+ if ('function' == typeof config.consent) {
56
+ const result = tryGetFunctionResult(config.consent);
57
+ if (isC15TOptions(result)) return result;
58
+ }
59
+ if ('object' == typeof config.c15t && null !== config.c15t && isC15TOptions(config.c15t.options)) return config.c15t.options;
60
+ if ('object' == typeof config.default && null !== config.default && isC15TOptions(config.default.options)) return config.default.options;
61
+ if ('object' == typeof config.c15tInstance && null !== config.c15tInstance && isC15TOptions(config.c15tInstance.options)) return config.c15tInstance.options;
62
+ if ('object' == typeof config.instance && null !== config.instance && isC15TOptions(config.instance.options)) return config.instance.options;
63
+ if ('object' == typeof config.consent && null !== config.consent && isC15TOptions(config.consent.options)) return config.consent.options;
64
+ if ('object' == typeof config.config && null !== config.config && isC15TOptions(config.config.options)) return config.config.options;
65
+ console.debug('No valid configuration found in any of the expected locations');
66
+ return null;
67
+ }
68
+ const configFileNames = [
69
+ 'c15t.config',
70
+ 'c15t.backend',
71
+ 'c15t',
72
+ 'c15t.client',
73
+ 'consent.config',
74
+ 'consent.backend',
75
+ 'consent',
76
+ 'cmp.config',
77
+ 'cmp.backend',
78
+ 'cmp'
79
+ ];
80
+ const constants_extensions = [
81
+ '.js',
82
+ '.jsx',
83
+ '.ts',
84
+ '.tsx',
85
+ '.cjs',
86
+ '.cts',
87
+ '.mjs',
88
+ '.mts',
89
+ '.server.cjs',
90
+ '.server.cts',
91
+ '.server.js',
92
+ '.server.jsx',
93
+ '.server.mjs',
94
+ '.server.mts',
95
+ '.server.ts',
96
+ '.server.tsx'
97
+ ];
98
+ let possiblePaths = configFileNames.flatMap((name)=>constants_extensions.map((ext)=>`${name}${ext}`));
99
+ const directories = [
100
+ '',
101
+ 'lib/server/',
102
+ 'server/',
103
+ 'lib/',
104
+ 'utils/',
105
+ 'config/',
106
+ 'src/',
107
+ 'app/'
108
+ ];
109
+ possiblePaths = directories.flatMap((dir)=>possiblePaths.map((file)=>`${dir}${file}`));
110
+ const jitiOptions = (context, cwd)=>{
111
+ const alias = context.config.getPathAliases(cwd) || {};
112
+ return {
113
+ extensions: [
114
+ '.ts',
115
+ '.tsx',
116
+ '.js',
117
+ '.jsx',
118
+ '.mjs',
119
+ '.cjs',
120
+ '.mts',
121
+ '.cts'
122
+ ],
123
+ alias
124
+ };
125
+ };
126
+ async function getConfig(contextOrOptions) {
127
+ const context = 'logger' in contextOrOptions ? contextOrOptions : {
128
+ ...contextOrOptions,
129
+ logger: {
130
+ debug: console.debug,
131
+ info: console.info,
132
+ warn: console.warn,
133
+ error: console.error
134
+ },
135
+ flags: {
136
+ config: contextOrOptions.configPath
137
+ },
138
+ error: {
139
+ handleError: (error)=>{
140
+ console.error('Error loading configuration:', error);
141
+ return null;
142
+ }
57
143
  }
58
- return `text('${name}')`;
59
- }, getType = function(fieldName, field) {
60
- const snakeCaseName = convertToSnakeCase(fieldName);
61
- const type = field.type;
62
- const typeMap = {
63
- string: {
64
- sqlite: `text('${snakeCaseName}')`,
65
- pg: `text('${snakeCaseName}')`,
66
- mysql: getMySQLStringType(field, snakeCaseName)
67
- },
68
- boolean: {
69
- sqlite: `integer('${snakeCaseName}', { mode: 'boolean' })`,
70
- pg: `boolean('${snakeCaseName}')`,
71
- mysql: `boolean('${snakeCaseName}')`
72
- },
73
- number: {
74
- sqlite: `integer('${snakeCaseName}')`,
75
- pg: "bigint" in field && field.bigint ? `bigint('${snakeCaseName}', { mode: 'number' })` : `integer('${snakeCaseName}')`,
76
- mysql: "bigint" in field && field.bigint ? `bigint('${snakeCaseName}', { mode: 'number' })` : `int('${snakeCaseName}')`
77
- },
78
- date: {
79
- sqlite: `integer('${snakeCaseName}', { mode: 'timestamp' })`,
80
- pg: `timestamp('${snakeCaseName}')`,
81
- mysql: `timestamp('${snakeCaseName}')`
82
- },
83
- // Add JSON type support
84
- json: {
85
- sqlite: `text('${snakeCaseName}')`,
86
- // SQLite uses TEXT for JSON
87
- pg: `json('${snakeCaseName}')`,
88
- // PostgreSQL native JSON
89
- mysql: `json('${snakeCaseName}')`
90
- // MySQL native JSON
91
- }
92
- };
93
- if (!typeMap[type]) {
94
- return `text('${snakeCaseName}')`;
144
+ };
145
+ const { cwd, logger, flags } = context;
146
+ const configPath = flags.config;
147
+ let foundConfigPath = null;
148
+ try {
149
+ let options = null;
150
+ const customJitiOptions = jitiOptions(context, cwd);
151
+ if (configPath) {
152
+ foundConfigPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].resolve(cwd, configPath);
153
+ logger.debug(`Using explicitly provided config path: ${foundConfigPath}`);
154
+ } else {
155
+ const prioritizedDirs = [
156
+ cwd,
157
+ __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cwd, 'packages/cli')
158
+ ];
159
+ const primaryName = 'c15t.config';
160
+ const extensions = [
161
+ '.ts',
162
+ '.js',
163
+ '.mjs'
164
+ ];
165
+ for (const dir of prioritizedDirs){
166
+ for (const ext of extensions){
167
+ const checkPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(dir, `${primaryName}${ext}`);
168
+ try {
169
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].access(checkPath);
170
+ foundConfigPath = checkPath;
171
+ logger.debug(`Found config via manual check: ${foundConfigPath}`);
172
+ break;
173
+ } catch {}
174
+ }
175
+ if (foundConfigPath) break;
176
+ }
95
177
  }
96
- const dbType = databaseType && ["sqlite", "pg", "mysql"].includes(databaseType) ? databaseType : "sqlite";
97
- return typeMap[type][dbType];
98
- };
99
- let modelName = usePlural ? `${tables[table].modelName}s` : tables[table].modelName;
100
- if (!modelName) {
101
- modelName = table;
102
- }
103
- const fields = tables[table].fields;
104
- const id = databaseType === "mysql" ? `varchar("id", { length: 36 }).primaryKey()` : `text("id").primaryKey()`;
105
- const tableNameForSQL = convertToSnakeCase(modelName);
106
- if (isFirstTable) {
107
- code += "\n\n";
108
- isFirstTable = false;
109
- } else {
110
- code += "\n\n";
111
- }
112
- const schema = `export const ${modelName} = ${databaseType}Table("${tableNameForSQL}", {
113
- id: ${id},
114
- ${Object.keys(fields).map((field) => {
115
- if (Object.prototype.hasOwnProperty.call(fields, field)) {
116
- const attr = fields[field];
117
- return ` ${field}: ${getType(field, attr)}${attr.required ? ".notNull()" : ""}${attr.unique ? ".unique()" : ""}${attr.references ? `.references(()=> ${usePlural ? `${attr.references.model}s` : attr.references.model}.${attr.references.field}, { onDelete: 'cascade' })` : ""}`;
178
+ if (foundConfigPath) try {
179
+ logger.debug(`Loading configuration from resolved path: ${foundConfigPath}`);
180
+ const result = await (0, __WEBPACK_EXTERNAL_MODULE_c12__.loadConfig)({
181
+ configFile: foundConfigPath,
182
+ jitiOptions: customJitiOptions
183
+ });
184
+ logger.debug('Raw config loading result:', result);
185
+ if (result.config) {
186
+ logger.debug('Trying to extract config from result.config');
187
+ options = extractOptionsFromConfig(result.config);
188
+ }
189
+ if (options) {
190
+ logger.debug('Extracted config:', options);
191
+ if (isC15TOptions(options) || isClientOptions(options)) {
192
+ logger.debug('Configuration validated successfully.');
193
+ return options;
194
+ }
195
+ logger.debug('Loaded config does not match expected schema');
196
+ } else logger.debug('No configuration extracted from loaded file');
197
+ logger.debug('Raw loaded configuration:', result);
198
+ return null;
199
+ } catch (error) {
200
+ logger.debug('Error loading config from explicit path:', error);
201
+ try {
202
+ logger.debug(`Trying to require module directly: ${foundConfigPath}`);
203
+ const importedModule = await import(foundConfigPath);
204
+ logger.debug('Directly imported module:', importedModule);
205
+ const extracted = extractOptionsFromConfig(importedModule);
206
+ if (extracted && (isC15TOptions(extracted) || isClientOptions(extracted))) {
207
+ logger.debug('Found valid config through direct import');
208
+ return extracted;
209
+ }
210
+ } catch (importError) {
211
+ logger.debug('Error importing module directly:', importError);
212
+ }
213
+ return null;
118
214
  }
119
- return "";
120
- }).filter(Boolean).join(",\n")}
121
- });`;
122
- code += schema;
215
+ return options;
216
+ } catch (error) {
217
+ if ('error' in context && context.error && 'function' == typeof context.error.handleError) return context.error.handleError(error, 'Error loading configuration');
218
+ console.error('Error loading configuration:', error);
219
+ return null;
123
220
  }
124
- }
125
- return {
126
- code,
127
- fileName: filePath,
128
- overwrite: fileExist
129
- };
130
- };
131
-
132
- const CREATE_TABLE_REGEX = /create\s+table\s+"([^"]+)"\s+\((.*)\)/i;
133
- const CREATE_INDEX_REGEX = /create\s+index\s+"?([^"\s]+)"?\s+on\s+"?([^"\s]+)"?/i;
134
- const NOT_NULL_REGEX = /\bnot null\b/gi;
135
- const PRIMARY_KEY_REGEX = /\bprimary key\b/gi;
136
- const REFERENCES_REGEX = /\breferences\b/gi;
137
- const UNIQUE_REGEX = /\bunique\b/gi;
138
- const CREATE_TABLE_KEYWORD_REGEX = /\bcreate\s+table\b/gi;
139
- const CREATE_INDEX_KEYWORD_REGEX = /\bcreate\s+index\b/gi;
140
- const ALTER_TABLE_REGEX = /\balter\s+table\b/gi;
141
- const INSERT_INTO_REGEX = /\binsert\s+into\b/gi;
142
- const UPDATE_REGEX = /\bupdate\b/gi;
143
- const DELETE_FROM_REGEX = /\bdelete\s+from\b/gi;
144
- const SELECT_REGEX = /\bselect\b/gi;
145
- const FROM_REGEX = /\bfrom\b/gi;
146
- const WHERE_REGEX = /\bwhere\b/gi;
147
- const JOIN_REGEX = /\bjoin\b/gi;
148
- const ON_REGEX = /\bon\b/gi;
149
- const AND_REGEX = /\band\b/gi;
150
- const OR_REGEX = /\bor\b/gi;
151
- const BOOLEAN_FIELD_REGEX = /("is[A-Z][a-zA-Z0-9]*")\s+integer/g;
152
- const DATE_FIELD_REGEX = /("(?:created|updated|expires)At")\s+date/gi;
153
- const TEXT_FIELD_REGEX = /("(?:name|code|description|id)")\s+text/gi;
154
- const JSON_FIELD_REGEX = /("(?:metadata|config|data|settings|options|preferences|attributes)")\s+text/gi;
155
- function formatSQL(sql, databaseType = "sqlite", options) {
156
- const dbType = databaseType === "pg" ? "postgresql" : databaseType;
157
- const statements = sql.split(";").filter((stmt) => stmt.trim());
158
- const rollbackStatements = [];
159
- const formattedStatements = statements.map((statement) => {
160
- const trimmedStmt = statement.trim().toLowerCase();
161
- if (trimmedStmt.startsWith("create table")) {
162
- const match = statement.match(CREATE_TABLE_REGEX);
163
- if (match) {
164
- const [_, tableName, columnsStr] = match;
165
- rollbackStatements.unshift(`DROP TABLE IF EXISTS "${tableName}"`);
166
- const columns = columnsStr.split(",").map((col) => col.trim());
167
- const formattedColumns = columns.map((col) => {
168
- let formattedCol = col.replace(NOT_NULL_REGEX, "NOT NULL").replace(PRIMARY_KEY_REGEX, "PRIMARY KEY").replace(REFERENCES_REGEX, "REFERENCES").replace(UNIQUE_REGEX, "UNIQUE");
169
- if (dbType === "postgresql") {
170
- formattedCol = formattedCol.replace(BOOLEAN_FIELD_REGEX, "$1 boolean").replace(DATE_FIELD_REGEX, "$1 timestamp with time zone").replace(TEXT_FIELD_REGEX, "$1 varchar(255)").replace(JSON_FIELD_REGEX, "$1 jsonb");
171
- } else if (dbType === "mysql") {
172
- formattedCol = formattedCol.replace(BOOLEAN_FIELD_REGEX, "$1 TINYINT(1)").replace(DATE_FIELD_REGEX, "$1 DATETIME").replace(TEXT_FIELD_REGEX, "$1 VARCHAR(255)").replace(JSON_FIELD_REGEX, "$1 JSON");
173
- } else if (dbType === "sqlite") {
174
- formattedCol = formattedCol.replace(
175
- JSON_FIELD_REGEX,
176
- "$1 text -- stored as JSON"
177
- );
178
- }
179
- return formattedCol;
180
- }).map((col) => ` ${col}`).join(",\n");
181
- return `CREATE TABLE IF NOT EXISTS "${tableName}" (
182
- ${formattedColumns}
183
- );`;
184
- }
185
- }
186
- if (trimmedStmt.startsWith("create index")) {
187
- const indexMatch = statement.match(CREATE_INDEX_REGEX);
188
- if (indexMatch) {
189
- const [_, indexName] = indexMatch;
190
- rollbackStatements.unshift(`DROP INDEX IF EXISTS "${indexName}"`);
191
- return `CREATE INDEX IF NOT EXISTS "${indexName}" ${statement.substring(statement.toLowerCase().indexOf("on")).trim()};`;
192
- }
193
- }
194
- return `${statement.trim().replace(CREATE_TABLE_KEYWORD_REGEX, "CREATE TABLE").replace(CREATE_INDEX_KEYWORD_REGEX, "CREATE INDEX").replace(ALTER_TABLE_REGEX, "ALTER TABLE").replace(INSERT_INTO_REGEX, "INSERT INTO").replace(UPDATE_REGEX, "UPDATE").replace(DELETE_FROM_REGEX, "DELETE FROM").replace(SELECT_REGEX, "SELECT").replace(FROM_REGEX, "FROM").replace(WHERE_REGEX, "WHERE").replace(JOIN_REGEX, "JOIN").replace(ON_REGEX, "ON").replace(AND_REGEX, "AND").replace(OR_REGEX, "OR")};`;
195
- }).join("\n\n");
196
- const useTransactions = dbType !== "d1";
197
- const transactionStart = useTransactions ? dbType === "mysql" ? "START TRANSACTION;" : "BEGIN;" : "";
198
- const transactionEnd = useTransactions ? "COMMIT;" : "";
199
- const timestamp = options?.timestamp || (/* @__PURE__ */ new Date()).toISOString();
200
- return `-- Migration generated by C15T (${timestamp})
201
- -- Database type: ${dbType}
202
- -- Description: Automatically generated schema migration
203
- --
204
- -- Wrapped in a transaction for atomicity
205
- -- To roll back this migration, use the ROLLBACK section below
221
+ }
222
+ function showHelpMenu(context, version, commands, flags) {
223
+ const { logger } = context;
224
+ logger.debug('Displaying help menu using command and flag structures.');
225
+ const commandLines = commands.map((cmd)=>` ${cmd.name.padEnd(10)} ${cmd.description}`).join('\n');
226
+ const optionLines = flags.map((flag)=>{
227
+ const names = flag.names.join(', ');
228
+ const valuePlaceholder = flag.expectsValue ? ' <value>' : '';
229
+ return ` ${(names + valuePlaceholder).padEnd(20)} ${flag.description}`;
230
+ }).join('\n');
231
+ const helpContent = `c15t CLI version ${version}
206
232
 
207
- ${transactionStart}
208
- -- MIGRATION
209
- ${formattedStatements}
210
- ${transactionEnd}
233
+ Available Commands:
234
+ ${commandLines}
211
235
 
212
- -- ROLLBACK
213
- -- Uncomment the section below to roll back this migration
214
- /*
215
- ${transactionStart}
236
+ Options:
237
+ ${optionLines}
216
238
 
217
- ${rollbackStatements.join(";\n\n")};
239
+ Run a command directly (e.g., ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('c15t generate')}) or select one interactively when no command is provided.
218
240
 
219
- ${transactionEnd}
220
- */`;
241
+ For more help, visit: https://c15t.dev`;
242
+ logger.debug('Help menu content generated.');
243
+ logger.note(helpContent, 'Usage');
221
244
  }
222
- const generateMigrations = async ({
223
- options,
224
- file,
225
- adapter
226
- }) => {
227
- const { compileMigrations } = await getMigrations(options);
228
- const migrations = await compileMigrations();
229
- let databaseType = "sqlite";
230
- if (adapter?.options?.provider) {
231
- databaseType = adapter.options.provider;
232
- } else if (options.database && "options" in options.database && options.database.options && typeof options.database.options === "object" && "provider" in options.database.options) {
233
- databaseType = options.database.options.provider;
234
- }
235
- const isTest = process.env.NODE_ENV === "test" || file?.includes("test");
236
- const testTimestamp = options?._testTimestamp;
237
- const formatOptions = {
238
- timestamp: testTimestamp || (isTest ? "2023-01-01T00:00:00.000Z" : void 0)
239
- };
240
- const formattedMigrations = formatSQL(
241
- migrations,
242
- databaseType,
243
- formatOptions
244
- );
245
- const generatedFileName = file || `./c15t_migrations/${Date.now()}_create_tables.sql`;
246
- return {
247
- code: formattedMigrations,
248
- fileName: generatedFileName
249
- };
245
+ const validLogLevels = [
246
+ 'error',
247
+ 'warn',
248
+ 'info',
249
+ 'debug'
250
+ ];
251
+ const formatArgs = (args)=>{
252
+ if (0 === args.length) return '';
253
+ return `\n${args.map((arg)=>` - ${JSON.stringify(arg, null, 2)}`).join('\n')}`;
250
254
  };
251
-
252
- function capitalizeFirstLetter(str) {
253
- return str.charAt(0).toUpperCase() + str.slice(1);
254
- }
255
-
256
- const generatePrismaSchema = async ({
257
- adapter,
258
- options,
259
- file
260
- }) => {
261
- const provider = adapter.options?.provider || "postgresql";
262
- const tables = getConsentTables(options);
263
- const filePath = file || "./prisma/schema.prisma";
264
- const schemaPrismaExist = existsSync(path.join(process.cwd(), filePath));
265
- let schemaPrisma = "";
266
- if (schemaPrismaExist) {
267
- schemaPrisma = await fs.readFile(
268
- path.join(process.cwd(), filePath),
269
- "utf-8"
270
- );
271
- } else {
272
- schemaPrisma = getNewPrisma(provider);
273
- }
274
- const manyToManyRelations = /* @__PURE__ */ new Map();
275
- for (const table in tables) {
276
- if (Object.hasOwn(tables, table)) {
277
- const fields = tables[table]?.fields;
278
- for (const field in fields) {
279
- if (Object.hasOwn(fields, field)) {
280
- const attr = fields[field];
281
- if (attr?.references) {
282
- const referencedModel = capitalizeFirstLetter(
283
- attr.references.model
284
- );
285
- if (!manyToManyRelations.has(referencedModel)) {
286
- manyToManyRelations.set(referencedModel, /* @__PURE__ */ new Set());
255
+ const formatLogMessage = (logLevel, message, args = [])=>{
256
+ const messageStr = 'string' == typeof message ? message : String(message);
257
+ const formattedArgs = formatArgs(args);
258
+ switch(logLevel){
259
+ case 'error':
260
+ return `${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bgRed(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].black(' error '))} ${messageStr}${formattedArgs}`;
261
+ case 'warn':
262
+ return `${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bgYellow(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].black(' warning '))} ${messageStr}${formattedArgs}`;
263
+ case 'info':
264
+ return `${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bgGreen(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].black(' info '))} ${messageStr}${formattedArgs}`;
265
+ case 'debug':
266
+ return `${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bgBlack(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].white(' debug '))} ${messageStr}${formattedArgs}`;
267
+ case 'success':
268
+ return `${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bgGreen(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].white(' success '))} ${messageStr}${formattedArgs}`;
269
+ case 'failed':
270
+ return `${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bgRed(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].white(' failed '))} ${messageStr}${formattedArgs}`;
271
+ default:
272
+ {
273
+ const levelStr = logLevel;
274
+ return `[${levelStr.toUpperCase()}] ${messageStr}${formattedArgs}`;
287
275
  }
288
- manyToManyRelations.get(referencedModel).add(capitalizeFirstLetter(table));
289
- }
290
- }
291
- }
292
276
  }
293
- }
294
- const schema = produceSchema(schemaPrisma, (builder) => {
295
- function getPrismaType(type, isOptional, isBigint) {
296
- const isJsonField = type === "json" || type === "jsonb";
297
- if (isJsonField) {
298
- if (provider === "postgresql" || provider === "mysql") {
299
- return isOptional ? "Json?" : "Json";
277
+ };
278
+ const logMessage = (logLevel, message, ...args)=>{
279
+ const formattedMessage = formatLogMessage(logLevel, message, args);
280
+ switch(logLevel){
281
+ case 'error':
282
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.error(formattedMessage);
283
+ break;
284
+ case 'warn':
285
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.warn(formattedMessage);
286
+ break;
287
+ case 'info':
288
+ case 'debug':
289
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.info(formattedMessage);
290
+ break;
291
+ case 'success':
292
+ case 'failed':
293
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.outro(formattedMessage);
294
+ break;
295
+ default:
296
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.message(formattedMessage);
297
+ }
298
+ };
299
+ const createCliLogger = (level)=>{
300
+ const baseLogger = (0, __WEBPACK_EXTERNAL_MODULE__doubletie_logger_91c58a8f__.createLogger)({
301
+ level,
302
+ appName: 'c15t',
303
+ log: (logLevel, message, ...args)=>{
304
+ logMessage(logLevel, message, ...args);
300
305
  }
301
- return isOptional ? "String?" : 'String @map("json_as_text")';
302
- }
303
- if (type === "string") {
304
- return isOptional ? "String?" : "String";
305
- }
306
- if (type === "number" && isBigint) {
307
- return isOptional ? "BigInt?" : "BigInt";
308
- }
309
- if (type === "number") {
310
- return isOptional ? "Int?" : "Int";
311
- }
312
- if (type === "boolean") {
313
- return isOptional ? "Boolean?" : "Boolean";
314
- }
315
- if (type === "date") {
316
- return isOptional ? "DateTime?" : "DateTime";
317
- }
318
- if (type === "string[]") {
319
- return "String[]";
320
- }
321
- if (type === "number[]") {
322
- return "Int[]";
323
- }
324
- return "String";
325
- }
326
- for (const table in tables) {
327
- if (Object.hasOwn(tables, table)) {
328
- const fields = tables[table]?.fields;
329
- const originalTable = tables[table]?.modelName;
330
- const modelName = capitalizeFirstLetter(originalTable || table);
331
- const prismaModel = builder.findByType("model", { name: modelName });
332
- if (!prismaModel) {
333
- if (provider === "mongodb") {
334
- builder.model(modelName).field("id", "String").attribute("id").attribute(`map("_id")`);
335
- } else {
336
- builder.model(modelName).field("id", "String").attribute("id");
337
- }
306
+ });
307
+ const extendedLogger = baseLogger;
308
+ extendedLogger.message = (message)=>{
309
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.message(message);
310
+ };
311
+ extendedLogger.note = (message, ...args)=>{
312
+ const messageStr = 'string' == typeof message ? message : String(message);
313
+ const title = args.length > 0 && 'string' == typeof args[0] ? args[0] : void 0;
314
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.note(messageStr, title, {
315
+ format: (line)=>line
316
+ });
317
+ };
318
+ extendedLogger.success = (message, ...args)=>{
319
+ logMessage('success', message, ...args);
320
+ };
321
+ extendedLogger.failed = (message, ...args)=>{
322
+ logMessage('failed', message, ...args);
323
+ };
324
+ extendedLogger.outro = (message)=>{
325
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.outro(message);
326
+ };
327
+ return extendedLogger;
328
+ };
329
+ const TELEMETRY_DISABLED_ENV = 'C15T_TELEMETRY_DISABLED';
330
+ var telemetry_TelemetryEventName = /*#__PURE__*/ function(TelemetryEventName) {
331
+ TelemetryEventName["CLI_INVOKED"] = "cli.invoked";
332
+ TelemetryEventName["CLI_COMPLETED"] = "cli.completed";
333
+ TelemetryEventName["CLI_EXITED"] = "cli.exited";
334
+ TelemetryEventName["CLI_ENVIRONMENT_DETECTED"] = "cli.environment_detected";
335
+ TelemetryEventName["COMMAND_EXECUTED"] = "command.executed";
336
+ TelemetryEventName["COMMAND_SUCCEEDED"] = "command.succeeded";
337
+ TelemetryEventName["COMMAND_FAILED"] = "command.failed";
338
+ TelemetryEventName["COMMAND_UNKNOWN"] = "command.unknown";
339
+ TelemetryEventName["INTERACTIVE_MENU_OPENED"] = "ui.menu.opened";
340
+ TelemetryEventName["INTERACTIVE_MENU_EXITED"] = "ui.menu.exited";
341
+ TelemetryEventName["CONFIG_LOADED"] = "config.loaded";
342
+ TelemetryEventName["CONFIG_ERROR"] = "config.error";
343
+ TelemetryEventName["CONFIG_UPDATED"] = "config.updated";
344
+ TelemetryEventName["HELP_DISPLAYED"] = "help.displayed";
345
+ TelemetryEventName["VERSION_DISPLAYED"] = "version.displayed";
346
+ TelemetryEventName["ONBOARDING_STARTED"] = "onboarding.started";
347
+ TelemetryEventName["ONBOARDING_COMPLETED"] = "onboarding.completed";
348
+ TelemetryEventName["ONBOARDING_EXITED"] = "onboarding.exited";
349
+ TelemetryEventName["ONBOARDING_STORAGE_MODE_SELECTED"] = "onboarding.storage_mode_selected";
350
+ TelemetryEventName["ONBOARDING_C15T_MODE_CONFIGURED"] = "onboarding.c15t_mode_configured";
351
+ TelemetryEventName["ONBOARDING_OFFLINE_MODE_CONFIGURED"] = "onboarding.offline_mode_configured";
352
+ TelemetryEventName["ONBOARDING_SELF_HOSTED_CONFIGURED"] = "onboarding.self_hosted_configured";
353
+ TelemetryEventName["ONBOARDING_CUSTOM_MODE_CONFIGURED"] = "onboarding.custom_mode_configured";
354
+ TelemetryEventName["ONBOARDING_DEPENDENCIES_CHOICE"] = "onboarding.dependencies_choice";
355
+ TelemetryEventName["ONBOARDING_DEPENDENCIES_INSTALLED"] = "onboarding.dependencies_installed";
356
+ TelemetryEventName["ONBOARDING_GITHUB_STAR"] = "onboarding.github_star";
357
+ TelemetryEventName["ERROR_OCCURRED"] = "error.occurred";
358
+ TelemetryEventName["MIGRATION_STARTED"] = "migration.started";
359
+ TelemetryEventName["MIGRATION_PLANNED"] = "migration.planned";
360
+ TelemetryEventName["MIGRATION_EXECUTED"] = "migration.executed";
361
+ TelemetryEventName["MIGRATION_COMPLETED"] = "migration.completed";
362
+ TelemetryEventName["MIGRATION_FAILED"] = "migration.failed";
363
+ TelemetryEventName["GENERATE_STARTED"] = "generate.started";
364
+ TelemetryEventName["GENERATE_COMPLETED"] = "generate.completed";
365
+ TelemetryEventName["GENERATE_FAILED"] = "generate.failed";
366
+ return TelemetryEventName;
367
+ }({});
368
+ class Telemetry {
369
+ client = null;
370
+ disabled;
371
+ defaultProperties;
372
+ distinctId;
373
+ apiKey = 'phc_ViY5LtTmh4kqoumXZB2olPFoTz4AbbDfrogNgFi1MH3';
374
+ debug = false;
375
+ logger;
376
+ constructor(options){
377
+ const envDisabled = '1' === process.env[TELEMETRY_DISABLED_ENV] || process.env[TELEMETRY_DISABLED_ENV]?.toLowerCase() === 'true';
378
+ const hasValidApiKey = !!(this.apiKey && '' !== this.apiKey.trim());
379
+ this.disabled = options?.disabled ?? envDisabled ?? !hasValidApiKey;
380
+ this.defaultProperties = options?.defaultProperties ?? {};
381
+ this.logger = options?.logger;
382
+ this.distinctId = this.generateAnonymousId();
383
+ if (this.disabled) {
384
+ if (!hasValidApiKey) this.logDebug('Telemetry disabled: No API key provided');
385
+ } else this.initClient(options?.client);
386
+ }
387
+ trackEventSync(eventName, properties = {}) {
388
+ if (this.disabled || !this.client) {
389
+ if (this.debug) this.logDebug('Telemetry disabled or client not initialized');
390
+ return;
338
391
  }
339
- for (const field in fields) {
340
- if (Object.hasOwn(fields, field)) {
341
- const attr = fields[field];
342
- const existingField = builder.findByType("field", {
343
- name: field,
344
- within: prismaModel?.properties
392
+ const safeProperties = {};
393
+ for (const [key, value] of Object.entries(properties))if ('config' !== key && void 0 !== value) safeProperties[key] = value;
394
+ if (this.debug) this.logDebug(`Sending telemetry event: ${eventName}`);
395
+ try {
396
+ this.client.capture({
397
+ distinctId: this.distinctId,
398
+ event: eventName,
399
+ properties: {
400
+ ...this.defaultProperties,
401
+ ...safeProperties,
402
+ timestamp: new Date().toISOString()
403
+ }
345
404
  });
346
- if (existingField) {
347
- continue;
348
- }
349
- builder.model(modelName).field(
350
- field,
351
- getPrismaType(attr.type, !attr?.required, attr?.bigint || false)
352
- );
353
- if (attr.unique) {
354
- builder.model(modelName).blockAttribute(`unique([${field}])`);
355
- }
356
- if (attr.references) {
357
- builder.model(modelName).field(
358
- `${attr.references.model.toLowerCase()}`,
359
- capitalizeFirstLetter(attr.references.model)
360
- ).attribute(
361
- `relation(fields: [${field}], references: [${attr.references.field}], onDelete: Cascade)`
362
- );
405
+ this.client.flush();
406
+ if (this.debug) this.logDebug(`Flushed telemetry event: ${eventName}`);
407
+ } catch (error) {
408
+ if (this.debug) this.logDebug(`Error sending telemetry: ${error}`);
409
+ }
410
+ }
411
+ trackEvent(eventName, properties = {}) {
412
+ this.trackEventSync(eventName, properties);
413
+ }
414
+ trackCommand(command, args = [], flags = {}) {
415
+ if (this.disabled || !this.client) return;
416
+ const safeFlags = {};
417
+ for (const [key, value] of Object.entries(flags))if ('config' !== key && void 0 !== value) safeFlags[key] = value;
418
+ this.trackEvent("command.executed", {
419
+ command,
420
+ args: args.join(' '),
421
+ flagsData: JSON.stringify(safeFlags)
422
+ });
423
+ }
424
+ trackError(error, command) {
425
+ if (this.disabled || !this.client) return;
426
+ this.trackEvent("error.occurred", {
427
+ command,
428
+ error: error.message,
429
+ errorName: error.name,
430
+ stack: 'development' === process.env.NODE_ENV ? error.stack : void 0
431
+ });
432
+ }
433
+ setLogLevel(level) {
434
+ if (this.client && 'debug' === level) {
435
+ this.debug = true;
436
+ this.client.debug(true);
437
+ this.logDebug('Telemetry debug mode enabled');
438
+ }
439
+ }
440
+ disable() {
441
+ this.disabled = true;
442
+ }
443
+ enable() {
444
+ this.disabled = false;
445
+ if (!this.client) this.initClient();
446
+ }
447
+ isDisabled() {
448
+ return this.disabled;
449
+ }
450
+ async shutdown() {
451
+ if (this.client) {
452
+ await this.client.shutdown();
453
+ this.client = null;
454
+ }
455
+ }
456
+ setLogger(logger) {
457
+ this.logger = logger;
458
+ }
459
+ logDebug(message, ...args) {
460
+ if (this.logger) this.logger.debug(message, ...args);
461
+ else console.debug(message, ...args);
462
+ }
463
+ initClient(customClient) {
464
+ if (customClient) {
465
+ this.client = customClient;
466
+ if (this.debug) this.logDebug('Using custom PostHog client');
467
+ } else {
468
+ if (!this.apiKey || '' === this.apiKey.trim()) {
469
+ this.disabled = true;
470
+ this.logDebug('Telemetry disabled: No API key provided');
471
+ return;
363
472
  }
364
- if (!attr.unique && !attr.references && provider === "mysql" && attr.type === "string") {
365
- builder.model(modelName).field(field).attribute("db.Text");
473
+ const startTime = Date.now();
474
+ try {
475
+ const clientConfig = {
476
+ host: 'https://eu.i.posthog.com',
477
+ flushInterval: 0,
478
+ flushAt: 1,
479
+ requestTimeout: 3000
480
+ };
481
+ if (this.debug) this.logDebug('Initializing PostHog client with config:', JSON.stringify(clientConfig));
482
+ this.client = new __WEBPACK_EXTERNAL_MODULE_posthog_node_1b07bdf4__.PostHog(this.apiKey, clientConfig);
483
+ const initTime = Date.now() - startTime;
484
+ if (this.debug) this.logDebug('PostHog client initialized in', initTime, 'ms');
485
+ } catch (error) {
486
+ this.disabled = true;
487
+ const errorDetails = error instanceof Error ? {
488
+ message: error.message,
489
+ name: error.name,
490
+ stack: error.stack
491
+ } : {
492
+ rawError: String(error)
493
+ };
494
+ if (this.debug) this.logDebug('Telemetry disabled due to initialization error:', JSON.stringify(errorDetails, null, 2));
495
+ try {
496
+ if (this.debug) this.logDebug('Attempting fallback PostHog initialization');
497
+ this.client = new __WEBPACK_EXTERNAL_MODULE_posthog_node_1b07bdf4__.PostHog(this.apiKey);
498
+ this.disabled = false;
499
+ if (this.debug) this.logDebug('PostHog client initialized using fallback method');
500
+ } catch (fallbackError) {
501
+ this.logDebug('Fallback initialization also failed:', fallbackError instanceof Error ? fallbackError.message : String(fallbackError));
502
+ }
366
503
  }
367
- }
368
504
  }
369
- if (originalTable && originalTable !== modelName) {
370
- const hasMapAttribute = builder.findByType("attribute", {
371
- name: "map",
372
- within: prismaModel?.properties
373
- });
374
- if (!hasMapAttribute) {
375
- builder.model(modelName).blockAttribute("map", originalTable);
376
- }
505
+ }
506
+ generateAnonymousId() {
507
+ const machineId = __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__["default"].createHash('sha256').update(__WEBPACK_EXTERNAL_MODULE_node_os_74b4b876__["default"].hostname() + __WEBPACK_EXTERNAL_MODULE_node_os_74b4b876__["default"].platform() + __WEBPACK_EXTERNAL_MODULE_node_os_74b4b876__["default"].arch() + __WEBPACK_EXTERNAL_MODULE_node_os_74b4b876__["default"].totalmem()).digest('hex');
508
+ return machineId;
509
+ }
510
+ flushSync() {
511
+ if (this.disabled || !this.client) return;
512
+ try {
513
+ this.client.flush();
514
+ if (this.debug) this.logDebug('Manually flushed telemetry events');
515
+ } catch (error) {
516
+ if (this.debug) this.logDebug(`Error flushing telemetry: ${error}`);
517
+ }
518
+ }
519
+ }
520
+ function createTelemetry(options) {
521
+ return new Telemetry(options);
522
+ }
523
+ async function addAndInstallDependenciesViaPM(projectRoot, dependencies, packageManager) {
524
+ const depsToAdd = dependencies.map((dep)=>`${dep}`);
525
+ if (0 === depsToAdd.length) return;
526
+ let command = '';
527
+ let args = [];
528
+ switch(packageManager){
529
+ case 'npm':
530
+ command = 'npm';
531
+ args = [
532
+ 'install',
533
+ ...depsToAdd
534
+ ];
535
+ break;
536
+ case 'yarn':
537
+ command = 'yarn';
538
+ args = [
539
+ 'add',
540
+ ...depsToAdd
541
+ ];
542
+ break;
543
+ case 'pnpm':
544
+ command = 'pnpm';
545
+ args = [
546
+ 'add',
547
+ ...depsToAdd
548
+ ];
549
+ break;
550
+ default:
551
+ throw new Error(`Unsupported package manager for dependency addition: ${packageManager}`);
552
+ }
553
+ const child = (0, __WEBPACK_EXTERNAL_MODULE_node_child_process_27f17141__.spawn)(command, args, {
554
+ cwd: projectRoot,
555
+ stdio: 'inherit'
556
+ });
557
+ await (0, __WEBPACK_EXTERNAL_MODULE_node_events_0a6aefe7__.once)(child, 'exit');
558
+ }
559
+ function getManualInstallCommand(dependencies, packageManager) {
560
+ const depsToAdd = dependencies.map((dep)=>`${dep}`);
561
+ switch(packageManager){
562
+ case 'npm':
563
+ return `npm install ${depsToAdd.join(' ')}`;
564
+ case 'yarn':
565
+ return `yarn add ${depsToAdd.join(' ')}`;
566
+ case 'pnpm':
567
+ return `pnpm add ${depsToAdd.join(' ')}`;
568
+ default:
569
+ return `npm install ${depsToAdd.join(' ')}`;
570
+ }
571
+ }
572
+ async function detectFramework(projectRoot) {
573
+ try {
574
+ const packageJsonPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'package.json');
575
+ const packageJson = JSON.parse(await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].readFile(packageJsonPath, 'utf-8'));
576
+ const deps = {
577
+ ...packageJson.dependencies,
578
+ ...packageJson.devDependencies
579
+ };
580
+ const hasReact = 'react' in deps;
581
+ let framework = null;
582
+ if ('next' in deps) framework = 'Next.js';
583
+ else if ('@remix-run/react' in deps) framework = 'Remix';
584
+ else if ('@vitejs/plugin-react' in deps || '@vitejs/plugin-react-swc' in deps) framework = 'Vite + React';
585
+ else if ('gatsby' in deps) framework = 'Gatsby';
586
+ else if (hasReact) framework = 'React';
587
+ return {
588
+ framework,
589
+ hasReact
590
+ };
591
+ } catch (error) {
592
+ return {
593
+ framework: null,
594
+ hasReact: false
595
+ };
596
+ }
597
+ }
598
+ async function detectProjectRoot(cwd) {
599
+ let projectRoot = cwd;
600
+ try {
601
+ let prevDir = '';
602
+ while(projectRoot !== prevDir)try {
603
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].access(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'package.json'));
604
+ break;
605
+ } catch {
606
+ prevDir = projectRoot;
607
+ projectRoot = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].dirname(projectRoot);
608
+ }
609
+ if (projectRoot === prevDir) throw new Error('Could not find project root (no package.json found)');
610
+ return projectRoot;
611
+ } catch {
612
+ return cwd;
613
+ }
614
+ }
615
+ async function detectPackageManager(projectRoot) {
616
+ try {
617
+ const result = await (0, __WEBPACK_EXTERNAL_MODULE_package_manager_detector_detect_94d6a9ae__.detect)({
618
+ cwd: projectRoot
619
+ });
620
+ let detectedPm = null;
621
+ if ('string' == typeof result) detectedPm = result;
622
+ else if (result && 'object' == typeof result) {
623
+ if ('name' in result && 'string' == typeof result.name) detectedPm = result.name;
624
+ else if ('pm' in result && 'string' == typeof result.pm) detectedPm = result.pm;
377
625
  }
378
- }
379
- }
380
- for (const [
381
- referencedModel,
382
- relatedModels
383
- ] of manyToManyRelations.entries()) {
384
- for (const relatedModel of relatedModels) {
385
- const fieldName = `${relatedModel.toLowerCase()}s`;
386
- const model = builder.findByType("model", { name: referencedModel });
387
- if (model) {
388
- const existingField = builder.findByType("field", {
389
- name: fieldName,
390
- within: model.properties
391
- });
392
- if (!existingField) {
393
- builder.model(referencedModel).field(fieldName, `${relatedModel}[]`);
394
- }
626
+ if (detectedPm && ('npm' === detectedPm || 'yarn' === detectedPm || 'pnpm' === detectedPm)) return detectedPm;
627
+ let detectedValueStr = String(result);
628
+ if (result && 'object' == typeof result) detectedValueStr = JSON.stringify(result);
629
+ throw new Error(`Could not reliably detect package manager (detected: ${detectedValueStr}).`);
630
+ } catch (error) {
631
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.warn(`Automatic package manager detection failed: ${error instanceof Error ? error.message : String(error)}`);
632
+ const selectedPackageManager = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.select({
633
+ message: 'Please select your package manager:',
634
+ options: [
635
+ {
636
+ value: 'npm',
637
+ label: 'npm'
638
+ },
639
+ {
640
+ value: 'yarn',
641
+ label: 'yarn'
642
+ },
643
+ {
644
+ value: 'pnpm',
645
+ label: 'pnpm'
646
+ }
647
+ ],
648
+ initialValue: 'npm'
649
+ });
650
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(selectedPackageManager)) {
651
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.warn('Package manager selection cancelled. Exiting.');
652
+ process.exit(0);
395
653
  }
396
- }
654
+ return selectedPackageManager;
655
+ }
656
+ }
657
+ function generateClientConfigContent(mode, backendURL, useEnvFile) {
658
+ let configContent = '';
659
+ const validModes = [
660
+ 'c15t',
661
+ 'offline',
662
+ 'custom'
663
+ ];
664
+ if (!validModes.includes(mode)) throw new Error(`Invalid mode: ${mode}. Valid modes are: ${validModes.join(', ')}`);
665
+ switch(mode){
666
+ case 'c15t':
667
+ configContent = `// c15t Client Configuration
668
+ import type { ConsentManagerOptions } from '@c15t/react';
669
+
670
+ export const c15tConfig = {
671
+ // Using hosted c15t (consent.io) or self-hosted instance
672
+ mode: 'c15t',
673
+ backendURL: ${useEnvFile ? 'process.env.NEXT_PUBLIC_C15T_URL' : `'${backendURL || 'https://your-instance.c15t.dev'}'`},
674
+
675
+ // Optional: Add callback functions for various events
676
+ callbacks: {
677
+ onConsentSet: (response) => {
678
+ console.log('Consent has been saved');
397
679
  }
398
- });
399
- return {
400
- code: schema.trim() === schemaPrisma.trim() ? "" : schema,
401
- fileName: filePath
402
- };
403
- };
404
- const getNewPrisma = (provider) => `generator client {
405
- provider = "prisma-client-js"
406
680
  }
681
+ } satisfies ConsentManagerOptions;
682
+
683
+ // Use in your app layout:
684
+ // <ConsentManagerProvider options={c15tConfig}>
685
+ // {children}
686
+ // <CookieBanner />
687
+ // <ConsentManagerDialog />
688
+ // </ConsentManagerProvider>
689
+ `;
690
+ break;
691
+ case 'offline':
692
+ configContent = `// c15t Client Configuration
693
+ import type { ConsentManagerOptions } from '@c15t/react';
694
+
695
+ export const c15tConfig = {
696
+ // Using offline mode for browser-based storage
697
+ mode: 'offline',
407
698
 
408
- datasource db {
409
- provider = "${provider}"
410
- url = ${provider === "sqlite" ? `"file:./dev.db"` : `env("DATABASE_URL")`}
411
- }`;
699
+ // Optional: Add callback functions for various events
700
+ callbacks: {
701
+ onConsentSet: (response) => {
702
+ console.log('Consent has been saved locally');
703
+ }
704
+ }
705
+ } satisfies ConsentManagerOptions;
412
706
 
413
- const adapters = {
414
- prisma: generatePrismaSchema,
415
- drizzle: generateDrizzleSchema,
416
- kysely: generateMigrations
417
- };
418
- const getGenerator = (opts) => {
419
- const adapter = opts.adapter;
420
- const generator = adapter.id in adapters ? adapters[adapter.id] : null;
421
- if (!generator) {
422
- logger.error(`${adapter.id} is not supported.`);
423
- process.exit(1);
707
+ // Use in your app layout:
708
+ // <ConsentManagerProvider options={c15tConfig}>
709
+ // {children}
710
+ // <CookieBanner />
711
+ // <ConsentManagerDialog />
712
+ // </ConsentManagerProvider>
713
+ `;
714
+ break;
715
+ case 'custom':
716
+ configContent = `// c15t Client Configuration
717
+ import type { ConsentManagerOptions } from '@c15t/react';
718
+ import { createCustomHandlers } from './consent-handlers';
719
+
720
+ export const c15tConfig = {
721
+ // Using custom mode for complete control
722
+ mode: 'custom',
723
+ endpointHandlers: createCustomHandlers(),
724
+
725
+ // Optional: Add callback functions for various events
726
+ callbacks: {
727
+ onConsentSet: (response) => {
728
+ console.log('Consent has been saved');
729
+ }
424
730
  }
425
- return generator(opts);
426
- };
731
+ } satisfies ConsentManagerOptions;
427
732
 
428
- function addSvelteKitEnvModules(aliases) {
429
- aliases["$env/dynamic/private"] = createDataUriModule(
430
- createDynamicEnvModule()
431
- );
432
- aliases["$env/dynamic/public"] = createDataUriModule(
433
- createDynamicEnvModule()
434
- );
435
- aliases["$env/static/private"] = createDataUriModule(
436
- createStaticEnvModule(filterPrivateEnv("PUBLIC_", ""))
437
- );
438
- aliases["$env/static/public"] = createDataUriModule(
439
- createStaticEnvModule(filterPublicEnv("PUBLIC_", ""))
440
- );
441
- }
442
- function createDataUriModule(module) {
443
- return `data:text/javascript;charset=utf-8,${encodeURIComponent(module)}`;
444
- }
445
- function createStaticEnvModule(env) {
446
- const declarations = Object.keys(env).filter((k) => validIdentifier.test(k) && !reserved.has(k)).map((k) => `export const ${k} = ${JSON.stringify(env[k])};`);
447
- return `
448
- ${declarations.join("\n")}
449
- // jiti dirty hack: .unknown
450
- `;
451
- }
452
- function createDynamicEnvModule() {
453
- return `
454
- export const env = process.env;
455
- // jiti dirty hack: .unknown
456
- `;
733
+ // Use in your app layout:
734
+ // <ConsentManagerProvider options={c15tConfig}>
735
+ // {children}
736
+ // <CookieBanner />
737
+ // <ConsentManagerDialog />
738
+ // </ConsentManagerProvider>
739
+
740
+ // Don't forget to implement your custom handlers in consent-handlers.ts!
741
+ `;
742
+ break;
743
+ }
744
+ return configContent;
457
745
  }
458
- function filterPrivateEnv(publicPrefix, privatePrefix) {
459
- return Object.fromEntries(
460
- Object.entries(process.env).filter(
461
- ([k]) => k.startsWith(privatePrefix) && (!k.startsWith(publicPrefix))
462
- )
463
- );
746
+ function generateBackendConfigContent(adapterChoice, connectionString, filePath) {
747
+ let adapterImport = '';
748
+ let adapterConfig = '';
749
+ switch(adapterChoice){
750
+ case 'kysely-postgres':
751
+ adapterImport = `import { kyselyAdapter } from '@c15t/backend/db/adapters/kysely';\nimport { PostgresDialect } from 'kysely';\nimport { Pool } from 'pg';`;
752
+ adapterConfig = `kyselyAdapter({
753
+ dialect: new PostgresDialect({
754
+ pool: new Pool({
755
+ connectionString: ${connectionString ? `"${connectionString}"` : 'process.env.DATABASE_URL || "postgresql://user:password@host:port/db"'}
756
+ })
757
+ })
758
+ })`;
759
+ break;
760
+ case 'kysely-sqlite':
761
+ adapterImport = `import { kyselyAdapter } from '@c15t/backend/db/adapters/kysely';\nimport { SqliteDialect } from 'kysely';\nimport Database from 'better-sqlite3';`;
762
+ adapterConfig = `kyselyAdapter({
763
+ dialect: new SqliteDialect({
764
+ database: new Database("${filePath || './db.sqlite'}")
765
+ })
766
+ })`;
767
+ break;
768
+ default:
769
+ adapterImport = "import { memoryAdapter } from '@c15t/backend/db/adapters/memory';";
770
+ adapterConfig = 'memoryAdapter({})';
771
+ break;
772
+ }
773
+ return `// c15t Backend Configuration
774
+ // Generated by c15t CLI onboarding
775
+
776
+ import { c15tInstance } from '@c15t/backend';
777
+ ${adapterImport}
778
+
779
+ // WARNING: Database connection strings often contain sensitive credentials.
780
+ // Consider using environment variables instead of hardcoding these values.
781
+
782
+ // Define your c15t instance
783
+ const instance = c15tInstance({
784
+ appName: 'Your App Name',
785
+ basePath: '/api/c15t',
786
+ database: ${adapterConfig},
787
+ trustedOrigins: ['http://localhost:3000'],
788
+ });
789
+
790
+ export default instance;
791
+ `;
464
792
  }
465
- function filterPublicEnv(publicPrefix, privatePrefix) {
466
- return Object.fromEntries(
467
- Object.entries(process.env).filter(
468
- ([k]) => k.startsWith(publicPrefix) && (privatePrefix === "")
469
- )
470
- );
793
+ function generateEnvFileContent(backendURL) {
794
+ return `# c15t Configuration
795
+ # Note: This URL is public and can be safely committed to version control
796
+ NEXT_PUBLIC_C15T_URL=${backendURL}
797
+ `;
471
798
  }
472
- const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
473
- const reserved = /* @__PURE__ */ new Set([
474
- "do",
475
- "if",
476
- "in",
477
- "for",
478
- "let",
479
- "new",
480
- "try",
481
- "var",
482
- "case",
483
- "else",
484
- "enum",
485
- "eval",
486
- "null",
487
- "this",
488
- "true",
489
- "void",
490
- "with",
491
- "await",
492
- "break",
493
- "catch",
494
- "class",
495
- "const",
496
- "false",
497
- "super",
498
- "throw",
499
- "while",
500
- "yield",
501
- "delete",
502
- "export",
503
- "import",
504
- "public",
505
- "return",
506
- "static",
507
- "switch",
508
- "typeof",
509
- "default",
510
- "extends",
511
- "finally",
512
- "package",
513
- "private",
514
- "continue",
515
- "debugger",
516
- "function",
517
- "arguments",
518
- "interface",
519
- "protected",
520
- "implements",
521
- "instanceof"
522
- ]);
523
-
524
- const configFileNames = ["c15t", "consent", "cmp"];
525
- const extensions = [
526
- ".js",
527
- ".jsx",
528
- ".ts",
529
- ".tsx",
530
- ".cjs",
531
- ".cts",
532
- ".mjs",
533
- ".mts",
534
- ".server.cjs",
535
- ".server.cts",
536
- ".server.js",
537
- ".server.jsx",
538
- ".server.mjs",
539
- ".server.mts",
540
- ".server.ts",
541
- ".server.tsx"
542
- ];
543
- let possiblePaths = configFileNames.flatMap(
544
- (name) => extensions.map((ext) => `${name}${ext}`)
545
- );
546
- const directories = [
547
- "",
548
- "lib/server/",
549
- "server/",
550
- "lib/",
551
- "utils/",
552
- "config/",
553
- "src/",
554
- "app/"
555
- ];
556
- possiblePaths = directories.flatMap(
557
- (dir) => possiblePaths.map((file) => `${dir}${file}`)
558
- );
559
- const monorepoSubdirs = ["packages/*", "apps/*"];
560
- function stripJsonComments(jsonString) {
561
- return jsonString.replace(
562
- /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g,
563
- (m, g) => g ? "" : m
564
- ).replace(/,(?=\s*[}\]])/g, "");
799
+ async function setupC15tMode(context, projectRoot, spinner, initialBackendURL, handleCancel) {
800
+ const { logger, cwd } = context;
801
+ const needsAccount = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
802
+ message: 'Do you need to create a consent.io account?',
803
+ initialValue: true
804
+ });
805
+ if (handleCancel?.(needsAccount)) throw new Error('Setup cancelled');
806
+ if (needsAccount) {
807
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.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');
808
+ const shouldOpen = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
809
+ message: 'Open browser to sign up for consent.io?',
810
+ initialValue: true
811
+ });
812
+ if (handleCancel?.(shouldOpen)) throw new Error('Setup cancelled');
813
+ if (shouldOpen) try {
814
+ await (0, __WEBPACK_EXTERNAL_MODULE_open__["default"])('https://consent.io/dashboard/register?ref=cli');
815
+ const enterPressed = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.text({
816
+ message: 'Press Enter once you have created your instance and have the backendURL'
817
+ });
818
+ if (handleCancel?.(enterPressed)) throw new Error('Setup cancelled');
819
+ } catch (error) {
820
+ logger.warn('Failed to open browser automatically. Please visit https://consent.io/dashboard/register manually.');
821
+ }
822
+ }
823
+ const backendURLSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.text({
824
+ message: 'Enter your consent.io instance URL:',
825
+ placeholder: 'https://your-instance.c15t.dev',
826
+ initialValue: initialBackendURL,
827
+ validate: (value)=>{
828
+ if (!value || '' === value) return 'URL is required';
829
+ try {
830
+ const url = new URL(value);
831
+ if (!url.hostname.endsWith('.c15t.dev')) return 'Please enter a valid *.c15t.dev URL';
832
+ } catch {
833
+ return 'Please enter a valid URL';
834
+ }
835
+ }
836
+ });
837
+ if (handleCancel?.(backendURLSelection)) throw new Error('Setup cancelled');
838
+ if (!backendURLSelection || '' === backendURLSelection) {
839
+ logger.error('A valid consent.io URL is required');
840
+ throw new Error('A valid consent.io URL is required');
841
+ }
842
+ const backendURL = backendURLSelection;
843
+ const useEnvFileSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
844
+ message: 'Store the backendURL in a .env file? (Recommended, URL is public)',
845
+ initialValue: true
846
+ });
847
+ if (handleCancel?.(useEnvFileSelection)) throw new Error('Setup cancelled');
848
+ const useEnvFile = useEnvFileSelection;
849
+ const clientConfigContent = generateClientConfigContent('c15t', backendURL, useEnvFile);
850
+ const configPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.config.ts');
851
+ spinner.start('Creating client configuration file...');
852
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(configPath, clientConfigContent);
853
+ spinner.stop(formatLogMessage('info', `Client configuration created: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, configPath))}`));
854
+ if (useEnvFile) {
855
+ const envPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, '.env.local');
856
+ const envExamplePath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, '.env.example');
857
+ spinner.start('Creating environment files...');
858
+ const envContent = generateEnvFileContent(backendURL);
859
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(envPath, envContent);
860
+ logger.info(` - Created environment file: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, envPath))}`);
861
+ const envExampleContent = '# c15t Configuration\nNEXT_PUBLIC_C15T_URL=https://your-instance.c15t.dev\n';
862
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(envExamplePath, envExampleContent);
863
+ logger.info(` - Created example env file: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, envExamplePath))}`);
864
+ spinner.stop(formatLogMessage('info', 'Environment files created.'));
865
+ }
866
+ return {
867
+ clientConfigContent,
868
+ backendURL,
869
+ usingEnvFile: useEnvFile
870
+ };
565
871
  }
566
- function getPathAliases(cwd) {
567
- const tsConfigPath = path.join(cwd, "tsconfig.json");
568
- if (!fs$1.existsSync(tsConfigPath)) {
569
- const jsConfigPath = path.join(cwd, "jsconfig.json");
570
- if (!fs$1.existsSync(jsConfigPath)) {
571
- return null;
572
- }
573
- return extractAliasesFromConfigFile(jsConfigPath, cwd);
574
- }
575
- return extractAliasesFromConfigFile(tsConfigPath, cwd);
872
+ async function setupCustomMode(context, projectRoot, spinner) {
873
+ const { logger, cwd } = context;
874
+ const clientConfigContent = generateClientConfigContent('custom');
875
+ const configPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.config.ts');
876
+ spinner.start('Creating client configuration file...');
877
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(configPath, clientConfigContent);
878
+ spinner.stop(formatLogMessage('info', `Client configuration created: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, configPath))}`));
879
+ logger.info(`Remember to implement custom endpoint handlers (see ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, configPath))}).`);
880
+ return {
881
+ clientConfigContent
882
+ };
576
883
  }
577
- function extractAliasesFromConfigFile(configPath, cwd) {
578
- try {
579
- const configContent = fs$1.readFileSync(configPath, "utf8");
580
- const strippedConfigContent = stripJsonComments(configContent);
581
- const config = JSON.parse(strippedConfigContent);
582
- const { paths = {}, baseUrl = "." } = config.compilerOptions || {};
583
- const result = {};
584
- const obj = Object.entries(paths);
585
- for (const [alias, aliasPaths] of obj) {
586
- for (const aliasedPath of aliasPaths) {
587
- const resolvedBaseUrl = path.join(cwd, baseUrl);
588
- const finalAlias = alias.slice(-1) === "*" ? alias.slice(0, -1) : alias;
589
- const finalAliasedPath = aliasedPath.slice(-1) === "*" ? aliasedPath.slice(0, -1) : aliasedPath;
590
- result[finalAlias || ""] = path.join(resolvedBaseUrl, finalAliasedPath);
591
- }
592
- }
593
- addSvelteKitEnvModules(result);
594
- return result;
595
- } catch (error) {
596
- logger.warn(`Error parsing config file ${configPath}`, error);
597
- return null;
598
- }
884
+ async function setupOfflineMode(context, projectRoot, spinner, handleCancel) {
885
+ const { logger, cwd } = context;
886
+ const clientConfigContent = generateClientConfigContent('offline', void 0, false);
887
+ const configPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.config.ts');
888
+ spinner.start('Creating client configuration file...');
889
+ try {
890
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(configPath, clientConfigContent);
891
+ spinner.stop(formatLogMessage('info', `Client configuration created: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, configPath))}`));
892
+ } catch (error) {
893
+ spinner.stop(formatLogMessage('error', `Failed to create configuration file: ${error instanceof Error ? error.message : 'Unknown error'}`));
894
+ throw error;
895
+ } finally{}
896
+ return {
897
+ clientConfigContent
898
+ };
599
899
  }
600
- const jitiOptions = (cwd) => {
601
- const alias = getPathAliases(cwd) || {};
602
- return {
603
- transformOptions: {
604
- babel: {
605
- presets: [
606
- [
607
- babelPresetTypescript,
608
- {
609
- isTSX: true,
610
- allExtensions: true
900
+ async function setupSelfHostedMode(context, projectRoot, spinner, handleCancel) {
901
+ const { logger, cwd } = context;
902
+ let backendConfigContent = null;
903
+ const dependencies = [
904
+ '@c15t/backend'
905
+ ];
906
+ const setupBackendSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
907
+ message: 'Set up the backend configuration now?',
908
+ initialValue: true
909
+ });
910
+ if (handleCancel?.(setupBackendSelection)) throw new Error('Setup cancelled');
911
+ const setupBackend = setupBackendSelection;
912
+ let adapterChoice = 'memory';
913
+ if (setupBackend) {
914
+ const adapterSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.select({
915
+ message: 'Choose a database adapter:',
916
+ initialValue: 'kysely-sqlite',
917
+ options: [
918
+ {
919
+ value: 'kysely-sqlite',
920
+ label: 'Kysely (SQLite)',
921
+ hint: 'Simple setups/local dev'
922
+ },
923
+ {
924
+ value: 'kysely-postgres',
925
+ label: 'Kysely (PostgreSQL)',
926
+ hint: 'Production'
927
+ },
928
+ {
929
+ value: 'memory',
930
+ label: 'Memory',
931
+ hint: 'Testing/development only'
932
+ }
933
+ ]
934
+ });
935
+ if (handleCancel?.(adapterSelection)) throw new Error('Setup cancelled');
936
+ adapterChoice = adapterSelection;
937
+ let connectionString;
938
+ let dbPath;
939
+ if ('kysely-postgres' === adapterChoice) {
940
+ const connectionStringSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.text({
941
+ message: 'Enter PostgreSQL connection string:',
942
+ placeholder: 'postgresql://user:pass@host:port/db'
943
+ });
944
+ if (handleCancel?.(connectionStringSelection)) throw new Error('Setup cancelled');
945
+ if (!connectionStringSelection || '' === connectionStringSelection) {
946
+ logger.error('A valid PostgreSQL connection string is required');
947
+ throw new Error('A valid PostgreSQL connection string is required');
611
948
  }
612
- ],
613
- [babelPresetReact, { runtime: "automatic" }]
614
- ]
615
- }
616
- },
617
- extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
618
- alias
619
- };
620
- };
621
- function extractOptionsFromConfig(config) {
622
- if (config.c15t && typeof config.c15t === "function") {
623
- return config.c15t();
624
- }
625
- if (config.default && typeof config.default === "function") {
626
- return config.default();
627
- }
628
- if (config.c15tInstance && typeof config.c15tInstance === "function") {
629
- return config.c15tInstance();
630
- }
631
- if (config.consent && typeof config.consent === "function") {
632
- return config.consent();
633
- }
634
- return config.c15t?.options || config.default?.options || config.c15tInstance?.options || config.instance?.options || config.consent?.options || config.config?.options || // Also check for direct exports of options objects
635
- (config.default && typeof config.default === "object" && "appName" in config.default ? config.default : null) || // Finally check for direct exports of the instance
636
- (config.c15t && typeof config.c15t === "object" && "appName" in config.c15t ? config.c15t : null) || null;
637
- }
638
- function findDirectories(cwd, patterns) {
639
- const results = [];
640
- for (const pattern of patterns) {
641
- if (pattern.includes("*")) {
642
- const [prefix, _] = pattern.split("*");
643
- const basePath = path.join(cwd, prefix);
644
- try {
645
- if (fs$1.existsSync(basePath)) {
646
- const entries = fs$1.readdirSync(basePath, { withFileTypes: true });
647
- for (const entry of entries) {
648
- if (entry.isDirectory()) {
649
- results.push(path.join(prefix, entry.name));
949
+ connectionString = connectionStringSelection;
950
+ } else if ('kysely-sqlite' === adapterChoice) {
951
+ const dbPathSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.text({
952
+ message: 'Enter path for SQLite database file:',
953
+ placeholder: './db.sqlite',
954
+ initialValue: './db.sqlite'
955
+ });
956
+ if (handleCancel?.(dbPathSelection)) throw new Error('Setup cancelled');
957
+ if (!dbPathSelection || '' === dbPathSelection) {
958
+ logger.error('A valid database path is required');
959
+ throw new Error('A valid database path is required');
650
960
  }
651
- }
961
+ dbPath = dbPathSelection;
652
962
  }
653
- } catch {
654
- }
655
- } else if (fs$1.existsSync(path.join(cwd, pattern)) && fs$1.statSync(path.join(cwd, pattern)).isDirectory()) {
656
- results.push(pattern);
963
+ backendConfigContent = generateBackendConfigContent(adapterChoice, connectionString, dbPath);
964
+ const backendConfigPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.backend.ts');
965
+ spinner.start('Creating backend configuration file...');
966
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(backendConfigPath, backendConfigContent);
967
+ spinner.stop(formatLogMessage('info', `Backend configuration created: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, backendConfigPath))}`));
657
968
  }
658
- }
659
- return results;
660
- }
661
- function validateConfig(config) {
662
- if (!config) {
663
- return false;
664
- }
665
- return typeof config === "object";
969
+ const clientConfigContent = generateClientConfigContent('c15t', '/api/c15t', false);
970
+ const configPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.config.ts');
971
+ spinner.start('Creating client configuration file...');
972
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].writeFile(configPath, clientConfigContent);
973
+ spinner.stop(formatLogMessage('info', `Client configuration created: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, configPath))}`));
974
+ return {
975
+ clientConfigContent,
976
+ backendConfigContent,
977
+ dependencies,
978
+ adapterChoice
979
+ };
666
980
  }
667
- async function getConfig({
668
- cwd,
669
- configPath
670
- }) {
671
- const foundPaths = [];
672
- const failedImports = [];
673
- try {
674
- let configFile = null;
675
- if (configPath) {
676
- const resolvedPath = path.join(cwd, configPath);
677
- try {
678
- if (!fs$1.existsSync(resolvedPath)) {
679
- throw new C15TError(
680
- `Configuration file not found: ${resolvedPath}
681
- Make sure the path is correct and the file exists.`
682
- );
981
+ async function startOnboarding(context, existingConfig) {
982
+ const { logger, cwd, telemetry } = context;
983
+ const handleCancel = (value)=>{
984
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(value)) {
985
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_EXITED, {
986
+ reason: 'user_cancelled',
987
+ stage: 'setup'
988
+ });
989
+ context.error.handleCancel('Configuration cancelled.');
990
+ return true;
683
991
  }
684
- foundPaths.push(resolvedPath);
685
- const { config } = await loadConfig({
686
- configFile: resolvedPath,
687
- dotenv: true,
688
- jitiOptions: jitiOptions(cwd)
992
+ return false;
993
+ };
994
+ const isUpdate = !!existingConfig;
995
+ logger.info(isUpdate ? 'Starting configuration update...' : 'Starting onboarding process...');
996
+ telemetry.trackEvent(isUpdate ? telemetry_TelemetryEventName.CONFIG_UPDATED : telemetry_TelemetryEventName.ONBOARDING_STARTED, {
997
+ isUpdate
998
+ });
999
+ telemetry.flushSync();
1000
+ logger.note(isUpdate ? "Let's update your c15t configuration." : `Welcome to c15t! Let's set up your consent management configuration.\nFirst, we'll help you choose the best storage approach for your needs.`, isUpdate ? 'Update Configuration' : 'First time setup');
1001
+ const projectRoot = await detectProjectRoot(cwd);
1002
+ if (projectRoot !== cwd) logger.debug(`Project root identified: ${projectRoot}`);
1003
+ else logger.warn('Could not determine project root, using current directory');
1004
+ const s = __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.spinner();
1005
+ let spinnerActive = false;
1006
+ try {
1007
+ const packageManager = await detectPackageManager(projectRoot);
1008
+ const { framework, hasReact } = await detectFramework(projectRoot);
1009
+ logger.debug(`Detected package manager: ${packageManager}`);
1010
+ if (framework) logger.debug(`Detected framework: ${framework}`);
1011
+ logger.debug(`React detected: ${hasReact}`);
1012
+ telemetry.trackEvent(telemetry_TelemetryEventName.CLI_ENVIRONMENT_DETECTED, {
1013
+ packageManager,
1014
+ framework: framework || 'unknown',
1015
+ hasReact
689
1016
  });
690
- configFile = extractOptionsFromConfig(config);
691
- if (!configFile) {
692
- throw new C15TError(
693
- // biome-ignore lint/style/useTemplate: keep it split so its easier to read
694
- `Found config file at ${resolvedPath} but couldn't extract c15t options.
695
- Make sure you're exporting c15t with one of these patterns:
696
- - export const c15t = c15tInstance({...})
697
- - export const consent = c15tInstance({...})
698
- - export const c15tInstance = c15tInstance({...})
699
- - export default c15tInstance({...})`
700
- );
1017
+ let initialStorageMode;
1018
+ if (isUpdate && existingConfig && 'mode' in existingConfig) {
1019
+ if (isClientOptions(existingConfig)) initialStorageMode = existingConfig.mode;
1020
+ else if (isC15TOptions(existingConfig)) initialStorageMode = 'c15t';
701
1021
  }
702
- } catch (e) {
703
- if (fs$1.existsSync(resolvedPath)) {
704
- failedImports.push(resolvedPath);
705
- if (e instanceof C15TError) {
706
- throw e;
707
- }
708
- throw new C15TError(
709
- // biome-ignore lint/style/useTemplate: keep it split so its easier to read
710
- `Config file found at ${resolvedPath} but failed to load.
711
- This usually happens because of import problems:
712
- - Check for invalid import paths
713
- - Ensure all dependencies are installed
714
- - Verify path aliases in tsconfig.json
715
-
716
- Error details: ${e instanceof Error ? e.message : String(e)}`
717
- );
1022
+ let storageModeSelection;
1023
+ try {
1024
+ storageModeSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.select({
1025
+ message: isUpdate ? `Select storage mode (current: ${initialStorageMode || 'unknown'}):` : 'How would you like to store consent decisions?',
1026
+ initialValue: initialStorageMode || 'c15t',
1027
+ options: [
1028
+ {
1029
+ value: 'c15t',
1030
+ label: 'Hosted c15t (consent.io)',
1031
+ hint: 'Recommended: Fully managed service'
1032
+ },
1033
+ {
1034
+ value: 'offline',
1035
+ label: 'Offline Mode',
1036
+ hint: 'Store in browser, no backend needed'
1037
+ },
1038
+ {
1039
+ value: 'self-hosted',
1040
+ label: 'Self-Hosted',
1041
+ hint: 'Run your own c15t backend'
1042
+ },
1043
+ {
1044
+ value: 'custom',
1045
+ label: 'Custom Implementation',
1046
+ hint: 'Full control over storage logic'
1047
+ }
1048
+ ]
1049
+ });
1050
+ } catch (error) {
1051
+ logger.error('Error selecting storage mode:', error);
1052
+ throw error;
718
1053
  }
719
- throw e;
720
- }
721
- }
722
- if (!configFile) {
723
- const searchDirs = [""];
724
- searchDirs.push(...findDirectories(cwd, monorepoSubdirs));
725
- for (const dir of searchDirs) {
726
- for (const possiblePath of possiblePaths) {
727
- const configPath2 = path.join(dir, possiblePath);
728
- const fullPath = path.join(cwd, configPath2);
729
- if (!fs$1.existsSync(fullPath)) {
730
- continue;
731
- }
732
- foundPaths.push(fullPath);
733
- try {
734
- const { config } = await loadConfig({
735
- configFile: configPath2,
736
- jitiOptions: jitiOptions(cwd)
1054
+ if (handleCancel(storageModeSelection)) return;
1055
+ const storageMode = storageModeSelection;
1056
+ logger.debug(`Selected storage mode: ${storageMode}`);
1057
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_STORAGE_MODE_SELECTED, {
1058
+ storageMode,
1059
+ isUpdate
1060
+ });
1061
+ const dependenciesToAdd = [];
1062
+ let installDepsConfirmed = false;
1063
+ let ranInstall = false;
1064
+ if (hasReact) dependenciesToAdd.push('@c15t/react');
1065
+ else {
1066
+ dependenciesToAdd.push('c15t');
1067
+ if (null === framework) logger.note(`No React framework detected, installing base c15t package.\nIf you're using React, you might need to manually install @c15t/react instead.`, formatLogMessage('warn', 'Package Selection'));
1068
+ }
1069
+ if ('c15t' === storageMode) {
1070
+ let initialBackendURL;
1071
+ if (isUpdate && existingConfig && isClientOptions(existingConfig) && existingConfig.backendURL) initialBackendURL = existingConfig.backendURL;
1072
+ const c15tResult = await setupC15tMode(context, projectRoot, s, initialBackendURL, handleCancel);
1073
+ c15tResult.clientConfigContent;
1074
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_C15T_MODE_CONFIGURED, {
1075
+ usingEnvFile: c15tResult.usingEnvFile,
1076
+ hasInitialBackendURL: !!initialBackendURL
737
1077
  });
738
- if (Object.keys(config).length > 0) {
739
- configFile = extractOptionsFromConfig(config);
740
- if (configFile && validateConfig(configFile)) {
741
- logger.info(`\u2705 Using c15t config from ${fullPath}`);
742
- break;
743
- }
744
- }
745
- } catch (e) {
746
- if (typeof e === "object" && e && "message" in e && typeof e.message === "string" && e.message.includes(
747
- "This module cannot be imported from a Client Component module"
748
- )) {
749
- throw new C15TError(
750
- // biome-ignore lint/style/useTemplate: keep it split so its easier to read
751
- `Found config file at ${fullPath}, but it imports 'server-only'.
752
- Please temporarily remove the 'server-only' import while using the CLI,
753
- and you can add it back afterwards.`
754
- );
1078
+ } else if ('offline' === storageMode) {
1079
+ const offlineResult = await setupOfflineMode(context, projectRoot, s, handleCancel);
1080
+ offlineResult.clientConfigContent;
1081
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_OFFLINE_MODE_CONFIGURED, {});
1082
+ } else if ('self-hosted' === storageMode) {
1083
+ const selfHostedResult = await setupSelfHostedMode(context, projectRoot, s, handleCancel);
1084
+ selfHostedResult.clientConfigContent;
1085
+ selfHostedResult.backendConfigContent;
1086
+ dependenciesToAdd.push(...selfHostedResult.dependencies);
1087
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_SELF_HOSTED_CONFIGURED, {
1088
+ databaseType: selfHostedResult.adapterChoice,
1089
+ dependencies: selfHostedResult.dependencies.join(',')
1090
+ });
1091
+ } else if ('custom' === storageMode) {
1092
+ const customResult = await setupCustomMode(context, projectRoot, s);
1093
+ customResult.clientConfigContent;
1094
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_CUSTOM_MODE_CONFIGURED, {});
1095
+ }
1096
+ let addDeps = false;
1097
+ if (dependenciesToAdd.length > 0) {
1098
+ const depsString = dependenciesToAdd.map((d)=>__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(d)).join(', ');
1099
+ const addDepsSelection = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
1100
+ message: `${isUpdate ? 'Update' : 'Add'} required dependencies using ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(packageManager)}? (${depsString})`,
1101
+ initialValue: true
1102
+ });
1103
+ if (handleCancel(addDepsSelection)) return;
1104
+ addDeps = addDepsSelection;
1105
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_DEPENDENCIES_CHOICE, {
1106
+ confirmed: addDeps,
1107
+ dependencies: dependenciesToAdd.join(','),
1108
+ packageManager
1109
+ });
1110
+ if (addDeps) {
1111
+ installDepsConfirmed = true;
1112
+ s.start(`Running ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(packageManager)} to add and install dependencies... (this might take a moment)`);
1113
+ spinnerActive = true;
1114
+ try {
1115
+ await addAndInstallDependenciesViaPM(projectRoot, dependenciesToAdd, packageManager);
1116
+ s.stop(`✅ Dependencies installed: ${dependenciesToAdd.map((d)=>__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(d)).join(', ')}`);
1117
+ spinnerActive = false;
1118
+ ranInstall = true;
1119
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_DEPENDENCIES_INSTALLED, {
1120
+ success: true,
1121
+ dependencies: dependenciesToAdd.join(','),
1122
+ packageManager
1123
+ });
1124
+ } catch (installError) {
1125
+ s.stop(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].yellow('⚠️ Dependency installation failed.'));
1126
+ spinnerActive = false;
1127
+ logger.error('Installation Error:', installError);
1128
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_DEPENDENCIES_INSTALLED, {
1129
+ success: false,
1130
+ error: installError instanceof Error ? installError.message : String(installError),
1131
+ dependencies: dependenciesToAdd.join(','),
1132
+ packageManager
1133
+ });
1134
+ const pmCommand = getManualInstallCommand(dependenciesToAdd, packageManager);
1135
+ logger.info(`Please try running '${pmCommand}' manually in ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, projectRoot))}.`);
1136
+ }
755
1137
  }
756
- failedImports.push(fullPath);
757
- }
758
1138
  }
759
- if (configFile) {
760
- break;
1139
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.step('Configuration Complete! Next Steps:');
1140
+ const configPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.config.ts');
1141
+ const backendConfigPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, 'c15t.backend.ts');
1142
+ const relativeConfigPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, configPath);
1143
+ const importPath = `./${relativeConfigPath.replace(/\\/g, '/').replace(/\.(ts|js|tsx|jsx)$/, '')}`;
1144
+ const importStatement = __WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(`import { c15tConfig } from '${importPath}';`);
1145
+ switch(storageMode){
1146
+ case 'c15t':
1147
+ {
1148
+ let steps = '1. Ensure your consent.io instance is configured (trusted origins etc).\n';
1149
+ try {
1150
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].access(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, '.env.local'));
1151
+ steps += ` 2. Verify ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('NEXT_PUBLIC_C15T_URL')} in ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(projectRoot, '.env.local')))}.\n`;
1152
+ } catch {
1153
+ steps += ` 2. Verify ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('backendURL')} in ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(relativeConfigPath)}.\n`;
1154
+ }
1155
+ steps += ` 3. Import and use configuration in your app: ${importStatement}`;
1156
+ logger.info(steps);
1157
+ break;
1158
+ }
1159
+ case 'offline':
1160
+ logger.info(`1. Import and use configuration in your app: ${importStatement}`);
1161
+ break;
1162
+ case 'self-hosted':
1163
+ {
1164
+ let steps = '';
1165
+ try {
1166
+ await __WEBPACK_EXTERNAL_MODULE_node_fs_promises_153e37e0__["default"].access(backendConfigPath);
1167
+ steps += `1. Configure database connection in ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].relative(cwd, backendConfigPath))}.\n`;
1168
+ steps += ' 2. Set up API routes using the exported backend instance.\n';
1169
+ } catch {
1170
+ steps += '1. Set up your c15t backend instance and API routes.\n';
1171
+ }
1172
+ steps += ` 3. Ensure ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('backendURL')} in ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(relativeConfigPath)} points to your API.\n`;
1173
+ steps += ` 4. Import and use client configuration: ${importStatement}`;
1174
+ logger.info(steps);
1175
+ break;
1176
+ }
1177
+ case 'custom':
1178
+ {
1179
+ const steps = `1. Implement your custom endpoint handlers (referenced in ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(relativeConfigPath)}).\n 2. Import and use configuration in your app: ${importStatement}`;
1180
+ logger.info(steps);
1181
+ break;
1182
+ }
761
1183
  }
762
- }
763
- }
764
- if (!configFile) {
765
- if (foundPaths.length > 0) {
766
- logger.error(
767
- `\u274C Found ${foundPaths.length} potential config files, but couldn't load any of them:`
768
- );
769
- for (const filePath of foundPaths.slice(0, 3)) {
770
- logger.error(` - ${filePath}`);
1184
+ if (installDepsConfirmed && !ranInstall) logger.info(` - ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].yellow('Dependency installation failed.')} Please check errors and install manually.`);
1185
+ else if (!addDeps && dependenciesToAdd.length > 0) {
1186
+ const pmCommand = getManualInstallCommand(dependenciesToAdd, packageManager);
1187
+ logger.warn(` - Run ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan(pmCommand)} to install required dependencies.`);
771
1188
  }
772
- if (foundPaths.length > 3) {
773
- logger.error(` - ...and ${foundPaths.length - 3} more`);
1189
+ logger.note(`${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bold('✨ Setup complete!')} Your c15t configuration is ready to use. \n
1190
+
1191
+ We're building c15t as an ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bold('open source')} project to make consent management more accessible.
1192
+ If you find this useful, we'd really appreciate a GitHub star - it helps others discover the project!`, '🎉 Thanks for using c15t');
1193
+ const shouldOpenGithub = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
1194
+ message: 'Would you like to star c15t on GitHub now?',
1195
+ initialValue: true
1196
+ });
1197
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(shouldOpenGithub)) {
1198
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_GITHUB_STAR, {
1199
+ action: 'cancelled'
1200
+ });
1201
+ return context.error.handleCancel('GitHub star prompt cancelled. Exiting onboarding.');
774
1202
  }
775
- if (failedImports.length > 0) {
776
- logger.error("\n\u2753 Common issues that prevent loading config files:");
777
- logger.error(" - Missing dependencies (check your package.json)");
778
- logger.error(
779
- " - Import path issues (check your import statements)"
780
- );
781
- logger.error(
782
- " - Path alias configuration (check your tsconfig.json)"
783
- );
784
- logger.error(
785
- " - Export format (make sure you're exporting c15t, c15tInstance, consent, or default)"
786
- );
1203
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_GITHUB_STAR, {
1204
+ action: shouldOpenGithub ? 'opened_browser' : 'declined'
1205
+ });
1206
+ if (shouldOpenGithub) try {
1207
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.note('Your support helps us continue improving c15t.\nThank you for being part of our community!', '⭐ Star Us on GitHub');
1208
+ await (0, __WEBPACK_EXTERNAL_MODULE_open__["default"])('https://github.com/c15t/c15t');
1209
+ logger.success('GitHub repository opened. Thank you for your support!');
1210
+ } catch (error) {
1211
+ logger.debug('Failed to open browser:', error);
1212
+ logger.info(`You can star us later by visiting: ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('https://github.com/c15t/c15t')}`);
1213
+ }
1214
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_COMPLETED, {
1215
+ success: true,
1216
+ storageMode,
1217
+ installDependencies: ranInstall
1218
+ });
1219
+ logger.success('🚀 Setup completed successfully!');
1220
+ } catch (error) {
1221
+ if (spinnerActive) s.stop(__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].red('Onboarding failed.'));
1222
+ if (!__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(error)) {
1223
+ logger.error('An unexpected error occurred during onboarding:', error);
1224
+ if (error instanceof Error && error.message) logger.error(`Error details: ${error.message}`);
1225
+ logger.failed('Onboarding process could not be completed.');
1226
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_COMPLETED, {
1227
+ success: false,
1228
+ error: error instanceof Error ? error.message : String(error)
1229
+ });
787
1230
  }
788
- throw new C15TError("Unable to load any c15t configuration file");
789
- }
790
- logger.error(
791
- "\u274C No c15t configuration files found in standard locations"
792
- );
793
- logger.info("\n\u{1F4DD} Create a c15t.ts file with your configuration:");
794
- logger.info(`
795
- import { c15tInstance } from '@c15t/backend';
796
-
797
- export const c15t = c15tInstance({
798
- appName: 'My App',
799
- basePath: '/api/c15t',
800
- // Add your configuration here
801
- });
802
- `);
803
- throw new C15TError(
804
- "No c15t config file found. Create a c15t.ts file or specify with --config"
805
- );
806
- }
807
- return configFile;
808
- } catch (e) {
809
- if (typeof e === "object" && e && "message" in e && typeof e.message === "string" && e.message.includes(
810
- "This module cannot be imported from a Client Component module"
811
- )) {
812
- logger.error(
813
- "\u274C Server-only import detected in config file\nPlease temporarily remove the 'server-only' import while using the CLI,\nand you can add it back afterwards."
814
- );
815
- process.exit(1);
816
- }
817
- if (e instanceof C15TError) {
818
- logger.error(`\u274C ${e.message}`);
819
- } else {
820
- logger.error(`\u274C Couldn't read your c15t configuration`);
821
- logger.error(` Error: ${e instanceof Error ? e.message : String(e)}`);
822
1231
  }
823
- if (failedImports.length > 0) {
824
- logger.info(
825
- "\n\u{1F4A1} Tip: If you're having import issues, try running with verbose logging:"
826
- );
827
- logger.info(" DEBUG=c15t* npx c15t@latest <command>");
1232
+ }
1233
+ async function setupGenerateEnvironment(context) {
1234
+ const { logger, flags, cwd, error, telemetry } = context;
1235
+ logger.debug('Setting up generate environment...');
1236
+ logger.debug('Context flags:', flags);
1237
+ logger.debug(`Context CWD: ${cwd}`);
1238
+ if (!(0, __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.existsSync)(cwd)) {
1239
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_FAILED, {
1240
+ error: `Directory ${cwd} does not exist`,
1241
+ stage: 'setup'
1242
+ });
1243
+ return error.handleError(new Error(`The directory "${cwd}" does not exist`), 'Generate setup failed');
828
1244
  }
829
- process.exit(1);
830
- }
1245
+ logger.debug('Attempting to load configuration...');
1246
+ const config = await context.config.loadConfig();
1247
+ if (!config) {
1248
+ logger.debug('No config found during setup, generate command will handle onboarding.');
1249
+ try {
1250
+ const memAdapter = await (0, __WEBPACK_EXTERNAL_MODULE__c15t_backend_pkgs_db_adapters_cee37d0f__.getAdapter)({
1251
+ appName: 'temp-for-setup',
1252
+ database: {
1253
+ adapter: 'memory'
1254
+ }
1255
+ });
1256
+ return {
1257
+ config: null,
1258
+ adapter: memAdapter
1259
+ };
1260
+ } catch (adapterError) {
1261
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_FAILED, {
1262
+ error: adapterError instanceof Error ? adapterError.message : String(adapterError),
1263
+ stage: 'adapter_initialization'
1264
+ });
1265
+ return error.handleError(adapterError, 'Failed to initialize default memory adapter');
1266
+ }
1267
+ }
1268
+ logger.debug('Config loaded, initializing adapter...');
1269
+ let adapter;
1270
+ try {
1271
+ adapter = await (0, __WEBPACK_EXTERNAL_MODULE__c15t_backend_pkgs_db_adapters_cee37d0f__.getAdapter)(config);
1272
+ logger.debug('Adapter initialized successfully');
1273
+ } catch (e) {
1274
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_FAILED, {
1275
+ error: e instanceof Error ? e.message : String(e),
1276
+ stage: 'adapter_initialization_with_config'
1277
+ });
1278
+ return error.handleError(e, 'Failed to initialize database adapter');
1279
+ }
1280
+ if (!adapter) {
1281
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_FAILED, {
1282
+ error: 'Adapter initialization returned undefined',
1283
+ stage: 'adapter_initialization_check'
1284
+ });
1285
+ return error.handleError(new Error('Adapter initialization returned undefined'), 'Database adapter could not be initialized');
1286
+ }
1287
+ logger.debug('Environment setup complete');
1288
+ return {
1289
+ config,
1290
+ adapter
1291
+ };
831
1292
  }
832
-
833
- async function generateAction(opts) {
834
- const options = z.object({
835
- cwd: z.string(),
836
- config: z.string().optional(),
837
- output: z.string().optional(),
838
- y: z.boolean().optional()
839
- }).parse(opts);
840
- const cwd = path.resolve(options.cwd);
841
- if (!existsSync(cwd)) {
842
- logger.error(`The directory "${cwd}" does not exist.`);
843
- process.exit(1);
844
- }
845
- const config = await getConfig({
846
- cwd,
847
- configPath: options.config
848
- });
849
- if (!config) {
850
- logger.error(
851
- "No configuration file found. Add a `c15t.ts` file to your project or pass the path to the configuration file using the `--config` flag."
852
- );
853
- return;
854
- }
855
- const adapter = await getAdapter(config).catch((e) => {
856
- logger.error(e.message);
857
- process.exit(1);
858
- });
859
- const spinner = yoctoSpinner({ text: "preparing schema..." }).start();
860
- const schema = await getGenerator({
861
- adapter,
862
- file: options.output,
863
- options: config
864
- });
865
- spinner.stop();
866
- if (!schema.code) {
867
- logger.info("Your schema is already up to date.");
868
- process.exit(0);
869
- }
870
- if (schema.append || schema.overwrite) {
871
- let confirm2 = options.y;
872
- if (!confirm2) {
873
- const response = await prompts({
874
- type: "confirm",
875
- name: "confirm",
876
- message: `The file ${schema.fileName} already exists. Do you want to ${chalk.yellow(
877
- `${schema.overwrite ? "overwrite" : "append"}`
878
- )} the schema to the file?`
879
- });
880
- confirm2 = response.confirm;
881
- }
882
- if (confirm2) {
883
- const exist = existsSync(path.join(cwd, schema.fileName));
884
- if (!exist) {
885
- await fs.mkdir(path.dirname(path.join(cwd, schema.fileName)), {
886
- recursive: true
1293
+ async function generate(context) {
1294
+ const { logger, error, telemetry } = context;
1295
+ logger.debug('Starting generate command...');
1296
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_STARTED, {});
1297
+ const setupResult = await setupGenerateEnvironment(context);
1298
+ let { config, adapter } = setupResult;
1299
+ if (config) {
1300
+ let currentMode = 'unknown';
1301
+ if (isClientOptions(config)) {
1302
+ if (config.mode) currentMode = config.mode;
1303
+ } else if (isC15TOptions(config)) currentMode = 'backend';
1304
+ telemetry.trackEvent(telemetry_TelemetryEventName.CONFIG_LOADED, {
1305
+ type: currentMode,
1306
+ exists: true
1307
+ });
1308
+ const shouldUpdate = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
1309
+ message: formatLogMessage('warn', `A c15t configuration already exists. Would you like to update it before generating? (${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].dim(`Current mode: ${currentMode}`)})`),
1310
+ initialValue: false
887
1311
  });
888
- }
889
- if (schema.overwrite) {
890
- await fs.writeFile(path.join(cwd, schema.fileName), schema.code);
891
- } else {
892
- await fs.appendFile(path.join(cwd, schema.fileName), schema.code);
893
- }
894
- logger.success(
895
- `\u{1F680} Schema was ${schema.overwrite ? "overwritten" : "appended"} successfully!`
896
- );
897
- process.exit(0);
1312
+ if (!shouldUpdate) {
1313
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_COMPLETED, {
1314
+ success: false,
1315
+ reason: 'user_cancelled'
1316
+ });
1317
+ return error.handleCancel('Operation cancelled.');
1318
+ }
1319
+ if (shouldUpdate) {
1320
+ await startOnboarding(context, config);
1321
+ logger.debug('Reloading configuration after update...');
1322
+ const postUpdateResult = await setupGenerateEnvironment(context);
1323
+ if (!postUpdateResult.config) {
1324
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_FAILED, {
1325
+ error: 'Failed to load configuration after update'
1326
+ });
1327
+ return error.handleError(new Error('Failed to load configuration after update.'), 'Configuration Error');
1328
+ }
1329
+ config = postUpdateResult.config;
1330
+ postUpdateResult.adapter;
1331
+ logger.info('Configuration updated successfully.');
1332
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_COMPLETED, {
1333
+ success: true,
1334
+ configUpdated: true
1335
+ });
1336
+ } else {
1337
+ logger.debug('Proceeding with existing configuration.');
1338
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_COMPLETED, {
1339
+ success: true,
1340
+ configUpdated: false
1341
+ });
1342
+ }
898
1343
  } else {
899
- logger.error("Schema generation aborted.");
900
- process.exit(1);
1344
+ logger.info('No configuration found.');
1345
+ telemetry.trackEvent(telemetry_TelemetryEventName.CONFIG_LOADED, {
1346
+ exists: false
1347
+ });
1348
+ const shouldOnboard = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
1349
+ message: 'No c15t configuration found. Would you like to create one now?',
1350
+ initialValue: true
1351
+ });
1352
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(shouldOnboard) || !shouldOnboard) {
1353
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_COMPLETED, {
1354
+ success: false,
1355
+ reason: 'onboarding_declined'
1356
+ });
1357
+ return error.handleCancel('Configuration setup cancelled.');
1358
+ }
1359
+ await startOnboarding(context);
1360
+ logger.debug('Reloading configuration after onboarding...');
1361
+ const postOnboardResult = await setupGenerateEnvironment(context);
1362
+ if (!postOnboardResult.config) {
1363
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_FAILED, {
1364
+ error: 'Failed to load configuration even after onboarding'
1365
+ });
1366
+ return error.handleError(new Error('Failed to load configuration even after onboarding.'), 'Configuration Error');
1367
+ }
1368
+ config = postOnboardResult.config;
1369
+ postOnboardResult.adapter;
1370
+ logger.info('New configuration loaded successfully.');
1371
+ telemetry.trackEvent(telemetry_TelemetryEventName.GENERATE_COMPLETED, {
1372
+ success: true,
1373
+ newConfigCreated: true
1374
+ });
901
1375
  }
902
- }
903
- let confirm = options.y;
904
- if (!confirm) {
905
- const response = await prompts({
906
- type: "confirm",
907
- name: "confirm",
908
- message: `Do you want to generate the schema to ${chalk.yellow(
909
- schema.fileName
910
- )}?`
1376
+ }
1377
+ async function executeMigrations(context, runMigrationsFn) {
1378
+ const { logger, telemetry } = context;
1379
+ logger.info('Executing migrations...');
1380
+ const s = __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.spinner();
1381
+ s.start('Running migrations...');
1382
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_EXECUTED, {
1383
+ status: 'started'
911
1384
  });
912
- confirm = response.confirm;
913
- }
914
- if (!confirm) {
915
- logger.error("Schema generation aborted.");
916
- process.exit(1);
917
- }
918
- if (!options.output) {
919
- const dirExist = existsSync(path.dirname(path.join(cwd, schema.fileName)));
920
- if (!dirExist) {
921
- await fs.mkdir(path.dirname(path.join(cwd, schema.fileName)), {
922
- recursive: true
923
- });
1385
+ try {
1386
+ await runMigrationsFn();
1387
+ s.stop('Migrations completed successfully!');
1388
+ logger.success('🚀 Database migrated successfully');
1389
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_EXECUTED, {
1390
+ status: 'completed'
1391
+ });
1392
+ } catch (error) {
1393
+ logger.error('Migration failed.');
1394
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_EXECUTED, {
1395
+ status: 'failed',
1396
+ error: error instanceof Error ? error.message : String(error)
1397
+ });
1398
+ context.error.handleError(error, 'Error running migrations');
924
1399
  }
925
- }
926
- await fs.writeFile(
927
- options.output || path.join(cwd, schema.fileName),
928
- schema.code
929
- );
930
- logger.success("\u{1F680} Schema was generated successfully!");
931
- process.exit(0);
932
1400
  }
933
- const generate = new Command("generate").option(
934
- "-c, --cwd <cwd>",
935
- "the working directory. defaults to the current directory.",
936
- process.cwd()
937
- ).option(
938
- "--config <config>",
939
- "the path to the configuration file. defaults to the first configuration file found."
940
- ).option("--output <output>", "the file to output to the generated schema").option("-y, --y", "automatically answer yes to all prompts", false).action(generateAction);
941
-
942
- async function migrateAction(opts) {
943
- const options = z.object({
944
- cwd: z.string(),
945
- config: z.string().optional(),
946
- y: z.boolean().optional()
947
- }).parse(opts);
948
- const cwd = path.resolve(options.cwd);
949
- if (!existsSync(cwd)) {
950
- logger.error(`The directory "${cwd}" does not exist.`);
951
- process.exit(1);
952
- }
953
- const config = await getConfig({
954
- cwd,
955
- configPath: options.config
956
- });
957
- if (!config) {
958
- logger.error(
959
- "No configuration file found. Add a `c15t.ts` file to your project or pass the path to the configuration file using the `--config` flag."
960
- );
961
- return;
962
- }
963
- const db = await getAdapter(config);
964
- if (!db) {
965
- logger.error(
966
- "Invalid database configuration. Make sure you're not using adapters. Migrate command only works with built-in Kysely adapter."
967
- );
968
- process.exit(1);
969
- }
970
- if (db.id !== "kysely") {
971
- if (db.id === "prisma") {
972
- logger.error(
973
- "The migrate command only works with the built-in Kysely adapter. For Prisma, run `npx @c15t/cli generate` to create the schema, then use Prisma\u2019s migrate or push to apply it."
974
- );
975
- process.exit(0);
976
- }
977
- if (db.id === "drizzle") {
978
- logger.error(
979
- "The migrate command only works with the built-in Kysely adapter. For Drizzle, run `npx @c15t/cli generate` to create the schema, then use Drizzle\u2019s migrate or push to apply it."
980
- );
981
- process.exit(0);
982
- }
983
- logger.error("Migrate command isn't supported for this adapter.");
984
- process.exit(1);
985
- }
986
- const spinner = yoctoSpinner({ text: "preparing migration..." }).start();
987
- const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(config);
988
- if (!toBeAdded.length && !toBeCreated.length) {
989
- spinner.stop();
990
- logger.info("\u{1F680} No migrations needed.");
991
- process.exit(0);
992
- }
993
- spinner.stop();
994
- logger.info("\u{1F511} The migration will affect the following:");
995
- for (const table of [...toBeCreated, ...toBeAdded]) {
996
- console.log(
997
- "->",
998
- chalk.magenta(Object.keys(table.fields).join(", ")),
999
- chalk.white("fields on"),
1000
- chalk.yellow(`${table.table}`),
1001
- chalk.white("table.")
1002
- );
1003
- }
1004
- let migrate2 = options.y;
1005
- if (!migrate2) {
1006
- const response = await prompts({
1007
- type: "confirm",
1008
- name: "migrate",
1009
- message: "Are you sure you want to run these migrations?",
1010
- initial: false
1401
+ async function planMigrations(context, config, skipConfirmation) {
1402
+ const { logger } = context;
1403
+ logger.info('Planning migrations...');
1404
+ logger.debug('Config:', config);
1405
+ logger.debug(`Skip confirmation: ${skipConfirmation}`);
1406
+ const s = __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.spinner();
1407
+ s.start('Preparing migration plan...');
1408
+ let migrationData;
1409
+ try {
1410
+ migrationData = await (0, __WEBPACK_EXTERNAL_MODULE__c15t_backend_pkgs_migrations_80b6e3bd__.getMigrations)(config);
1411
+ logger.debug('Migration data:', migrationData);
1412
+ } catch (err) {
1413
+ s.stop('Migration preparation failed.');
1414
+ if (err instanceof Error) logger.error(err.message);
1415
+ else logger.error(String(err));
1416
+ logger.failed('Migration planning failed');
1417
+ return {
1418
+ shouldRun: false,
1419
+ runMigrationsFn: null
1420
+ };
1421
+ }
1422
+ if (!migrationData) {
1423
+ s.stop('Could not retrieve migration data.');
1424
+ logger.failed('Migration planning failed');
1425
+ return {
1426
+ shouldRun: false,
1427
+ runMigrationsFn: null
1428
+ };
1429
+ }
1430
+ const { toBeAdded, toBeCreated, runMigrations } = migrationData;
1431
+ logger.debug('Migrations to be added:', toBeAdded);
1432
+ logger.debug('Migrations to be created:', toBeCreated);
1433
+ if (!toBeAdded.length && !toBeCreated.length) {
1434
+ s.stop('No migrations needed.');
1435
+ logger.info('🚀 Database is up to date');
1436
+ return {
1437
+ shouldRun: false,
1438
+ runMigrationsFn: null
1439
+ };
1440
+ }
1441
+ s.stop('Migration plan prepared.');
1442
+ logger.info('🔑 The following migrations will be applied:');
1443
+ for (const table of [
1444
+ ...toBeCreated,
1445
+ ...toBeAdded
1446
+ ]){
1447
+ const fields = Object.keys(table.fields).join(', ');
1448
+ const tableName = table.table;
1449
+ logger.info(` + Table ${tableName}: Add fields [${fields}]`);
1450
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.message(` ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('+')} Table ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].yellow(tableName)}: Add fields [${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].green(fields)}]`);
1451
+ }
1452
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.message('');
1453
+ let shouldMigrate = skipConfirmation;
1454
+ if (!shouldMigrate) {
1455
+ shouldMigrate = await context.confirm('Apply these migrations to the database?', false);
1456
+ logger.debug(`User confirmation: ${shouldMigrate}`);
1457
+ }
1458
+ if (!shouldMigrate) {
1459
+ logger.failed('Migration cancelled');
1460
+ return {
1461
+ shouldRun: false,
1462
+ runMigrationsFn: null
1463
+ };
1464
+ }
1465
+ logger.debug('Proceeding with migration execution');
1466
+ return {
1467
+ shouldRun: true,
1468
+ runMigrationsFn: runMigrations
1469
+ };
1470
+ }
1471
+ async function loadConfigAndOnboard(context) {
1472
+ const { logger } = context;
1473
+ logger.debug('Checking for existing configuration...');
1474
+ let config;
1475
+ try {
1476
+ config = await context.config.loadConfig();
1477
+ } catch (error) {
1478
+ return context.error.handleError(error, 'Unexpected error during configuration loading');
1479
+ }
1480
+ if (!config) {
1481
+ logger.info('No config found, starting onboarding.');
1482
+ await startOnboarding(context);
1483
+ logger.debug('Exiting after triggering onboarding.');
1484
+ process.exit(0);
1485
+ }
1486
+ logger.debug('Configuration loaded successfully.');
1487
+ return config;
1488
+ }
1489
+ function validateAdapterIsKysely(context, adapter) {
1490
+ const { logger, error } = context;
1491
+ logger.debug('Validating adapter:', adapter);
1492
+ if (!adapter || 'kysely' !== adapter.id) {
1493
+ let message = 'Invalid or unsupported database configuration for migrate. Migrate command only works with built-in Kysely adapter.';
1494
+ if (adapter?.id === 'prisma') message = "The migrate command only works with the built-in Kysely adapter. For Prisma, run `npx @c15t/cli generate` to create the schema, then use Prisma's migrate or push to apply it.";
1495
+ else if (adapter?.id === 'drizzle') message = "The migrate command only works with the built-in Kysely adapter. For Drizzle, run `npx @c15t/cli generate` to create the schema, then use Drizzle's migrate or push to apply it.";
1496
+ error.handleError(new Error('Adapter validation failed: Not using Kysely'), message);
1497
+ }
1498
+ }
1499
+ async function setupEnvironment(context) {
1500
+ const { logger, flags, cwd, error } = context;
1501
+ logger.info('Setting up migration environment...');
1502
+ logger.debug('Flags:', flags);
1503
+ logger.debug(`Working directory: ${cwd}`);
1504
+ if (!(0, __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.existsSync)(cwd)) return error.handleError(new Error(`The directory "${cwd}" does not exist`), 'Migration setup failed');
1505
+ const config = await loadConfigAndOnboard(context);
1506
+ logger.debug('Config loaded:', config);
1507
+ let adapter;
1508
+ try {
1509
+ logger.debug('Initializing database adapter...');
1510
+ adapter = await (0, __WEBPACK_EXTERNAL_MODULE__c15t_backend_pkgs_db_adapters_cee37d0f__.getAdapter)(config);
1511
+ logger.debug('Adapter initialized:', adapter);
1512
+ } catch (e) {
1513
+ return error.handleError(e, 'Failed to initialize database adapter');
1514
+ }
1515
+ validateAdapterIsKysely(context, adapter);
1516
+ logger.info('✅ Environment setup complete');
1517
+ return {
1518
+ config,
1519
+ adapter: adapter
1520
+ };
1521
+ }
1522
+ async function migrate(context) {
1523
+ const { logger, flags, telemetry } = context;
1524
+ logger.info('Starting migration process...');
1525
+ logger.debug('Context:', context);
1526
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_STARTED, {
1527
+ skipConfirmation: true === flags.y
1011
1528
  });
1012
- migrate2 = response.migrate;
1013
- }
1014
- if (!migrate2) {
1015
- logger.info("Migration cancelled.");
1016
- process.exit(0);
1017
- }
1018
- spinner?.start("migrating...");
1019
- await runMigrations();
1020
- spinner.stop();
1021
- logger.info("\u{1F680} migration was completed successfully!");
1022
- process.exit(0);
1529
+ const skipConfirmation = flags.y;
1530
+ try {
1531
+ const { config } = await setupEnvironment(context);
1532
+ const planResult = await planMigrations(context, config, skipConfirmation);
1533
+ logger.debug('Plan result:', planResult);
1534
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_PLANNED, {
1535
+ shouldRun: planResult.shouldRun,
1536
+ hasMigrations: !!planResult.runMigrationsFn
1537
+ });
1538
+ if (planResult.shouldRun && planResult.runMigrationsFn) {
1539
+ await executeMigrations(context, planResult.runMigrationsFn);
1540
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_COMPLETED, {
1541
+ success: true
1542
+ });
1543
+ } else {
1544
+ logger.debug('Skipping migration execution based on plan result');
1545
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_COMPLETED, {
1546
+ success: true,
1547
+ reason: planResult.shouldRun ? 'no_migrations_needed' : 'user_cancelled'
1548
+ });
1549
+ }
1550
+ } catch (error) {
1551
+ telemetry.trackEvent(telemetry_TelemetryEventName.MIGRATION_FAILED, {
1552
+ error: error instanceof Error ? error.message : String(error)
1553
+ });
1554
+ context.error.handleError(error, 'An unexpected error occurred during the migration process');
1555
+ }
1023
1556
  }
1024
- const migrate = new Command("migrate").option(
1025
- "-c, --cwd <cwd>",
1026
- "the working directory. defaults to the current directory.",
1027
- process.cwd()
1028
- ).option(
1029
- "--config <config>",
1030
- "the path to the configuration file. defaults to the first configuration file found."
1031
- ).option(
1032
- "-y, --y",
1033
- "automatically accept and run migrations without prompting",
1034
- false
1035
- ).action(migrateAction);
1036
-
1037
- const generateSecret = new Command("secret").action(() => {
1038
- const secret = Crypto.randomBytes(32).toString("hex");
1039
- logger.info(`
1040
- Add the following to your .env file:
1041
- ${chalk.gray("# Auth Secret") + chalk.green(`
1042
- C15T_SECRET=${secret}`)}`);
1043
- });
1044
-
1045
- function getPackageInfo() {
1046
- const packageJsonPath = path.join("package.json");
1047
- return fs$2.readJSONSync(packageJsonPath);
1557
+ async function displayIntro(context, version) {
1558
+ const { logger } = context;
1559
+ logger.info(`${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bold('Welcome!')} Let's get you set up.`);
1560
+ logger.message('');
1561
+ let figletText = 'c15t';
1562
+ try {
1563
+ figletText = await new Promise((resolve)=>{
1564
+ __WEBPACK_EXTERNAL_MODULE_figlet__["default"].text('c15t', {
1565
+ font: 'Nancyj-Improved',
1566
+ horizontalLayout: 'default',
1567
+ verticalLayout: 'default',
1568
+ width: 80,
1569
+ whitespaceBreak: true
1570
+ }, (err, data)=>{
1571
+ if (err) {
1572
+ logger.debug('Failed to generate figlet text');
1573
+ resolve('c15t');
1574
+ } else resolve(data || 'c15t');
1575
+ });
1576
+ });
1577
+ } catch (error) {
1578
+ logger.debug('Error generating figlet text', error);
1579
+ }
1580
+ const customColor = {
1581
+ teal10: (text)=>`\x1b[38;2;10;80;70m${text}\x1b[0m`,
1582
+ teal20: (text)=>`\x1b[38;2;15;100;90m${text}\x1b[0m`,
1583
+ teal30: (text)=>`\x1b[38;2;20;120;105m${text}\x1b[0m`,
1584
+ teal40: (text)=>`\x1b[38;2;25;150;130m${text}\x1b[0m`,
1585
+ teal50: (text)=>`\x1b[38;2;30;170;150m${text}\x1b[0m`,
1586
+ teal75: (text)=>`\x1b[38;2;34;211;187m${text}\x1b[0m`,
1587
+ teal90: (text)=>`\x1b[38;2;45;225;205m${text}\x1b[0m`,
1588
+ teal100: (text)=>`\x1b[38;2;65;235;220m${text}\x1b[0m`
1589
+ };
1590
+ const lines = figletText.split('\n');
1591
+ const coloredLines = lines.map((line, index)=>{
1592
+ const position = index / (lines.length - 1);
1593
+ if (position < 0.1) return customColor.teal10(line);
1594
+ if (position < 0.2) return customColor.teal20(line);
1595
+ if (position < 0.3) return customColor.teal30(line);
1596
+ if (position < 0.4) return customColor.teal40(line);
1597
+ if (position < 0.5) return customColor.teal50(line);
1598
+ if (position < 0.65) return customColor.teal75(line);
1599
+ if (position < 0.8) return customColor.teal90(line);
1600
+ return customColor.teal100(line);
1601
+ });
1602
+ logger.message(coloredLines.join('\n'));
1048
1603
  }
1049
-
1050
- process.on("SIGINT", () => process.exit(0));
1051
- process.on("SIGTERM", () => process.exit(0));
1604
+ function createConfigManagement(context) {
1605
+ const { logger, error } = context;
1606
+ return {
1607
+ loadConfig: async ()=>{
1608
+ logger.debug('Attempting to load configuration...');
1609
+ try {
1610
+ const configResult = await getConfig(context);
1611
+ const config = configResult ?? null;
1612
+ logger.debug('Config loading result:', config);
1613
+ if (config) logger.debug('Configuration loaded successfully.');
1614
+ else logger.debug('No configuration found.');
1615
+ return config;
1616
+ } catch (err) {
1617
+ return error.handleError(err, 'Error loading configuration');
1618
+ }
1619
+ },
1620
+ requireConfig: async ()=>{
1621
+ const config = await context.config.loadConfig();
1622
+ if (!config) return error.handleError(new Error('Configuration required but not found'), 'Missing required configuration');
1623
+ return config;
1624
+ },
1625
+ getPathAliases: (configDir)=>{
1626
+ const cwd = configDir || context.cwd;
1627
+ const tsConfigPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cwd, 'tsconfig.json');
1628
+ const jsConfigPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cwd, 'jsconfig.json');
1629
+ const configPath = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].existsSync(tsConfigPath) ? tsConfigPath : __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].existsSync(jsConfigPath) ? jsConfigPath : null;
1630
+ if (!configPath) return null;
1631
+ try {
1632
+ return extractAliasesFromConfigFile(context, configPath, cwd);
1633
+ } catch (extractError) {
1634
+ logger.warn(`Error extracting path aliases from ${configPath}:`, extractError);
1635
+ return null;
1636
+ }
1637
+ }
1638
+ };
1639
+ }
1640
+ function stripJsonComments(jsonString) {
1641
+ return jsonString.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g)=>g ? '' : m).replace(/,(?=\s*[}\]])/g, '');
1642
+ }
1643
+ function extractAliasesFromConfigFile(context, configPath, cwd) {
1644
+ try {
1645
+ const configContent = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].readFileSync(configPath, 'utf8');
1646
+ const strippedConfigContent = stripJsonComments(configContent);
1647
+ const config = JSON.parse(strippedConfigContent);
1648
+ const { paths = {}, baseUrl = '.' } = config.compilerOptions || {};
1649
+ const result = {};
1650
+ const obj = Object.entries(paths);
1651
+ for (const [alias, aliasPaths] of obj)for (const aliasedPath of aliasPaths){
1652
+ const resolvedBaseUrl = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cwd, baseUrl);
1653
+ const finalAlias = '*' === alias.slice(-1) ? alias.slice(0, -1) : alias;
1654
+ const finalAliasedPath = '*' === aliasedPath.slice(-1) ? aliasedPath.slice(0, -1) : aliasedPath;
1655
+ result[finalAlias || ''] = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(resolvedBaseUrl, finalAliasedPath);
1656
+ }
1657
+ if (hasSvelteKit(cwd)) addSvelteKitEnvModules(result);
1658
+ return result;
1659
+ } catch (error) {
1660
+ context.logger.warn(`Error parsing config file ${configPath}`, error);
1661
+ return null;
1662
+ }
1663
+ }
1664
+ function hasSvelteKit(cwd) {
1665
+ try {
1666
+ const packageJsonPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cwd, 'package.json');
1667
+ if (!__WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].existsSync(packageJsonPath)) return false;
1668
+ const packageJson = JSON.parse(__WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].readFileSync(packageJsonPath, 'utf8'));
1669
+ const deps = {
1670
+ ...packageJson.dependencies,
1671
+ ...packageJson.devDependencies
1672
+ };
1673
+ return '@sveltejs/kit' in deps;
1674
+ } catch (error) {
1675
+ return false;
1676
+ }
1677
+ }
1678
+ function addSvelteKitEnvModules(aliases) {
1679
+ aliases['$app/'] = '$app/';
1680
+ aliases['$lib/'] = '$lib/';
1681
+ aliases['$env/'] = '$env/';
1682
+ aliases['$service-worker'] = '$service-worker';
1683
+ }
1684
+ function createErrorHandlers(context) {
1685
+ const { logger } = context;
1686
+ return {
1687
+ handleError: (error, message)=>{
1688
+ logger.error(message, error);
1689
+ if (error instanceof Error) logger.error(error.message);
1690
+ else logger.error(String(error));
1691
+ logger.failed(`${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].red('Operation failed unexpectedly.')}`);
1692
+ process.exit(1);
1693
+ },
1694
+ handleCancel: (message = 'Operation cancelled.')=>{
1695
+ logger.debug(`Handling cancellation: ${message}`);
1696
+ logger.failed(message);
1697
+ process.exit(0);
1698
+ }
1699
+ };
1700
+ }
1701
+ function createFileSystem(context) {
1702
+ const { logger, cwd } = context;
1703
+ return {
1704
+ getPackageInfo: ()=>{
1705
+ logger.debug('Reading package.json');
1706
+ const packageJsonPath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cwd, 'package.json');
1707
+ logger.debug(`package.json path: ${packageJsonPath}`);
1708
+ try {
1709
+ const packageInfo = __WEBPACK_EXTERNAL_MODULE_fs_extra_ce68a66b__["default"].readJSONSync(packageJsonPath);
1710
+ logger.debug('Successfully read package.json');
1711
+ return {
1712
+ name: packageInfo?.name || 'unknown',
1713
+ version: packageInfo?.version || 'unknown',
1714
+ ...packageInfo
1715
+ };
1716
+ } catch (error) {
1717
+ logger.error(`Error reading package.json at ${packageJsonPath}:`, error);
1718
+ return {
1719
+ name: 'unknown',
1720
+ version: 'unknown'
1721
+ };
1722
+ }
1723
+ }
1724
+ };
1725
+ }
1726
+ const globalFlags = [
1727
+ {
1728
+ names: [
1729
+ '--help',
1730
+ '-h'
1731
+ ],
1732
+ description: 'Show this help message.',
1733
+ type: 'special',
1734
+ expectsValue: false
1735
+ },
1736
+ {
1737
+ names: [
1738
+ '--version',
1739
+ '-v'
1740
+ ],
1741
+ description: 'Show the CLI version.',
1742
+ type: 'special',
1743
+ expectsValue: false
1744
+ },
1745
+ {
1746
+ names: [
1747
+ '--logger'
1748
+ ],
1749
+ description: 'Set log level (fatal, error, warn, info, debug).',
1750
+ type: 'string',
1751
+ expectsValue: true
1752
+ },
1753
+ {
1754
+ names: [
1755
+ '--config'
1756
+ ],
1757
+ description: 'Specify path to configuration file.',
1758
+ type: 'string',
1759
+ expectsValue: true
1760
+ },
1761
+ {
1762
+ names: [
1763
+ '-y'
1764
+ ],
1765
+ description: 'Skip confirmation prompts (use with caution).',
1766
+ type: 'boolean',
1767
+ expectsValue: false
1768
+ },
1769
+ {
1770
+ names: [
1771
+ '--no-telemetry'
1772
+ ],
1773
+ description: 'Disable telemetry data collection.',
1774
+ type: 'boolean',
1775
+ expectsValue: false
1776
+ }
1777
+ ];
1778
+ function parseCliArgs(rawArgs, commands) {
1779
+ const parsedFlags = {};
1780
+ const potentialCommandArgsAndUndefined = [];
1781
+ let commandName;
1782
+ const commandArgs = [];
1783
+ for (const flag of globalFlags){
1784
+ const primaryName = flag.names[0]?.replace(/^--/, '').replace(/^-/, '');
1785
+ if (primaryName) parsedFlags[primaryName] = 'boolean' === flag.type ? false : void 0;
1786
+ }
1787
+ for(let i = 0; i < rawArgs.length; i++){
1788
+ const arg = rawArgs[i];
1789
+ if ('string' != typeof arg) continue;
1790
+ let argIsFlagOrValue = false;
1791
+ for (const flag of globalFlags)if (flag.names.includes(arg)) {
1792
+ const primaryName = flag.names[0]?.replace(/^--/, '').replace(/^-/, '');
1793
+ if (primaryName) {
1794
+ argIsFlagOrValue = true;
1795
+ if ('boolean' === flag.type) parsedFlags[primaryName] = true;
1796
+ else if (flag.expectsValue) {
1797
+ const nextArg = rawArgs[i + 1];
1798
+ if (nextArg && !nextArg.startsWith('-')) {
1799
+ parsedFlags[primaryName] = nextArg;
1800
+ i++;
1801
+ } else __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.log.warn(formatLogMessage('warn', `Flag ${arg} expects a value, but none was found or the next item is a flag.`));
1802
+ } else parsedFlags[primaryName] = true;
1803
+ }
1804
+ break;
1805
+ }
1806
+ if (!argIsFlagOrValue) potentialCommandArgsAndUndefined.push(arg);
1807
+ }
1808
+ const potentialCommandArgs = potentialCommandArgsAndUndefined.filter((arg)=>'string' == typeof arg);
1809
+ commandName = potentialCommandArgs.find((arg)=>commands.some((cmd)=>cmd.name === arg));
1810
+ for (const arg of potentialCommandArgs)if (arg !== commandName) commandArgs.push(arg);
1811
+ return {
1812
+ commandName,
1813
+ commandArgs,
1814
+ parsedFlags
1815
+ };
1816
+ }
1817
+ function createUserInteraction(context) {
1818
+ const { logger, error } = context;
1819
+ return {
1820
+ confirm: async (message, initialValue)=>{
1821
+ logger.debug(`Confirm action: "${message}", Initial: ${initialValue}`);
1822
+ const confirmed = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.confirm({
1823
+ message,
1824
+ initialValue
1825
+ });
1826
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(confirmed)) {
1827
+ error.handleCancel();
1828
+ return false;
1829
+ }
1830
+ logger.debug(`Confirmation result: ${confirmed}`);
1831
+ return confirmed;
1832
+ }
1833
+ };
1834
+ }
1835
+ function createCliContext(rawArgs, cwd, commands) {
1836
+ const { commandName, commandArgs, parsedFlags } = parseCliArgs(rawArgs, commands);
1837
+ let desiredLogLevel = 'info';
1838
+ const levelArg = parsedFlags.logger;
1839
+ if ('string' == typeof levelArg) if (validLogLevels.includes(levelArg)) desiredLogLevel = levelArg;
1840
+ else console.warn(`[CLI Setup] Invalid log level '${levelArg}' provided via --logger. Using default 'info'.`);
1841
+ else if (true === levelArg) console.warn("[CLI Setup] --logger flag found but no level specified. Using default 'info'.");
1842
+ const logger = createCliLogger(desiredLogLevel);
1843
+ logger.debug(`Logger initialized with level: ${desiredLogLevel}`);
1844
+ const baseContext = {
1845
+ logger,
1846
+ flags: parsedFlags,
1847
+ commandName,
1848
+ commandArgs,
1849
+ cwd
1850
+ };
1851
+ const context = baseContext;
1852
+ context.error = createErrorHandlers(context);
1853
+ const userInteraction = createUserInteraction(context);
1854
+ context.confirm = userInteraction.confirm;
1855
+ context.config = createConfigManagement(context);
1856
+ context.fs = createFileSystem(context);
1857
+ const telemetryDisabled = true === parsedFlags['no-telemetry'];
1858
+ context.telemetry = createTelemetry({
1859
+ disabled: telemetryDisabled,
1860
+ defaultProperties: {
1861
+ cliVersion: context.fs.getPackageInfo().version
1862
+ },
1863
+ logger: context.logger
1864
+ });
1865
+ context.telemetry.setLogLevel(desiredLogLevel);
1866
+ if (telemetryDisabled) logger.debug('Telemetry is disabled by user preference');
1867
+ else logger.debug('Telemetry initialized');
1868
+ logger.debug('CLI context fully initialized with all utilities');
1869
+ return context;
1870
+ }
1871
+ const src_commands = [
1872
+ {
1873
+ name: 'generate',
1874
+ label: 'generate',
1875
+ hint: 'Generate schema/code',
1876
+ description: 'Generate schema/code based on your c15t config.',
1877
+ action: (context)=>generate(context)
1878
+ },
1879
+ {
1880
+ name: 'migrate',
1881
+ label: 'migrate',
1882
+ hint: 'Run database migrations',
1883
+ description: 'Run database migrations based on your c15t config.',
1884
+ action: (context)=>migrate(context)
1885
+ },
1886
+ {
1887
+ name: 'github',
1888
+ label: 'Github',
1889
+ hint: 'Star us on GitHub',
1890
+ description: 'Open our GitHub repository to give us a star.',
1891
+ action: async (context)=>{
1892
+ const { logger } = context;
1893
+ logger.note(`We're building c15t as an ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].bold('open source')} project to make consent management more accessible.\nIf you find this useful, we'd really appreciate a GitHub star - it helps others discover the project!`, '⭐ Star Us on GitHub');
1894
+ await (0, __WEBPACK_EXTERNAL_MODULE_open__["default"])('https://github.com/c15t/c15t');
1895
+ logger.success('Thank you for your support!');
1896
+ }
1897
+ },
1898
+ {
1899
+ name: 'docs',
1900
+ label: 'c15t docs',
1901
+ hint: 'Open documentation',
1902
+ description: 'Open the c15t documentation in your browser.',
1903
+ action: async (context)=>{
1904
+ const { logger } = context;
1905
+ await (0, __WEBPACK_EXTERNAL_MODULE_open__["default"])('https://c15t.com/docs?ref=cli');
1906
+ logger.success('Documentation opened in your browser.');
1907
+ }
1908
+ }
1909
+ ];
1052
1910
  async function main() {
1053
- const program = new Command("c15t");
1054
- const packageInfo = await getPackageInfo();
1055
- program.addCommand(migrate).addCommand(generate).addCommand(generateSecret).version(packageInfo.version || "1.1.2").description("c15t CLI");
1056
- program.parse();
1911
+ const rawArgs = process.argv.slice(2);
1912
+ const cwd = process.cwd();
1913
+ const context = createCliContext(rawArgs, cwd, src_commands);
1914
+ const { logger, flags, commandName, commandArgs, error, telemetry } = context;
1915
+ const packageInfo = context.fs.getPackageInfo();
1916
+ const version = packageInfo.version;
1917
+ if (!telemetry.isDisabled()) logger.note(`c15t collects anonymous usage data to help improve the CLI.
1918
+ This data is not personally identifiable and helps us prioritize features.
1919
+ To disable telemetry, use the ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('--no-telemetry')}
1920
+ flag or set ${__WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyan('C15T_TELEMETRY_DISABLED=1')} in your environment.`, `${formatLogMessage('info', 'Telemetry Notice')}`);
1921
+ try {
1922
+ telemetry.trackEvent(telemetry_TelemetryEventName.CLI_INVOKED, {
1923
+ version,
1924
+ nodeVersion: process.version,
1925
+ platform: process.platform
1926
+ });
1927
+ telemetry.flushSync();
1928
+ } catch (error) {
1929
+ logger.debug('Failed to track CLI invocation:', error);
1930
+ }
1931
+ if (flags.version) {
1932
+ logger.debug('Version flag detected');
1933
+ logger.message(`c15t CLI version ${version}`);
1934
+ telemetry.trackEvent(telemetry_TelemetryEventName.VERSION_DISPLAYED, {
1935
+ version
1936
+ });
1937
+ telemetry.flushSync();
1938
+ await telemetry.shutdown();
1939
+ process.exit(0);
1940
+ }
1941
+ if (flags.help) {
1942
+ logger.debug('Help flag detected. Displaying help and exiting.');
1943
+ telemetry.trackEvent(telemetry_TelemetryEventName.HELP_DISPLAYED, {
1944
+ version
1945
+ });
1946
+ telemetry.flushSync();
1947
+ showHelpMenu(context, version, src_commands, globalFlags);
1948
+ await telemetry.shutdown();
1949
+ process.exit(0);
1950
+ }
1951
+ logger.debug('Raw process arguments:', process.argv);
1952
+ logger.debug('Parsed command name:', commandName);
1953
+ logger.debug('Parsed command args:', commandArgs);
1954
+ logger.debug('Parsed global flags:', flags);
1955
+ await displayIntro(context, version);
1956
+ logger.debug(`Current working directory: ${cwd}`);
1957
+ logger.debug(`Config path flag: ${flags.config}`);
1958
+ let clientConfig;
1959
+ let backendConfig;
1960
+ try {
1961
+ const loadedConfig = await getConfig(context);
1962
+ if (loadedConfig) if (isClientOptions(loadedConfig)) clientConfig = loadedConfig;
1963
+ else if (isC15TOptions(loadedConfig)) backendConfig = loadedConfig;
1964
+ else logger.warn('Loaded configuration is of an unknown type.');
1965
+ if (loadedConfig) telemetry.trackEvent(telemetry_TelemetryEventName.CONFIG_LOADED, {
1966
+ configType: clientConfig ? 'client' : backendConfig ? 'backend' : 'unknown',
1967
+ hasBackend: Boolean(backendConfig)
1968
+ });
1969
+ } catch (loadError) {
1970
+ telemetry.trackEvent(telemetry_TelemetryEventName.CONFIG_ERROR, {
1971
+ error: loadError instanceof Error ? loadError.message : String(loadError)
1972
+ });
1973
+ return error.handleError(loadError, 'An unexpected error occurred during configuration loading');
1974
+ }
1975
+ if (!clientConfig) {
1976
+ telemetry.trackEvent(telemetry_TelemetryEventName.ONBOARDING_STARTED, {});
1977
+ await startOnboarding(context);
1978
+ await telemetry.shutdown();
1979
+ return;
1980
+ }
1981
+ const coloredConsentIo = __WEBPACK_EXTERNAL_MODULE_picocolors__["default"].cyanBright('consent.io');
1982
+ const backendStatus = backendConfig ? 'Backend configuration loaded' : `Using ${coloredConsentIo} for your c15t deployment`;
1983
+ logger.info(`Client configuration successfully loaded and validated \n ${backendStatus}`);
1984
+ logger.debug('Client config details:', clientConfig);
1985
+ if (backendConfig) logger.debug('Backend config details:', backendConfig);
1986
+ try {
1987
+ if (commandName) {
1988
+ const command = src_commands.find((cmd)=>cmd.name === commandName);
1989
+ if (command) {
1990
+ logger.info(`Executing command: ${command.name}`);
1991
+ telemetry.trackCommand(command.name, commandArgs, flags);
1992
+ await command.action(context);
1993
+ telemetry.trackEvent(telemetry_TelemetryEventName.COMMAND_SUCCEEDED, {
1994
+ command: command.name,
1995
+ executionTime: Date.now() - performance.now()
1996
+ });
1997
+ telemetry.flushSync();
1998
+ } else {
1999
+ logger.error(`Unknown command: ${commandName}`);
2000
+ telemetry.trackEvent(telemetry_TelemetryEventName.COMMAND_UNKNOWN, {
2001
+ unknownCommand: commandName
2002
+ });
2003
+ telemetry.flushSync();
2004
+ logger.info('Run c15t --help to see available commands.');
2005
+ await telemetry.shutdown();
2006
+ process.exit(1);
2007
+ }
2008
+ } else {
2009
+ logger.debug('No command specified, entering interactive selection.');
2010
+ telemetry.trackEvent(telemetry_TelemetryEventName.INTERACTIVE_MENU_OPENED, {});
2011
+ const promptOptions = src_commands.map((cmd)=>({
2012
+ value: cmd.name,
2013
+ label: cmd.label,
2014
+ hint: cmd.hint
2015
+ }));
2016
+ promptOptions.push({
2017
+ value: 'exit',
2018
+ label: 'exit',
2019
+ hint: 'Close the CLI'
2020
+ });
2021
+ const selectedCommandName = await __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.select({
2022
+ message: formatLogMessage('info', 'Which command would you like to run?'),
2023
+ options: promptOptions
2024
+ });
2025
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(selectedCommandName) || 'exit' === selectedCommandName) {
2026
+ logger.debug('Interactive selection cancelled or exit chosen.');
2027
+ telemetry.trackEvent(telemetry_TelemetryEventName.INTERACTIVE_MENU_EXITED, {
2028
+ action: __WEBPACK_EXTERNAL_MODULE__clack_prompts_3cae1695__.isCancel(selectedCommandName) ? 'cancelled' : 'exit'
2029
+ });
2030
+ context.error.handleCancel('Operation cancelled.');
2031
+ } else {
2032
+ const selectedCommand = src_commands.find((cmd)=>cmd.name === selectedCommandName);
2033
+ if (selectedCommand) {
2034
+ logger.debug(`User selected command: ${selectedCommand.name}`);
2035
+ telemetry.trackCommand(selectedCommand.name, [], flags);
2036
+ await selectedCommand.action(context);
2037
+ telemetry.trackEvent(telemetry_TelemetryEventName.COMMAND_SUCCEEDED, {
2038
+ command: selectedCommand.name,
2039
+ executionTime: Date.now() - performance.now()
2040
+ });
2041
+ telemetry.flushSync();
2042
+ } else {
2043
+ telemetry.trackEvent(telemetry_TelemetryEventName.COMMAND_UNKNOWN, {
2044
+ unknownCommand: String(selectedCommandName)
2045
+ });
2046
+ telemetry.flushSync();
2047
+ error.handleError(new Error(`Command '${selectedCommandName}' not found`), 'An internal error occurred');
2048
+ }
2049
+ }
2050
+ }
2051
+ logger.debug('Command execution completed');
2052
+ telemetry.trackEvent(telemetry_TelemetryEventName.CLI_COMPLETED, {
2053
+ success: true
2054
+ });
2055
+ telemetry.flushSync();
2056
+ } catch (executionError) {
2057
+ telemetry.trackEvent(telemetry_TelemetryEventName.COMMAND_FAILED, {
2058
+ command: commandName,
2059
+ error: executionError instanceof Error ? executionError.message : String(executionError)
2060
+ });
2061
+ telemetry.flushSync();
2062
+ error.handleError(executionError, 'An unexpected error occurred during command execution');
2063
+ }
2064
+ await telemetry.shutdown();
1057
2065
  }
1058
2066
  main();
2067
+ export { main };