kfg 0.0.1 → 1.1.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.cjs ADDED
@@ -0,0 +1,1200 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ConfigFS: () => ConfigFS,
34
+ ConfigJS: () => ConfigJS,
35
+ ConfigJSDriver: () => ConfigJSDriver,
36
+ FileFSConfigJS: () => FileFSConfigJS,
37
+ KFS_JOIN_SYMBOL: () => KFS_JOIN_SYMBOL,
38
+ KFS_MANY_SYMBOL: () => KFS_MANY_SYMBOL,
39
+ Kfg: () => Kfg,
40
+ KfgDriver: () => KfgDriver,
41
+ KfgFS: () => KfgFS,
42
+ KfgFileFS: () => KfgFileFS,
43
+ c: () => c,
44
+ cfs: () => cfs,
45
+ envDriver: () => envDriver,
46
+ jsonDriver: () => jsonDriver,
47
+ k: () => k,
48
+ kfs: () => kfs
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/kfg-fs.ts
53
+ var fs = __toESM(require("fs"), 1);
54
+
55
+ // src/utils/object.ts
56
+ function getProperty(obj, path3) {
57
+ return path3.split(".").reduce((acc, key) => acc?.[key], obj);
58
+ }
59
+ function setProperty(obj, path3, value) {
60
+ const keys = path3.split(".");
61
+ const lastKey = keys.pop();
62
+ let target = obj;
63
+ for (const key of keys) {
64
+ if (target[key] === void 0 || target[key] === null) {
65
+ target[key] = {};
66
+ } else if (typeof target[key] !== "object") {
67
+ throw new Error(`Cannot set property on non-object at path: ${key}`);
68
+ }
69
+ target = target[key];
70
+ }
71
+ target[lastKey] = value;
72
+ }
73
+ function isObject(item) {
74
+ return item !== null && typeof item === "object" && !Array.isArray(item);
75
+ }
76
+ function deepMerge(target, source) {
77
+ const output = { ...target };
78
+ if (isObject(target) && isObject(source)) {
79
+ Object.keys(source).forEach((key) => {
80
+ const sourceValue = source[key];
81
+ const targetValue = target[key];
82
+ if (isObject(sourceValue) && isObject(targetValue)) {
83
+ output[key] = deepMerge(
84
+ targetValue,
85
+ sourceValue
86
+ );
87
+ } else {
88
+ output[key] = sourceValue;
89
+ }
90
+ });
91
+ }
92
+ return output;
93
+ }
94
+ function flattenObject(obj, prefix = "") {
95
+ return Object.keys(obj).reduce(
96
+ (acc, k2) => {
97
+ const pre = prefix.length ? `${prefix}.` : "";
98
+ if (isObject(obj[k2])) {
99
+ Object.assign(acc, flattenObject(obj[k2], pre + k2));
100
+ } else {
101
+ acc[pre + k2] = obj[k2];
102
+ }
103
+ return acc;
104
+ },
105
+ {}
106
+ );
107
+ }
108
+ function unflattenObject(obj) {
109
+ const result = {};
110
+ for (const key in obj) {
111
+ setProperty(result, key, obj[key]);
112
+ }
113
+ return result;
114
+ }
115
+ function deleteProperty(obj, path3) {
116
+ const keys = path3.split(".");
117
+ const lastKey = keys.pop();
118
+ let target = obj;
119
+ for (const key of keys) {
120
+ if (target?.[key] === void 0) {
121
+ return false;
122
+ }
123
+ target = target[key];
124
+ }
125
+ if (typeof target === "object" && target !== null) {
126
+ return delete target[lastKey];
127
+ }
128
+ return false;
129
+ }
130
+
131
+ // src/utils/schema.ts
132
+ var import_typebox = require("@sinclair/typebox");
133
+ var import_value = require("@sinclair/typebox/value");
134
+ function addSmartDefaults(schemaNode) {
135
+ if (schemaNode.type !== "object" || !schemaNode.properties) {
136
+ return;
137
+ }
138
+ let allChildrenOptional = true;
139
+ for (const key in schemaNode.properties) {
140
+ const prop = schemaNode.properties[key];
141
+ if (prop[Symbol.for("TypeBox.Kind")] === "Unsafe") {
142
+ continue;
143
+ }
144
+ if (prop.type === "object" && prop[Symbol.for("TypeBox.Kind")]) {
145
+ addSmartDefaults(prop);
146
+ }
147
+ const hasDefault = prop.default !== void 0;
148
+ const isOptional = import_value.Value.Check(import_typebox.Type.Object({ temp: prop }), {});
149
+ if (!hasDefault && !isOptional) {
150
+ allChildrenOptional = false;
151
+ }
152
+ }
153
+ if (allChildrenOptional && schemaNode.default === void 0) {
154
+ schemaNode.default = {};
155
+ }
156
+ }
157
+ function buildTypeBoxSchema(definition) {
158
+ if (definition[Symbol.for("TypeBox.Kind")] === "Object") {
159
+ return definition;
160
+ }
161
+ const properties = {};
162
+ for (const key in definition) {
163
+ const value = definition[key];
164
+ const isObject2 = typeof value === "object" && value !== null && !value[Symbol.for("TypeBox.Kind")];
165
+ if (isObject2) {
166
+ properties[key] = buildTypeBoxSchema(value);
167
+ } else {
168
+ properties[key] = value;
169
+ }
170
+ }
171
+ return import_typebox.Type.Object(properties, { additionalProperties: true });
172
+ }
173
+ function buildDefaultObject(definition) {
174
+ const schema = buildTypeBoxSchema(definition);
175
+ addSmartDefaults(schema);
176
+ return import_value.Value.Default(schema, {});
177
+ }
178
+ function makeSchemaOptional(definition) {
179
+ const newDefinition = {};
180
+ for (const key in definition) {
181
+ const value = definition[key];
182
+ if (value?.[Symbol.for("TypeBox.Kind")]) {
183
+ const schema = value;
184
+ const isOptional = import_value.Value.Check(import_typebox.Type.Object({ temp: schema }), {});
185
+ if (schema.important || isOptional) {
186
+ newDefinition[key] = schema;
187
+ } else {
188
+ newDefinition[key] = import_typebox.Type.Optional(schema);
189
+ }
190
+ } else if (typeof value === "object" && value !== null) {
191
+ newDefinition[key] = makeSchemaOptional(value);
192
+ } else {
193
+ newDefinition[key] = value;
194
+ }
195
+ }
196
+ return newDefinition;
197
+ }
198
+
199
+ // src/kfg.ts
200
+ var Kfg = class {
201
+ driver;
202
+ schema;
203
+ loaded = false;
204
+ _lastOptions;
205
+ /**
206
+ * Creates a new instance of Kfg.
207
+ * @param driver The driver to use for loading and saving the configuration.
208
+ * @param schema The schema to use for validating the configuration.
209
+ */
210
+ constructor(driver, schema) {
211
+ this.driver = driver.clone();
212
+ this.schema = schema;
213
+ }
214
+ /**
215
+ * Reloads the configuration.
216
+ * @param options - The loading options.
217
+ */
218
+ reload(options) {
219
+ this.loaded = false;
220
+ return this.load(options || this._lastOptions);
221
+ }
222
+ /**
223
+ * Loads the configuration.
224
+ * @param options - The loading options.
225
+ */
226
+ load(options) {
227
+ this._lastOptions = options;
228
+ let schemaToLoad = this.schema;
229
+ if (options?.only_importants) {
230
+ schemaToLoad = makeSchemaOptional(this.schema);
231
+ }
232
+ const result = this.driver.load(schemaToLoad, options);
233
+ if (this.driver.async) {
234
+ return result.then(() => {
235
+ this.loaded = true;
236
+ });
237
+ }
238
+ this.loaded = true;
239
+ return result;
240
+ }
241
+ /**
242
+ * Gets a value from the configuration.
243
+ * @param path The path to the value.
244
+ * @returns The value at the given path.
245
+ */
246
+ get(path3) {
247
+ if (!this.loaded) {
248
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
249
+ }
250
+ return this.driver.get(path3);
251
+ }
252
+ /**
253
+ * Checks if a value exists in the configuration.
254
+ * @param paths The paths to the values.
255
+ * @returns True if all values exist, false otherwise.
256
+ */
257
+ has(...paths) {
258
+ if (!this.loaded) {
259
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
260
+ }
261
+ return this.driver.has(...paths);
262
+ }
263
+ /**
264
+ * Gets a value from the configuration.
265
+ * @param path The path to the value.
266
+ * @returns The value at the given path.
267
+ */
268
+ root(path3) {
269
+ if (!this.loaded) {
270
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
271
+ }
272
+ return this.driver.get(path3);
273
+ }
274
+ /**
275
+ * Sets a value in the configuration.
276
+ * @param path The path to the value.
277
+ * @param value The new value.
278
+ * @param options The options for setting the value.
279
+ */
280
+ set(path3, value, options) {
281
+ if (!this.loaded) {
282
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
283
+ }
284
+ return this.driver.set(path3, value, options);
285
+ }
286
+ /**
287
+ * Inserts a partial value into an object in the configuration.
288
+ * @param path The path to the object.
289
+ * @param partial The partial value to insert.
290
+ */
291
+ insert(path3, partial) {
292
+ if (!this.loaded) {
293
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
294
+ }
295
+ return this.driver.insert(path3, partial);
296
+ }
297
+ /**
298
+ * Injects a partial value directly into the root configuration object.
299
+ * @param data The partial data to inject.
300
+ */
301
+ inject(data) {
302
+ if (!this.loaded) {
303
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
304
+ }
305
+ return this.driver.inject(data);
306
+ }
307
+ /**
308
+ * Deletes a value from the configuration.
309
+ * @param path The path to the value.
310
+ */
311
+ del(path3) {
312
+ if (!this.loaded) {
313
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
314
+ }
315
+ return this.driver.del(path3);
316
+ }
317
+ /**
318
+ * Gets the schema for a given path.
319
+ * @param path The path to the schema.
320
+ * @returns The schema at the given path.
321
+ */
322
+ conf(path3) {
323
+ if (!this.loaded) {
324
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
325
+ }
326
+ return getProperty(this.schema, path3);
327
+ }
328
+ /**
329
+ * Returns the schema definition for a given path.
330
+ * @param path The path to the schema.
331
+ * @returns The schema at the given path.
332
+ */
333
+ schematic(path3) {
334
+ return this.conf(path3);
335
+ }
336
+ /**
337
+ * Returns cached data
338
+ * @returns
339
+ */
340
+ async toJSON() {
341
+ if (!this.loaded) {
342
+ throw new Error("[Kfg] Config not loaded. Call load() first.");
343
+ }
344
+ if (this.driver.async) {
345
+ return Promise.resolve(this.driver.data);
346
+ }
347
+ return this.driver.data;
348
+ }
349
+ };
350
+
351
+ // src/fs-factory.ts
352
+ var import_typebox2 = require("@sinclair/typebox");
353
+ var KFS_MANY_SYMBOL = Symbol.for("Kfg.many");
354
+ var KFS_JOIN_SYMBOL = Symbol.for("Kfg.join");
355
+ var _cfs = {
356
+ /**
357
+ * Creates a many-to-many relation in a schema.
358
+ * @param kfgFs The KfgFS instance to relate to.
359
+ * @param options The schema options.
360
+ */
361
+ many: (kfgFs, options) => import_typebox2.Type.Unsafe(
362
+ import_typebox2.Type.Array(import_typebox2.Type.String(), {
363
+ ...options,
364
+ [KFS_MANY_SYMBOL]: {
365
+ kfgFs
366
+ }
367
+ })
368
+ ),
369
+ join: (kfgFs, options) => import_typebox2.Type.Unsafe(
370
+ import_typebox2.Type.Object(
371
+ {},
372
+ {
373
+ ...options,
374
+ [KFS_JOIN_SYMBOL]: {
375
+ kfgFs,
376
+ fk: options?.fk
377
+ }
378
+ }
379
+ )
380
+ )
381
+ };
382
+ var cfs = {
383
+ ..._cfs
384
+ };
385
+ var kfs = cfs;
386
+
387
+ // src/kfg-fs.ts
388
+ var KfgFileFS = class extends Kfg {
389
+ /**
390
+ * Creates a new instance of KfgFileFS.
391
+ * @param driver The driver to use for loading and saving the configuration.
392
+ * @param schema The schema to use for validating the configuration.
393
+ * @param filePath The path to the configuration file.
394
+ */
395
+ constructor(driver, schema, filePath) {
396
+ super(driver, schema);
397
+ this.filePath = filePath;
398
+ }
399
+ /**
400
+ * Loads the configuration from the file.
401
+ * @param options The loading options.
402
+ */
403
+ load(options) {
404
+ const loadOptions = { ...options, path: this.filePath };
405
+ return super.load(loadOptions);
406
+ }
407
+ /**
408
+ * Sets a value in the configuration.
409
+ * @param path The path to the value.
410
+ * @param value The new value.
411
+ * @param options The options for setting the value.
412
+ */
413
+ //@ts-ignore KfgFS change internal logic of Kfg, ignore this
414
+ set(path3, value, options) {
415
+ return super.set(path3, value, options);
416
+ }
417
+ /**
418
+ * Inserts a partial value into an object in the configuration.
419
+ * @param path The path to the object.
420
+ * @param partial The partial value to insert.
421
+ */
422
+ //@ts-ignore KfgFS change internal logic of Kfg, ignore this
423
+ insert(path3, value) {
424
+ return super.insert(path3, value);
425
+ }
426
+ /**
427
+ * Gets a value from the configuration.
428
+ * @param path The path to the value.
429
+ * @returns The value at the given path.
430
+ */
431
+ //@ts-ignore KfgFS change internal logic of Kfg, ignore this
432
+ root(path3) {
433
+ return super.root(path3);
434
+ }
435
+ /**
436
+ * Checks if a value exists in the configuration.
437
+ * @param paths The paths to the values.
438
+ * @returns True if all values exist, false otherwise.
439
+ */
440
+ has(...paths) {
441
+ return super.has(...paths);
442
+ }
443
+ /**
444
+ * Gets the schema for a given path.
445
+ * @param path The path to the schema.
446
+ * @returns The schema at the given path.
447
+ */
448
+ conf(path3) {
449
+ return super.conf(path3);
450
+ }
451
+ /**
452
+ * Returns cached data
453
+ * @returns
454
+ */
455
+ //@ts-ignore more recursive types, ignore
456
+ toJSON() {
457
+ return super.toJSON();
458
+ }
459
+ /**
460
+ * Saves the configuration to the file.
461
+ */
462
+ save() {
463
+ return this.driver.set(null, this.driver.data);
464
+ }
465
+ /**
466
+ * Gets the related configurations for a many-to-many relation.
467
+ * @param path The path to the relation.
468
+ * @returns An array of related configurations.
469
+ */
470
+ getMany(path3) {
471
+ const schema = this.conf(path3);
472
+ const manyInfo = schema[KFS_MANY_SYMBOL];
473
+ if (!manyInfo) {
474
+ throw new Error(`[KfgFS] '${path3}' is not a many-relation field.`);
475
+ }
476
+ const ids = this.get(path3);
477
+ if (!ids || !Array.isArray(ids)) {
478
+ return void 0;
479
+ }
480
+ const related = manyInfo.kfgFs;
481
+ const files = ids.map((id) => related.file(id));
482
+ if (this.driver.async) {
483
+ return Promise.all(files);
484
+ }
485
+ return files;
486
+ }
487
+ /**
488
+ * Gets the related configuration for a one-to-one relation.
489
+ * @param path The path to the relation.
490
+ * @returns The related configuration.
491
+ */
492
+ getJoin(path3) {
493
+ const schema = this.conf(path3);
494
+ const joinInfo = schema[KFS_JOIN_SYMBOL];
495
+ if (!joinInfo) {
496
+ throw new Error(`[KfgFS] '${path3}' is not a join-relation field.`);
497
+ }
498
+ const fkValue = this.get(joinInfo.fk);
499
+ if (!fkValue) {
500
+ return void 0;
501
+ }
502
+ const related = joinInfo.kfgFs;
503
+ const instance = related.file(fkValue);
504
+ if (this.driver.async) {
505
+ return instance.then(
506
+ async (instance2) => {
507
+ await instance2.load();
508
+ return instance2;
509
+ }
510
+ );
511
+ }
512
+ const loadedInstance = instance;
513
+ loadedInstance.load();
514
+ return loadedInstance;
515
+ }
516
+ /**
517
+ * Returns the file path of the configuration.
518
+ */
519
+ toString() {
520
+ return this.filePath;
521
+ }
522
+ };
523
+ var KfgFS = class {
524
+ /**
525
+ * Creates a new instance of KfgFS.
526
+ * @param driver The driver to use for loading and saving the configurations.
527
+ * @param schema The schema to use for validating the configurations.
528
+ * @param config The configuration options.
529
+ */
530
+ constructor(driver, schema, config) {
531
+ this.driver = driver;
532
+ this.schema = schema;
533
+ this.config = config;
534
+ }
535
+ pathFn;
536
+ /**
537
+ * Initializes the KfgFS instance with a path function.
538
+ * @param pathFn A function that returns the file path for a given ID.
539
+ */
540
+ init(pathFn) {
541
+ this.pathFn = pathFn;
542
+ }
543
+ /**
544
+ * Gets the file path for a given ID.
545
+ * @param id The ID of the configuration file.
546
+ * @returns The file path.
547
+ */
548
+ getPath(id) {
549
+ if (!this.pathFn) {
550
+ throw new Error(
551
+ "[KfgFS] KfgFS not initialized. Call init() first."
552
+ );
553
+ }
554
+ return this.pathFn(id);
555
+ }
556
+ /**
557
+ * Checks if a configuration file exists for a given ID.
558
+ * @param id The ID of the configuration file.
559
+ * @returns True if the file exists, false otherwise.
560
+ */
561
+ exist(id) {
562
+ const filePath = this.getPath(id);
563
+ return fs.existsSync(filePath);
564
+ }
565
+ /**
566
+ * Creates a new configuration file for a given ID.
567
+ * @param id The ID of the configuration file.
568
+ * @param data Optional initial data for the configuration.
569
+ * @returns The created KfgFileFS instance.
570
+ */
571
+ create(id, data) {
572
+ if (this.exist(id)) {
573
+ throw new Error(`[KfgFS] Config with id '${id}' already exists.`);
574
+ }
575
+ const instanceOrPromise = this.file(id);
576
+ const initialize = (instance) => {
577
+ if (data) {
578
+ instance.inject(data);
579
+ }
580
+ const saveResult = instance.save();
581
+ if (this.driver.async) {
582
+ return saveResult.then(() => instance);
583
+ }
584
+ return instance;
585
+ };
586
+ if (this.driver.async) {
587
+ return instanceOrPromise.then(
588
+ initialize
589
+ );
590
+ } else {
591
+ return initialize(instanceOrPromise);
592
+ }
593
+ }
594
+ /**
595
+ * Gets a file-based configuration for a given ID.
596
+ * @param id The ID of the configuration file.
597
+ * @returns A KfgFileFS instance.
598
+ */
599
+ file(id) {
600
+ const filePath = this.getPath(id);
601
+ const newDriver = new this.driver.constructor(
602
+ this.driver.options
603
+ );
604
+ const fileInstance = new KfgFileFS(newDriver, this.schema, filePath);
605
+ const loadResult = fileInstance.load(this.config);
606
+ if (this.driver.async) {
607
+ return loadResult.then(
608
+ () => fileInstance
609
+ );
610
+ }
611
+ return fileInstance;
612
+ }
613
+ /**
614
+ * Deletes a configuration file.
615
+ * @param id The ID of the configuration file.
616
+ */
617
+ del(id) {
618
+ const filePath = this.getPath(id);
619
+ if (fs.existsSync(filePath)) {
620
+ fs.unlinkSync(filePath);
621
+ }
622
+ }
623
+ /**
624
+ * Copies a configuration file.
625
+ * @param fromId The ID of the source configuration file.
626
+ * @param toId The ID of the destination configuration file.
627
+ */
628
+ copy(fromId, toId) {
629
+ const fromPath = this.getPath(fromId);
630
+ const toPath = this.getPath(toId);
631
+ fs.copyFileSync(fromPath, toPath);
632
+ }
633
+ /**
634
+ * Gets the configuration data for a given ID.
635
+ * @param id The ID of the configuration file.
636
+ * @returns The configuration data.
637
+ */
638
+ toJSON(id) {
639
+ const fileInstance = this.file(id);
640
+ if (fileInstance instanceof Promise) {
641
+ return fileInstance.then((i) => i.toJSON());
642
+ } else {
643
+ return fileInstance.toJSON();
644
+ }
645
+ }
646
+ };
647
+
648
+ // src/kfg-driver.ts
649
+ var import_value2 = require("@sinclair/typebox/value");
650
+ var KfgDriver = class {
651
+ /**
652
+ * Creates a new instance of KfgDriver.
653
+ * @param options The driver options.
654
+ */
655
+ constructor(options) {
656
+ this.options = options;
657
+ this.identify = options.identify;
658
+ this.async = options.async;
659
+ this.config = options.config || {};
660
+ this._onLoad = options.onLoad;
661
+ this._onSet = options.onSet;
662
+ this._onDel = options.onDel;
663
+ }
664
+ identify;
665
+ async = void 0;
666
+ config;
667
+ data = {};
668
+ comments;
669
+ compiledSchema;
670
+ store = {};
671
+ _onSet;
672
+ _onDel;
673
+ // Utilities passed to drivers
674
+ buildDefaultObject = buildDefaultObject;
675
+ deepMerge = deepMerge;
676
+ /**
677
+ * Clones the driver.
678
+ * @returns A new instance of the driver with the same options.
679
+ */
680
+ clone() {
681
+ const Constructor = this.constructor;
682
+ return new Constructor(this.options);
683
+ }
684
+ /**
685
+ * Injects data directly into the driver's data store.
686
+ * This data is merged with the existing data.
687
+ * @param data The data to inject.
688
+ */
689
+ inject(data) {
690
+ this.data = this.deepMerge(this.data, data);
691
+ return this.async ? Promise.resolve() : void 0;
692
+ }
693
+ /**
694
+ * Loads the configuration.
695
+ * @param schema The schema to use for validating the configuration.
696
+ * @param options The loading options.
697
+ */
698
+ load(schema, options = {}) {
699
+ this.compiledSchema = buildTypeBoxSchema(schema);
700
+ addSmartDefaults(this.compiledSchema);
701
+ this.config = { ...this.config, ...options };
702
+ const processResult = (result) => {
703
+ this.data = result;
704
+ this.validate(this.data);
705
+ };
706
+ if (this._onLoad) {
707
+ const loadResult = this._onLoad.call(this, schema, this.config);
708
+ if (this.async) {
709
+ return loadResult.then(processResult);
710
+ }
711
+ processResult(loadResult);
712
+ }
713
+ return void 0;
714
+ }
715
+ /**
716
+ * Gets a value from the configuration.
717
+ * @param path The path to the value.
718
+ * @returns The value at the given path.
719
+ */
720
+ get(path3) {
721
+ const value = getProperty(this.data, path3);
722
+ if (this.async) {
723
+ return Promise.resolve(value);
724
+ }
725
+ return value;
726
+ }
727
+ /**
728
+ * Checks if a value exists in the configuration.
729
+ * @param paths The paths to the values.
730
+ * @returns True if all values exist, false otherwise.
731
+ */
732
+ has(...paths) {
733
+ const hasAllProps = paths.every(
734
+ (path3) => getProperty(this.data, path3) !== void 0
735
+ );
736
+ if (this.async) {
737
+ return Promise.resolve(hasAllProps);
738
+ }
739
+ return hasAllProps;
740
+ }
741
+ /**
742
+ * Sets a value in the configuration.
743
+ * @param path The path to the value.
744
+ * @param value The new value.
745
+ * @param options The options for setting the value.
746
+ */
747
+ set(path3, value, options) {
748
+ if (path3) {
749
+ setProperty(this.data, path3, value);
750
+ }
751
+ if (this._onSet) {
752
+ return this._onSet.call(this, path3, value, options);
753
+ }
754
+ return this.async ? Promise.resolve() : void 0;
755
+ }
756
+ /**
757
+ * Inserts a partial value into an object in the configuration.
758
+ * @param path The path to the object.
759
+ * @param partial The partial value to insert.
760
+ */
761
+ insert(path3, partial) {
762
+ const currentObject = getProperty(this.data, path3);
763
+ if (typeof currentObject !== "object" || currentObject === null) {
764
+ throw new Error(`Cannot insert into non-object at path: ${path3}`);
765
+ }
766
+ Object.assign(currentObject, partial);
767
+ return this.set(path3, currentObject);
768
+ }
769
+ /**
770
+ * Deletes a value from the configuration.
771
+ * @param path The path to the value.
772
+ */
773
+ del(path3) {
774
+ deleteProperty(this.data, path3);
775
+ if (this._onDel) {
776
+ return this._onDel.call(this, path3);
777
+ }
778
+ return this.async ? Promise.resolve() : void 0;
779
+ }
780
+ /**
781
+ * Validates the configuration against the schema.
782
+ * @param config The configuration to validate.
783
+ */
784
+ validate(config = this.data) {
785
+ if (!this.compiledSchema) return;
786
+ const configWithDefaults = import_value2.Value.Default(this.compiledSchema, config);
787
+ import_value2.Value.Convert(this.compiledSchema, configWithDefaults);
788
+ if (!import_value2.Value.Check(this.compiledSchema, configWithDefaults)) {
789
+ const errors = [...import_value2.Value.Errors(this.compiledSchema, configWithDefaults)];
790
+ throw new Error(
791
+ `[Kfg] Validation failed:
792
+ ${errors.map((e) => `- ${e.path}: ${e.message}`).join("\n")}`
793
+ // Corrected: escaped backtick in template literal
794
+ );
795
+ }
796
+ this.data = configWithDefaults;
797
+ }
798
+ };
799
+
800
+ // src/old.ts
801
+ var ConfigJS = Kfg;
802
+ var ConfigFS = KfgFS;
803
+ var FileFSConfigJS = KfgFileFS;
804
+ var ConfigJSDriver = KfgDriver;
805
+
806
+ // src/drivers/env-driver.ts
807
+ var fs2 = __toESM(require("fs"), 1);
808
+ var path = __toESM(require("path"), 1);
809
+
810
+ // src/utils/env.ts
811
+ function parse(content) {
812
+ const result = {};
813
+ const lines = content.split(/\r?\n/);
814
+ for (const line of lines) {
815
+ const trimmedLine = line.trim();
816
+ if (!trimmedLine || trimmedLine.startsWith("#")) {
817
+ continue;
818
+ }
819
+ const match = trimmedLine.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/);
820
+ if (!match) continue;
821
+ const [, key, rawValue] = match;
822
+ let value = rawValue.trim();
823
+ if (!value.startsWith('"') && !value.startsWith("'")) {
824
+ const commentIndex = value.indexOf("#");
825
+ if (commentIndex > -1) {
826
+ value = value.substring(0, commentIndex).trim();
827
+ }
828
+ }
829
+ if (value.startsWith("'") && value.endsWith("'")) {
830
+ value = value.substring(1, value.length - 1);
831
+ } else if (value.startsWith('"') && value.endsWith('"')) {
832
+ value = value.substring(1, value.length - 1);
833
+ }
834
+ result[key] = value;
835
+ }
836
+ return result;
837
+ }
838
+ function updateEnvContent(content, key, value, description) {
839
+ const lines = content.split(/\r?\n/);
840
+ let keyFound = false;
841
+ const newLines = [...lines];
842
+ let formattedValue;
843
+ if (Array.isArray(value)) {
844
+ formattedValue = JSON.stringify(value);
845
+ } else {
846
+ const stringValue = String(value);
847
+ formattedValue = /[\s"'#]/.test(stringValue) ? `"${stringValue.replace(/"/g, '"').replace(/\n/g, "\\n")}"` : stringValue;
848
+ }
849
+ let lineIndex = -1;
850
+ for (let i = 0; i < lines.length; i++) {
851
+ if (new RegExp(`^s*${key}s*=s*`).test(lines[i])) {
852
+ keyFound = true;
853
+ lineIndex = i;
854
+ break;
855
+ }
856
+ }
857
+ if (keyFound) {
858
+ newLines[lineIndex] = `${key}=${formattedValue}`;
859
+ if (description) {
860
+ const comment = `# ${description}`;
861
+ if (lineIndex === 0 || !newLines[lineIndex - 1].trim().startsWith(comment)) {
862
+ if (lineIndex > 0 && !newLines[lineIndex - 1].trim().startsWith("#")) {
863
+ newLines.splice(lineIndex, 0, comment);
864
+ }
865
+ }
866
+ }
867
+ } else {
868
+ if (newLines[newLines.length - 1] !== "") {
869
+ newLines.push("");
870
+ }
871
+ if (description) {
872
+ newLines.push(`# ${description}`);
873
+ }
874
+ newLines.push(`${key}=${formattedValue}`);
875
+ }
876
+ return newLines.join("\n");
877
+ }
878
+ function removeEnvKey(content, key) {
879
+ const lines = content.split(/\r?\n/);
880
+ const keyRegex = new RegExp(`^\\s*${key}\\s*=\\s*`);
881
+ const newLines = [];
882
+ for (const line of lines) {
883
+ if (keyRegex.test(line)) {
884
+ if (newLines.length > 0 && newLines[newLines.length - 1].trim().startsWith("#")) {
885
+ newLines.pop();
886
+ }
887
+ } else {
888
+ newLines.push(line);
889
+ }
890
+ }
891
+ return newLines.join("\n");
892
+ }
893
+
894
+ // src/drivers/env-driver.ts
895
+ function getFilePath(config) {
896
+ return path.resolve(process.cwd(), config.path || ".env");
897
+ }
898
+ function coerceType(value, schema) {
899
+ if (value === void 0) return void 0;
900
+ const type = schema.type;
901
+ if (type === "number") return Number(value);
902
+ if (type === "boolean") return String(value).toLowerCase() === "true";
903
+ if (type === "array" && typeof value === "string") {
904
+ const trimmedValue = value.trim();
905
+ if (trimmedValue.startsWith("[") && trimmedValue.endsWith("]")) {
906
+ try {
907
+ return JSON.parse(trimmedValue);
908
+ } catch {
909
+ }
910
+ }
911
+ }
912
+ return value;
913
+ }
914
+ function traverseSchema(schema, envValues, prefix = []) {
915
+ const builtConfig = {};
916
+ for (const key in schema) {
917
+ const currentPath = [...prefix, key];
918
+ const definition = schema[key];
919
+ const isTypeBoxSchema = (def) => !!def[Symbol.for("TypeBox.Kind")];
920
+ if (isTypeBoxSchema(definition)) {
921
+ const prop = definition.prop;
922
+ const envKey = prop || currentPath.join("_").toUpperCase();
923
+ let value = envValues[envKey];
924
+ if (value === void 0) {
925
+ value = definition.default;
926
+ }
927
+ if (value !== void 0) {
928
+ builtConfig[key] = coerceType(value, definition);
929
+ }
930
+ } else if (typeof definition === "object" && definition !== null) {
931
+ const nestedConfig = traverseSchema(
932
+ definition,
933
+ envValues,
934
+ currentPath
935
+ );
936
+ builtConfig[key] = nestedConfig;
937
+ }
938
+ }
939
+ return builtConfig;
940
+ }
941
+ var envDriver = new KfgDriver({
942
+ identify: "env-driver",
943
+ async: false,
944
+ config: { path: ".env" },
945
+ onLoad(schema, _opts) {
946
+ const filePath = getFilePath(this.config);
947
+ const fileContent = fs2.existsSync(filePath) ? fs2.readFileSync(filePath, "utf-8") : "";
948
+ const envFileValues = parse(fileContent);
949
+ const processEnv = Object.fromEntries(
950
+ Object.entries(process.env).filter(([, v]) => v !== void 0)
951
+ );
952
+ const allEnvValues = { ...processEnv, ...envFileValues };
953
+ const envData = traverseSchema(schema, allEnvValues);
954
+ const defaultData = this.buildDefaultObject(schema);
955
+ this.store = this.deepMerge(defaultData, envData);
956
+ return this.store;
957
+ },
958
+ onSet(key, value, options) {
959
+ const envKey = key.replace(/\./g, "_").toUpperCase();
960
+ const filePath = getFilePath(this.config);
961
+ const currentContent = fs2.existsSync(filePath) ? fs2.readFileSync(filePath, "utf-8") : "";
962
+ const newContent = updateEnvContent(
963
+ currentContent,
964
+ envKey,
965
+ value,
966
+ options?.description
967
+ );
968
+ fs2.writeFileSync(filePath, newContent);
969
+ },
970
+ onDel(key) {
971
+ const envKey = key.replace(/\./g, "_").toUpperCase();
972
+ const filePath = getFilePath(this.config);
973
+ if (!fs2.existsSync(filePath)) {
974
+ return;
975
+ }
976
+ const currentContent = fs2.readFileSync(filePath, "utf-8");
977
+ const newContent = removeEnvKey(currentContent, envKey);
978
+ fs2.writeFileSync(filePath, newContent);
979
+ }
980
+ });
981
+
982
+ // src/drivers/json-driver.ts
983
+ var fs3 = __toESM(require("fs"), 1);
984
+ var path2 = __toESM(require("path"), 1);
985
+ function stripComments(data) {
986
+ const comments = {};
987
+ function recurse(currentData, prefix = "") {
988
+ const keys = Object.keys(currentData);
989
+ for (const key of keys) {
990
+ if (key.endsWith(":comment")) {
991
+ const dataKey = key.replace(/:comment$/, "");
992
+ const commentPath = prefix ? `${prefix}.${dataKey}` : dataKey;
993
+ comments[commentPath] = currentData[key];
994
+ delete currentData[key];
995
+ }
996
+ }
997
+ for (const key of keys) {
998
+ if (typeof currentData[key] === "object" && currentData[key] !== null && !key.endsWith(":comment")) {
999
+ const nestedPrefix = prefix ? `${prefix}.${key}` : key;
1000
+ recurse(currentData[key], nestedPrefix);
1001
+ }
1002
+ }
1003
+ }
1004
+ recurse(data);
1005
+ return comments;
1006
+ }
1007
+ function getFilePath2(config) {
1008
+ return path2.resolve(process.cwd(), config.path || "config.json");
1009
+ }
1010
+ var jsonDriver = new KfgDriver({
1011
+ identify: "json-driver",
1012
+ async: false,
1013
+ config: { path: "config.json", keyroot: false },
1014
+ onLoad(schema, _opts) {
1015
+ this.comments = this.comments || {};
1016
+ const defaultData = this.buildDefaultObject(schema);
1017
+ const filePath = getFilePath2(this.config);
1018
+ let loadedData = {};
1019
+ if (fs3.existsSync(filePath)) {
1020
+ try {
1021
+ const fileContent = fs3.readFileSync(filePath, "utf-8");
1022
+ if (fileContent) {
1023
+ loadedData = JSON.parse(fileContent);
1024
+ }
1025
+ } catch (_e) {
1026
+ }
1027
+ }
1028
+ if (this.config.keyroot) {
1029
+ const flatData = loadedData;
1030
+ const comments = {};
1031
+ const data = {};
1032
+ for (const key in flatData) {
1033
+ if (key.endsWith(":comment")) {
1034
+ comments[key.replace(/:comment$/, "")] = flatData[key];
1035
+ } else {
1036
+ data[key] = flatData[key];
1037
+ }
1038
+ }
1039
+ this.comments = comments;
1040
+ loadedData = unflattenObject(data);
1041
+ } else {
1042
+ this.comments = stripComments(loadedData);
1043
+ }
1044
+ this.store = this.deepMerge(defaultData, loadedData);
1045
+ return this.store;
1046
+ },
1047
+ onSet(key, _value, options) {
1048
+ if (key) {
1049
+ this.comments = this.comments || {};
1050
+ if (options?.description) {
1051
+ this.comments[key] = options.description;
1052
+ }
1053
+ }
1054
+ let dataToSave;
1055
+ if (this.config.keyroot) {
1056
+ dataToSave = flattenObject(this.data);
1057
+ for (const path3 in this.comments) {
1058
+ dataToSave[`${path3}:comment`] = this.comments[path3];
1059
+ }
1060
+ } else {
1061
+ const dataWithComments = JSON.parse(JSON.stringify(this.data));
1062
+ for (const path3 in this.comments) {
1063
+ const keys = path3.split(".");
1064
+ const propName = keys.pop();
1065
+ const parentPath = keys.join(".");
1066
+ const parentObject = parentPath ? getProperty(dataWithComments, parentPath) : dataWithComments;
1067
+ if (typeof parentObject === "object" && parentObject !== null) {
1068
+ parentObject[`${propName}:comment`] = this.comments[path3];
1069
+ }
1070
+ }
1071
+ dataToSave = dataWithComments;
1072
+ }
1073
+ const filePath = getFilePath2(this.config);
1074
+ fs3.writeFileSync(filePath, JSON.stringify(dataToSave, null, 2));
1075
+ },
1076
+ onDel(key) {
1077
+ if (this.comments?.[key]) {
1078
+ delete this.comments[key];
1079
+ }
1080
+ let dataToSave;
1081
+ if (this.config.keyroot) {
1082
+ dataToSave = flattenObject(this.data);
1083
+ for (const path3 in this.comments) {
1084
+ dataToSave[`${path3}:comment`] = this.comments[path3];
1085
+ }
1086
+ } else {
1087
+ const dataWithComments = JSON.parse(JSON.stringify(this.data));
1088
+ for (const path3 in this.comments) {
1089
+ const keys = path3.split(".");
1090
+ const propName = keys.pop();
1091
+ const parentPath = keys.join(".");
1092
+ const parentObject = parentPath ? getProperty(dataWithComments, parentPath) : dataWithComments;
1093
+ if (typeof parentObject === "object" && parentObject !== null) {
1094
+ parentObject[`${propName}:comment`] = this.comments[path3];
1095
+ }
1096
+ }
1097
+ dataToSave = dataWithComments;
1098
+ }
1099
+ const filePath = getFilePath2(this.config);
1100
+ fs3.writeFileSync(filePath, JSON.stringify(dataToSave, null, 2));
1101
+ }
1102
+ });
1103
+
1104
+ // src/factory.ts
1105
+ var import_typebox3 = require("@sinclair/typebox");
1106
+ function getEnumValues(values) {
1107
+ if (Array.isArray(values)) {
1108
+ return values;
1109
+ }
1110
+ const isNumericEnum = Object.values(values).some(
1111
+ (v) => typeof v === "number"
1112
+ );
1113
+ if (isNumericEnum) {
1114
+ return Object.values(values).filter(
1115
+ (v) => typeof v === "number"
1116
+ );
1117
+ }
1118
+ return Object.values(values).filter((v) => typeof v === "string");
1119
+ }
1120
+ var _c = {
1121
+ /** Creates a String schema. */
1122
+ String: (options) => import_typebox3.Type.String(options),
1123
+ /** Creates a Number schema. */
1124
+ Number: (options) => import_typebox3.Type.Number(options),
1125
+ /** Creates a Boolean schema. */
1126
+ Boolean: (options) => import_typebox3.Type.Boolean(options),
1127
+ /** Creates an Object schema. */
1128
+ Object: (properties, options) => import_typebox3.Type.Object(properties, options),
1129
+ /** Creates an Array schema. */
1130
+ Array: (items, options) => import_typebox3.Type.Array(items, options),
1131
+ /** Creates a Record schema. */
1132
+ Record: (key, value, options) => import_typebox3.Type.Record(key, value, options),
1133
+ /** Creates a Union of Literals from a string array, const array, or a TypeScript enum. */
1134
+ Enum: (values, options) => {
1135
+ const enumValues = getEnumValues(values);
1136
+ return import_typebox3.Type.Union(
1137
+ enumValues.map((v) => import_typebox3.Type.Literal(v)),
1138
+ options
1139
+ //@ts-expect-error ignore
1140
+ );
1141
+ },
1142
+ /** Creates a string schema with 'ipv4' format. */
1143
+ IP: (options) => import_typebox3.Type.String({ ...options, format: "ipv4" }),
1144
+ /** Creates a string schema with 'ipv6' format. */
1145
+ IPv6: (options) => import_typebox3.Type.String({ ...options, format: "ipv6" }),
1146
+ /** Creates a string schema with 'email' format. */
1147
+ Email: (options) => import_typebox3.Type.String({ ...options, format: "email" }),
1148
+ /** Creates a string schema with 'uri' format. */
1149
+ URL: (options) => import_typebox3.Type.String({ ...options, format: "uri" }),
1150
+ /** Creates an Any schema. */
1151
+ Any: () => import_typebox3.Type.Any(),
1152
+ /** Creates an Optional schema. */
1153
+ Optional: (schema) => import_typebox3.Type.Optional(schema),
1154
+ /** Creates a Number schema that defaults to a random value if not provided. */
1155
+ Random: (options) => {
1156
+ const { max = 100, ...rest } = options || {};
1157
+ return import_typebox3.Type.Number({
1158
+ ...rest,
1159
+ [Symbol.for("isRandom")]: true,
1160
+ max
1161
+ });
1162
+ }
1163
+ };
1164
+ var c = {
1165
+ ..._c,
1166
+ string: _c.String,
1167
+ number: _c.Number,
1168
+ boolean: _c.Boolean,
1169
+ object: _c.Object,
1170
+ array: _c.Array,
1171
+ record: _c.Record,
1172
+ enum: _c.Enum,
1173
+ ip: _c.IP,
1174
+ ipv6: _c.IPv6,
1175
+ email: _c.Email,
1176
+ url: _c.URL,
1177
+ any: _c.Any,
1178
+ optional: _c.Optional,
1179
+ random: _c.Random
1180
+ };
1181
+ var k = c;
1182
+ // Annotate the CommonJS export names for ESM import in node:
1183
+ 0 && (module.exports = {
1184
+ ConfigFS,
1185
+ ConfigJS,
1186
+ ConfigJSDriver,
1187
+ FileFSConfigJS,
1188
+ KFS_JOIN_SYMBOL,
1189
+ KFS_MANY_SYMBOL,
1190
+ Kfg,
1191
+ KfgDriver,
1192
+ KfgFS,
1193
+ KfgFileFS,
1194
+ c,
1195
+ cfs,
1196
+ envDriver,
1197
+ jsonDriver,
1198
+ k,
1199
+ kfs
1200
+ });