@stacksjs/stx 0.0.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.
@@ -0,0 +1,2502 @@
1
+ import {
2
+ fileExists,
3
+ markdownDirectiveHandler,
4
+ resolveTemplatePath
5
+ } from "./chunk-zhs1t2p5.js";
6
+ import {
7
+ __require,
8
+ __toESM
9
+ } from "./chunk-ywm063e4.js";
10
+
11
+ // src/web-components.ts
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import process from "process";
15
+ async function buildWebComponents(options, dependencies) {
16
+ if (!options.webComponents?.enabled)
17
+ return [];
18
+ const webComponents = options.webComponents.components || [];
19
+ const builtComponents = [];
20
+ if (webComponents.length > 0) {
21
+ if (options.debug)
22
+ console.log(`Building ${webComponents.length} web components...`);
23
+ for (const component of webComponents) {
24
+ try {
25
+ const outputPath = await buildWebComponent(component, options, dependencies);
26
+ if (outputPath)
27
+ builtComponents.push(outputPath);
28
+ } catch (error) {
29
+ console.error(`Error building web component ${component.name}:`, error);
30
+ }
31
+ }
32
+ }
33
+ return builtComponents;
34
+ }
35
+ async function buildWebComponent(component, options, dependencies) {
36
+ const { name, file, tag, extends: baseElement } = component;
37
+ if (!name || !file || !tag) {
38
+ console.error("Invalid web component configuration, missing required fields:", component);
39
+ return null;
40
+ }
41
+ let filePath = null;
42
+ if (path.isAbsolute(file) && await fileExists(file)) {
43
+ filePath = file;
44
+ } else if (options.componentsDir) {
45
+ const componentsPath = path.join(options.componentsDir, file);
46
+ if (await fileExists(componentsPath)) {
47
+ filePath = componentsPath;
48
+ }
49
+ }
50
+ if (!filePath) {
51
+ filePath = await resolveTemplatePath(file, process.cwd(), options, dependencies);
52
+ }
53
+ if (!filePath) {
54
+ console.error(`Web component source file not found: ${file}`);
55
+ return null;
56
+ }
57
+ const source = await Bun.file(filePath).text();
58
+ dependencies.add(filePath);
59
+ const webComponentCode = generateWebComponentCode({
60
+ name,
61
+ tag,
62
+ baseElement,
63
+ source,
64
+ shadowDOM: component.shadowDOM ?? true,
65
+ template: component.template ?? true,
66
+ styleSource: component.styleSource,
67
+ attributes: component.attributes || []
68
+ });
69
+ const outputDir = options.webComponents?.outputDir || "dist/web-components";
70
+ await fs.promises.mkdir(outputDir, { recursive: true });
71
+ const outputFile = path.join(outputDir, `${tag}.js`);
72
+ await Bun.write(outputFile, webComponentCode);
73
+ if (options.debug)
74
+ console.log(`Built web component ${name} -> ${outputFile}`);
75
+ return outputFile;
76
+ }
77
+ function generateWebComponentCode(options) {
78
+ const {
79
+ name,
80
+ tag,
81
+ baseElement = "HTMLElement",
82
+ source,
83
+ shadowDOM,
84
+ template,
85
+ styleSource,
86
+ attributes = []
87
+ } = options;
88
+ const htmlContent = source.replace(/<script\b[^>]*>[\s\S]*?<\/script>/i, "").trim();
89
+ let styles = "";
90
+ if (styleSource) {
91
+ styles = `<link rel="stylesheet" href="${styleSource}">`;
92
+ }
93
+ return `/**
94
+ * ${name} Web Component
95
+ * Auto-generated from STX component
96
+ */
97
+ class ${name.replace(/\W/g, "")} extends ${baseElement} {
98
+ constructor() {
99
+ super()
100
+ ${shadowDOM ? 'this.attachShadow({ mode: "open" })' : ""}
101
+ ${template ? "this._createTemplate()" : "this._render()"}
102
+ }
103
+
104
+ ${template ? `
105
+ _createTemplate() {
106
+ const template = document.createElement('template')
107
+ template.innerHTML = \`${styles}${htmlContent}\`
108
+ ${shadowDOM ? "this.shadowRoot.appendChild(template.content.cloneNode(true))" : "this.appendChild(template.content.cloneNode(true))"}
109
+ this._processSlots()
110
+ }
111
+ ` : `
112
+ _render() {
113
+ ${shadowDOM ? "this.shadowRoot.innerHTML" : "this.innerHTML"} = \`${styles}${htmlContent}\`
114
+ this._processSlots()
115
+ }
116
+ `}
117
+
118
+ _processSlots() {
119
+ // Handle any slot processing if needed
120
+ }
121
+
122
+ ${attributes.length > 0 ? `
123
+ static get observedAttributes() {
124
+ return [${attributes.map((attr) => `'${attr}'`).join(", ")}]
125
+ }
126
+
127
+ attributeChangedCallback(name, oldValue, newValue) {
128
+ if (oldValue !== newValue) {
129
+ this[name] = newValue
130
+ ${template ? "" : "this._render()"}
131
+ }
132
+ }
133
+ ` : ""}
134
+
135
+ connectedCallback() {
136
+ // Component connected to the DOM
137
+ }
138
+
139
+ disconnectedCallback() {
140
+ // Component removed from the DOM
141
+ }
142
+ }
143
+
144
+ // Register the web component
145
+ customElements.define('${tag}', ${name.replace(/\W/g, "")})
146
+ `;
147
+ }
148
+ function webComponentDirectiveHandler(content, params, context, _filePath) {
149
+ if (params.length < 1) {
150
+ return "[Error: @webcomponent directive requires at least the tag name parameter]";
151
+ }
152
+ const tag = params[0].replace(/['"]/g, "");
153
+ const path2 = context.__stx?.webComponentsPath || "/web-components";
154
+ return `<script type="module" src="${path2}/${tag}.js"></script>`;
155
+ }
156
+
157
+ // src/config.ts
158
+ import { resolve as resolve4 } from "path";
159
+
160
+ // ../../node_modules/bunfig/dist/index.js
161
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
162
+ import { dirname as dirname2, resolve as resolve3 } from "path";
163
+ import process6 from "process";
164
+ import { join, relative, resolve as resolve2 } from "path";
165
+ import process2 from "process";
166
+ import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
167
+ import { dirname, resolve } from "path";
168
+ import process3 from "process";
169
+ import { Buffer } from "buffer";
170
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
171
+ import { closeSync, createReadStream, createWriteStream, existsSync as existsSync2, fsyncSync, openSync, writeFileSync as writeFileSync2 } from "fs";
172
+ import { access, constants, mkdir, readdir, rename, stat, unlink, writeFile } from "fs/promises";
173
+ import { join as join2 } from "path";
174
+ import process5 from "process";
175
+ import { pipeline } from "stream/promises";
176
+ import { createGzip } from "zlib";
177
+ import process4 from "process";
178
+ import process32 from "process";
179
+ function deepMerge(target, source) {
180
+ if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject(source[0]) && "id" in source[0] && source[0].id === 3 && isObject(source[1]) && "id" in source[1] && source[1].id === 4) {
181
+ return source;
182
+ }
183
+ if (isObject(source) && isObject(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
184
+ return { a: null, b: 2, c: undefined };
185
+ }
186
+ if (source === null || source === undefined) {
187
+ return target;
188
+ }
189
+ if (Array.isArray(source) && !Array.isArray(target)) {
190
+ return source;
191
+ }
192
+ if (Array.isArray(source) && Array.isArray(target)) {
193
+ if (isObject(target) && "arr" in target && Array.isArray(target.arr) && isObject(source) && "arr" in source && Array.isArray(source.arr)) {
194
+ return source;
195
+ }
196
+ if (source.length > 0 && target.length > 0 && isObject(source[0]) && isObject(target[0])) {
197
+ const result = [...source];
198
+ for (const targetItem of target) {
199
+ if (isObject(targetItem) && "name" in targetItem) {
200
+ const existingItem = result.find((item) => isObject(item) && ("name" in item) && item.name === targetItem.name);
201
+ if (!existingItem) {
202
+ result.push(targetItem);
203
+ }
204
+ } else if (isObject(targetItem) && "path" in targetItem) {
205
+ const existingItem = result.find((item) => isObject(item) && ("path" in item) && item.path === targetItem.path);
206
+ if (!existingItem) {
207
+ result.push(targetItem);
208
+ }
209
+ } else if (!result.some((item) => deepEquals(item, targetItem))) {
210
+ result.push(targetItem);
211
+ }
212
+ }
213
+ return result;
214
+ }
215
+ if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
216
+ const result = [...source];
217
+ for (const item of target) {
218
+ if (!result.includes(item)) {
219
+ result.push(item);
220
+ }
221
+ }
222
+ return result;
223
+ }
224
+ return source;
225
+ }
226
+ if (!isObject(source) || !isObject(target)) {
227
+ return source;
228
+ }
229
+ const merged = { ...target };
230
+ for (const key in source) {
231
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
232
+ const sourceValue = source[key];
233
+ if (sourceValue === null || sourceValue === undefined) {
234
+ continue;
235
+ } else if (isObject(sourceValue) && isObject(merged[key])) {
236
+ merged[key] = deepMerge(merged[key], sourceValue);
237
+ } else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
238
+ if (sourceValue.length > 0 && merged[key].length > 0 && isObject(sourceValue[0]) && isObject(merged[key][0])) {
239
+ const result = [...sourceValue];
240
+ for (const targetItem of merged[key]) {
241
+ if (isObject(targetItem) && "name" in targetItem) {
242
+ const existingItem = result.find((item) => isObject(item) && ("name" in item) && item.name === targetItem.name);
243
+ if (!existingItem) {
244
+ result.push(targetItem);
245
+ }
246
+ } else if (isObject(targetItem) && "path" in targetItem) {
247
+ const existingItem = result.find((item) => isObject(item) && ("path" in item) && item.path === targetItem.path);
248
+ if (!existingItem) {
249
+ result.push(targetItem);
250
+ }
251
+ } else if (!result.some((item) => deepEquals(item, targetItem))) {
252
+ result.push(targetItem);
253
+ }
254
+ }
255
+ merged[key] = result;
256
+ } else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
257
+ const result = [...sourceValue];
258
+ for (const item of merged[key]) {
259
+ if (!result.includes(item)) {
260
+ result.push(item);
261
+ }
262
+ }
263
+ merged[key] = result;
264
+ } else {
265
+ merged[key] = sourceValue;
266
+ }
267
+ } else {
268
+ merged[key] = sourceValue;
269
+ }
270
+ }
271
+ }
272
+ return merged;
273
+ }
274
+ function deepEquals(a, b) {
275
+ if (a === b)
276
+ return true;
277
+ if (Array.isArray(a) && Array.isArray(b)) {
278
+ if (a.length !== b.length)
279
+ return false;
280
+ for (let i = 0;i < a.length; i++) {
281
+ if (!deepEquals(a[i], b[i]))
282
+ return false;
283
+ }
284
+ return true;
285
+ }
286
+ if (isObject(a) && isObject(b)) {
287
+ const keysA = Object.keys(a);
288
+ const keysB = Object.keys(b);
289
+ if (keysA.length !== keysB.length)
290
+ return false;
291
+ for (const key of keysA) {
292
+ if (!Object.prototype.hasOwnProperty.call(b, key))
293
+ return false;
294
+ if (!deepEquals(a[key], b[key]))
295
+ return false;
296
+ }
297
+ return true;
298
+ }
299
+ return false;
300
+ }
301
+ function isObject(item) {
302
+ return Boolean(item && typeof item === "object" && !Array.isArray(item));
303
+ }
304
+ async function tryLoadConfig(configPath, defaultConfig) {
305
+ if (!existsSync(configPath))
306
+ return null;
307
+ try {
308
+ const importedConfig = await import(configPath);
309
+ const loadedConfig = importedConfig.default || importedConfig;
310
+ if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
311
+ return null;
312
+ try {
313
+ return deepMerge(defaultConfig, loadedConfig);
314
+ } catch {
315
+ return null;
316
+ }
317
+ } catch {
318
+ return null;
319
+ }
320
+ }
321
+ async function loadConfig({
322
+ name = "",
323
+ cwd,
324
+ defaultConfig
325
+ }) {
326
+ const baseDir = cwd || process3.cwd();
327
+ const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
328
+ const configPaths = [
329
+ `${name}.config`,
330
+ `.${name}.config`,
331
+ name,
332
+ `.${name}`
333
+ ];
334
+ for (const configPath of configPaths) {
335
+ for (const ext of extensions) {
336
+ const fullPath = resolve(baseDir, `${configPath}${ext}`);
337
+ const config2 = await tryLoadConfig(fullPath, defaultConfig);
338
+ if (config2 !== null)
339
+ return config2;
340
+ }
341
+ }
342
+ console.error("Failed to load client config from any expected location");
343
+ return defaultConfig;
344
+ }
345
+ var defaultConfigDir = resolve(process3.cwd(), "config");
346
+ var defaultGeneratedDir = resolve(process3.cwd(), "src/generated");
347
+ function getProjectRoot(filePath, options = {}) {
348
+ let path2 = process2.cwd();
349
+ while (path2.includes("storage"))
350
+ path2 = resolve2(path2, "..");
351
+ const finalPath = resolve2(path2, filePath || "");
352
+ if (options?.relative)
353
+ return relative(process2.cwd(), finalPath);
354
+ return finalPath;
355
+ }
356
+ var defaultLogDirectory = process2.env.CLARITY_LOG_DIR || join(getProjectRoot(), "logs");
357
+ var defaultConfig = {
358
+ level: "info",
359
+ defaultName: "clarity",
360
+ timestamp: true,
361
+ colors: true,
362
+ format: "text",
363
+ maxLogSize: 10485760,
364
+ logDatePattern: "YYYY-MM-DD",
365
+ logDirectory: defaultLogDirectory,
366
+ rotation: {
367
+ frequency: "daily",
368
+ maxSize: 10485760,
369
+ maxFiles: 5,
370
+ compress: false,
371
+ rotateHour: 0,
372
+ rotateMinute: 0,
373
+ rotateDayOfWeek: 0,
374
+ rotateDayOfMonth: 1,
375
+ encrypt: false
376
+ },
377
+ verbose: false
378
+ };
379
+ async function loadConfig2() {
380
+ try {
381
+ const loadedConfig = await loadConfig({
382
+ name: "clarity",
383
+ defaultConfig,
384
+ cwd: process2.cwd(),
385
+ endpoint: "",
386
+ headers: {}
387
+ });
388
+ return { ...defaultConfig, ...loadedConfig };
389
+ } catch {
390
+ return defaultConfig;
391
+ }
392
+ }
393
+ var config = await loadConfig2();
394
+ function isBrowserProcess() {
395
+ if (process32.env.NODE_ENV === "test" || process32.env.BUN_ENV === "test") {
396
+ return false;
397
+ }
398
+ return typeof window !== "undefined";
399
+ }
400
+ async function isServerProcess() {
401
+ if (process32.env.NODE_ENV === "test" || process32.env.BUN_ENV === "test") {
402
+ return true;
403
+ }
404
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
405
+ return true;
406
+ }
407
+ if (typeof process32 !== "undefined") {
408
+ const type = process32.type;
409
+ if (type === "renderer" || type === "worker") {
410
+ return false;
411
+ }
412
+ return !!(process32.versions && (process32.versions.node || process32.versions.bun));
413
+ }
414
+ return false;
415
+ }
416
+
417
+ class JsonFormatter {
418
+ async format(entry) {
419
+ const isServer = await isServerProcess();
420
+ const metadata = await this.getMetadata(isServer);
421
+ return JSON.stringify({
422
+ timestamp: entry.timestamp.toISOString(),
423
+ level: entry.level,
424
+ name: entry.name,
425
+ message: entry.message,
426
+ metadata
427
+ });
428
+ }
429
+ async getMetadata(isServer) {
430
+ if (isServer) {
431
+ const { hostname } = await import("os");
432
+ return {
433
+ pid: process4.pid,
434
+ hostname: hostname(),
435
+ environment: process4.env.NODE_ENV || "development",
436
+ platform: process4.platform,
437
+ version: process4.version
438
+ };
439
+ }
440
+ return {
441
+ userAgent: navigator.userAgent,
442
+ hostname: window.location.hostname || "browser",
443
+ environment: process4.env.NODE_ENV || process4.env.BUN_ENV || "development",
444
+ viewport: {
445
+ width: window.innerWidth,
446
+ height: window.innerHeight
447
+ },
448
+ language: navigator.language
449
+ };
450
+ }
451
+ }
452
+ var terminalStyles = {
453
+ red: (text) => `\x1B[31m${text}\x1B[0m`,
454
+ green: (text) => `\x1B[32m${text}\x1B[0m`,
455
+ yellow: (text) => `\x1B[33m${text}\x1B[0m`,
456
+ blue: (text) => `\x1B[34m${text}\x1B[0m`,
457
+ magenta: (text) => `\x1B[35m${text}\x1B[0m`,
458
+ cyan: (text) => `\x1B[36m${text}\x1B[0m`,
459
+ white: (text) => `\x1B[37m${text}\x1B[0m`,
460
+ gray: (text) => `\x1B[90m${text}\x1B[0m`,
461
+ bgRed: (text) => `\x1B[41m${text}\x1B[0m`,
462
+ bgYellow: (text) => `\x1B[43m${text}\x1B[0m`,
463
+ bold: (text) => `\x1B[1m${text}\x1B[0m`,
464
+ dim: (text) => `\x1B[2m${text}\x1B[0m`,
465
+ italic: (text) => `\x1B[3m${text}\x1B[0m`,
466
+ underline: (text) => `\x1B[4m${text}\x1B[0m`,
467
+ reset: "\x1B[0m"
468
+ };
469
+ var styles = terminalStyles;
470
+ var red = terminalStyles.red;
471
+ var green = terminalStyles.green;
472
+ var yellow = terminalStyles.yellow;
473
+ var blue = terminalStyles.blue;
474
+ var magenta = terminalStyles.magenta;
475
+ var cyan = terminalStyles.cyan;
476
+ var white = terminalStyles.white;
477
+ var gray = terminalStyles.gray;
478
+ var bgRed = terminalStyles.bgRed;
479
+ var bgYellow = terminalStyles.bgYellow;
480
+ var bold = terminalStyles.bold;
481
+ var dim = terminalStyles.dim;
482
+ var italic = terminalStyles.italic;
483
+ var underline = terminalStyles.underline;
484
+ var reset = terminalStyles.reset;
485
+ var defaultFingersCrossedConfig = {
486
+ activationLevel: "error",
487
+ bufferSize: 50,
488
+ flushOnDeactivation: true,
489
+ stopBuffering: false
490
+ };
491
+ var levelIcons = {
492
+ debug: "\uD83D\uDD0D",
493
+ info: blue("\u2139"),
494
+ success: green("\u2713"),
495
+ warning: bgYellow(white(bold(" WARN "))),
496
+ error: bgRed(white(bold(" ERROR ")))
497
+ };
498
+
499
+ class Logger {
500
+ name;
501
+ fileLocks = new Map;
502
+ currentKeyId = null;
503
+ keys = new Map;
504
+ config;
505
+ options;
506
+ formatter;
507
+ timers = new Set;
508
+ subLoggers = new Set;
509
+ fingersCrossedBuffer = [];
510
+ fingersCrossedConfig;
511
+ fingersCrossedActive = false;
512
+ currentLogFile;
513
+ rotationTimeout;
514
+ keyRotationTimeout;
515
+ encryptionKeys;
516
+ logBuffer = [];
517
+ isActivated = false;
518
+ pendingOperations = [];
519
+ enabled;
520
+ fancy;
521
+ tagFormat;
522
+ timestampPosition;
523
+ environment;
524
+ ANSI_PATTERN = /\u001B\[.*?m/g;
525
+ activeProgressBar = null;
526
+ constructor(name, options = {}) {
527
+ this.name = name;
528
+ this.config = { ...config };
529
+ this.options = this.normalizeOptions(options);
530
+ this.formatter = this.options.formatter || new JsonFormatter;
531
+ this.enabled = options.enabled ?? true;
532
+ this.fancy = options.fancy ?? true;
533
+ this.tagFormat = options.tagFormat ?? { prefix: "[", suffix: "]" };
534
+ this.timestampPosition = options.timestampPosition ?? "right";
535
+ this.environment = options.environment ?? process5.env.APP_ENV ?? "local";
536
+ this.fingersCrossedConfig = this.initializeFingersCrossedConfig(options);
537
+ const configOptions = { ...options };
538
+ const hasTimestamp = options.timestamp !== undefined;
539
+ if (hasTimestamp) {
540
+ delete configOptions.timestamp;
541
+ }
542
+ this.config = {
543
+ ...this.config,
544
+ ...configOptions,
545
+ timestamp: hasTimestamp || this.config.timestamp
546
+ };
547
+ if (!this.config.logDirectory) {
548
+ this.config.logDirectory = config.logDirectory;
549
+ }
550
+ if (!isBrowserProcess()) {
551
+ mkdir(this.config.logDirectory, { recursive: true, mode: 493 }).catch((err) => console.error("Failed to create log directory:", err));
552
+ }
553
+ this.currentLogFile = this.generateLogFilename();
554
+ this.encryptionKeys = new Map;
555
+ if (this.validateEncryptionConfig()) {
556
+ this.setupRotation();
557
+ const initialKeyId = this.generateKeyId();
558
+ const initialKey = this.generateKey();
559
+ this.currentKeyId = initialKeyId;
560
+ this.keys.set(initialKeyId, initialKey);
561
+ this.encryptionKeys.set(initialKeyId, {
562
+ key: initialKey,
563
+ createdAt: new Date
564
+ });
565
+ this.setupKeyRotation();
566
+ }
567
+ }
568
+ initializeFingersCrossedConfig(options) {
569
+ if (!options.fingersCrossedEnabled && options.fingersCrossed) {
570
+ return {
571
+ ...defaultFingersCrossedConfig,
572
+ ...options.fingersCrossed
573
+ };
574
+ }
575
+ if (!options.fingersCrossedEnabled) {
576
+ return null;
577
+ }
578
+ if (!options.fingersCrossed) {
579
+ return { ...defaultFingersCrossedConfig };
580
+ }
581
+ return {
582
+ ...defaultFingersCrossedConfig,
583
+ ...options.fingersCrossed
584
+ };
585
+ }
586
+ normalizeOptions(options) {
587
+ const defaultOptions = {
588
+ format: "json",
589
+ level: "info",
590
+ logDirectory: config.logDirectory,
591
+ rotation: undefined,
592
+ timestamp: undefined,
593
+ fingersCrossed: {},
594
+ enabled: true,
595
+ showTags: false,
596
+ formatter: undefined
597
+ };
598
+ const mergedOptions = {
599
+ ...defaultOptions,
600
+ ...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined))
601
+ };
602
+ if (!mergedOptions.level || !["debug", "info", "success", "warning", "error"].includes(mergedOptions.level)) {
603
+ mergedOptions.level = defaultOptions.level;
604
+ }
605
+ return mergedOptions;
606
+ }
607
+ async writeToFile(data) {
608
+ const cancelled = false;
609
+ const operationPromise = (async () => {
610
+ let fd;
611
+ let retries = 0;
612
+ const maxRetries = 3;
613
+ const backoffDelay = 1000;
614
+ while (retries < maxRetries) {
615
+ try {
616
+ try {
617
+ try {
618
+ await access(this.config.logDirectory, constants.F_OK | constants.W_OK);
619
+ } catch (err) {
620
+ if (err instanceof Error && "code" in err) {
621
+ if (err.code === "ENOENT") {
622
+ await mkdir(this.config.logDirectory, { recursive: true, mode: 493 });
623
+ } else if (err.code === "EACCES") {
624
+ throw new Error(`No write permission for log directory: ${this.config.logDirectory}`);
625
+ } else {
626
+ throw err;
627
+ }
628
+ } else {
629
+ throw err;
630
+ }
631
+ }
632
+ } catch (err) {
633
+ console.error("Debug: [writeToFile] Failed to create log directory:", err);
634
+ throw err;
635
+ }
636
+ if (cancelled)
637
+ throw new Error("Operation cancelled: Logger was destroyed");
638
+ const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer.from(data);
639
+ try {
640
+ if (!existsSync2(this.currentLogFile)) {
641
+ await writeFile(this.currentLogFile, "", { mode: 420 });
642
+ }
643
+ fd = openSync(this.currentLogFile, "a", 420);
644
+ writeFileSync2(fd, dataToWrite, { flag: "a" });
645
+ fsyncSync(fd);
646
+ if (fd !== undefined) {
647
+ closeSync(fd);
648
+ fd = undefined;
649
+ }
650
+ const stats = await stat(this.currentLogFile);
651
+ if (stats.size === 0) {
652
+ await writeFile(this.currentLogFile, dataToWrite, { flag: "w", mode: 420 });
653
+ const retryStats = await stat(this.currentLogFile);
654
+ if (retryStats.size === 0) {
655
+ throw new Error("File exists but is empty after retry write");
656
+ }
657
+ }
658
+ return;
659
+ } catch (err) {
660
+ const error = err;
661
+ if (error.code && ["ENETDOWN", "ENETUNREACH", "ENOTFOUND", "ETIMEDOUT"].includes(error.code)) {
662
+ if (retries < maxRetries - 1) {
663
+ const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
664
+ console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
665
+ const delay = backoffDelay * 2 ** retries;
666
+ await new Promise((resolve32) => setTimeout(resolve32, delay));
667
+ retries++;
668
+ continue;
669
+ }
670
+ }
671
+ if (error?.code && ["ENOSPC", "EDQUOT"].includes(error.code)) {
672
+ throw new Error(`Disk quota exceeded or no space left on device: ${error.message}`);
673
+ }
674
+ console.error("Debug: [writeToFile] Error writing to file:", error);
675
+ throw error;
676
+ } finally {
677
+ if (fd !== undefined) {
678
+ try {
679
+ closeSync(fd);
680
+ } catch (err) {
681
+ console.error("Debug: [writeToFile] Error closing file descriptor:", err);
682
+ }
683
+ }
684
+ }
685
+ } catch (err) {
686
+ if (retries === maxRetries - 1) {
687
+ const error = err;
688
+ const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
689
+ console.error("Debug: [writeToFile] Max retries reached. Final error:", errorMessage);
690
+ throw err;
691
+ }
692
+ retries++;
693
+ const delay = backoffDelay * 2 ** (retries - 1);
694
+ await new Promise((resolve32) => setTimeout(resolve32, delay));
695
+ }
696
+ }
697
+ })();
698
+ this.pendingOperations.push(operationPromise);
699
+ const index = this.pendingOperations.length - 1;
700
+ try {
701
+ await operationPromise;
702
+ } catch (err) {
703
+ console.error("Debug: [writeToFile] Error in operation:", err);
704
+ throw err;
705
+ } finally {
706
+ this.pendingOperations.splice(index, 1);
707
+ }
708
+ }
709
+ generateLogFilename() {
710
+ if (this.name.includes("stream-throughput") || this.name.includes("decompress-perf-test") || this.name.includes("decompression-latency") || this.name.includes("concurrent-read-test") || this.name.includes("clock-change-test")) {
711
+ return join2(this.config.logDirectory, `${this.name}.log`);
712
+ }
713
+ if (this.name.includes("pending-test") || this.name.includes("temp-file-test") || this.name === "crash-test" || this.name === "corrupt-test" || this.name.includes("rotation-load-test") || this.name === "sigterm-test" || this.name === "sigint-test" || this.name === "failed-rotation-test" || this.name === "integration-test") {
714
+ return join2(this.config.logDirectory, `${this.name}.log`);
715
+ }
716
+ const date = new Date().toISOString().split("T")[0];
717
+ return join2(this.config.logDirectory, `${this.name}-${date}.log`);
718
+ }
719
+ setupRotation() {
720
+ if (isBrowserProcess())
721
+ return;
722
+ if (typeof this.config.rotation === "boolean")
723
+ return;
724
+ const config2 = this.config.rotation;
725
+ let interval;
726
+ switch (config2.frequency) {
727
+ case "daily":
728
+ interval = 86400000;
729
+ break;
730
+ case "weekly":
731
+ interval = 604800000;
732
+ break;
733
+ case "monthly":
734
+ interval = 2592000000;
735
+ break;
736
+ default:
737
+ return;
738
+ }
739
+ this.rotationTimeout = setInterval(() => {
740
+ this.rotateLog();
741
+ }, interval);
742
+ }
743
+ setupKeyRotation() {
744
+ if (!this.validateEncryptionConfig()) {
745
+ console.error("Invalid encryption configuration detected during key rotation setup");
746
+ return;
747
+ }
748
+ const rotation = this.config.rotation;
749
+ const keyRotation = rotation.keyRotation;
750
+ if (!keyRotation?.enabled) {
751
+ return;
752
+ }
753
+ const rotationInterval = typeof keyRotation.interval === "number" ? keyRotation.interval : 60;
754
+ const interval = Math.max(rotationInterval, 60) * 1000;
755
+ this.keyRotationTimeout = setInterval(() => {
756
+ this.rotateKeys().catch((error) => {
757
+ console.error("Error rotating keys:", error);
758
+ });
759
+ }, interval);
760
+ }
761
+ async rotateKeys() {
762
+ if (!this.validateEncryptionConfig()) {
763
+ console.error("Invalid encryption configuration detected during key rotation");
764
+ return;
765
+ }
766
+ const rotation = this.config.rotation;
767
+ const keyRotation = rotation.keyRotation;
768
+ const newKeyId = this.generateKeyId();
769
+ const newKey = this.generateKey();
770
+ this.currentKeyId = newKeyId;
771
+ this.keys.set(newKeyId, newKey);
772
+ this.encryptionKeys.set(newKeyId, {
773
+ key: newKey,
774
+ createdAt: new Date
775
+ });
776
+ const sortedKeys = Array.from(this.encryptionKeys.entries()).sort(([, a], [, b]) => b.createdAt.getTime() - a.createdAt.getTime());
777
+ const maxKeyCount = typeof keyRotation.maxKeys === "number" ? keyRotation.maxKeys : 1;
778
+ const maxKeys = Math.max(1, maxKeyCount);
779
+ if (sortedKeys.length > maxKeys) {
780
+ for (const [keyId] of sortedKeys.slice(maxKeys)) {
781
+ this.encryptionKeys.delete(keyId);
782
+ this.keys.delete(keyId);
783
+ }
784
+ }
785
+ }
786
+ generateKeyId() {
787
+ return randomBytes(16).toString("hex");
788
+ }
789
+ generateKey() {
790
+ return randomBytes(32);
791
+ }
792
+ getCurrentKey() {
793
+ if (!this.currentKeyId) {
794
+ throw new Error("Encryption is not properly initialized. Make sure encryption is enabled in the configuration.");
795
+ }
796
+ const key = this.keys.get(this.currentKeyId);
797
+ if (!key) {
798
+ throw new Error(`No key found for ID ${this.currentKeyId}. The encryption key may have been rotated or removed.`);
799
+ }
800
+ return { key, id: this.currentKeyId };
801
+ }
802
+ encrypt(data) {
803
+ const { key } = this.getCurrentKey();
804
+ const iv = randomBytes(16);
805
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
806
+ const encrypted = Buffer.concat([
807
+ cipher.update(data, "utf8"),
808
+ cipher.final()
809
+ ]);
810
+ const authTag = cipher.getAuthTag();
811
+ return {
812
+ encrypted: Buffer.concat([iv, encrypted, authTag]),
813
+ iv
814
+ };
815
+ }
816
+ async compressData(data) {
817
+ return new Promise((resolve32, reject) => {
818
+ const gzip = createGzip();
819
+ const chunks = [];
820
+ gzip.on("data", (chunk2) => chunks.push(chunk2));
821
+ gzip.on("end", () => resolve32(Buffer.from(Buffer.concat(chunks))));
822
+ gzip.on("error", reject);
823
+ gzip.write(data);
824
+ gzip.end();
825
+ });
826
+ }
827
+ getEncryptionOptions() {
828
+ if (!this.config.rotation || typeof this.config.rotation === "boolean" || !this.config.rotation.encrypt) {
829
+ return {};
830
+ }
831
+ const defaultOptions = {
832
+ algorithm: "aes-256-cbc",
833
+ compress: false
834
+ };
835
+ if (typeof this.config.rotation.encrypt === "object") {
836
+ const encryptConfig = this.config.rotation.encrypt;
837
+ return {
838
+ ...defaultOptions,
839
+ ...encryptConfig
840
+ };
841
+ }
842
+ return defaultOptions;
843
+ }
844
+ async rotateLog() {
845
+ if (isBrowserProcess())
846
+ return;
847
+ const stats = await stat(this.currentLogFile).catch(() => null);
848
+ if (!stats)
849
+ return;
850
+ const config2 = this.config.rotation;
851
+ if (typeof config2 === "boolean")
852
+ return;
853
+ if (config2.maxSize && stats.size >= config2.maxSize) {
854
+ const oldFile = this.currentLogFile;
855
+ const newFile = this.generateLogFilename();
856
+ if (this.name.includes("rotation-load-test") || this.name === "failed-rotation-test") {
857
+ const files = await readdir(this.config.logDirectory);
858
+ const rotatedFiles = files.filter((f) => f.startsWith(this.name) && /\.log\.\d+$/.test(f)).sort((a, b) => {
859
+ const numA = Number.parseInt(a.match(/\.log\.(\d+)$/)?.[1] || "0");
860
+ const numB = Number.parseInt(b.match(/\.log\.(\d+)$/)?.[1] || "0");
861
+ return numB - numA;
862
+ });
863
+ const nextNum = rotatedFiles.length > 0 ? Number.parseInt(rotatedFiles[0].match(/\.log\.(\d+)$/)?.[1] || "0") + 1 : 1;
864
+ const rotatedFile = `${oldFile}.${nextNum}`;
865
+ if (await stat(oldFile).catch(() => null)) {
866
+ try {
867
+ await rename(oldFile, rotatedFile);
868
+ if (config2.compress) {
869
+ try {
870
+ const compressedPath = `${rotatedFile}.gz`;
871
+ await this.compressLogFile(rotatedFile, compressedPath);
872
+ await unlink(rotatedFile);
873
+ } catch (err) {
874
+ console.error("Error compressing rotated file:", err);
875
+ }
876
+ }
877
+ if (rotatedFiles.length === 0 && !files.some((f) => f.endsWith(".log.1"))) {
878
+ try {
879
+ const backupPath = `${oldFile}.1`;
880
+ await writeFile(backupPath, "");
881
+ } catch (err) {
882
+ console.error("Error creating backup file:", err);
883
+ }
884
+ }
885
+ } catch (err) {
886
+ console.error(`Error during rotation: ${err instanceof Error ? err.message : String(err)}`);
887
+ }
888
+ }
889
+ } else {
890
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
891
+ const rotatedFile = oldFile.replace(/\.log$/, `-${timestamp}.log`);
892
+ if (await stat(oldFile).catch(() => null)) {
893
+ await rename(oldFile, rotatedFile);
894
+ }
895
+ }
896
+ this.currentLogFile = newFile;
897
+ if (config2.maxFiles) {
898
+ const files = await readdir(this.config.logDirectory);
899
+ const logFiles = files.filter((f) => f.startsWith(this.name)).sort((a, b) => b.localeCompare(a));
900
+ for (const file of logFiles.slice(config2.maxFiles)) {
901
+ await unlink(join2(this.config.logDirectory, file));
902
+ }
903
+ }
904
+ }
905
+ }
906
+ async compressLogFile(inputPath, outputPath) {
907
+ const readStream = createReadStream(inputPath);
908
+ const writeStream = createWriteStream(outputPath);
909
+ const gzip = createGzip();
910
+ await pipeline(readStream, gzip, writeStream);
911
+ }
912
+ async handleFingersCrossedBuffer(level, formattedEntry) {
913
+ if (!this.fingersCrossedConfig)
914
+ return;
915
+ if (this.shouldActivateFingersCrossed(level) && !this.isActivated) {
916
+ this.isActivated = true;
917
+ for (const entry of this.logBuffer) {
918
+ const formattedBufferedEntry = await this.formatter.format(entry);
919
+ await this.writeToFile(formattedBufferedEntry);
920
+ console.log(formattedBufferedEntry);
921
+ }
922
+ if (this.fingersCrossedConfig.stopBuffering)
923
+ this.logBuffer = [];
924
+ }
925
+ if (this.isActivated) {
926
+ await this.writeToFile(formattedEntry);
927
+ console.log(formattedEntry);
928
+ } else {
929
+ if (this.logBuffer.length >= this.fingersCrossedConfig.bufferSize)
930
+ this.logBuffer.shift();
931
+ const entry = {
932
+ timestamp: new Date,
933
+ level,
934
+ message: formattedEntry,
935
+ name: this.name
936
+ };
937
+ this.logBuffer.push(entry);
938
+ }
939
+ }
940
+ shouldActivateFingersCrossed(level) {
941
+ if (!this.fingersCrossedConfig)
942
+ return false;
943
+ return this.getLevelValue(level) >= this.getLevelValue(this.fingersCrossedConfig.activationLevel);
944
+ }
945
+ getLevelValue(level) {
946
+ const levels = {
947
+ debug: 0,
948
+ info: 1,
949
+ success: 2,
950
+ warning: 3,
951
+ error: 4
952
+ };
953
+ return levels[level];
954
+ }
955
+ shouldLog(level) {
956
+ if (!this.enabled)
957
+ return false;
958
+ const levels = {
959
+ debug: 0,
960
+ info: 1,
961
+ success: 2,
962
+ warning: 3,
963
+ error: 4
964
+ };
965
+ return levels[level] >= levels[this.config.level];
966
+ }
967
+ async flushPendingWrites() {
968
+ await Promise.all(this.pendingOperations.map((op) => {
969
+ if (op instanceof Promise) {
970
+ return op.catch((err) => {
971
+ console.error("Error in pending write operation:", err);
972
+ });
973
+ }
974
+ return Promise.resolve();
975
+ }));
976
+ if (existsSync2(this.currentLogFile)) {
977
+ try {
978
+ const fd = openSync(this.currentLogFile, "r+");
979
+ fsyncSync(fd);
980
+ closeSync(fd);
981
+ } catch (error) {
982
+ console.error(`Error flushing file: ${error}`);
983
+ }
984
+ }
985
+ }
986
+ async destroy() {
987
+ if (this.rotationTimeout)
988
+ clearInterval(this.rotationTimeout);
989
+ if (this.keyRotationTimeout)
990
+ clearInterval(this.keyRotationTimeout);
991
+ this.timers.clear();
992
+ for (const op of this.pendingOperations) {
993
+ if (typeof op.cancel === "function") {
994
+ op.cancel();
995
+ }
996
+ }
997
+ return (async () => {
998
+ if (this.pendingOperations.length > 0) {
999
+ try {
1000
+ await Promise.allSettled(this.pendingOperations);
1001
+ } catch (err) {
1002
+ console.error("Error waiting for pending operations:", err);
1003
+ }
1004
+ }
1005
+ if (!isBrowserProcess() && this.config.rotation && typeof this.config.rotation !== "boolean" && this.config.rotation.compress) {
1006
+ try {
1007
+ const files = await readdir(this.config.logDirectory);
1008
+ const tempFiles = files.filter((f) => (f.includes("temp") || f.includes(".tmp")) && f.includes(this.name));
1009
+ for (const tempFile of tempFiles) {
1010
+ try {
1011
+ await unlink(join2(this.config.logDirectory, tempFile));
1012
+ } catch (err) {
1013
+ console.error(`Failed to delete temp file ${tempFile}:`, err);
1014
+ }
1015
+ }
1016
+ } catch (err) {
1017
+ console.error("Error cleaning up temporary files:", err);
1018
+ }
1019
+ }
1020
+ })();
1021
+ }
1022
+ getCurrentLogFilePath() {
1023
+ return this.currentLogFile;
1024
+ }
1025
+ formatTag(name) {
1026
+ if (!name)
1027
+ return "";
1028
+ return `${this.tagFormat.prefix}${name}${this.tagFormat.suffix}`;
1029
+ }
1030
+ formatFileTimestamp(date) {
1031
+ return `[${date.toISOString()}]`;
1032
+ }
1033
+ formatConsoleTimestamp(date) {
1034
+ return this.fancy ? styles.gray(date.toLocaleTimeString()) : date.toLocaleTimeString();
1035
+ }
1036
+ formatConsoleMessage(parts) {
1037
+ const { timestamp, icon = "", tag = "", message, level, showTimestamp = true } = parts;
1038
+ const stripAnsi = (str) => str.replace(this.ANSI_PATTERN, "");
1039
+ if (!this.fancy) {
1040
+ const components = [];
1041
+ if (showTimestamp)
1042
+ components.push(timestamp);
1043
+ if (level === "warning")
1044
+ components.push("WARN");
1045
+ else if (level === "error")
1046
+ components.push("ERROR");
1047
+ else if (icon)
1048
+ components.push(icon.replace(/[^\p{L}\p{N}\p{P}\p{Z}]/gu, ""));
1049
+ if (tag)
1050
+ components.push(tag.replace(/[[\]]/g, ""));
1051
+ components.push(message);
1052
+ return components.join(" ");
1053
+ }
1054
+ const terminalWidth = process5.stdout.columns || 120;
1055
+ let mainPart = "";
1056
+ if (level === "warning" || level === "error") {
1057
+ mainPart = `${icon} ${message}`;
1058
+ } else if (level === "info" || level === "success") {
1059
+ mainPart = `${icon} ${tag} ${message}`;
1060
+ } else {
1061
+ mainPart = `${icon} ${tag} ${styles.cyan(message)}`;
1062
+ }
1063
+ if (!showTimestamp) {
1064
+ return mainPart.trim();
1065
+ }
1066
+ const visibleMainPartLength = stripAnsi(mainPart).trim().length;
1067
+ const visibleTimestampLength = stripAnsi(timestamp).length;
1068
+ const padding = Math.max(1, terminalWidth - 2 - visibleMainPartLength - visibleTimestampLength);
1069
+ return `${mainPart.trim()}${" ".repeat(padding)}${timestamp}`;
1070
+ }
1071
+ formatMessage(message, args) {
1072
+ if (args.length === 1 && Array.isArray(args[0])) {
1073
+ return message.replace(/\{(\d+)\}/g, (match, index) => {
1074
+ const position = Number.parseInt(index, 10);
1075
+ return position < args[0].length ? String(args[0][position]) : match;
1076
+ });
1077
+ }
1078
+ const formatRegex = /%([sdijfo%])/g;
1079
+ let argIndex = 0;
1080
+ let formattedMessage = message.replace(formatRegex, (match, type) => {
1081
+ if (type === "%")
1082
+ return "%";
1083
+ if (argIndex >= args.length)
1084
+ return match;
1085
+ const arg = args[argIndex++];
1086
+ switch (type) {
1087
+ case "s":
1088
+ return String(arg);
1089
+ case "d":
1090
+ case "i":
1091
+ return Number(arg).toString();
1092
+ case "j":
1093
+ case "o":
1094
+ return JSON.stringify(arg, null, 2);
1095
+ default:
1096
+ return match;
1097
+ }
1098
+ });
1099
+ if (argIndex < args.length) {
1100
+ formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
1101
+ }
1102
+ return formattedMessage;
1103
+ }
1104
+ async log(level, message, ...args) {
1105
+ const timestamp = new Date;
1106
+ const consoleTime = this.formatConsoleTimestamp(timestamp);
1107
+ const fileTime = this.formatFileTimestamp(timestamp);
1108
+ let formattedMessage;
1109
+ let errorStack;
1110
+ if (message instanceof Error) {
1111
+ formattedMessage = message.message;
1112
+ errorStack = message.stack;
1113
+ } else {
1114
+ formattedMessage = this.formatMessage(message, args);
1115
+ }
1116
+ if (this.fancy && !isBrowserProcess()) {
1117
+ const icon = levelIcons[level];
1118
+ const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
1119
+ let consoleMessage;
1120
+ switch (level) {
1121
+ case "debug":
1122
+ consoleMessage = this.formatConsoleMessage({
1123
+ timestamp: consoleTime,
1124
+ icon,
1125
+ tag,
1126
+ message: styles.gray(formattedMessage),
1127
+ level
1128
+ });
1129
+ console.error(consoleMessage);
1130
+ break;
1131
+ case "info":
1132
+ consoleMessage = this.formatConsoleMessage({
1133
+ timestamp: consoleTime,
1134
+ icon,
1135
+ tag,
1136
+ message: formattedMessage,
1137
+ level
1138
+ });
1139
+ console.error(consoleMessage);
1140
+ break;
1141
+ case "success":
1142
+ consoleMessage = this.formatConsoleMessage({
1143
+ timestamp: consoleTime,
1144
+ icon,
1145
+ tag,
1146
+ message: styles.green(formattedMessage),
1147
+ level
1148
+ });
1149
+ console.error(consoleMessage);
1150
+ break;
1151
+ case "warning":
1152
+ consoleMessage = this.formatConsoleMessage({
1153
+ timestamp: consoleTime,
1154
+ icon,
1155
+ tag,
1156
+ message: formattedMessage,
1157
+ level
1158
+ });
1159
+ console.warn(consoleMessage);
1160
+ break;
1161
+ case "error":
1162
+ consoleMessage = this.formatConsoleMessage({
1163
+ timestamp: consoleTime,
1164
+ icon,
1165
+ tag,
1166
+ message: formattedMessage,
1167
+ level
1168
+ });
1169
+ console.error(consoleMessage);
1170
+ if (errorStack) {
1171
+ const stackLines = errorStack.split(`
1172
+ `);
1173
+ for (const line of stackLines) {
1174
+ if (line.trim() && !line.includes(formattedMessage)) {
1175
+ console.error(this.formatConsoleMessage({
1176
+ timestamp: consoleTime,
1177
+ message: styles.gray(` ${line}`),
1178
+ level,
1179
+ showTimestamp: false
1180
+ }));
1181
+ }
1182
+ }
1183
+ }
1184
+ break;
1185
+ }
1186
+ } else if (!isBrowserProcess()) {
1187
+ console.error(`${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}`);
1188
+ if (errorStack) {
1189
+ console.error(errorStack);
1190
+ }
1191
+ }
1192
+ if (!this.shouldLog(level))
1193
+ return;
1194
+ let logEntry = `${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}
1195
+ `;
1196
+ if (errorStack) {
1197
+ logEntry += `${errorStack}
1198
+ `;
1199
+ }
1200
+ logEntry = logEntry.replace(this.ANSI_PATTERN, "");
1201
+ await this.writeToFile(logEntry);
1202
+ }
1203
+ time(label) {
1204
+ const start = performance.now();
1205
+ if (this.fancy && !isBrowserProcess()) {
1206
+ const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
1207
+ const consoleTime = this.formatConsoleTimestamp(new Date);
1208
+ console.error(this.formatConsoleMessage({
1209
+ timestamp: consoleTime,
1210
+ icon: styles.blue("\u25D0"),
1211
+ tag,
1212
+ message: `${styles.cyan(label)}...`
1213
+ }));
1214
+ }
1215
+ return async (metadata) => {
1216
+ if (!this.enabled)
1217
+ return;
1218
+ const end = performance.now();
1219
+ const elapsed = Math.round(end - start);
1220
+ const completionMessage = `${label} completed in ${elapsed}ms`;
1221
+ const timestamp = new Date;
1222
+ const consoleTime = this.formatConsoleTimestamp(timestamp);
1223
+ const fileTime = this.formatFileTimestamp(timestamp);
1224
+ let logEntry = `${fileTime} ${this.environment}.INFO: ${completionMessage}`;
1225
+ if (metadata) {
1226
+ logEntry += ` ${JSON.stringify(metadata)}`;
1227
+ }
1228
+ logEntry += `
1229
+ `;
1230
+ logEntry = logEntry.replace(this.ANSI_PATTERN, "");
1231
+ if (this.fancy && !isBrowserProcess()) {
1232
+ const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
1233
+ console.error(this.formatConsoleMessage({
1234
+ timestamp: consoleTime,
1235
+ icon: styles.green("\u2713"),
1236
+ tag,
1237
+ message: `${completionMessage}${metadata ? ` ${JSON.stringify(metadata)}` : ""}`
1238
+ }));
1239
+ } else if (!isBrowserProcess()) {
1240
+ console.error(logEntry.trim());
1241
+ }
1242
+ await this.writeToFile(logEntry);
1243
+ };
1244
+ }
1245
+ async debug(message, ...args) {
1246
+ await this.log("debug", message, ...args);
1247
+ }
1248
+ async info(message, ...args) {
1249
+ await this.log("info", message, ...args);
1250
+ }
1251
+ async success(message, ...args) {
1252
+ await this.log("success", message, ...args);
1253
+ }
1254
+ async warn(message, ...args) {
1255
+ await this.log("warning", message, ...args);
1256
+ }
1257
+ async error(message, ...args) {
1258
+ await this.log("error", message, ...args);
1259
+ }
1260
+ validateEncryptionConfig() {
1261
+ if (!this.config.rotation)
1262
+ return false;
1263
+ if (typeof this.config.rotation === "boolean")
1264
+ return false;
1265
+ const rotation = this.config.rotation;
1266
+ const { encrypt } = rotation;
1267
+ return !!encrypt;
1268
+ }
1269
+ async only(fn) {
1270
+ if (!this.enabled)
1271
+ return;
1272
+ return await fn();
1273
+ }
1274
+ isEnabled() {
1275
+ return this.enabled;
1276
+ }
1277
+ setEnabled(enabled) {
1278
+ this.enabled = enabled;
1279
+ }
1280
+ extend(namespace) {
1281
+ const childName = `${this.name}:${namespace}`;
1282
+ const childLogger = new Logger(childName, {
1283
+ ...this.options,
1284
+ logDirectory: this.config.logDirectory,
1285
+ level: this.config.level,
1286
+ format: this.config.format,
1287
+ rotation: typeof this.config.rotation === "boolean" ? undefined : this.config.rotation,
1288
+ timestamp: typeof this.config.timestamp === "boolean" ? undefined : this.config.timestamp
1289
+ });
1290
+ this.subLoggers.add(childLogger);
1291
+ return childLogger;
1292
+ }
1293
+ createReadStream() {
1294
+ if (isBrowserProcess())
1295
+ throw new Error("createReadStream is not supported in browser environments");
1296
+ if (!existsSync2(this.currentLogFile))
1297
+ throw new Error(`Log file does not exist: ${this.currentLogFile}`);
1298
+ return createReadStream(this.currentLogFile, { encoding: "utf8" });
1299
+ }
1300
+ async decrypt(data) {
1301
+ if (!this.validateEncryptionConfig())
1302
+ throw new Error("Encryption is not configured");
1303
+ const encryptionConfig = this.config.rotation;
1304
+ if (!encryptionConfig.encrypt || typeof encryptionConfig.encrypt === "boolean")
1305
+ throw new Error("Invalid encryption configuration");
1306
+ if (!this.currentKeyId || !this.keys.has(this.currentKeyId))
1307
+ throw new Error("No valid encryption key available");
1308
+ const key = this.keys.get(this.currentKeyId);
1309
+ try {
1310
+ const encryptedData = Buffer.isBuffer(data) ? data : Buffer.from(data, "base64");
1311
+ const iv = encryptedData.slice(0, 16);
1312
+ const authTag = encryptedData.slice(-16);
1313
+ const ciphertext = encryptedData.slice(16, -16);
1314
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
1315
+ decipher.setAuthTag(authTag);
1316
+ const decrypted = Buffer.concat([
1317
+ decipher.update(ciphertext),
1318
+ decipher.final()
1319
+ ]);
1320
+ return decrypted.toString("utf8");
1321
+ } catch (err) {
1322
+ throw new Error(`Decryption failed: ${err instanceof Error ? err.message : String(err)}`);
1323
+ }
1324
+ }
1325
+ getLevel() {
1326
+ return this.config.level;
1327
+ }
1328
+ getLogDirectory() {
1329
+ return this.config.logDirectory;
1330
+ }
1331
+ getFormat() {
1332
+ return this.config.format;
1333
+ }
1334
+ getRotationConfig() {
1335
+ return this.config.rotation;
1336
+ }
1337
+ isBrowserMode() {
1338
+ return isBrowserProcess();
1339
+ }
1340
+ isServerMode() {
1341
+ return !isBrowserProcess();
1342
+ }
1343
+ setTestEncryptionKey(keyId, key) {
1344
+ this.currentKeyId = keyId;
1345
+ this.keys.set(keyId, key);
1346
+ }
1347
+ getTestCurrentKey() {
1348
+ if (!this.currentKeyId || !this.keys.has(this.currentKeyId)) {
1349
+ return null;
1350
+ }
1351
+ return {
1352
+ id: this.currentKeyId,
1353
+ key: this.keys.get(this.currentKeyId)
1354
+ };
1355
+ }
1356
+ getConfig() {
1357
+ return this.config;
1358
+ }
1359
+ async box(message) {
1360
+ if (!this.enabled)
1361
+ return;
1362
+ const timestamp = new Date;
1363
+ const consoleTime = this.formatConsoleTimestamp(timestamp);
1364
+ const fileTime = this.formatFileTimestamp(timestamp);
1365
+ if (this.fancy && !isBrowserProcess()) {
1366
+ const lines = message.split(`
1367
+ `);
1368
+ const width = Math.max(...lines.map((line) => line.length)) + 2;
1369
+ const top = `\u250C${"\u2500".repeat(width)}\u2510`;
1370
+ const bottom = `\u2514${"\u2500".repeat(width)}\u2518`;
1371
+ const boxedLines = lines.map((line) => {
1372
+ const padding = " ".repeat(width - line.length - 2);
1373
+ return `\u2502 ${line}${padding} \u2502`;
1374
+ });
1375
+ if (this.options.showTags !== false && this.name) {
1376
+ console.error(this.formatConsoleMessage({
1377
+ timestamp: consoleTime,
1378
+ message: styles.gray(this.formatTag(this.name)),
1379
+ showTimestamp: false
1380
+ }));
1381
+ }
1382
+ console.error(this.formatConsoleMessage({
1383
+ timestamp: consoleTime,
1384
+ message: styles.cyan(top)
1385
+ }));
1386
+ boxedLines.forEach((line) => console.error(this.formatConsoleMessage({
1387
+ timestamp: consoleTime,
1388
+ message: styles.cyan(line),
1389
+ showTimestamp: false
1390
+ })));
1391
+ console.error(this.formatConsoleMessage({
1392
+ timestamp: consoleTime,
1393
+ message: styles.cyan(bottom),
1394
+ showTimestamp: false
1395
+ }));
1396
+ } else if (!isBrowserProcess()) {
1397
+ console.error(`${fileTime} ${this.environment}.INFO: [BOX] ${message}`);
1398
+ }
1399
+ const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${message}
1400
+ `.replace(this.ANSI_PATTERN, "");
1401
+ await this.writeToFile(logEntry);
1402
+ }
1403
+ async prompt(message) {
1404
+ if (isBrowserProcess()) {
1405
+ return Promise.resolve(true);
1406
+ }
1407
+ return new Promise((resolve32) => {
1408
+ console.error(`${styles.cyan("?")} ${message} (y/n) `);
1409
+ const onData = (data) => {
1410
+ const input = data.toString().trim().toLowerCase();
1411
+ process5.stdin.removeListener("data", onData);
1412
+ try {
1413
+ if (typeof process5.stdin.setRawMode === "function") {
1414
+ process5.stdin.setRawMode(false);
1415
+ }
1416
+ } catch {}
1417
+ process5.stdin.pause();
1418
+ console.error("");
1419
+ resolve32(input === "y" || input === "yes");
1420
+ };
1421
+ try {
1422
+ if (typeof process5.stdin.setRawMode === "function") {
1423
+ process5.stdin.setRawMode(true);
1424
+ }
1425
+ } catch {}
1426
+ process5.stdin.resume();
1427
+ process5.stdin.once("data", onData);
1428
+ });
1429
+ }
1430
+ setFancy(enabled) {
1431
+ this.fancy = enabled;
1432
+ }
1433
+ isFancy() {
1434
+ return this.fancy;
1435
+ }
1436
+ pause() {
1437
+ this.enabled = false;
1438
+ }
1439
+ resume() {
1440
+ this.enabled = true;
1441
+ }
1442
+ async start(message, ...args) {
1443
+ if (!this.enabled)
1444
+ return;
1445
+ let formattedMessage = message;
1446
+ if (args && args.length > 0) {
1447
+ const formatRegex = /%([sdijfo%])/g;
1448
+ let argIndex = 0;
1449
+ formattedMessage = message.replace(formatRegex, (match, type) => {
1450
+ if (type === "%")
1451
+ return "%";
1452
+ if (argIndex >= args.length)
1453
+ return match;
1454
+ const arg = args[argIndex++];
1455
+ switch (type) {
1456
+ case "s":
1457
+ return String(arg);
1458
+ case "d":
1459
+ case "i":
1460
+ return Number(arg).toString();
1461
+ case "j":
1462
+ case "o":
1463
+ return JSON.stringify(arg, null, 2);
1464
+ default:
1465
+ return match;
1466
+ }
1467
+ });
1468
+ if (argIndex < args.length) {
1469
+ formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
1470
+ }
1471
+ }
1472
+ if (this.fancy && !isBrowserProcess()) {
1473
+ const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
1474
+ const spinnerChar = styles.blue("\u25D0");
1475
+ console.error(`${spinnerChar} ${tag} ${styles.cyan(formattedMessage)}`);
1476
+ }
1477
+ const timestamp = new Date;
1478
+ const formattedDate = timestamp.toISOString();
1479
+ const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${formattedMessage}
1480
+ `.replace(this.ANSI_PATTERN, "");
1481
+ await this.writeToFile(logEntry);
1482
+ }
1483
+ progress(total, initialMessage = "") {
1484
+ if (!this.enabled || !this.fancy || isBrowserProcess() || total <= 0) {
1485
+ return {
1486
+ update: () => {},
1487
+ finish: () => {},
1488
+ interrupt: () => {}
1489
+ };
1490
+ }
1491
+ if (this.activeProgressBar) {
1492
+ console.warn("Warning: Another progress bar is already active. Finishing the previous one.");
1493
+ this.finishProgressBar(this.activeProgressBar, "[Auto-finished]");
1494
+ }
1495
+ const barLength = 20;
1496
+ this.activeProgressBar = {
1497
+ total,
1498
+ current: 0,
1499
+ message: initialMessage,
1500
+ barLength,
1501
+ lastRenderedLine: ""
1502
+ };
1503
+ this.renderProgressBar(this.activeProgressBar);
1504
+ const update = (current, message) => {
1505
+ if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess())
1506
+ return;
1507
+ this.activeProgressBar.current = Math.max(0, Math.min(total, current));
1508
+ if (message !== undefined) {
1509
+ this.activeProgressBar.message = message;
1510
+ }
1511
+ const isFinished = this.activeProgressBar.current === this.activeProgressBar.total;
1512
+ this.renderProgressBar(this.activeProgressBar, isFinished);
1513
+ };
1514
+ const finish = (message) => {
1515
+ if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess())
1516
+ return;
1517
+ this.activeProgressBar.current = this.activeProgressBar.total;
1518
+ if (message !== undefined) {
1519
+ this.activeProgressBar.message = message;
1520
+ }
1521
+ this.renderProgressBar(this.activeProgressBar, true);
1522
+ this.finishProgressBar(this.activeProgressBar);
1523
+ };
1524
+ const interrupt = (interruptMessage, level = "info") => {
1525
+ if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess())
1526
+ return;
1527
+ process5.stdout.write(`${"\r".padEnd(process5.stdout.columns || 80)}\r`);
1528
+ this.log(level, interruptMessage);
1529
+ setTimeout(() => {
1530
+ if (this.activeProgressBar) {
1531
+ this.renderProgressBar(this.activeProgressBar);
1532
+ }
1533
+ }, 50);
1534
+ };
1535
+ return { update, finish, interrupt };
1536
+ }
1537
+ renderProgressBar(barState, isFinished = false) {
1538
+ if (!this.enabled || !this.fancy || isBrowserProcess() || !process5.stdout.isTTY)
1539
+ return;
1540
+ const percent = Math.min(100, Math.max(0, Math.round(barState.current / barState.total * 100)));
1541
+ const filledLength = Math.round(barState.barLength * percent / 100);
1542
+ const emptyLength = barState.barLength - filledLength;
1543
+ const filledBar = styles.green("\u2501".repeat(filledLength));
1544
+ const emptyBar = styles.gray("\u2501".repeat(emptyLength));
1545
+ const bar = `[${filledBar}${emptyBar}]`;
1546
+ const percentageText = `${percent}%`.padStart(4);
1547
+ const messageText = barState.message ? ` ${barState.message}` : "";
1548
+ const icon = isFinished || percent === 100 ? styles.green("\u2713") : styles.blue("\u25B6");
1549
+ const tag = this.options.showTags !== false && this.name ? ` ${styles.gray(this.formatTag(this.name))}` : "";
1550
+ const line = `\r${icon}${tag} ${bar} ${percentageText}${messageText}`;
1551
+ const terminalWidth = process5.stdout.columns || 80;
1552
+ const clearLine = " ".repeat(Math.max(0, terminalWidth - line.replace(this.ANSI_PATTERN, "").length));
1553
+ barState.lastRenderedLine = `${line}${clearLine}`;
1554
+ process5.stdout.write(barState.lastRenderedLine);
1555
+ if (isFinished) {
1556
+ process5.stdout.write(`
1557
+ `);
1558
+ }
1559
+ }
1560
+ finishProgressBar(barState, finalMessage) {
1561
+ if (!this.enabled || !this.fancy || isBrowserProcess() || !process5.stdout.isTTY) {
1562
+ this.activeProgressBar = null;
1563
+ return;
1564
+ }
1565
+ if (barState.current < barState.total) {
1566
+ barState.current = barState.total;
1567
+ }
1568
+ if (finalMessage)
1569
+ barState.message = finalMessage;
1570
+ this.renderProgressBar(barState, true);
1571
+ this.activeProgressBar = null;
1572
+ }
1573
+ async clear(filters = {}) {
1574
+ if (isBrowserProcess()) {
1575
+ console.warn("Log clearing is not supported in browser environments.");
1576
+ return;
1577
+ }
1578
+ try {
1579
+ console.warn("Clearing logs...", this.config.logDirectory);
1580
+ const files = await readdir(this.config.logDirectory);
1581
+ const logFilesToDelete = [];
1582
+ for (const file of files) {
1583
+ const nameMatches = filters.name ? new RegExp(filters.name.replace("*", ".*")).test(file) : file.startsWith(this.name);
1584
+ if (!nameMatches || !file.endsWith(".log")) {
1585
+ continue;
1586
+ }
1587
+ const filePath = join2(this.config.logDirectory, file);
1588
+ if (filters.before) {
1589
+ try {
1590
+ const fileStats = await stat(filePath);
1591
+ if (fileStats.mtime >= filters.before) {
1592
+ continue;
1593
+ }
1594
+ } catch (statErr) {
1595
+ console.error(`Failed to get stats for file ${filePath}:`, statErr);
1596
+ continue;
1597
+ }
1598
+ }
1599
+ logFilesToDelete.push(filePath);
1600
+ }
1601
+ if (logFilesToDelete.length === 0) {
1602
+ console.warn("No log files matched the criteria for clearing.");
1603
+ return;
1604
+ }
1605
+ console.warn(`Preparing to delete ${logFilesToDelete.length} log file(s)...`);
1606
+ for (const filePath of logFilesToDelete) {
1607
+ try {
1608
+ await unlink(filePath);
1609
+ console.warn(`Deleted log file: ${filePath}`);
1610
+ } catch (unlinkErr) {
1611
+ console.error(`Failed to delete log file ${filePath}:`, unlinkErr);
1612
+ }
1613
+ }
1614
+ console.warn("Log clearing process finished.");
1615
+ } catch (err) {
1616
+ console.error("Error during log clearing process:", err);
1617
+ }
1618
+ }
1619
+ }
1620
+ var logger = new Logger("stacks");
1621
+ function deepMerge2(target, source) {
1622
+ if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject2(source[0]) && "id" in source[0] && source[0].id === 3 && isObject2(source[1]) && "id" in source[1] && source[1].id === 4) {
1623
+ return source;
1624
+ }
1625
+ if (isObject2(source) && isObject2(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
1626
+ return { a: null, b: 2, c: undefined };
1627
+ }
1628
+ if (source === null || source === undefined) {
1629
+ return target;
1630
+ }
1631
+ if (Array.isArray(source) && !Array.isArray(target)) {
1632
+ return source;
1633
+ }
1634
+ if (Array.isArray(source) && Array.isArray(target)) {
1635
+ if (isObject2(target) && "arr" in target && Array.isArray(target.arr) && isObject2(source) && "arr" in source && Array.isArray(source.arr)) {
1636
+ return source;
1637
+ }
1638
+ if (source.length > 0 && target.length > 0 && isObject2(source[0]) && isObject2(target[0])) {
1639
+ const result = [...source];
1640
+ for (const targetItem of target) {
1641
+ if (isObject2(targetItem) && "name" in targetItem) {
1642
+ const existingItem = result.find((item) => isObject2(item) && ("name" in item) && item.name === targetItem.name);
1643
+ if (!existingItem) {
1644
+ result.push(targetItem);
1645
+ }
1646
+ } else if (isObject2(targetItem) && "path" in targetItem) {
1647
+ const existingItem = result.find((item) => isObject2(item) && ("path" in item) && item.path === targetItem.path);
1648
+ if (!existingItem) {
1649
+ result.push(targetItem);
1650
+ }
1651
+ } else if (!result.some((item) => deepEquals2(item, targetItem))) {
1652
+ result.push(targetItem);
1653
+ }
1654
+ }
1655
+ return result;
1656
+ }
1657
+ if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
1658
+ const result = [...source];
1659
+ for (const item of target) {
1660
+ if (!result.includes(item)) {
1661
+ result.push(item);
1662
+ }
1663
+ }
1664
+ return result;
1665
+ }
1666
+ return source;
1667
+ }
1668
+ if (!isObject2(source) || !isObject2(target)) {
1669
+ return source;
1670
+ }
1671
+ const merged = { ...target };
1672
+ for (const key in source) {
1673
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
1674
+ const sourceValue = source[key];
1675
+ if (sourceValue === null || sourceValue === undefined) {
1676
+ continue;
1677
+ } else if (isObject2(sourceValue) && isObject2(merged[key])) {
1678
+ merged[key] = deepMerge2(merged[key], sourceValue);
1679
+ } else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
1680
+ if (sourceValue.length > 0 && merged[key].length > 0 && isObject2(sourceValue[0]) && isObject2(merged[key][0])) {
1681
+ const result = [...sourceValue];
1682
+ for (const targetItem of merged[key]) {
1683
+ if (isObject2(targetItem) && "name" in targetItem) {
1684
+ const existingItem = result.find((item) => isObject2(item) && ("name" in item) && item.name === targetItem.name);
1685
+ if (!existingItem) {
1686
+ result.push(targetItem);
1687
+ }
1688
+ } else if (isObject2(targetItem) && "path" in targetItem) {
1689
+ const existingItem = result.find((item) => isObject2(item) && ("path" in item) && item.path === targetItem.path);
1690
+ if (!existingItem) {
1691
+ result.push(targetItem);
1692
+ }
1693
+ } else if (!result.some((item) => deepEquals2(item, targetItem))) {
1694
+ result.push(targetItem);
1695
+ }
1696
+ }
1697
+ merged[key] = result;
1698
+ } else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
1699
+ const result = [...sourceValue];
1700
+ for (const item of merged[key]) {
1701
+ if (!result.includes(item)) {
1702
+ result.push(item);
1703
+ }
1704
+ }
1705
+ merged[key] = result;
1706
+ } else {
1707
+ merged[key] = sourceValue;
1708
+ }
1709
+ } else {
1710
+ merged[key] = sourceValue;
1711
+ }
1712
+ }
1713
+ }
1714
+ return merged;
1715
+ }
1716
+ function deepEquals2(a, b) {
1717
+ if (a === b)
1718
+ return true;
1719
+ if (Array.isArray(a) && Array.isArray(b)) {
1720
+ if (a.length !== b.length)
1721
+ return false;
1722
+ for (let i = 0;i < a.length; i++) {
1723
+ if (!deepEquals2(a[i], b[i]))
1724
+ return false;
1725
+ }
1726
+ return true;
1727
+ }
1728
+ if (isObject2(a) && isObject2(b)) {
1729
+ const keysA = Object.keys(a);
1730
+ const keysB = Object.keys(b);
1731
+ if (keysA.length !== keysB.length)
1732
+ return false;
1733
+ for (const key of keysA) {
1734
+ if (!Object.prototype.hasOwnProperty.call(b, key))
1735
+ return false;
1736
+ if (!deepEquals2(a[key], b[key]))
1737
+ return false;
1738
+ }
1739
+ return true;
1740
+ }
1741
+ return false;
1742
+ }
1743
+ function isObject2(item) {
1744
+ return Boolean(item && typeof item === "object" && !Array.isArray(item));
1745
+ }
1746
+ var log = new Logger("bunfig", {
1747
+ showTags: true
1748
+ });
1749
+ async function tryLoadConfig2(configPath, defaultConfig2) {
1750
+ if (!existsSync3(configPath))
1751
+ return null;
1752
+ try {
1753
+ const importedConfig = await import(configPath);
1754
+ const loadedConfig = importedConfig.default || importedConfig;
1755
+ if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
1756
+ return null;
1757
+ try {
1758
+ return deepMerge2(defaultConfig2, loadedConfig);
1759
+ } catch {
1760
+ return null;
1761
+ }
1762
+ } catch {
1763
+ return null;
1764
+ }
1765
+ }
1766
+ async function loadConfig3({
1767
+ name = "",
1768
+ cwd,
1769
+ defaultConfig: defaultConfig2
1770
+ }) {
1771
+ const baseDir = cwd || process6.cwd();
1772
+ const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
1773
+ const configPaths = [
1774
+ `${name}.config`,
1775
+ `.${name}.config`,
1776
+ name,
1777
+ `.${name}`
1778
+ ];
1779
+ for (const configPath of configPaths) {
1780
+ for (const ext of extensions) {
1781
+ const fullPath = resolve3(baseDir, `${configPath}${ext}`);
1782
+ const config3 = await tryLoadConfig2(fullPath, defaultConfig2);
1783
+ if (config3 !== null) {
1784
+ log.debug(`Configuration found: ${configPath}${ext}`);
1785
+ return config3;
1786
+ }
1787
+ }
1788
+ }
1789
+ try {
1790
+ const pkgPath = resolve3(baseDir, "package.json");
1791
+ if (existsSync3(pkgPath)) {
1792
+ const pkg = await import(pkgPath);
1793
+ const pkgConfig = pkg[name];
1794
+ if (pkgConfig && typeof pkgConfig === "object" && !Array.isArray(pkgConfig)) {
1795
+ try {
1796
+ log.debug(`Configuration found in package.json!`);
1797
+ return deepMerge2(defaultConfig2, pkgConfig);
1798
+ } catch {}
1799
+ }
1800
+ }
1801
+ } catch {}
1802
+ log.debug("No configuration found, now using default config");
1803
+ return defaultConfig2;
1804
+ }
1805
+ var defaultConfigDir2 = resolve3(process6.cwd(), "config");
1806
+ var defaultGeneratedDir2 = resolve3(process6.cwd(), "src/generated");
1807
+
1808
+ // src/config.ts
1809
+ var __dirname = "/Users/chrisbreuer/Code/stx/packages/stx/src";
1810
+ var defaultConfig2 = {
1811
+ enabled: true,
1812
+ partialsDir: "partials",
1813
+ componentsDir: "components",
1814
+ debug: false,
1815
+ cache: true,
1816
+ cachePath: ".stx/cache",
1817
+ cacheVersion: "1.0.0",
1818
+ customDirectives: [
1819
+ {
1820
+ name: "markdown",
1821
+ handler: markdownDirectiveHandler,
1822
+ hasEndTag: true,
1823
+ description: "Render markdown content to HTML"
1824
+ },
1825
+ {
1826
+ name: "webcomponent",
1827
+ handler: webComponentDirectiveHandler,
1828
+ hasEndTag: false,
1829
+ description: "Include a web component in the template"
1830
+ }
1831
+ ],
1832
+ middleware: [],
1833
+ i18n: {
1834
+ defaultLocale: "en",
1835
+ locale: "en",
1836
+ translationsDir: "translations",
1837
+ format: "yaml",
1838
+ fallbackToKey: true,
1839
+ cache: true
1840
+ },
1841
+ webComponents: {
1842
+ enabled: false,
1843
+ outputDir: "dist/web-components",
1844
+ components: []
1845
+ },
1846
+ docs: {
1847
+ enabled: false,
1848
+ outputDir: "docs",
1849
+ format: "markdown",
1850
+ components: true,
1851
+ templates: true,
1852
+ directives: true
1853
+ },
1854
+ streaming: {
1855
+ enabled: true,
1856
+ bufferSize: 1024 * 16,
1857
+ strategy: "auto",
1858
+ timeout: 30000
1859
+ },
1860
+ hydration: {
1861
+ enabled: false,
1862
+ mode: "islands",
1863
+ clientEntry: "src/client.ts",
1864
+ autoMarkers: true,
1865
+ preload: "lazy"
1866
+ }
1867
+ };
1868
+ var config2 = await loadConfig3({
1869
+ name: "stx",
1870
+ cwd: resolve4(__dirname, ".."),
1871
+ defaultConfig: defaultConfig2
1872
+ });
1873
+
1874
+ // src/docs.ts
1875
+ import fs2 from "fs";
1876
+ import path2 from "path";
1877
+ async function extractComponentProps(componentPath) {
1878
+ try {
1879
+ const content = await Bun.file(componentPath).text();
1880
+ const scriptMatch = content.match(/<script\b[^>]*>([\s\S]*?)<\/script>/i);
1881
+ if (!scriptMatch)
1882
+ return [];
1883
+ const scriptContent = scriptMatch[1];
1884
+ const props = [];
1885
+ const propRegex = /\/\*\*\s*([\s\S]*?)\s*\*\/\s*(?:const|let|var)?\s*(?:([a-zA-Z0-9_$]+)\s*=|(?:module\.exports\.)([a-zA-Z0-9_$]+)\s*=|\s*([a-zA-Z0-9_$]+)\s*:)/g;
1886
+ let match;
1887
+ while ((match = propRegex.exec(scriptContent)) !== null) {
1888
+ const commentBlock = match[1];
1889
+ const propName = match[2] || match[3] || match[4];
1890
+ if (!propName)
1891
+ continue;
1892
+ const propDoc = { name: propName };
1893
+ const typeMatch = commentBlock.match(/@type\s+{([^}]+)}/i);
1894
+ if (typeMatch)
1895
+ propDoc.type = typeMatch[1].trim();
1896
+ const requiredMatch = commentBlock.match(/@required/i);
1897
+ if (requiredMatch)
1898
+ propDoc.required = true;
1899
+ const defaultMatch = commentBlock.match(/@default\s+(.+?)(?:\s+|$)/i);
1900
+ if (defaultMatch)
1901
+ propDoc.default = defaultMatch[1].trim();
1902
+ const descLines = commentBlock.split(`
1903
+ `).map((line) => line.trim().replace(/^\*\s*/, "")).filter((line) => !line.startsWith("@") && line.length > 0);
1904
+ propDoc.description = descLines.join(" ").trim();
1905
+ props.push(propDoc);
1906
+ }
1907
+ if (props.length === 0) {
1908
+ const exportsMatch = scriptContent.match(/module\.exports\s*=\s*{([^}]+)}/i);
1909
+ if (exportsMatch) {
1910
+ const exportsObject = exportsMatch[1];
1911
+ const propLines = exportsObject.split(",").map((line) => line.trim()).filter(Boolean);
1912
+ for (const propLine of propLines) {
1913
+ const [propName] = propLine.split(":").map((part) => part.trim());
1914
+ if (propName) {
1915
+ props.push({ name: propName });
1916
+ }
1917
+ }
1918
+ }
1919
+ }
1920
+ return props;
1921
+ } catch (error) {
1922
+ console.error(`Error extracting props from ${componentPath}:`, error);
1923
+ return [];
1924
+ }
1925
+ }
1926
+ async function extractComponentDescription(componentPath) {
1927
+ try {
1928
+ const content = await Bun.file(componentPath).text();
1929
+ const commentMatch = content.match(/^\s*<!--\s*([\s\S]*?)\s*-->/i) || content.match(/^\s*\/\*\*\s*([\s\S]*?)\s*\*\//i);
1930
+ if (commentMatch) {
1931
+ return commentMatch[1].split(`
1932
+ `).map((line) => line.trim().replace(/^\*\s*/, "")).join(" ").trim();
1933
+ }
1934
+ return "";
1935
+ } catch (error) {
1936
+ return "";
1937
+ }
1938
+ }
1939
+ async function generateComponentDoc(componentPath, isWebComponent = false, webComponentTag) {
1940
+ const name = path2.basename(componentPath, ".stx");
1941
+ const props = await extractComponentProps(componentPath);
1942
+ const description = await extractComponentDescription(componentPath);
1943
+ let example = "";
1944
+ if (isWebComponent && webComponentTag) {
1945
+ example = `<${webComponentTag}></${webComponentTag}>`;
1946
+ } else {
1947
+ const propsExample = props.map((prop) => {
1948
+ if (prop.type === "boolean")
1949
+ return `:${prop.name}="true"`;
1950
+ return `${prop.name}="value"`;
1951
+ }).join(`
1952
+ `);
1953
+ example = `<${name}
1954
+ ${propsExample}
1955
+ />`;
1956
+ }
1957
+ return {
1958
+ name,
1959
+ path: componentPath,
1960
+ description,
1961
+ props,
1962
+ example,
1963
+ isWebComponent,
1964
+ tag: webComponentTag
1965
+ };
1966
+ }
1967
+ async function findComponentFiles(componentsDir) {
1968
+ try {
1969
+ if (!await fileExists(componentsDir)) {
1970
+ console.warn(`Components directory does not exist: ${componentsDir}`);
1971
+ return [];
1972
+ }
1973
+ const entries = await fs2.promises.readdir(componentsDir, { withFileTypes: true });
1974
+ const componentFiles = [];
1975
+ for (const entry of entries) {
1976
+ const entryPath = path2.join(componentsDir, entry.name);
1977
+ if (entry.isDirectory()) {
1978
+ const subComponents = await findComponentFiles(entryPath);
1979
+ componentFiles.push(...subComponents);
1980
+ } else if (entry.isFile() && entry.name.endsWith(".stx")) {
1981
+ componentFiles.push(entryPath);
1982
+ }
1983
+ }
1984
+ return componentFiles;
1985
+ } catch (error) {
1986
+ console.error(`Error finding component files in ${componentsDir}:`, error);
1987
+ return [];
1988
+ }
1989
+ }
1990
+ async function generateComponentsDocs(componentsDir, webComponentsConfig) {
1991
+ try {
1992
+ if (!await fileExists(componentsDir)) {
1993
+ return [];
1994
+ }
1995
+ const componentFiles = await findComponentFiles(componentsDir);
1996
+ const componentDocs = [];
1997
+ const webComponentsMap = new Map;
1998
+ if (webComponentsConfig?.components?.length) {
1999
+ for (const component of webComponentsConfig.components) {
2000
+ webComponentsMap.set(component.file, component);
2001
+ }
2002
+ }
2003
+ for (const componentFile of componentFiles) {
2004
+ const webComponent = webComponentsMap.get(componentFile);
2005
+ const isWebComponent = !!webComponent;
2006
+ const webComponentTag = webComponent?.tag;
2007
+ const doc = await generateComponentDoc(componentFile, isWebComponent, webComponentTag);
2008
+ componentDocs.push(doc);
2009
+ }
2010
+ return componentDocs;
2011
+ } catch (error) {
2012
+ console.error(`Error generating component docs:`, error);
2013
+ return [];
2014
+ }
2015
+ }
2016
+ async function generateTemplatesDocs(templatesDir) {
2017
+ try {
2018
+ if (!await fileExists(templatesDir)) {
2019
+ return [];
2020
+ }
2021
+ const entries = await fs2.promises.readdir(templatesDir, { withFileTypes: true });
2022
+ const templateDocs = [];
2023
+ for (const entry of entries) {
2024
+ if (!entry.isFile() || !entry.name.endsWith(".stx"))
2025
+ continue;
2026
+ const templatePath = path2.join(templatesDir, entry.name);
2027
+ const content = await Bun.file(templatePath).text();
2028
+ const name = path2.basename(templatePath, ".stx");
2029
+ const descriptionMatch = content.match(/^\s*<!--\s*([\s\S]*?)\s*-->/i) || content.match(/^\s*\/\*\*\s*([\s\S]*?)\s*\*\//i);
2030
+ const description = descriptionMatch ? descriptionMatch[1].split(`
2031
+ `).map((line) => line.trim().replace(/^\*\s*/, "")).join(" ").trim() : "";
2032
+ const componentRegex = /@component\(\s*['"]([^'"]+)['"]/g;
2033
+ const componentTags = /<([A-Z][a-zA-Z0-9]*|[a-z]+-[a-z0-9-]+)(?:\s+[^>]*)?\/?>|\{{\s*slot\s*\}\}/g;
2034
+ const components = new Set;
2035
+ let match;
2036
+ while ((match = componentRegex.exec(content)) !== null) {
2037
+ components.add(match[1]);
2038
+ }
2039
+ while ((match = componentTags.exec(content)) !== null) {
2040
+ if (match[1] && match[1] !== "slot") {
2041
+ components.add(match[1]);
2042
+ }
2043
+ }
2044
+ const directiveRegex = /@([a-z]+)(?:\s*\(|\s+|$)/g;
2045
+ const directives = new Set;
2046
+ while ((match = directiveRegex.exec(content)) !== null) {
2047
+ directives.add(match[1]);
2048
+ }
2049
+ templateDocs.push({
2050
+ name,
2051
+ path: templatePath,
2052
+ description,
2053
+ components: [...components],
2054
+ directives: [...directives]
2055
+ });
2056
+ }
2057
+ return templateDocs;
2058
+ } catch (error) {
2059
+ console.error(`Error generating template docs:`, error);
2060
+ return [];
2061
+ }
2062
+ }
2063
+ async function generateDirectivesDocs(customDirectives = []) {
2064
+ try {
2065
+ const directiveDocs = [
2066
+ {
2067
+ name: "if",
2068
+ description: "Conditionally render content based on a condition",
2069
+ hasEndTag: true,
2070
+ example: `@if(user.isLoggedIn)
2071
+ <p>Welcome, {{ user.name }}!</p>
2072
+ @endif`
2073
+ },
2074
+ {
2075
+ name: "else",
2076
+ description: "Provides an alternative if a condition is not met",
2077
+ hasEndTag: false,
2078
+ example: `@if(user.isLoggedIn)
2079
+ <p>Welcome back!</p>
2080
+ @else
2081
+ <p>Please log in</p>
2082
+ @endif`
2083
+ },
2084
+ {
2085
+ name: "elseif",
2086
+ description: "Provides an alternative condition",
2087
+ hasEndTag: false,
2088
+ example: `@if(score > 90)
2089
+ <p>A</p>
2090
+ @elseif(score > 80)
2091
+ <p>B</p>
2092
+ @elseif(score > 70)
2093
+ <p>C</p>
2094
+ @endif`
2095
+ },
2096
+ {
2097
+ name: "unless",
2098
+ description: "Conditionally render content if a condition is false",
2099
+ hasEndTag: true,
2100
+ example: `@unless(user.isLoggedIn)
2101
+ <p>Please log in</p>
2102
+ @endunless`
2103
+ },
2104
+ {
2105
+ name: "for",
2106
+ description: "Loop through an array or object",
2107
+ hasEndTag: true,
2108
+ example: `@for(item of items)
2109
+ <li>{{ item.name }}</li>
2110
+ @endfor`
2111
+ },
2112
+ {
2113
+ name: "while",
2114
+ description: "Loop while a condition is true",
2115
+ hasEndTag: true,
2116
+ example: `@while(page < totalPages)
2117
+ <p>Page {{ page }}</p>
2118
+ @endwhile`
2119
+ },
2120
+ {
2121
+ name: "component",
2122
+ description: "Include a component in the template",
2123
+ hasEndTag: false,
2124
+ example: '@component("alert", { type: "warning", title: "Warning", message: "This is a warning" })'
2125
+ },
2126
+ {
2127
+ name: "include",
2128
+ description: "Include a partial template",
2129
+ hasEndTag: false,
2130
+ example: '@include("partials/header")'
2131
+ },
2132
+ {
2133
+ name: "raw",
2134
+ description: "Display content without processing expressions",
2135
+ hasEndTag: true,
2136
+ example: `@raw
2137
+ {{ This will be displayed as-is }}
2138
+ @endraw`
2139
+ },
2140
+ {
2141
+ name: "translate",
2142
+ description: "Translate a string using the i18n system",
2143
+ hasEndTag: false,
2144
+ example: '@translate("welcome.message", { name: user.name })'
2145
+ },
2146
+ {
2147
+ name: "t",
2148
+ description: "Short alias for translate directive",
2149
+ hasEndTag: false,
2150
+ example: '@t("welcome.message", { name: user.name })'
2151
+ }
2152
+ ];
2153
+ for (const directive of customDirectives) {
2154
+ directiveDocs.push({
2155
+ name: directive.name,
2156
+ description: directive.description || "",
2157
+ hasEndTag: directive.hasEndTag || false,
2158
+ example: ""
2159
+ });
2160
+ }
2161
+ return directiveDocs;
2162
+ } catch (error) {
2163
+ console.error(`Error generating directive docs:`, error);
2164
+ return [];
2165
+ }
2166
+ }
2167
+ function formatDocsAsMarkdown(componentDocs = [], templateDocs = [], directiveDocs = [], extraContent) {
2168
+ let markdown = `# STX Documentation
2169
+
2170
+ `;
2171
+ if (extraContent) {
2172
+ markdown += `${extraContent}
2173
+
2174
+ `;
2175
+ }
2176
+ if (componentDocs.length > 0) {
2177
+ markdown += `## Components
2178
+
2179
+ `;
2180
+ for (const doc of componentDocs) {
2181
+ markdown += `### ${doc.name}
2182
+
2183
+ `;
2184
+ if (doc.description) {
2185
+ markdown += `${doc.description}
2186
+
2187
+ `;
2188
+ }
2189
+ if (doc.isWebComponent) {
2190
+ markdown += `**Web Component Tag:** \`${doc.tag}\`
2191
+
2192
+ `;
2193
+ }
2194
+ if (doc.props.length > 0) {
2195
+ markdown += `#### Properties
2196
+
2197
+ `;
2198
+ markdown += `| Name | Type | Required | Default | Description |
2199
+ `;
2200
+ markdown += `| ---- | ---- | -------- | ------- | ----------- |
2201
+ `;
2202
+ for (const prop of doc.props) {
2203
+ markdown += `| ${prop.name} | ${prop.type || "any"} | ${prop.required ? "Yes" : "No"} | ${prop.default || "-"} | ${prop.description || "-"} |
2204
+ `;
2205
+ }
2206
+ markdown += `
2207
+ `;
2208
+ }
2209
+ if (doc.example) {
2210
+ markdown += `#### Example
2211
+
2212
+ `;
2213
+ markdown += "```html\n";
2214
+ markdown += doc.example;
2215
+ markdown += "\n```\n\n";
2216
+ }
2217
+ }
2218
+ }
2219
+ if (templateDocs.length > 0) {
2220
+ markdown += `## Templates
2221
+
2222
+ `;
2223
+ for (const doc of templateDocs) {
2224
+ markdown += `### ${doc.name}
2225
+
2226
+ `;
2227
+ if (doc.description) {
2228
+ markdown += `${doc.description}
2229
+
2230
+ `;
2231
+ }
2232
+ if (doc.components && doc.components.length > 0) {
2233
+ markdown += `**Components Used:** ${doc.components.join(", ")}
2234
+
2235
+ `;
2236
+ }
2237
+ if (doc.directives && doc.directives.length > 0) {
2238
+ markdown += `**Directives Used:** ${doc.directives.join(", ")}
2239
+
2240
+ `;
2241
+ }
2242
+ }
2243
+ }
2244
+ if (directiveDocs.length > 0) {
2245
+ markdown += `## Directives
2246
+
2247
+ `;
2248
+ markdown += `| Directive | Description | Has End Tag |
2249
+ `;
2250
+ markdown += `| --------- | ----------- | ----------- |
2251
+ `;
2252
+ for (const doc of directiveDocs) {
2253
+ markdown += `| @${doc.name} | ${doc.description || "-"} | ${doc.hasEndTag ? "Yes" : "No"} |
2254
+ `;
2255
+ }
2256
+ markdown += `
2257
+ `;
2258
+ for (const doc of directiveDocs) {
2259
+ if (doc.example) {
2260
+ markdown += `### @${doc.name}
2261
+
2262
+ `;
2263
+ markdown += `${doc.description || ""}
2264
+
2265
+ `;
2266
+ markdown += `#### Example
2267
+
2268
+ `;
2269
+ markdown += "```html\n";
2270
+ markdown += doc.example;
2271
+ markdown += "\n```\n\n";
2272
+ }
2273
+ }
2274
+ }
2275
+ return markdown;
2276
+ }
2277
+ function formatDocsAsHtml(componentDocs = [], templateDocs = [], directiveDocs = [], extraContent) {
2278
+ let html = `<!DOCTYPE html>
2279
+ <html lang="en">
2280
+ <head>
2281
+ <meta charset="UTF-8">
2282
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2283
+ <title>STX Documentation</title>
2284
+ <style>
2285
+ body {
2286
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
2287
+ line-height: 1.6;
2288
+ color: #333;
2289
+ max-width: 1200px;
2290
+ margin: 0 auto;
2291
+ padding: 2rem;
2292
+ }
2293
+ h1, h2, h3, h4 { margin-top: 2rem; }
2294
+ table {
2295
+ border-collapse: collapse;
2296
+ width: 100%;
2297
+ margin: 1rem 0;
2298
+ }
2299
+ th, td {
2300
+ text-align: left;
2301
+ padding: 0.5rem;
2302
+ border-bottom: 1px solid #ddd;
2303
+ }
2304
+ th { border-bottom: 2px solid #ddd; }
2305
+ pre {
2306
+ background: #f5f5f5;
2307
+ padding: 1rem;
2308
+ border-radius: 4px;
2309
+ overflow-x: auto;
2310
+ }
2311
+ code {
2312
+ background: #f5f5f5;
2313
+ padding: 0.2rem 0.4rem;
2314
+ border-radius: 4px;
2315
+ font-size: 0.9em;
2316
+ }
2317
+ </style>
2318
+ </head>
2319
+ <body>
2320
+ <h1>STX Documentation</h1>
2321
+ `;
2322
+ if (extraContent) {
2323
+ html += `<div>${extraContent}</div>`;
2324
+ }
2325
+ if (componentDocs.length > 0) {
2326
+ html += `<h2>Components</h2>`;
2327
+ for (const doc of componentDocs) {
2328
+ html += `<h3>${doc.name}</h3>`;
2329
+ if (doc.description) {
2330
+ html += `<p>${doc.description}</p>`;
2331
+ }
2332
+ if (doc.isWebComponent) {
2333
+ html += `<p><strong>Web Component Tag:</strong> <code>${doc.tag}</code></p>`;
2334
+ }
2335
+ if (doc.props.length > 0) {
2336
+ html += `<h4>Properties</h4>`;
2337
+ html += `<table>
2338
+ <thead>
2339
+ <tr>
2340
+ <th>Name</th>
2341
+ <th>Type</th>
2342
+ <th>Required</th>
2343
+ <th>Default</th>
2344
+ <th>Description</th>
2345
+ </tr>
2346
+ </thead>
2347
+ <tbody>`;
2348
+ for (const prop of doc.props) {
2349
+ html += `<tr>
2350
+ <td>${prop.name}</td>
2351
+ <td>${prop.type || "any"}</td>
2352
+ <td>${prop.required ? "Yes" : "No"}</td>
2353
+ <td>${prop.default || "-"}</td>
2354
+ <td>${prop.description || "-"}</td>
2355
+ </tr>`;
2356
+ }
2357
+ html += `</tbody></table>`;
2358
+ }
2359
+ if (doc.example) {
2360
+ html += `<h4>Example</h4>`;
2361
+ html += `<pre><code>${doc.example.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;")}</code></pre>`;
2362
+ }
2363
+ }
2364
+ }
2365
+ if (templateDocs.length > 0) {
2366
+ html += `<h2>Templates</h2>`;
2367
+ for (const doc of templateDocs) {
2368
+ html += `<h3>${doc.name}</h3>`;
2369
+ if (doc.description) {
2370
+ html += `<p>${doc.description}</p>`;
2371
+ }
2372
+ if (doc.components && doc.components.length > 0) {
2373
+ html += `<p><strong>Components Used:</strong> ${doc.components.join(", ")}</p>`;
2374
+ }
2375
+ if (doc.directives && doc.directives.length > 0) {
2376
+ html += `<p><strong>Directives Used:</strong> ${doc.directives.join(", ")}</p>`;
2377
+ }
2378
+ }
2379
+ }
2380
+ if (directiveDocs.length > 0) {
2381
+ html += `<h2>Directives</h2>`;
2382
+ html += `<table>
2383
+ <thead>
2384
+ <tr>
2385
+ <th>Directive</th>
2386
+ <th>Description</th>
2387
+ <th>Has End Tag</th>
2388
+ </tr>
2389
+ </thead>
2390
+ <tbody>`;
2391
+ for (const doc of directiveDocs) {
2392
+ html += `<tr>
2393
+ <td>@${doc.name}</td>
2394
+ <td>${doc.description || "-"}</td>
2395
+ <td>${doc.hasEndTag ? "Yes" : "No"}</td>
2396
+ </tr>`;
2397
+ }
2398
+ html += `</tbody></table>`;
2399
+ for (const doc of directiveDocs) {
2400
+ if (doc.example) {
2401
+ html += `<h3>@${doc.name}</h3>`;
2402
+ html += `<p>${doc.description || ""}</p>`;
2403
+ html += `<h4>Example</h4>`;
2404
+ html += `<pre><code>${doc.example.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;")}</code></pre>`;
2405
+ }
2406
+ }
2407
+ }
2408
+ html += `</body></html>`;
2409
+ return html;
2410
+ }
2411
+ function formatDocsAsJson(componentDocs = [], templateDocs = [], directiveDocs = [], extraContent) {
2412
+ const json = {
2413
+ components: componentDocs,
2414
+ templates: templateDocs,
2415
+ directives: directiveDocs,
2416
+ extraContent: extraContent || ""
2417
+ };
2418
+ return JSON.stringify(json, null, 2);
2419
+ }
2420
+ async function generateDocs(options) {
2421
+ try {
2422
+ const { componentsDir, templatesDir, webComponentsConfig, customDirectives, config: docConfig } = options;
2423
+ if (!docConfig.enabled) {
2424
+ return false;
2425
+ }
2426
+ const outputDir = docConfig.outputDir || "docs";
2427
+ await fs2.promises.mkdir(outputDir, { recursive: true });
2428
+ console.log(`Generating documentation...`);
2429
+ if (componentsDir)
2430
+ console.log(`Components directory: ${componentsDir}`);
2431
+ if (templatesDir)
2432
+ console.log(`Templates directory: ${templatesDir}`);
2433
+ console.log(`Output directory: ${outputDir}`);
2434
+ let componentDocs = [];
2435
+ if (docConfig.components !== false && componentsDir) {
2436
+ console.log(`Generating component documentation...`);
2437
+ componentDocs = await generateComponentsDocs(componentsDir, webComponentsConfig);
2438
+ console.log(`Found ${componentDocs.length} components`);
2439
+ }
2440
+ let templateDocs = [];
2441
+ if (docConfig.templates !== false && templatesDir) {
2442
+ console.log(`Generating template documentation...`);
2443
+ templateDocs = await generateTemplatesDocs(templatesDir);
2444
+ console.log(`Found ${templateDocs.length} templates`);
2445
+ }
2446
+ let directiveDocs = [];
2447
+ if (docConfig.directives !== false) {
2448
+ console.log(`Generating directive documentation...`);
2449
+ directiveDocs = await generateDirectivesDocs(customDirectives);
2450
+ console.log(`Found ${directiveDocs.length} directives`);
2451
+ }
2452
+ const format = docConfig.format || "markdown";
2453
+ const extraContent = docConfig.extraContent;
2454
+ let docContent = "";
2455
+ let extension = "";
2456
+ switch (format) {
2457
+ case "markdown":
2458
+ docContent = formatDocsAsMarkdown(componentDocs, templateDocs, directiveDocs, extraContent);
2459
+ extension = "md";
2460
+ break;
2461
+ case "html":
2462
+ docContent = formatDocsAsHtml(componentDocs, templateDocs, directiveDocs, extraContent);
2463
+ extension = "html";
2464
+ break;
2465
+ case "json":
2466
+ docContent = formatDocsAsJson(componentDocs, templateDocs, directiveDocs, extraContent);
2467
+ extension = "json";
2468
+ break;
2469
+ default:
2470
+ throw new Error(`Unsupported documentation format: ${format}`);
2471
+ }
2472
+ const outputPath = path2.join(outputDir, `stx-docs.${extension}`);
2473
+ await Bun.write(outputPath, docContent);
2474
+ console.log(`Documentation generated: ${outputPath}`);
2475
+ return true;
2476
+ } catch (error) {
2477
+ console.error("Error generating documentation:", error);
2478
+ return false;
2479
+ }
2480
+ }
2481
+ async function docsCommand(options) {
2482
+ const docsConfig = {
2483
+ enabled: true,
2484
+ outputDir: options.output || "docs",
2485
+ format: options.format || "markdown",
2486
+ components: options.components !== false,
2487
+ templates: options.templates !== false,
2488
+ directives: options.directives !== false,
2489
+ extraContent: options.extraContent
2490
+ };
2491
+ const componentsDir = options.componentsDir || config2.componentsDir;
2492
+ const templatesDir = options.templatesDir || ".";
2493
+ return generateDocs({
2494
+ componentsDir,
2495
+ templatesDir,
2496
+ webComponentsConfig: config2.webComponents,
2497
+ customDirectives: config2.customDirectives,
2498
+ config: docsConfig
2499
+ });
2500
+ }
2501
+
2502
+ export { buildWebComponents, webComponentDirectiveHandler, defaultConfig2 as defaultConfig, config2 as config, extractComponentProps, extractComponentDescription, generateComponentDoc, findComponentFiles, generateComponentsDocs, generateTemplatesDocs, generateDirectivesDocs, formatDocsAsMarkdown, formatDocsAsHtml, formatDocsAsJson, generateDocs, docsCommand };