@revealui/setup 0.2.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.
package/dist/index.js ADDED
@@ -0,0 +1,473 @@
1
+ // src/environment/generators.ts
2
+ import { randomBytes } from "crypto";
3
+ function generateSecret(length = 32) {
4
+ return randomBytes(length).toString("hex");
5
+ }
6
+ function generatePassword(length = 16) {
7
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
8
+ let password = "";
9
+ const randomValues = randomBytes(length);
10
+ for (let i = 0; i < length; i++) {
11
+ password += chars[randomValues[i] % chars.length];
12
+ }
13
+ return password;
14
+ }
15
+ function updateEnvValue(content, key, value) {
16
+ const regex = new RegExp(`^${key}=.*$`, "m");
17
+ if (regex.test(content)) {
18
+ return content.replace(regex, `${key}=${value}`);
19
+ }
20
+ return `${content.trimEnd()}
21
+ ${key}=${value}
22
+ `;
23
+ }
24
+ function parseEnvContent(content) {
25
+ const env = {};
26
+ const lines = content.split("\n");
27
+ for (const line of lines) {
28
+ const trimmed = line.trim();
29
+ if (!trimmed || trimmed.startsWith("#")) continue;
30
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
31
+ if (match) {
32
+ const [, key, value] = match;
33
+ env[key.trim()] = value.trim();
34
+ }
35
+ }
36
+ return env;
37
+ }
38
+
39
+ // src/environment/setup.ts
40
+ import { copyFile, readFile, writeFile } from "fs/promises";
41
+ import { join } from "path";
42
+ import inquirer from "inquirer";
43
+
44
+ // src/utils/logger.ts
45
+ var LOG_LEVELS = {
46
+ debug: 0,
47
+ info: 1,
48
+ warn: 2,
49
+ error: 3,
50
+ silent: 4
51
+ };
52
+ function getColors(enabled) {
53
+ if (!enabled) {
54
+ return {
55
+ reset: "",
56
+ red: "",
57
+ green: "",
58
+ yellow: "",
59
+ blue: "",
60
+ cyan: "",
61
+ magenta: "",
62
+ dim: "",
63
+ bold: ""
64
+ };
65
+ }
66
+ return {
67
+ reset: "\x1B[0m",
68
+ red: "\x1B[31m",
69
+ green: "\x1B[32m",
70
+ yellow: "\x1B[33m",
71
+ blue: "\x1B[34m",
72
+ cyan: "\x1B[36m",
73
+ magenta: "\x1B[35m",
74
+ dim: "\x1B[2m",
75
+ bold: "\x1B[1m"
76
+ };
77
+ }
78
+ function createLogger(options = {}) {
79
+ const {
80
+ level = process.env.LOG_LEVEL || "info",
81
+ prefix = "",
82
+ colors = process.env.FORCE_COLOR !== "0" && process.stdout.isTTY !== false,
83
+ timestamps = false
84
+ } = options;
85
+ const currentLevel = LOG_LEVELS[level] ?? LOG_LEVELS.info;
86
+ const c = getColors(colors);
87
+ function shouldLog(msgLevel) {
88
+ return LOG_LEVELS[msgLevel] >= currentLevel;
89
+ }
90
+ function formatPrefix() {
91
+ const parts = [];
92
+ if (timestamps) {
93
+ parts.push(`${c.dim}[${(/* @__PURE__ */ new Date()).toISOString()}]${c.reset}`);
94
+ }
95
+ if (prefix) {
96
+ parts.push(`${c.cyan}[${prefix}]${c.reset}`);
97
+ }
98
+ return parts.length > 0 ? `${parts.join(" ")} ` : "";
99
+ }
100
+ function formatArgs(args) {
101
+ if (args.length === 0) return "";
102
+ return " " + args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
103
+ }
104
+ return {
105
+ debug(msg, ...args) {
106
+ if (!shouldLog("debug")) return;
107
+ console.log(`${formatPrefix()}${c.dim}[DEBUG]${c.reset} ${msg}${formatArgs(args)}`);
108
+ },
109
+ info(msg, ...args) {
110
+ if (!shouldLog("info")) return;
111
+ console.log(`${formatPrefix()}${c.blue}[INFO]${c.reset} ${msg}${formatArgs(args)}`);
112
+ },
113
+ warn(msg, ...args) {
114
+ if (!shouldLog("warn")) return;
115
+ console.warn(`${formatPrefix()}${c.yellow}[WARN]${c.reset} ${msg}${formatArgs(args)}`);
116
+ },
117
+ error(msg, ...args) {
118
+ if (!shouldLog("error")) return;
119
+ console.error(`${formatPrefix()}${c.red}[ERROR]${c.reset} ${msg}${formatArgs(args)}`);
120
+ },
121
+ success(msg, ...args) {
122
+ if (!shouldLog("info")) return;
123
+ console.log(`${formatPrefix()}${c.green}[OK]${c.reset} ${msg}${formatArgs(args)}`);
124
+ },
125
+ warning(msg, ...args) {
126
+ this.warn(msg, ...args);
127
+ },
128
+ header(msg) {
129
+ if (!shouldLog("info")) return;
130
+ const line = "=".repeat(msg.length + 4);
131
+ console.log(`
132
+ ${c.cyan}${line}`);
133
+ console.log(`| ${msg} |`);
134
+ console.log(`${line}${c.reset}
135
+ `);
136
+ },
137
+ divider() {
138
+ if (!shouldLog("info")) return;
139
+ console.log(`${c.dim}${"\u2500".repeat(60)}${c.reset}`);
140
+ },
141
+ table(data) {
142
+ if (!shouldLog("info")) return;
143
+ console.table(data);
144
+ },
145
+ group(label) {
146
+ if (!shouldLog("info")) return;
147
+ console.group(`${c.bold}${label}${c.reset}`);
148
+ },
149
+ groupEnd() {
150
+ if (!shouldLog("info")) return;
151
+ console.groupEnd();
152
+ },
153
+ progress(current, total, label = "") {
154
+ if (!shouldLog("info")) return;
155
+ const percent = Math.round(current / total * 100);
156
+ const filled = Math.round(percent / 5);
157
+ const empty = 20 - filled;
158
+ const bar = `${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}`;
159
+ const labelText = label ? ` ${label}` : "";
160
+ process.stdout.write(`\r${c.cyan}${bar}${c.reset} ${percent}%${labelText}`);
161
+ if (current === total) {
162
+ console.log();
163
+ }
164
+ }
165
+ };
166
+ }
167
+ function handleASTParseError(filePath, error, logger2) {
168
+ const message = error instanceof Error ? error.message : String(error);
169
+ logger2.warning(`AST Parse Error in ${filePath}: ${message}`);
170
+ }
171
+ var logger = createLogger();
172
+
173
+ // src/validators/env.ts
174
+ function validateEnv(required, env) {
175
+ const missing = [];
176
+ const invalid = [];
177
+ for (const variable of required) {
178
+ const value = env[variable.name];
179
+ if (variable.required && (!value || value.trim() === "")) {
180
+ missing.push(variable.name);
181
+ continue;
182
+ }
183
+ if (value && variable.validator && !variable.validator(value)) {
184
+ invalid.push(variable.name);
185
+ }
186
+ }
187
+ return {
188
+ valid: missing.length === 0 && invalid.length === 0,
189
+ missing,
190
+ invalid
191
+ };
192
+ }
193
+ var validators = {
194
+ /**
195
+ * Validates PostgreSQL connection string format
196
+ */
197
+ postgresUrl: (value) => {
198
+ try {
199
+ const url = new URL(value);
200
+ return url.protocol === "postgresql:" || url.protocol === "postgres:";
201
+ } catch {
202
+ return false;
203
+ }
204
+ },
205
+ /**
206
+ * Validates Stripe secret key format
207
+ */
208
+ stripeSecretKey: (value) => {
209
+ return value.startsWith("sk_test_") || value.startsWith("sk_live_");
210
+ },
211
+ /**
212
+ * Validates Stripe publishable key format
213
+ */
214
+ stripePublishableKey: (value) => {
215
+ return value.startsWith("pk_test_") || value.startsWith("pk_live_");
216
+ },
217
+ /**
218
+ * Validates URL format
219
+ */
220
+ url: (value) => {
221
+ try {
222
+ new URL(value);
223
+ return true;
224
+ } catch {
225
+ return false;
226
+ }
227
+ },
228
+ /**
229
+ * Validates minimum length
230
+ */
231
+ minLength: (min) => (value) => {
232
+ return value.length >= min;
233
+ },
234
+ /**
235
+ * Validates email format
236
+ */
237
+ email: (value) => {
238
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
239
+ }
240
+ };
241
+ var REQUIRED_ENV_VARS = [
242
+ {
243
+ name: "REVEALUI_SECRET",
244
+ description: "Secret key for JWT tokens and session encryption (min 32 chars)",
245
+ required: true,
246
+ validator: validators.minLength(32)
247
+ },
248
+ {
249
+ name: "POSTGRES_URL",
250
+ description: "PostgreSQL connection string",
251
+ required: true,
252
+ validator: validators.postgresUrl
253
+ },
254
+ {
255
+ name: "BLOB_READ_WRITE_TOKEN",
256
+ description: "Vercel Blob storage token",
257
+ required: true
258
+ },
259
+ {
260
+ name: "STRIPE_SECRET_KEY",
261
+ description: "Stripe secret key (sk_test_... or sk_live_...)",
262
+ required: true,
263
+ validator: validators.stripeSecretKey
264
+ },
265
+ {
266
+ name: "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY",
267
+ description: "Stripe publishable key (pk_test_... or pk_live_...)",
268
+ required: true,
269
+ validator: validators.stripePublishableKey
270
+ }
271
+ ];
272
+ var OPTIONAL_ENV_VARS = [
273
+ {
274
+ name: "STRIPE_WEBHOOK_SECRET",
275
+ description: "Stripe webhook secret (whsec_...)",
276
+ required: false
277
+ },
278
+ {
279
+ name: "NEXT_PUBLIC_SUPABASE_URL",
280
+ description: "Supabase project URL",
281
+ required: false,
282
+ validator: validators.url
283
+ },
284
+ {
285
+ name: "NEXT_PUBLIC_SUPABASE_ANON_KEY",
286
+ description: "Supabase anonymous key",
287
+ required: false
288
+ },
289
+ {
290
+ name: "REVEALUI_ADMIN_EMAIL",
291
+ description: "Initial admin email",
292
+ required: false,
293
+ validator: validators.email
294
+ },
295
+ {
296
+ name: "REVEALUI_ADMIN_PASSWORD",
297
+ description: "Initial admin password (min 12 chars)",
298
+ required: false,
299
+ validator: validators.minLength(12)
300
+ }
301
+ ];
302
+
303
+ // src/environment/setup.ts
304
+ async function setupEnvironment(options) {
305
+ const {
306
+ projectRoot,
307
+ templatePath = join(projectRoot, ".env.template"),
308
+ outputPath = join(projectRoot, ".env.development.local"),
309
+ force = false,
310
+ generateOnly = false,
311
+ interactive = true,
312
+ customVariables,
313
+ logger: logger2 = createLogger({ prefix: "Setup" })
314
+ } = options;
315
+ const requiredVars = customVariables || REQUIRED_ENV_VARS;
316
+ logger2.header("Environment Setup");
317
+ try {
318
+ await readFile(templatePath, "utf-8");
319
+ } catch {
320
+ logger2.error(`.env.template not found at: ${templatePath}`);
321
+ logger2.info("Please ensure .env.template exists in the project root.");
322
+ return {
323
+ success: false,
324
+ envPath: outputPath,
325
+ missing: requiredVars.map((v) => v.name),
326
+ invalid: []
327
+ };
328
+ }
329
+ let outputExists = false;
330
+ try {
331
+ await readFile(outputPath, "utf-8");
332
+ outputExists = true;
333
+ } catch {
334
+ }
335
+ if (outputExists && !force) {
336
+ if (interactive) {
337
+ logger2.warn(".env.development.local already exists");
338
+ const { overwrite } = await inquirer.prompt([
339
+ {
340
+ type: "confirm",
341
+ name: "overwrite",
342
+ message: "Overwrite existing file?",
343
+ default: false
344
+ }
345
+ ]);
346
+ if (!overwrite) {
347
+ logger2.info("Setup cancelled. Use force: true to overwrite.");
348
+ return {
349
+ success: false,
350
+ envPath: outputPath,
351
+ missing: [],
352
+ invalid: []
353
+ };
354
+ }
355
+ } else {
356
+ logger2.info("Output file exists. Set force: true to overwrite.");
357
+ return {
358
+ success: false,
359
+ envPath: outputPath,
360
+ missing: [],
361
+ invalid: []
362
+ };
363
+ }
364
+ }
365
+ logger2.info("Copying .env.template to .env.development.local...");
366
+ await copyFile(templatePath, outputPath);
367
+ logger2.success("Template copied");
368
+ if (generateOnly) {
369
+ await generateSecrets(outputPath, logger2);
370
+ logger2.success("Secrets generated");
371
+ return {
372
+ success: true,
373
+ envPath: outputPath,
374
+ missing: [],
375
+ invalid: []
376
+ };
377
+ }
378
+ let envContent = await readFile(outputPath, "utf-8");
379
+ const currentEnv = parseEnvContent(envContent);
380
+ const validation = validateEnv(requiredVars, currentEnv);
381
+ if (interactive && (validation.missing.length > 0 || validation.invalid.length > 0)) {
382
+ logger2.info("Some required values need to be configured:");
383
+ logger2.divider();
384
+ for (const varName of validation.missing) {
385
+ const variable = requiredVars.find((v) => v.name === varName);
386
+ if (!variable) continue;
387
+ logger2.info(`${varName}: ${variable.description}`);
388
+ if (varName === "REVEALUI_SECRET") {
389
+ const secret = generateSecret(32);
390
+ envContent = updateEnvValue(envContent, varName, secret);
391
+ logger2.success(`Generated ${varName}`);
392
+ } else {
393
+ const { value } = await inquirer.prompt([
394
+ {
395
+ type: "input",
396
+ name: "value",
397
+ message: `Enter value for ${varName}:`,
398
+ validate: (input) => {
399
+ if (!input.trim()) {
400
+ return "Value cannot be empty (press Ctrl+C to skip)";
401
+ }
402
+ if (variable.validator && !variable.validator(input.trim())) {
403
+ return `Invalid format for ${varName}`;
404
+ }
405
+ return true;
406
+ }
407
+ }
408
+ ]);
409
+ if (value.trim()) {
410
+ envContent = updateEnvValue(envContent, varName, value.trim());
411
+ logger2.success(`Set ${varName}`);
412
+ }
413
+ }
414
+ }
415
+ await writeFile(outputPath, envContent);
416
+ logger2.success("Environment file updated");
417
+ } else if (!interactive && validation.missing.length > 0) {
418
+ for (const varName of validation.missing) {
419
+ if (varName === "REVEALUI_SECRET") {
420
+ const secret = generateSecret(32);
421
+ envContent = updateEnvValue(envContent, varName, secret);
422
+ logger2.success(`Generated ${varName}`);
423
+ }
424
+ }
425
+ await writeFile(outputPath, envContent);
426
+ }
427
+ const finalContent = await readFile(outputPath, "utf-8");
428
+ const finalEnv = parseEnvContent(finalContent);
429
+ const finalValidation = validateEnv(requiredVars, finalEnv);
430
+ logger2.divider();
431
+ if (finalValidation.valid) {
432
+ logger2.success("Environment setup complete!");
433
+ return {
434
+ success: true,
435
+ envPath: outputPath,
436
+ missing: [],
437
+ invalid: []
438
+ };
439
+ }
440
+ logger2.warn("Setup incomplete - some variables still need to be configured:");
441
+ for (const varName of finalValidation.missing) {
442
+ logger2.warn(` - ${varName}`);
443
+ }
444
+ logger2.info("Edit .env.development.local to add missing values.");
445
+ return {
446
+ success: false,
447
+ envPath: outputPath,
448
+ missing: finalValidation.missing,
449
+ invalid: finalValidation.invalid
450
+ };
451
+ }
452
+ async function generateSecrets(envPath, logger2) {
453
+ let content = await readFile(envPath, "utf-8");
454
+ const secret = generateSecret(32);
455
+ content = updateEnvValue(content, "REVEALUI_SECRET", secret);
456
+ await writeFile(envPath, content);
457
+ logger2.success("Generated REVEALUI_SECRET");
458
+ }
459
+ export {
460
+ OPTIONAL_ENV_VARS,
461
+ REQUIRED_ENV_VARS,
462
+ createLogger,
463
+ generatePassword,
464
+ generateSecret,
465
+ handleASTParseError,
466
+ logger,
467
+ parseEnvContent,
468
+ setupEnvironment,
469
+ updateEnvValue,
470
+ validateEnv,
471
+ validators
472
+ };
473
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/environment/generators.ts","../src/environment/setup.ts","../src/utils/logger.ts","../src/validators/env.ts"],"sourcesContent":["/**\n * Environment secret and password generators\n */\n\nimport { randomBytes } from 'node:crypto'\n\n/**\n * Generates a secure random secret.\n *\n * @param length - Length in bytes (default: 32 bytes = 64 hex chars)\n * @returns Hex-encoded random secret\n *\n * @example\n * ```typescript\n * const secret = generateSecret() // 64 char hex string\n * ```\n */\nexport function generateSecret(length = 32): string {\n return randomBytes(length).toString('hex')\n}\n\n/**\n * Generates a secure password with alphanumeric and special characters.\n *\n * @param length - Password length (default: 16)\n * @returns Random password\n *\n * @example\n * ```typescript\n * const password = generatePassword(16) // e.g., \"aB3!xY9@pQ5#mN7$\"\n * ```\n */\nexport function generatePassword(length = 16): string {\n const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*'\n let password = ''\n const randomValues = randomBytes(length)\n for (let i = 0; i < length; i++) {\n password += chars[randomValues[i] % chars.length]\n }\n return password\n}\n\n/**\n * Updates a value in environment file content.\n *\n * @param content - Original env file content\n * @param key - Environment variable name\n * @param value - New value\n * @returns Updated env file content\n *\n * @example\n * ```typescript\n * const updated = updateEnvValue(content, 'DB_URL', 'postgresql://...')\n * ```\n */\nexport function updateEnvValue(content: string, key: string, value: string): string {\n const regex = new RegExp(`^${key}=.*$`, 'm')\n\n if (regex.test(content)) {\n // Replace existing value\n return content.replace(regex, `${key}=${value}`)\n }\n\n // Add new line at the end\n return `${content.trimEnd()}\\n${key}=${value}\\n`\n}\n\n/**\n * Parses environment file content into key-value pairs.\n *\n * @param content - Environment file content\n * @returns Parsed environment variables\n *\n * @example\n * ```typescript\n * const env = parseEnvContent('DB_URL=postgresql://...\\nAPI_KEY=abc123')\n * // { DB_URL: 'postgresql://...', API_KEY: 'abc123' }\n * ```\n */\nexport function parseEnvContent(content: string): Record<string, string> {\n const env: Record<string, string> = {}\n const lines = content.split('\\n')\n\n for (const line of lines) {\n // Skip comments and empty lines\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) continue\n\n // Parse key=value\n const match = trimmed.match(/^([^=]+)=(.*)$/)\n if (match) {\n const [, key, value] = match\n env[key.trim()] = value.trim()\n }\n }\n\n return env\n}\n","/**\n * Environment setup orchestration\n */\n\nimport { copyFile, readFile, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport inquirer from 'inquirer'\nimport { createLogger } from '../utils/logger.js'\nimport { type EnvVariable, REQUIRED_ENV_VARS, validateEnv } from '../validators/env.js'\nimport { generateSecret, parseEnvContent, updateEnvValue } from './generators.js'\n\nexport interface SetupEnvironmentOptions {\n projectRoot: string\n templatePath?: string\n outputPath?: string\n force?: boolean\n generateOnly?: boolean\n interactive?: boolean\n customVariables?: EnvVariable[]\n logger?: ReturnType<typeof createLogger>\n}\n\nexport interface SetupEnvironmentResult {\n success: boolean\n envPath: string\n missing: string[]\n invalid: string[]\n}\n\n/**\n * Sets up environment variables for a project.\n *\n * @param options - Setup configuration options\n * @returns Setup result with validation info\n *\n * @example\n * ```typescript\n * const result = await setupEnvironment({\n * projectRoot: '/path/to/project',\n * interactive: true\n * })\n * ```\n */\nexport async function setupEnvironment(\n options: SetupEnvironmentOptions,\n): Promise<SetupEnvironmentResult> {\n const {\n projectRoot,\n templatePath = join(projectRoot, '.env.template'),\n outputPath = join(projectRoot, '.env.development.local'),\n force = false,\n generateOnly = false,\n interactive = true,\n customVariables,\n logger = createLogger({ prefix: 'Setup' }),\n } = options\n\n const requiredVars = customVariables || REQUIRED_ENV_VARS\n\n logger.header('Environment Setup')\n\n // Check if template exists\n try {\n await readFile(templatePath, 'utf-8')\n } catch {\n logger.error(`.env.template not found at: ${templatePath}`)\n logger.info('Please ensure .env.template exists in the project root.')\n return {\n success: false,\n envPath: outputPath,\n missing: requiredVars.map((v) => v.name),\n invalid: [],\n }\n }\n\n // Check if output file already exists\n let outputExists = false\n try {\n await readFile(outputPath, 'utf-8')\n outputExists = true\n } catch {\n // File doesn't exist, which is fine\n }\n\n if (outputExists && !force) {\n if (interactive) {\n logger.warn('.env.development.local already exists')\n const { overwrite } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'overwrite',\n message: 'Overwrite existing file?',\n default: false,\n },\n ])\n if (!overwrite) {\n logger.info('Setup cancelled. Use force: true to overwrite.')\n return {\n success: false,\n envPath: outputPath,\n missing: [],\n invalid: [],\n }\n }\n } else {\n // Non-interactive mode without force, don't overwrite\n logger.info('Output file exists. Set force: true to overwrite.')\n return {\n success: false,\n envPath: outputPath,\n missing: [],\n invalid: [],\n }\n }\n }\n\n // Copy template\n logger.info('Copying .env.template to .env.development.local...')\n await copyFile(templatePath, outputPath)\n logger.success('Template copied')\n\n if (generateOnly) {\n // Just generate secrets and update the file\n await generateSecrets(outputPath, logger)\n logger.success('Secrets generated')\n return {\n success: true,\n envPath: outputPath,\n missing: [],\n invalid: [],\n }\n }\n\n // Parse the template to get current values\n let envContent = await readFile(outputPath, 'utf-8')\n const currentEnv = parseEnvContent(envContent)\n\n // Check for missing required values\n const validation = validateEnv(requiredVars, currentEnv)\n\n if (interactive && (validation.missing.length > 0 || validation.invalid.length > 0)) {\n logger.info('Some required values need to be configured:')\n logger.divider()\n\n for (const varName of validation.missing) {\n const variable = requiredVars.find((v) => v.name === varName)\n if (!variable) continue\n\n logger.info(`${varName}: ${variable.description}`)\n\n if (varName === 'REVEALUI_SECRET') {\n // Auto-generate secret\n const secret = generateSecret(32)\n envContent = updateEnvValue(envContent, varName, secret)\n logger.success(`Generated ${varName}`)\n } else {\n // Prompt for value\n const { value } = await inquirer.prompt([\n {\n type: 'input',\n name: 'value',\n message: `Enter value for ${varName}:`,\n validate: (input: string) => {\n if (!input.trim()) {\n return 'Value cannot be empty (press Ctrl+C to skip)'\n }\n if (variable.validator && !variable.validator(input.trim())) {\n return `Invalid format for ${varName}`\n }\n return true\n },\n },\n ])\n\n if (value.trim()) {\n envContent = updateEnvValue(envContent, varName, value.trim())\n logger.success(`Set ${varName}`)\n }\n }\n }\n\n // Save updated content\n await writeFile(outputPath, envContent)\n logger.success('Environment file updated')\n } else if (!interactive && validation.missing.length > 0) {\n // Non-interactive mode with missing values - just generate secrets\n for (const varName of validation.missing) {\n if (varName === 'REVEALUI_SECRET') {\n const secret = generateSecret(32)\n envContent = updateEnvValue(envContent, varName, secret)\n logger.success(`Generated ${varName}`)\n }\n }\n await writeFile(outputPath, envContent)\n }\n\n // Final validation\n const finalContent = await readFile(outputPath, 'utf-8')\n const finalEnv = parseEnvContent(finalContent)\n const finalValidation = validateEnv(requiredVars, finalEnv)\n\n logger.divider()\n\n if (finalValidation.valid) {\n logger.success('Environment setup complete!')\n return {\n success: true,\n envPath: outputPath,\n missing: [],\n invalid: [],\n }\n }\n\n logger.warn('Setup incomplete - some variables still need to be configured:')\n for (const varName of finalValidation.missing) {\n logger.warn(` - ${varName}`)\n }\n logger.info('Edit .env.development.local to add missing values.')\n\n return {\n success: false,\n envPath: outputPath,\n missing: finalValidation.missing,\n invalid: finalValidation.invalid,\n }\n}\n\n/**\n * Generates secrets and updates the env file.\n */\nasync function generateSecrets(\n envPath: string,\n logger: ReturnType<typeof createLogger>,\n): Promise<void> {\n let content = await readFile(envPath, 'utf-8')\n\n // Generate REVEALUI_SECRET\n const secret = generateSecret(32)\n content = updateEnvValue(content, 'REVEALUI_SECRET', secret)\n\n await writeFile(envPath, content)\n logger.success('Generated REVEALUI_SECRET')\n}\n","/**\n * Unified Logger for RevealUI Scripts\n *\n * Provides consistent logging across all scripts with color support,\n * structured output, and log level filtering.\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nexport interface LoggerOptions {\n level?: LogLevel\n prefix?: string\n colors?: boolean\n timestamps?: boolean\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n}\n\ninterface ColorMap {\n reset: string\n red: string\n green: string\n yellow: string\n blue: string\n cyan: string\n magenta: string\n dim: string\n bold: string\n}\n\nfunction getColors(enabled: boolean): ColorMap {\n if (!enabled) {\n return {\n reset: '',\n red: '',\n green: '',\n yellow: '',\n blue: '',\n cyan: '',\n magenta: '',\n dim: '',\n bold: '',\n }\n }\n return {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n green: '\\x1b[32m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n cyan: '\\x1b[36m',\n magenta: '\\x1b[35m',\n dim: '\\x1b[2m',\n bold: '\\x1b[1m',\n }\n}\n\nexport interface Logger {\n debug: (msg: string, ...args: unknown[]) => void\n info: (msg: string, ...args: unknown[]) => void\n warn: (msg: string, ...args: unknown[]) => void\n error: (msg: string, ...args: unknown[]) => void\n success: (msg: string, ...args: unknown[]) => void\n warning: (msg: string, ...args: unknown[]) => void\n header: (msg: string) => void\n divider: () => void\n table: (data: Record<string, unknown>[]) => void\n group: (label: string) => void\n groupEnd: () => void\n progress: (current: number, total: number, label?: string) => void\n}\n\n/**\n * Creates a logger instance with configurable options.\n *\n * @example\n * ```typescript\n * const logger = createLogger({ level: 'info', prefix: 'MyScript' })\n * logger.info('Starting process')\n * logger.success('Completed!')\n * ```\n */\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const {\n level = (process.env.LOG_LEVEL as LogLevel) || 'info',\n prefix = '',\n colors = process.env.FORCE_COLOR !== '0' && process.stdout.isTTY !== false,\n timestamps = false,\n } = options\n\n const currentLevel = LOG_LEVELS[level] ?? LOG_LEVELS.info\n const c = getColors(colors)\n\n function shouldLog(msgLevel: LogLevel): boolean {\n return LOG_LEVELS[msgLevel] >= currentLevel\n }\n\n function formatPrefix(): string {\n const parts: string[] = []\n if (timestamps) {\n parts.push(`${c.dim}[${new Date().toISOString()}]${c.reset}`)\n }\n if (prefix) {\n parts.push(`${c.cyan}[${prefix}]${c.reset}`)\n }\n return parts.length > 0 ? `${parts.join(' ')} ` : ''\n }\n\n function formatArgs(args: unknown[]): string {\n if (args.length === 0) return ''\n return (\n ' ' +\n args.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))).join(' ')\n )\n }\n\n return {\n debug(msg: string, ...args: unknown[]) {\n if (!shouldLog('debug')) return\n console.log(`${formatPrefix()}${c.dim}[DEBUG]${c.reset} ${msg}${formatArgs(args)}`)\n },\n\n info(msg: string, ...args: unknown[]) {\n if (!shouldLog('info')) return\n console.log(`${formatPrefix()}${c.blue}[INFO]${c.reset} ${msg}${formatArgs(args)}`)\n },\n\n warn(msg: string, ...args: unknown[]) {\n if (!shouldLog('warn')) return\n console.warn(`${formatPrefix()}${c.yellow}[WARN]${c.reset} ${msg}${formatArgs(args)}`)\n },\n\n error(msg: string, ...args: unknown[]) {\n if (!shouldLog('error')) return\n console.error(`${formatPrefix()}${c.red}[ERROR]${c.reset} ${msg}${formatArgs(args)}`)\n },\n\n success(msg: string, ...args: unknown[]) {\n if (!shouldLog('info')) return\n console.log(`${formatPrefix()}${c.green}[OK]${c.reset} ${msg}${formatArgs(args)}`)\n },\n\n warning(msg: string, ...args: unknown[]) {\n this.warn(msg, ...args)\n },\n\n header(msg: string) {\n if (!shouldLog('info')) return\n const line = '='.repeat(msg.length + 4)\n console.log(`\\n${c.cyan}${line}`)\n console.log(`| ${msg} |`)\n console.log(`${line}${c.reset}\\n`)\n },\n\n divider() {\n if (!shouldLog('info')) return\n console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`)\n },\n\n table(data: Record<string, unknown>[]) {\n if (!shouldLog('info')) return\n console.table(data)\n },\n\n group(label: string) {\n if (!shouldLog('info')) return\n console.group(`${c.bold}${label}${c.reset}`)\n },\n\n groupEnd() {\n if (!shouldLog('info')) return\n console.groupEnd()\n },\n\n progress(current: number, total: number, label = '') {\n if (!shouldLog('info')) return\n const percent = Math.round((current / total) * 100)\n const filled = Math.round(percent / 5)\n const empty = 20 - filled\n const bar = `${'█'.repeat(filled)}${'░'.repeat(empty)}`\n const labelText = label ? ` ${label}` : ''\n process.stdout.write(`\\r${c.cyan}${bar}${c.reset} ${percent}%${labelText}`)\n if (current === total) {\n console.log() // New line when complete\n }\n },\n }\n}\n\n/**\n * Standardized error handler for AST parsing errors\n */\nexport function handleASTParseError(filePath: string, error: unknown, logger: Logger): void {\n const message = error instanceof Error ? error.message : String(error)\n logger.warning(`AST Parse Error in ${filePath}: ${message}`)\n}\n\n/**\n * Default logger instance for quick usage\n */\nexport const logger = createLogger()\n","/**\n * Environment variable validation\n */\n\nexport interface EnvVariable {\n name: string\n description: string\n required: boolean\n validator?: (value: string) => boolean\n}\n\nexport interface ValidationResult {\n valid: boolean\n missing: string[]\n invalid: string[]\n}\n\n/**\n * Validates environment variables against required schema.\n *\n * @param required - Array of required environment variable definitions\n * @param env - Environment variable object to validate\n * @returns Validation result\n *\n * @example\n * ```typescript\n * const result = validateEnv([\n * { name: 'DB_URL', description: 'Database URL', required: true }\n * ], process.env)\n * ```\n */\nexport function validateEnv(\n required: EnvVariable[],\n env: Record<string, string | undefined>,\n): ValidationResult {\n const missing: string[] = []\n const invalid: string[] = []\n\n for (const variable of required) {\n const value = env[variable.name]\n\n // Check if required variable is missing\n if (variable.required && (!value || value.trim() === '')) {\n missing.push(variable.name)\n continue\n }\n\n // Check if value passes custom validator\n if (value && variable.validator && !variable.validator(value)) {\n invalid.push(variable.name)\n }\n }\n\n return {\n valid: missing.length === 0 && invalid.length === 0,\n missing,\n invalid,\n }\n}\n\n/**\n * Common environment variable validators\n */\nexport const validators = {\n /**\n * Validates PostgreSQL connection string format\n */\n postgresUrl: (value: string): boolean => {\n try {\n const url = new URL(value)\n return url.protocol === 'postgresql:' || url.protocol === 'postgres:'\n } catch {\n return false\n }\n },\n\n /**\n * Validates Stripe secret key format\n */\n stripeSecretKey: (value: string): boolean => {\n return value.startsWith('sk_test_') || value.startsWith('sk_live_')\n },\n\n /**\n * Validates Stripe publishable key format\n */\n stripePublishableKey: (value: string): boolean => {\n return value.startsWith('pk_test_') || value.startsWith('pk_live_')\n },\n\n /**\n * Validates URL format\n */\n url: (value: string): boolean => {\n try {\n new URL(value)\n return true\n } catch {\n return false\n }\n },\n\n /**\n * Validates minimum length\n */\n minLength:\n (min: number) =>\n (value: string): boolean => {\n return value.length >= min\n },\n\n /**\n * Validates email format\n */\n email: (value: string): boolean => {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)\n },\n}\n\n/**\n * Common required environment variables for RevealUI\n */\nexport const REQUIRED_ENV_VARS: EnvVariable[] = [\n {\n name: 'REVEALUI_SECRET',\n description: 'Secret key for JWT tokens and session encryption (min 32 chars)',\n required: true,\n validator: validators.minLength(32),\n },\n {\n name: 'POSTGRES_URL',\n description: 'PostgreSQL connection string',\n required: true,\n validator: validators.postgresUrl,\n },\n {\n name: 'BLOB_READ_WRITE_TOKEN',\n description: 'Vercel Blob storage token',\n required: true,\n },\n {\n name: 'STRIPE_SECRET_KEY',\n description: 'Stripe secret key (sk_test_... or sk_live_...)',\n required: true,\n validator: validators.stripeSecretKey,\n },\n {\n name: 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY',\n description: 'Stripe publishable key (pk_test_... or pk_live_...)',\n required: true,\n validator: validators.stripePublishableKey,\n },\n]\n\n/**\n * Optional environment variables\n */\nexport const OPTIONAL_ENV_VARS: EnvVariable[] = [\n {\n name: 'STRIPE_WEBHOOK_SECRET',\n description: 'Stripe webhook secret (whsec_...)',\n required: false,\n },\n {\n name: 'NEXT_PUBLIC_SUPABASE_URL',\n description: 'Supabase project URL',\n required: false,\n validator: validators.url,\n },\n {\n name: 'NEXT_PUBLIC_SUPABASE_ANON_KEY',\n description: 'Supabase anonymous key',\n required: false,\n },\n {\n name: 'REVEALUI_ADMIN_EMAIL',\n description: 'Initial admin email',\n required: false,\n validator: validators.email,\n },\n {\n name: 'REVEALUI_ADMIN_PASSWORD',\n description: 'Initial admin password (min 12 chars)',\n required: false,\n validator: validators.minLength(12),\n },\n]\n"],"mappings":"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","names":["logger","logger"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Unified Logger for RevealUI Scripts
3
+ *
4
+ * Provides consistent logging across all scripts with color support,
5
+ * structured output, and log level filtering.
6
+ */
7
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
8
+ interface LoggerOptions {
9
+ level?: LogLevel;
10
+ prefix?: string;
11
+ colors?: boolean;
12
+ timestamps?: boolean;
13
+ }
14
+ interface Logger {
15
+ debug: (msg: string, ...args: unknown[]) => void;
16
+ info: (msg: string, ...args: unknown[]) => void;
17
+ warn: (msg: string, ...args: unknown[]) => void;
18
+ error: (msg: string, ...args: unknown[]) => void;
19
+ success: (msg: string, ...args: unknown[]) => void;
20
+ warning: (msg: string, ...args: unknown[]) => void;
21
+ header: (msg: string) => void;
22
+ divider: () => void;
23
+ table: (data: Record<string, unknown>[]) => void;
24
+ group: (label: string) => void;
25
+ groupEnd: () => void;
26
+ progress: (current: number, total: number, label?: string) => void;
27
+ }
28
+ /**
29
+ * Creates a logger instance with configurable options.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const logger = createLogger({ level: 'info', prefix: 'MyScript' })
34
+ * logger.info('Starting process')
35
+ * logger.success('Completed!')
36
+ * ```
37
+ */
38
+ declare function createLogger(options?: LoggerOptions): Logger;
39
+ /**
40
+ * Standardized error handler for AST parsing errors
41
+ */
42
+ declare function handleASTParseError(filePath: string, error: unknown, logger: Logger): void;
43
+ /**
44
+ * Default logger instance for quick usage
45
+ */
46
+ declare const logger: Logger;
47
+
48
+ export { type LogLevel, type Logger, type LoggerOptions, createLogger, handleASTParseError, logger };