kfg 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/dist/index.cjs +1895 -865
  2. package/dist/index.d.ts +251 -356
  3. package/dist/index.js +1886 -843
  4. package/package.json +14 -8
package/dist/index.cjs CHANGED
@@ -30,27 +30,326 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- ConfigFS: () => ConfigFS,
33
+ AsyncJsonDriver: () => AsyncJsonDriver,
34
+ AsyncSqliteDriver: () => AsyncSqliteDriver,
34
35
  ConfigJS: () => ConfigJS,
35
36
  ConfigJSDriver: () => ConfigJSDriver,
36
- FileFSConfigJS: () => FileFSConfigJS,
37
- KFS_JOIN_SYMBOL: () => KFS_JOIN_SYMBOL,
38
- KFS_MANY_SYMBOL: () => KFS_MANY_SYMBOL,
37
+ EnvDriver: () => EnvDriver,
38
+ JsonDriver: () => JsonDriver,
39
39
  Kfg: () => Kfg,
40
40
  KfgDriver: () => KfgDriver,
41
- KfgFS: () => KfgFS,
42
- KfgFileFS: () => KfgFileFS,
41
+ SqliteDriver: () => SqliteDriver,
43
42
  c: () => c,
44
- cfs: () => cfs,
45
- envDriver: () => envDriver,
46
- jsonDriver: () => jsonDriver,
47
43
  k: () => k,
48
- kfs: () => kfs
44
+ m: () => m
49
45
  });
50
46
  module.exports = __toCommonJS(index_exports);
51
47
 
52
- // src/kfg-fs.ts
48
+ // src/drivers/env-driver.ts
53
49
  var fs = __toESM(require("fs"), 1);
50
+ var path = __toESM(require("path"), 1);
51
+
52
+ // src/kfg-driver.ts
53
+ var KfgDriver = class {
54
+ /**
55
+ * Creates a new instance of KfgDriver.
56
+ * @param definition The driver definition.
57
+ */
58
+ constructor(definition) {
59
+ this.definition = definition;
60
+ this.identify = definition.identify;
61
+ this.async = definition.async;
62
+ this.config = definition.config || {};
63
+ this.onMount = definition.onMount;
64
+ this.onUnmount = definition.onUnmount;
65
+ this.onRequest = definition.onRequest;
66
+ this.onGet = definition.onGet;
67
+ this.onUpdate = definition.onUpdate;
68
+ this.onDelete = definition.onDelete;
69
+ this.onMerge = definition.onMerge;
70
+ this.onHas = definition.onHas;
71
+ this.onInject = definition.onInject;
72
+ this.onToJSON = definition.onToJSON;
73
+ this.save = definition.save;
74
+ this.onCreate = definition.onCreate;
75
+ this.onList = definition.onList;
76
+ this.onSize = definition.onSize;
77
+ }
78
+ identify;
79
+ async;
80
+ config;
81
+ // Hooks from the Driver interface
82
+ onMount;
83
+ onUnmount;
84
+ onRequest;
85
+ onGet;
86
+ onUpdate;
87
+ onDelete;
88
+ onMerge;
89
+ onHas;
90
+ onInject;
91
+ onToJSON;
92
+ save;
93
+ onCreate;
94
+ onList;
95
+ onSize;
96
+ /**
97
+ * Mounts the driver.
98
+ */
99
+ mount(kfg, opts) {
100
+ if (opts) {
101
+ kfg.$store.set("~driver", { ...this.config, ...opts });
102
+ }
103
+ if (this.onMount) {
104
+ return this.onMount(kfg, opts);
105
+ }
106
+ return this.async ? Promise.resolve() : void 0;
107
+ }
108
+ /**
109
+ * Unmounts the driver.
110
+ */
111
+ unmount(kfg) {
112
+ if (this.onUnmount) {
113
+ this.onUnmount(kfg);
114
+ }
115
+ }
116
+ /**
117
+ * Called before an operation.
118
+ */
119
+ request(kfg, opts) {
120
+ if (this.onRequest) {
121
+ return this.onRequest(kfg, opts);
122
+ }
123
+ return this.async ? Promise.resolve() : void 0;
124
+ }
125
+ get(kfg, key) {
126
+ const run = () => {
127
+ if (this.onGet) {
128
+ return this.onGet(kfg, { path: key });
129
+ }
130
+ throw new Error("Driver does not implement onGet");
131
+ };
132
+ if (this.async) {
133
+ return this.request(kfg, { path: key }).then(
134
+ run
135
+ );
136
+ }
137
+ this.request(kfg, { path: key });
138
+ return run();
139
+ }
140
+ set(kfg, key, value, options) {
141
+ const run = () => {
142
+ if (this.onUpdate) {
143
+ return this.onUpdate(kfg, { path: key, value, ...options });
144
+ }
145
+ throw new Error("Driver does not implement onUpdate");
146
+ };
147
+ if (this.async) {
148
+ return this.request(kfg, { path: key, value, ...options }).then(run);
149
+ }
150
+ this.request(kfg, { path: key, value, ...options });
151
+ return run();
152
+ }
153
+ has(kfg, ...keys) {
154
+ const run = () => {
155
+ if (this.onHas) {
156
+ return this.onHas(kfg, { paths: keys });
157
+ }
158
+ throw new Error("Driver does not implement onHas");
159
+ };
160
+ if (this.async) {
161
+ return this.request(kfg, { paths: keys }).then(
162
+ run
163
+ );
164
+ }
165
+ this.request(kfg, { paths: keys });
166
+ return run();
167
+ }
168
+ del(kfg, key, options) {
169
+ const run = () => {
170
+ if (this.onDelete) {
171
+ return this.onDelete(kfg, { path: key, ...options });
172
+ }
173
+ throw new Error("Driver does not implement onDelete");
174
+ };
175
+ if (this.async) {
176
+ return this.request(kfg, { path: key, ...options }).then(run);
177
+ }
178
+ this.request(kfg, { path: key, ...options });
179
+ return run();
180
+ }
181
+ saveTo(kfg, data) {
182
+ const run = () => {
183
+ if (this.save) {
184
+ return this.save(kfg, data);
185
+ }
186
+ };
187
+ if (this.async) {
188
+ return this.request(kfg, { data }).then(run);
189
+ }
190
+ this.request(kfg, { data });
191
+ return run();
192
+ }
193
+ insert(kfg, path3, partial) {
194
+ const run = (target) => {
195
+ if (typeof target !== "object" || target === null) {
196
+ throw new Error(`Cannot insert into non-object at path: ${path3}`);
197
+ }
198
+ Object.assign(target, partial);
199
+ return this.set(kfg, path3, target);
200
+ };
201
+ const result = this.get(kfg, path3);
202
+ if (this.async) {
203
+ return result.then(run);
204
+ }
205
+ return run(result);
206
+ }
207
+ inject(kfg, data) {
208
+ const run = () => {
209
+ if (this.onMerge) {
210
+ return this.onMerge(kfg, { data });
211
+ }
212
+ if (this.onInject) {
213
+ return this.onInject(kfg, { data });
214
+ }
215
+ throw new Error("Driver does not implement onMerge/onInject");
216
+ };
217
+ if (this.async) {
218
+ return this.request(kfg, { data }).then(run);
219
+ }
220
+ this.request(kfg, { data });
221
+ return run();
222
+ }
223
+ toJSON(kfg) {
224
+ const run = () => {
225
+ if (this.onToJSON) {
226
+ return this.onToJSON(kfg);
227
+ }
228
+ throw new Error("Driver does not implement onToJSON");
229
+ };
230
+ if (this.async) {
231
+ return this.request(kfg, {}).then(run);
232
+ }
233
+ this.request(kfg, {});
234
+ return run();
235
+ }
236
+ create(kfg, data) {
237
+ const run = () => {
238
+ if (this.onCreate) {
239
+ return this.onCreate(kfg, { data });
240
+ }
241
+ throw new Error("Driver does not implement onCreate");
242
+ };
243
+ if (this.async) {
244
+ return this.request(kfg, { data }).then(run);
245
+ }
246
+ this.request(kfg, { data });
247
+ return run();
248
+ }
249
+ list(kfg, opts) {
250
+ const run = () => {
251
+ if (this.onList) {
252
+ return this.onList(kfg, opts);
253
+ }
254
+ throw new Error("Driver does not implement onList");
255
+ };
256
+ if (this.async) {
257
+ return this.request(kfg, opts).then(run);
258
+ }
259
+ this.request(kfg, opts);
260
+ return run();
261
+ }
262
+ size(kfg) {
263
+ if (this.onSize) {
264
+ return this.onSize(kfg);
265
+ }
266
+ return 0;
267
+ }
268
+ };
269
+
270
+ // src/utils/env.ts
271
+ function parse(content) {
272
+ const result = {};
273
+ const lines = content.split(/\r?\n/);
274
+ for (const line of lines) {
275
+ const trimmedLine = line.trim();
276
+ if (!trimmedLine || trimmedLine.startsWith("#")) {
277
+ continue;
278
+ }
279
+ const match = trimmedLine.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/);
280
+ if (!match) continue;
281
+ const [, key, rawValue] = match;
282
+ let value = rawValue.trim();
283
+ if (!value.startsWith('"') && !value.startsWith("'")) {
284
+ const commentIndex = value.indexOf("#");
285
+ if (commentIndex > -1) {
286
+ value = value.substring(0, commentIndex).trim();
287
+ }
288
+ }
289
+ if (value.startsWith("'") && value.endsWith("'")) {
290
+ value = value.substring(1, value.length - 1);
291
+ } else if (value.startsWith('"') && value.endsWith('"')) {
292
+ value = value.substring(1, value.length - 1);
293
+ }
294
+ result[key] = value;
295
+ }
296
+ return result;
297
+ }
298
+ function updateEnvContent(content, key, value, description) {
299
+ const lines = content.split(/\r?\n/);
300
+ let keyFound = false;
301
+ const newLines = [...lines];
302
+ let formattedValue;
303
+ if (Array.isArray(value)) {
304
+ formattedValue = JSON.stringify(value);
305
+ } else {
306
+ const stringValue = String(value);
307
+ formattedValue = /[\s"'#]/.test(stringValue) ? `"${stringValue.replace(/"/g, '"').replace(/\n/g, "\\n")}"` : stringValue;
308
+ }
309
+ let lineIndex = -1;
310
+ for (let i = 0; i < lines.length; i++) {
311
+ if (new RegExp(`^s*${key}s*=s*`).test(lines[i])) {
312
+ keyFound = true;
313
+ lineIndex = i;
314
+ break;
315
+ }
316
+ }
317
+ if (keyFound) {
318
+ newLines[lineIndex] = `${key}=${formattedValue}`;
319
+ if (description) {
320
+ const comment = `# ${description}`;
321
+ if (lineIndex === 0 || !newLines[lineIndex - 1].trim().startsWith(comment)) {
322
+ if (lineIndex > 0 && !newLines[lineIndex - 1].trim().startsWith("#")) {
323
+ newLines.splice(lineIndex, 0, comment);
324
+ }
325
+ }
326
+ }
327
+ } else {
328
+ if (newLines[newLines.length - 1] !== "") {
329
+ newLines.push("");
330
+ }
331
+ if (description) {
332
+ newLines.push(`# ${description}`);
333
+ }
334
+ newLines.push(`${key}=${formattedValue}`);
335
+ }
336
+ return newLines.join("\n");
337
+ }
338
+ function removeEnvKey(content, key) {
339
+ const lines = content.split(/\r?\n/);
340
+ const keyRegex = new RegExp(`^\\s*${key}\\s*=\\s*`);
341
+ const newLines = [];
342
+ for (const line of lines) {
343
+ if (keyRegex.test(line)) {
344
+ if (newLines.length > 0 && newLines[newLines.length - 1].trim().startsWith("#")) {
345
+ newLines.pop();
346
+ }
347
+ } else {
348
+ newLines.push(line);
349
+ }
350
+ }
351
+ return newLines.join("\n");
352
+ }
54
353
 
55
354
  // src/utils/object.ts
56
355
  function getProperty(obj, path3) {
@@ -138,10 +437,10 @@ function addSmartDefaults(schemaNode) {
138
437
  let allChildrenOptional = true;
139
438
  for (const key in schemaNode.properties) {
140
439
  const prop = schemaNode.properties[key];
141
- if (prop[Symbol.for("TypeBox.Kind")] === "Unsafe") {
440
+ if (prop[/* @__PURE__ */ Symbol.for("TypeBox.Kind")] === "Unsafe") {
142
441
  continue;
143
442
  }
144
- if (prop.type === "object" && prop[Symbol.for("TypeBox.Kind")]) {
443
+ if (prop.type === "object" && prop[/* @__PURE__ */ Symbol.for("TypeBox.Kind")]) {
145
444
  addSmartDefaults(prop);
146
445
  }
147
446
  const hasDefault = prop.default !== void 0;
@@ -155,13 +454,13 @@ function addSmartDefaults(schemaNode) {
155
454
  }
156
455
  }
157
456
  function buildTypeBoxSchema(definition) {
158
- if (definition[Symbol.for("TypeBox.Kind")] === "Object") {
457
+ if (definition[/* @__PURE__ */ Symbol.for("TypeBox.Kind")] === "Object") {
159
458
  return definition;
160
459
  }
161
460
  const properties = {};
162
461
  for (const key in definition) {
163
462
  const value = definition[key];
164
- const isObject2 = typeof value === "object" && value !== null && !value[Symbol.for("TypeBox.Kind")];
463
+ const isObject2 = typeof value === "object" && value !== null && !value[/* @__PURE__ */ Symbol.for("TypeBox.Kind")];
165
464
  if (isObject2) {
166
465
  properties[key] = buildTypeBoxSchema(value);
167
466
  } else {
@@ -179,7 +478,7 @@ function makeSchemaOptional(definition) {
179
478
  const newDefinition = {};
180
479
  for (const key in definition) {
181
480
  const value = definition[key];
182
- if (value?.[Symbol.for("TypeBox.Kind")]) {
481
+ if (value?.[/* @__PURE__ */ Symbol.for("TypeBox.Kind")]) {
183
482
  const schema = value;
184
483
  const isOptional = import_value.Value.Check(import_typebox.Type.Object({ temp: schema }), {});
185
484
  if (schema.important || isOptional) {
@@ -196,791 +495,133 @@ function makeSchemaOptional(definition) {
196
495
  return newDefinition;
197
496
  }
198
497
 
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.");
498
+ // src/drivers/env-driver.ts
499
+ function getFilePath(config) {
500
+ return path.resolve(process.cwd(), config.path || ".env");
501
+ }
502
+ function coerceType(value, schema) {
503
+ if (value === void 0) return void 0;
504
+ const type = schema.type;
505
+ if (type === "number") return Number(value);
506
+ if (type === "boolean") return String(value).toLowerCase() === "true";
507
+ if (type === "array" && typeof value === "string") {
508
+ const trimmedValue = value.trim();
509
+ if (trimmedValue.startsWith("[") && trimmedValue.endsWith("]")) {
510
+ try {
511
+ return JSON.parse(trimmedValue);
512
+ } catch {
513
+ }
249
514
  }
250
- return this.driver.get(path3);
251
515
  }
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;
516
+ return value;
517
+ }
518
+ function traverseSchema(schema, envValues, prefix = []) {
519
+ const builtConfig = {};
520
+ for (const key in schema) {
521
+ const currentPath = [...prefix, key];
522
+ const definition = schema[key];
523
+ const isTypeBoxSchema = (def) => !!def[/* @__PURE__ */ Symbol.for("TypeBox.Kind")];
524
+ if (isTypeBoxSchema(definition)) {
525
+ const prop = definition.prop;
526
+ const envKey = prop || currentPath.join("_").toUpperCase();
527
+ let value = envValues[envKey];
528
+ if (value === void 0) {
529
+ value = definition.default;
530
+ }
531
+ if (value !== void 0) {
532
+ builtConfig[key] = coerceType(value, definition);
533
+ }
534
+ } else if (typeof definition === "object" && definition !== null) {
535
+ const nestedConfig = traverseSchema(
536
+ definition,
537
+ envValues,
538
+ currentPath
539
+ );
540
+ builtConfig[key] = nestedConfig;
937
541
  }
938
542
  }
939
543
  return builtConfig;
940
544
  }
941
- var envDriver = new KfgDriver({
545
+ var EnvDriver = new KfgDriver({
942
546
  identify: "env-driver",
943
547
  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") : "";
548
+ config: {},
549
+ onMount(kfg, _opts) {
550
+ const cfg = kfg.$config;
551
+ const filePath = getFilePath(cfg);
552
+ const fileContent = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
948
553
  const envFileValues = parse(fileContent);
949
554
  const processEnv = Object.fromEntries(
950
555
  Object.entries(process.env).filter(([, v]) => v !== void 0)
951
556
  );
952
557
  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;
558
+ const envData = traverseSchema(kfg.schema, allEnvValues);
559
+ const defaultData = buildDefaultObject(kfg.schema);
560
+ const finalData = deepMerge(defaultData, envData);
561
+ kfg.$store.set("data", finalData);
562
+ return finalData;
563
+ },
564
+ onGet(kfg, { path: path3 }) {
565
+ const data = kfg.$store.get("data", {});
566
+ if (!path3) return data;
567
+ return getProperty(data, path3);
957
568
  },
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") : "";
569
+ onHas(kfg, { paths }) {
570
+ const data = kfg.$store.get("data", {});
571
+ return paths.every((path3) => getProperty(data, path3) !== void 0);
572
+ },
573
+ onUpdate(kfg, opts) {
574
+ const data = kfg.$store.get("data", {});
575
+ if (opts.path) {
576
+ setProperty(data, opts.path, opts.value);
577
+ }
578
+ kfg.$store.set("data", data);
579
+ if (!opts?.path) return;
580
+ const envKey = opts.path.replace(/\./g, "_").toUpperCase();
581
+ const filePath = getFilePath(kfg.$config);
582
+ const currentContent = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
962
583
  const newContent = updateEnvContent(
963
584
  currentContent,
964
585
  envKey,
965
- value,
966
- options?.description
586
+ opts.value,
587
+ opts.description
967
588
  );
968
- fs2.writeFileSync(filePath, newContent);
589
+ fs.writeFileSync(filePath, newContent);
969
590
  },
970
- onDel(key) {
971
- const envKey = key.replace(/\./g, "_").toUpperCase();
972
- const filePath = getFilePath(this.config);
973
- if (!fs2.existsSync(filePath)) {
591
+ onDelete(kfg, opts) {
592
+ const data = kfg.$store.get("data", {});
593
+ if (opts.path) {
594
+ deleteProperty(data, opts.path);
595
+ }
596
+ kfg.$store.set("data", data);
597
+ if (!opts?.path) return;
598
+ const envKey = opts.path.replace(/\./g, "_").toUpperCase();
599
+ const filePath = getFilePath(kfg.$config);
600
+ if (!fs.existsSync(filePath)) {
974
601
  return;
975
602
  }
976
- const currentContent = fs2.readFileSync(filePath, "utf-8");
603
+ const currentContent = fs.readFileSync(filePath, "utf-8");
977
604
  const newContent = removeEnvKey(currentContent, envKey);
978
- fs2.writeFileSync(filePath, newContent);
605
+ fs.writeFileSync(filePath, newContent);
606
+ },
607
+ onToJSON(kfg) {
608
+ return kfg.$store.get("data");
609
+ },
610
+ onInject(kfg, { data }) {
611
+ kfg.$store.merge("data", data);
612
+ },
613
+ onMerge(kfg, { data }) {
614
+ kfg.$store.merge("data", data);
615
+ },
616
+ onSize(kfg) {
617
+ const data = kfg.$store.get("data");
618
+ return data ? 1 : 0;
979
619
  }
980
620
  });
981
621
 
982
622
  // src/drivers/json-driver.ts
983
- var fs3 = __toESM(require("fs"), 1);
623
+ var fs2 = __toESM(require("fs"), 1);
624
+ var fsPromises = __toESM(require("fs/promises"), 1);
984
625
  var path2 = __toESM(require("path"), 1);
985
626
  function stripComments(data) {
986
627
  const comments = {};
@@ -1004,105 +645,1126 @@ function stripComments(data) {
1004
645
  recurse(data);
1005
646
  return comments;
1006
647
  }
1007
- function getFilePath2(config) {
1008
- return path2.resolve(process.cwd(), config.path || "config.json");
648
+ function getFilePath2(config) {
649
+ return path2.resolve(process.cwd(), config.path || "config.json");
650
+ }
651
+ function resolvePath(pattern, id) {
652
+ return pattern.replace("{id}", id);
653
+ }
654
+ function getPatternRegex(pattern) {
655
+ const escaped = pattern.replace(/[.*+?^${}()|[\\]/g, "\\$&");
656
+ const regexStr = escaped.replace(/\\{id\\}/g, "(.+)");
657
+ return new RegExp(`^${regexStr}$`);
658
+ }
659
+ function saveSync(kfg, config, data) {
660
+ if (kfg.multimode) {
661
+ const allData = data || kfg.$store.get("data");
662
+ const pattern = getFilePath2(config);
663
+ for (const id in allData) {
664
+ const item = allData[id];
665
+ const filePath2 = resolvePath(pattern, id);
666
+ const dir2 = path2.dirname(filePath2);
667
+ if (!fs2.existsSync(dir2)) fs2.mkdirSync(dir2, { recursive: true });
668
+ fs2.writeFileSync(filePath2, JSON.stringify(item, null, 2));
669
+ }
670
+ return;
671
+ }
672
+ const currentData = data || kfg.$store.get("data");
673
+ const comments = kfg.$store.get("comments", {});
674
+ let dataToSave;
675
+ if (config.keyroot) {
676
+ dataToSave = flattenObject(currentData);
677
+ for (const path3 in comments) {
678
+ dataToSave[`${path3}:comment`] = comments[path3];
679
+ }
680
+ } else {
681
+ const dataWithComments = JSON.parse(JSON.stringify(currentData));
682
+ for (const path3 in comments) {
683
+ const keys = path3.split(".");
684
+ const propName = keys.pop();
685
+ const parentPath = keys.join(".");
686
+ const parentObject = parentPath ? getProperty(dataWithComments, parentPath) : dataWithComments;
687
+ if (typeof parentObject === "object" && parentObject !== null) {
688
+ parentObject[`${propName}:comment`] = comments[path3];
689
+ }
690
+ }
691
+ dataToSave = dataWithComments;
692
+ }
693
+ const filePath = getFilePath2(config);
694
+ const dir = path2.dirname(filePath);
695
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
696
+ fs2.writeFileSync(filePath, JSON.stringify(dataToSave, null, 2));
697
+ }
698
+ async function saveAsync(kfg, config, data) {
699
+ if (kfg.multimode) {
700
+ const allData = data || kfg.$store.get("data");
701
+ const pattern = getFilePath2(config);
702
+ const promises = Object.keys(allData).map(async (id) => {
703
+ const item = allData[id];
704
+ const filePath2 = resolvePath(pattern, id);
705
+ const dir2 = path2.dirname(filePath2);
706
+ await fsPromises.mkdir(dir2, { recursive: true });
707
+ await fsPromises.writeFile(filePath2, JSON.stringify(item, null, 2));
708
+ });
709
+ await Promise.all(promises);
710
+ return;
711
+ }
712
+ const currentData = data || kfg.$store.get("data");
713
+ const comments = kfg.$store.get("comments", {});
714
+ let dataToSave;
715
+ if (config.keyroot) {
716
+ dataToSave = flattenObject(currentData);
717
+ for (const path3 in comments) {
718
+ dataToSave[`${path3}:comment`] = comments[path3];
719
+ }
720
+ } else {
721
+ const dataWithComments = JSON.parse(JSON.stringify(currentData));
722
+ for (const path3 in comments) {
723
+ const keys = path3.split(".");
724
+ const propName = keys.pop();
725
+ const parentPath = keys.join(".");
726
+ const parentObject = parentPath ? getProperty(dataWithComments, parentPath) : dataWithComments;
727
+ if (typeof parentObject === "object" && parentObject !== null) {
728
+ parentObject[`${propName}:comment`] = comments[path3];
729
+ }
730
+ }
731
+ dataToSave = dataWithComments;
732
+ }
733
+ const filePath = getFilePath2(config);
734
+ const dir = path2.dirname(filePath);
735
+ await fsPromises.mkdir(dir, { recursive: true });
736
+ await fsPromises.writeFile(filePath, JSON.stringify(dataToSave, null, 2));
737
+ }
738
+ var JsonDriver = new KfgDriver({
739
+ identify: "json-driver",
740
+ async: false,
741
+ config: {},
742
+ onMount(kfg) {
743
+ const cfg = kfg.$config;
744
+ const pattern = getFilePath2(cfg);
745
+ if (kfg.multimode || pattern.includes("{id}")) {
746
+ const loadedData2 = {};
747
+ const dir = path2.dirname(pattern);
748
+ if (fs2.existsSync(dir)) {
749
+ const files = fs2.readdirSync(dir);
750
+ const regex = getPatternRegex(pattern);
751
+ for (const file of files) {
752
+ const fullPath = path2.join(dir, file);
753
+ const match = fullPath.match(regex);
754
+ if (match) {
755
+ const id = match[1];
756
+ try {
757
+ const content = fs2.readFileSync(fullPath, "utf-8");
758
+ loadedData2[id] = JSON.parse(content);
759
+ } catch {
760
+ }
761
+ }
762
+ }
763
+ }
764
+ kfg.$store.set("data", loadedData2);
765
+ return loadedData2;
766
+ }
767
+ const defaultData = buildDefaultObject(kfg.schema);
768
+ const filePath = pattern;
769
+ let loadedData = {};
770
+ if (fs2.existsSync(filePath)) {
771
+ try {
772
+ const fileContent = fs2.readFileSync(filePath, "utf-8");
773
+ if (fileContent) {
774
+ loadedData = JSON.parse(fileContent);
775
+ }
776
+ } catch (_e) {
777
+ }
778
+ }
779
+ let comments = {};
780
+ if (cfg.keyroot) {
781
+ const flatData = loadedData;
782
+ const cmts = {};
783
+ const data = {};
784
+ for (const key in flatData) {
785
+ if (key.endsWith(":comment")) {
786
+ cmts[key.replace(/:comment$/, "")] = flatData[key];
787
+ } else {
788
+ data[key] = flatData[key];
789
+ }
790
+ }
791
+ comments = cmts;
792
+ loadedData = unflattenObject(data);
793
+ } else {
794
+ comments = stripComments(loadedData);
795
+ }
796
+ kfg.$store.set("comments", comments);
797
+ const finalData = deepMerge(defaultData, loadedData);
798
+ kfg.$store.set("data", finalData);
799
+ return finalData;
800
+ },
801
+ onGet(kfg, { path: path3 }) {
802
+ const data = kfg.$store.get("data", {});
803
+ if (!path3) return data;
804
+ return getProperty(data, path3);
805
+ },
806
+ onHas(kfg, { paths }) {
807
+ const data = kfg.$store.get("data", {});
808
+ return paths.every((path3) => getProperty(data, path3) !== void 0);
809
+ },
810
+ onUpdate(kfg, opts) {
811
+ const data = kfg.$store.get("data", {});
812
+ if (opts.path) {
813
+ setProperty(data, opts.path, opts.value);
814
+ }
815
+ kfg.$store.set("data", data);
816
+ if (kfg.multimode) {
817
+ const pattern = getFilePath2(kfg.$config);
818
+ const parts = opts.path.split(".");
819
+ const id = parts[0];
820
+ if (id && data[id]) {
821
+ const filePath = resolvePath(pattern, id);
822
+ const dir = path2.dirname(filePath);
823
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
824
+ fs2.writeFileSync(filePath, JSON.stringify(data[id], null, 2));
825
+ }
826
+ return;
827
+ }
828
+ if (opts?.path && opts?.description) {
829
+ const comments = kfg.$store.get("comments", {});
830
+ comments[opts.path] = opts.description;
831
+ kfg.$store.set("comments", comments);
832
+ }
833
+ saveSync(kfg, kfg.$config, data);
834
+ },
835
+ onDelete(kfg, opts) {
836
+ const data = kfg.$store.get("data", {});
837
+ if (opts.path) {
838
+ deleteProperty(data, opts.path);
839
+ }
840
+ kfg.$store.set("data", data);
841
+ if (kfg.multimode) {
842
+ const parts = opts.path.split(".");
843
+ if (parts.length === 1) {
844
+ const id = parts[0];
845
+ const pattern = getFilePath2(kfg.$config);
846
+ const filePath = resolvePath(pattern, id);
847
+ if (fs2.existsSync(filePath)) {
848
+ fs2.unlinkSync(filePath);
849
+ }
850
+ } else {
851
+ const id = parts[0];
852
+ const pattern = getFilePath2(kfg.$config);
853
+ const filePath = resolvePath(pattern, id);
854
+ if (data[id]) {
855
+ fs2.writeFileSync(filePath, JSON.stringify(data[id], null, 2));
856
+ }
857
+ }
858
+ return;
859
+ }
860
+ if (opts?.path) {
861
+ const comments = kfg.$store.get("comments", {});
862
+ if (comments[opts.path]) {
863
+ delete comments[opts.path];
864
+ kfg.$store.set("comments", comments);
865
+ }
866
+ }
867
+ saveSync(kfg, kfg.$config, data);
868
+ },
869
+ onCreate(kfg, { data }) {
870
+ if (kfg.multimode) {
871
+ const id = data.id;
872
+ if (!id)
873
+ throw new Error("Cannot create item without 'id' property in data.");
874
+ const storeData = kfg.$store.get("data", {});
875
+ storeData[id] = data;
876
+ kfg.$store.set("data", storeData);
877
+ const pattern = getFilePath2(kfg.$config);
878
+ const filePath = resolvePath(pattern, String(id));
879
+ const dir = path2.dirname(filePath);
880
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
881
+ fs2.writeFileSync(filePath, JSON.stringify(data, null, 2));
882
+ return data;
883
+ }
884
+ kfg.$store.set("data", data);
885
+ saveSync(kfg, kfg.$config, data);
886
+ return data;
887
+ },
888
+ onToJSON(kfg) {
889
+ return kfg.$store.get("data");
890
+ },
891
+ onInject(kfg, { data }) {
892
+ kfg.$store.merge("data", data);
893
+ },
894
+ onMerge(kfg, { data }) {
895
+ kfg.$store.merge("data", data);
896
+ },
897
+ save(kfg, data) {
898
+ saveSync(kfg, kfg.$config, data);
899
+ },
900
+ onSize(kfg) {
901
+ const data = kfg.$store.get("data");
902
+ if (!data) return 0;
903
+ if (kfg.multimode) return Object.keys(data).length;
904
+ return 1;
905
+ }
906
+ });
907
+ var AsyncJsonDriver = new KfgDriver({
908
+ identify: "async-json-driver",
909
+ async: true,
910
+ config: {},
911
+ async onMount(kfg) {
912
+ const cfg = kfg.$config;
913
+ const pattern = getFilePath2(cfg);
914
+ if (kfg.multimode || pattern.includes("{id}")) {
915
+ const loadedData2 = {};
916
+ const dir = path2.dirname(pattern);
917
+ try {
918
+ await fsPromises.access(dir);
919
+ const files = await fsPromises.readdir(dir);
920
+ const regex = getPatternRegex(pattern);
921
+ await Promise.all(
922
+ files.map(async (file) => {
923
+ const fullPath = path2.join(dir, file);
924
+ const match = fullPath.match(regex);
925
+ if (match) {
926
+ const id = match[1];
927
+ try {
928
+ const content = await fsPromises.readFile(fullPath, "utf-8");
929
+ loadedData2[id] = JSON.parse(content);
930
+ } catch {
931
+ }
932
+ }
933
+ })
934
+ );
935
+ } catch {
936
+ }
937
+ kfg.$store.set("data", loadedData2);
938
+ return loadedData2;
939
+ }
940
+ const defaultData = buildDefaultObject(kfg.schema);
941
+ const filePath = pattern;
942
+ let loadedData = {};
943
+ try {
944
+ await fsPromises.access(filePath);
945
+ const fileContent = await fsPromises.readFile(filePath, "utf-8");
946
+ if (fileContent) {
947
+ loadedData = JSON.parse(fileContent);
948
+ }
949
+ } catch (_e) {
950
+ }
951
+ let comments = {};
952
+ if (cfg.keyroot) {
953
+ const flatData = loadedData;
954
+ const cmts = {};
955
+ const data = {};
956
+ for (const key in flatData) {
957
+ if (key.endsWith(":comment")) {
958
+ cmts[key.replace(/:comment$/, "")] = flatData[key];
959
+ } else {
960
+ data[key] = flatData[key];
961
+ }
962
+ }
963
+ comments = cmts;
964
+ loadedData = unflattenObject(data);
965
+ } else {
966
+ comments = stripComments(loadedData);
967
+ }
968
+ kfg.$store.set("comments", comments);
969
+ const finalData = deepMerge(defaultData, loadedData);
970
+ kfg.$store.set("data", finalData);
971
+ return finalData;
972
+ },
973
+ onGet(kfg, { path: path3 }) {
974
+ const data = kfg.$store.get("data", {});
975
+ if (!path3) return Promise.resolve(data);
976
+ return Promise.resolve(getProperty(data, path3));
977
+ },
978
+ onHas(kfg, { paths }) {
979
+ const data = kfg.$store.get("data", {});
980
+ return Promise.resolve(
981
+ paths.every((path3) => getProperty(data, path3) !== void 0)
982
+ );
983
+ },
984
+ async onUpdate(kfg, opts) {
985
+ const data = kfg.$store.get("data", {});
986
+ if (opts.path) {
987
+ setProperty(data, opts.path, opts.value);
988
+ }
989
+ kfg.$store.set("data", data);
990
+ if (kfg.multimode) {
991
+ const pattern = getFilePath2(kfg.$config);
992
+ const parts = opts.path.split(".");
993
+ const id = parts[0];
994
+ if (id && data[id]) {
995
+ const filePath = resolvePath(pattern, id);
996
+ const dir = path2.dirname(filePath);
997
+ await fsPromises.mkdir(dir, { recursive: true });
998
+ await fsPromises.writeFile(filePath, JSON.stringify(data[id], null, 2));
999
+ }
1000
+ return;
1001
+ }
1002
+ if (opts?.path && opts?.description) {
1003
+ const comments = kfg.$store.get("comments", {});
1004
+ comments[opts.path] = opts.description;
1005
+ kfg.$store.set("comments", comments);
1006
+ }
1007
+ await saveAsync(kfg, kfg.$config, data);
1008
+ },
1009
+ async onDelete(kfg, opts) {
1010
+ const data = kfg.$store.get("data", {});
1011
+ if (opts.path) {
1012
+ deleteProperty(data, opts.path);
1013
+ }
1014
+ kfg.$store.set("data", data);
1015
+ if (kfg.multimode) {
1016
+ const parts = opts.path.split(".");
1017
+ if (parts.length === 1) {
1018
+ const id = parts[0];
1019
+ const pattern = getFilePath2(kfg.$config);
1020
+ const filePath = resolvePath(pattern, id);
1021
+ try {
1022
+ await fsPromises.unlink(filePath);
1023
+ } catch {
1024
+ }
1025
+ } else {
1026
+ const id = parts[0];
1027
+ const pattern = getFilePath2(kfg.$config);
1028
+ const filePath = resolvePath(pattern, id);
1029
+ if (data[id]) {
1030
+ await fsPromises.writeFile(
1031
+ filePath,
1032
+ JSON.stringify(data[id], null, 2)
1033
+ );
1034
+ }
1035
+ }
1036
+ return;
1037
+ }
1038
+ if (opts?.path) {
1039
+ const comments = kfg.$store.get("comments", {});
1040
+ if (comments[opts.path]) {
1041
+ delete comments[opts.path];
1042
+ kfg.$store.set("comments", comments);
1043
+ }
1044
+ }
1045
+ await saveAsync(kfg, kfg.$config, data);
1046
+ },
1047
+ async onCreate(kfg, { data }) {
1048
+ if (kfg.multimode) {
1049
+ const id = data.id;
1050
+ if (!id)
1051
+ throw new Error("Cannot create item without 'id' property in data.");
1052
+ const storeData = kfg.$store.get("data", {});
1053
+ storeData[id] = data;
1054
+ kfg.$store.set("data", storeData);
1055
+ const pattern = getFilePath2(kfg.$config);
1056
+ const filePath = resolvePath(pattern, String(id));
1057
+ const dir = path2.dirname(filePath);
1058
+ await fsPromises.mkdir(dir, { recursive: true });
1059
+ await fsPromises.writeFile(filePath, JSON.stringify(data, null, 2));
1060
+ return data;
1061
+ }
1062
+ kfg.$store.set("data", data);
1063
+ await saveAsync(kfg, kfg.$config, data);
1064
+ return data;
1065
+ },
1066
+ onToJSON(kfg) {
1067
+ return Promise.resolve(kfg.$store.get("data"));
1068
+ },
1069
+ onInject(kfg, { data }) {
1070
+ kfg.$store.merge("data", data);
1071
+ return Promise.resolve();
1072
+ },
1073
+ onMerge(kfg, { data }) {
1074
+ kfg.$store.merge("data", data);
1075
+ return Promise.resolve();
1076
+ },
1077
+ save(kfg, data) {
1078
+ return saveAsync(kfg, kfg.$config, data);
1079
+ },
1080
+ onSize(kfg) {
1081
+ const data = kfg.$store.get("data");
1082
+ if (!data) return 0;
1083
+ if (kfg.multimode) return Object.keys(data).length;
1084
+ return 1;
1085
+ }
1086
+ });
1087
+
1088
+ // src/utils/sqlite.ts
1089
+ var import_node_fs = require("fs");
1090
+ var import_node_path = require("path");
1091
+ function loadSqliteDatabase() {
1092
+ const firsttry = typeof Bun !== "undefined" ? "bun:sqlite" : "node:sqlite";
1093
+ let sqlite;
1094
+ let moduleType;
1095
+ if (firsttry === "bun:sqlite") {
1096
+ try {
1097
+ const m2 = require("bun:sqlite");
1098
+ sqlite = m2.Database;
1099
+ moduleType = "bun:sqlite";
1100
+ } catch {
1101
+ try {
1102
+ const m2 = require("better-sqlite3");
1103
+ sqlite = m2?.default || m2;
1104
+ moduleType = "better-sqlite3";
1105
+ } catch {
1106
+ throw new Error(
1107
+ "Bun and better-sqlite3 database not found, update your environment"
1108
+ );
1109
+ }
1110
+ }
1111
+ } else {
1112
+ try {
1113
+ const m2 = require("sqlite");
1114
+ sqlite = m2.DatabaseSync;
1115
+ moduleType = "node:sqlite";
1116
+ } catch {
1117
+ try {
1118
+ const m2 = require("better-sqlite3");
1119
+ sqlite = m2?.default || m2;
1120
+ moduleType = "better-sqlite3";
1121
+ } catch {
1122
+ throw new Error(
1123
+ "Node and better-sqlite3 database not found, update your environment"
1124
+ );
1125
+ }
1126
+ }
1127
+ }
1128
+ return class KfgDatabase {
1129
+ db;
1130
+ module_type;
1131
+ constructor(path3) {
1132
+ this.module_type = moduleType;
1133
+ const dir = (0, import_node_path.dirname)(path3);
1134
+ if (!(0, import_node_fs.existsSync)(dir) && path3 !== ":memory:") {
1135
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
1136
+ }
1137
+ this.db = new sqlite(path3);
1138
+ }
1139
+ exec(sql) {
1140
+ this.db.exec(sql);
1141
+ }
1142
+ prepare(sql) {
1143
+ const stmt = this.db.prepare(sql);
1144
+ return {
1145
+ all: (...params) => {
1146
+ return stmt.all(...params);
1147
+ },
1148
+ get: (...params) => {
1149
+ return stmt.get(...params);
1150
+ },
1151
+ run: (...params) => {
1152
+ return stmt.run(...params);
1153
+ },
1154
+ finalize: () => {
1155
+ if ("finalize" in stmt) stmt.finalize();
1156
+ }
1157
+ };
1158
+ }
1159
+ transaction(fn) {
1160
+ const db = this.db;
1161
+ if (typeof db.transaction === "function") {
1162
+ return db.transaction(fn);
1163
+ }
1164
+ return () => {
1165
+ this.exec("BEGIN");
1166
+ try {
1167
+ fn();
1168
+ this.exec("COMMIT");
1169
+ } catch (e) {
1170
+ this.exec("ROLLBACK");
1171
+ throw e;
1172
+ }
1173
+ };
1174
+ }
1175
+ close() {
1176
+ this.db.close();
1177
+ }
1178
+ };
1179
+ }
1180
+ var KfgDatabase = loadSqliteDatabase();
1181
+ var sqlite_default = KfgDatabase;
1182
+
1183
+ // src/drivers/sqlite-driver.ts
1184
+ function getDb(config) {
1185
+ if (config.database && typeof config.database.prepare === "function") {
1186
+ return config.database;
1187
+ }
1188
+ return new sqlite_default(config.database || config.path || "config.db");
1189
+ }
1190
+ function ensureTable(db, table) {
1191
+ db.exec(`
1192
+ CREATE TABLE IF NOT EXISTS "${table}" (
1193
+ key TEXT,
1194
+ "group" TEXT,
1195
+ type TEXT,
1196
+ value TEXT,
1197
+ create_at INTEGER,
1198
+ update_at INTEGER,
1199
+ PRIMARY KEY (key, "group")
1200
+ )
1201
+ `);
1202
+ }
1203
+ function rowToValue(row) {
1204
+ let val = row.value;
1205
+ if (row.type === "number") val = Number(val);
1206
+ else if (row.type === "boolean") val = val === "true";
1207
+ else if (row.type === "object" || row.type === "array") {
1208
+ try {
1209
+ val = JSON.parse(val);
1210
+ } catch {
1211
+ }
1212
+ }
1213
+ return val;
1214
+ }
1215
+ function loadData(kfg) {
1216
+ const db = kfg.$store.get("db");
1217
+ const table = kfg.$config?.table || "settings";
1218
+ const stmt = db.prepare(`SELECT * FROM "${table}"`);
1219
+ const rows = stmt.all();
1220
+ stmt.finalize();
1221
+ const flat = {};
1222
+ for (const row of rows) {
1223
+ const fullPath = row.group ? `${row.group}.${row.key}` : row.key;
1224
+ flat[fullPath] = rowToValue(row);
1225
+ }
1226
+ const finalData = unflattenObject(flat);
1227
+ kfg.$store.set("data", finalData);
1228
+ return finalData;
1229
+ }
1230
+ function touch(kfg) {
1231
+ kfg.$store.set("lastAccess", Date.now());
1232
+ }
1233
+ function log(kfg, message, ...args) {
1234
+ if (kfg.$config?.logs) {
1235
+ console.log(`[SqliteDriver] ${message}`, ...args);
1236
+ }
1237
+ }
1238
+ function queueQuery(kfg, query, ...params) {
1239
+ const db = kfg.$store.get("db");
1240
+ if (!db) return;
1241
+ const queue = kfg.$store.get("queue", []);
1242
+ queue.push(() => {
1243
+ if (kfg.$config?.logs) console.log(`[SqliteDriver] Executing: ${query}`);
1244
+ const stmt = db.prepare(query);
1245
+ stmt.run(...params);
1246
+ stmt.finalize();
1247
+ });
1248
+ kfg.$store.set("queue", queue);
1009
1249
  }
1010
- var jsonDriver = new KfgDriver({
1011
- identify: "json-driver",
1250
+ var SqliteDriver = new KfgDriver({
1251
+ identify: "sqlite-driver",
1012
1252
  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);
1253
+ config: {
1254
+ logs: false
1255
+ },
1256
+ onMount(kfg, _opts) {
1257
+ const cfg = kfg.$config;
1258
+ const db = getDb(cfg);
1259
+ kfg.$store.set("db", db);
1260
+ kfg.$store.set("queue", []);
1261
+ touch(kfg);
1262
+ const table = cfg.table || "settings";
1263
+ ensureTable(db, table);
1264
+ const interval = setInterval(() => {
1265
+ const lastAccess = kfg.$store.get("lastAccess", 0);
1266
+ if (Date.now() - lastAccess > 5e3) {
1267
+ const data = kfg.$store.get("data");
1268
+ if (data) {
1269
+ log(kfg, "Clearing cache due to inactivity");
1270
+ kfg.$store.set("data", void 0);
1024
1271
  }
1025
- } catch (_e) {
1026
1272
  }
1273
+ }, 1e3);
1274
+ kfg.$store.set("interval", interval);
1275
+ return loadData(kfg);
1276
+ },
1277
+ onUnmount(kfg) {
1278
+ this.save?.(kfg);
1279
+ const interval = kfg.$store.get("interval");
1280
+ if (interval) clearInterval(interval);
1281
+ const db = kfg.$store.get("db");
1282
+ if (db?.close) db.close();
1283
+ },
1284
+ onGet(kfg, { path: path3 }) {
1285
+ touch(kfg);
1286
+ let data = kfg.$store.get("data");
1287
+ if (!data) {
1288
+ log(kfg, "Cache miss, reloading data");
1289
+ data = loadData(kfg);
1027
1290
  }
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];
1291
+ if (!path3) return data;
1292
+ return getProperty(data, path3);
1293
+ },
1294
+ onHas(kfg, { paths }) {
1295
+ touch(kfg);
1296
+ let data = kfg.$store.get("data");
1297
+ if (!data) {
1298
+ log(kfg, "Cache miss, reloading data");
1299
+ data = loadData(kfg);
1300
+ }
1301
+ return paths.every((path3) => getProperty(data, path3) !== void 0);
1302
+ },
1303
+ onUpdate(kfg, opts) {
1304
+ touch(kfg);
1305
+ let data = kfg.$store.get("data");
1306
+ if (!data) {
1307
+ data = loadData(kfg);
1308
+ }
1309
+ if (opts.path) {
1310
+ setProperty(data, opts.path, opts.value);
1311
+ }
1312
+ kfg.$store.set("data", data);
1313
+ const table = kfg.$config?.table || "settings";
1314
+ if (kfg.multimode) {
1315
+ const parts2 = opts.path.split(".");
1316
+ const id = parts2[0];
1317
+ if (parts2.length === 1) {
1318
+ const value2 = opts.value;
1319
+ const flat = flattenObject(value2);
1320
+ for (const key in flat) {
1321
+ const val = flat[key];
1322
+ const type2 = Array.isArray(val) ? "array" : typeof val;
1323
+ const valStr2 = type2 === "object" || type2 === "array" ? JSON.stringify(val) : String(val);
1324
+ const now2 = Date.now();
1325
+ const query2 = `INSERT INTO "${table}" (key, "group", type, value, create_at, update_at)
1326
+ VALUES (?, ?, ?, ?, ?, ?)
1327
+ ON CONFLICT(key, "group") DO UPDATE SET
1328
+ value = excluded.value,
1329
+ type = excluded.type,
1330
+ update_at = excluded.update_at`;
1331
+ queueQuery(kfg, query2, key, id, type2, valStr2, now2, now2);
1332
+ }
1333
+ return;
1334
+ } else {
1335
+ const subKey = parts2.slice(1).join(".");
1336
+ const value2 = opts.value;
1337
+ const flat = flattenObject(value2);
1338
+ if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) {
1339
+ const key = subKey;
1340
+ const val = value2;
1341
+ const type2 = Array.isArray(val) ? "array" : typeof val;
1342
+ const valStr2 = type2 === "object" || type2 === "array" ? JSON.stringify(val) : String(val);
1343
+ const now2 = Date.now();
1344
+ const query2 = `INSERT INTO "${table}" (key, "group", type, value, create_at, update_at)
1345
+ VALUES (?, ?, ?, ?, ?, ?)
1346
+ ON CONFLICT(key, "group") DO UPDATE SET
1347
+ value = excluded.value,
1348
+ type = excluded.type,
1349
+ update_at = excluded.update_at`;
1350
+ queueQuery(kfg, query2, key, id, type2, valStr2, now2, now2);
1035
1351
  } else {
1036
- data[key] = flatData[key];
1352
+ for (const k3 in flat) {
1353
+ const combinedKey = subKey + (k3 ? `.${k3}` : "");
1354
+ const val = flat[k3];
1355
+ const type2 = Array.isArray(val) ? "array" : typeof val;
1356
+ const valStr2 = type2 === "object" || type2 === "array" ? JSON.stringify(val) : String(val);
1357
+ const now2 = Date.now();
1358
+ const query2 = `INSERT INTO "${table}" (key, "group", type, value, create_at, update_at)
1359
+ VALUES (?, ?, ?, ?, ?, ?)
1360
+ ON CONFLICT(key, "group") DO UPDATE SET
1361
+ value = excluded.value,
1362
+ type = excluded.type,
1363
+ update_at = excluded.update_at`;
1364
+ queueQuery(kfg, query2, combinedKey, id, type2, valStr2, now2, now2);
1365
+ }
1037
1366
  }
1367
+ return;
1038
1368
  }
1039
- this.comments = comments;
1040
- loadedData = unflattenObject(data);
1041
- } else {
1042
- this.comments = stripComments(loadedData);
1043
1369
  }
1044
- this.store = this.deepMerge(defaultData, loadedData);
1045
- return this.store;
1370
+ const parts = opts.path.split(".");
1371
+ const k2 = parts.pop();
1372
+ const g = parts.join(".");
1373
+ const value = opts.value;
1374
+ const type = Array.isArray(value) ? "array" : typeof value;
1375
+ const valStr = type === "object" || type === "array" ? JSON.stringify(value) : String(value);
1376
+ const now = Date.now();
1377
+ const query = `INSERT INTO "${table}" (key, "group", type, value, create_at, update_at)
1378
+ VALUES (?, ?, ?, ?, ?, ?)
1379
+ ON CONFLICT(key, "group") DO UPDATE SET
1380
+ value = excluded.value,
1381
+ type = excluded.type,
1382
+ update_at = excluded.update_at`;
1383
+ log(kfg, "Queueing update", { key: k2, group: g, value: valStr });
1384
+ queueQuery(kfg, query, k2, g, type, valStr, now, now);
1046
1385
  },
1047
- onSet(key, _value, options) {
1048
- if (key) {
1049
- this.comments = this.comments || {};
1050
- if (options?.description) {
1051
- this.comments[key] = options.description;
1052
- }
1386
+ onDelete(kfg, opts) {
1387
+ touch(kfg);
1388
+ let data = kfg.$store.get("data");
1389
+ if (!data) {
1390
+ data = loadData(kfg);
1391
+ }
1392
+ if (opts.path) {
1393
+ deleteProperty(data, opts.path);
1394
+ }
1395
+ kfg.$store.set("data", data);
1396
+ const table = kfg.$config?.table || "settings";
1397
+ const parts = opts.path.split(".");
1398
+ if (kfg.multimode && parts.length === 1) {
1399
+ const id = parts[0];
1400
+ const query2 = `DELETE FROM "${table}" WHERE "group" = ?`;
1401
+ log(kfg, "Queueing group delete", { group: id });
1402
+ queueQuery(kfg, query2, id);
1403
+ return;
1404
+ }
1405
+ const k2 = parts.pop();
1406
+ const g = parts.join(".");
1407
+ const query = `DELETE FROM "${table}" WHERE key = ? AND "group" = ?`;
1408
+ log(kfg, "Queueing delete", { key: k2, group: g });
1409
+ queueQuery(kfg, query, k2, g);
1410
+ },
1411
+ onCreate(kfg, { data }) {
1412
+ touch(kfg);
1413
+ const id = data.id;
1414
+ if (!id) throw new Error("Cannot create item without 'id'.");
1415
+ let storeData = kfg.$store.get("data");
1416
+ if (!storeData) {
1417
+ storeData = loadData(kfg);
1053
1418
  }
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];
1419
+ storeData[id] = data;
1420
+ kfg.$store.set("data", storeData);
1421
+ const table = kfg.$config?.table || "settings";
1422
+ const flat = flattenObject(data);
1423
+ const now = Date.now();
1424
+ for (const key in flat) {
1425
+ const val = flat[key];
1426
+ const type = Array.isArray(val) ? "array" : typeof val;
1427
+ const valStr = type === "object" || type === "array" ? JSON.stringify(val) : String(val);
1428
+ const query = `INSERT INTO "${table}" (key, "group", type, value, create_at, update_at)
1429
+ VALUES (?, ?, ?, ?, ?, ?)
1430
+ ON CONFLICT(key, "group") DO UPDATE SET
1431
+ value = excluded.value,
1432
+ type = excluded.type,
1433
+ update_at = excluded.update_at`;
1434
+ queueQuery(kfg, query, key, id, type, valStr, now, now);
1435
+ }
1436
+ kfg.save();
1437
+ return data;
1438
+ },
1439
+ onToJSON(kfg) {
1440
+ touch(kfg);
1441
+ let data = kfg.$store.get("data");
1442
+ if (!data) {
1443
+ data = loadData(kfg);
1444
+ }
1445
+ return data;
1446
+ },
1447
+ onInject(kfg, { data }) {
1448
+ touch(kfg);
1449
+ let currentData = kfg.$store.get("data");
1450
+ if (!currentData) {
1451
+ currentData = loadData(kfg);
1452
+ }
1453
+ kfg.$store.merge("data", data);
1454
+ },
1455
+ onMerge(kfg, { data }) {
1456
+ touch(kfg);
1457
+ let currentData = kfg.$store.get("data");
1458
+ if (!currentData) {
1459
+ currentData = loadData(kfg);
1460
+ }
1461
+ kfg.$store.merge("data", data);
1462
+ },
1463
+ save(kfg) {
1464
+ touch(kfg);
1465
+ const db = kfg.$store.get("db");
1466
+ const queue = kfg.$store.get("queue", []);
1467
+ if (!db || queue.length === 0) return;
1468
+ log(kfg, `Saving ${queue.length} changes`);
1469
+ const transaction = db.transaction(() => {
1470
+ for (const query of queue) {
1471
+ query();
1059
1472
  }
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];
1473
+ });
1474
+ transaction();
1475
+ kfg.$store.set("queue", []);
1476
+ },
1477
+ onSize(kfg) {
1478
+ touch(kfg);
1479
+ const data = kfg.$store.get("data");
1480
+ if (!data) return 0;
1481
+ if (kfg.multimode) return Object.keys(data).length;
1482
+ return 1;
1483
+ }
1484
+ });
1485
+ var AsyncSqliteDriver = new KfgDriver({
1486
+ identify: "async-sqlite-driver",
1487
+ async: true,
1488
+ config: { logs: false },
1489
+ onMount(kfg, opts) {
1490
+ return Promise.resolve(SqliteDriver.mount(kfg, opts));
1491
+ },
1492
+ onUnmount(kfg) {
1493
+ SqliteDriver.unmount?.(kfg);
1494
+ },
1495
+ onGet(kfg, opts) {
1496
+ return Promise.resolve(SqliteDriver.get(kfg, opts.path));
1497
+ },
1498
+ onHas(kfg, opts) {
1499
+ return Promise.resolve(SqliteDriver.has(kfg, ...opts.paths));
1500
+ },
1501
+ onUpdate(kfg, opts) {
1502
+ return Promise.resolve(SqliteDriver.definition.onUpdate(kfg, opts));
1503
+ },
1504
+ onDelete(kfg, opts) {
1505
+ return Promise.resolve(SqliteDriver.definition.onDelete(kfg, opts));
1506
+ },
1507
+ onCreate(kfg, opts) {
1508
+ return Promise.resolve(SqliteDriver.definition.onCreate(kfg, opts));
1509
+ },
1510
+ save(kfg, data) {
1511
+ return Promise.resolve(SqliteDriver.save(kfg, data));
1512
+ },
1513
+ onToJSON(kfg) {
1514
+ return Promise.resolve(SqliteDriver.toJSON(kfg));
1515
+ },
1516
+ onInject(kfg, opts) {
1517
+ return Promise.resolve(SqliteDriver.inject(kfg, opts.data));
1518
+ },
1519
+ onMerge(kfg, opts) {
1520
+ return Promise.resolve(SqliteDriver.definition.onMerge(kfg, opts));
1521
+ },
1522
+ onSize(kfg) {
1523
+ return SqliteDriver.definition.onSize(kfg);
1524
+ }
1525
+ });
1526
+ AsyncSqliteDriver.definition.onMerge = (kfg, opts) => {
1527
+ return Promise.resolve(SqliteDriver.definition.onMerge(kfg, opts));
1528
+ };
1529
+
1530
+ // src/factory.ts
1531
+ var import_typebox3 = require("@sinclair/typebox");
1532
+ var import_value2 = require("@sinclair/typebox/value");
1533
+
1534
+ // src/rule.ts
1535
+ var import_typebox2 = require("@sinclair/typebox");
1536
+ function escapeRegexLiteral(input) {
1537
+ return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1538
+ }
1539
+ function mergePatterns(patterns) {
1540
+ if (!patterns.length) return void 0;
1541
+ if (patterns.length === 1) return patterns[0];
1542
+ return `^(?=${patterns.join(")(?=")})[\\s\\S]*$`;
1543
+ }
1544
+ function parseRegexRule(raw) {
1545
+ if (!raw) return void 0;
1546
+ const s = raw.trim();
1547
+ if (s.startsWith("/") && s.lastIndexOf("/") > 0) {
1548
+ const last = s.lastIndexOf("/");
1549
+ const body = s.slice(1, last);
1550
+ return body;
1551
+ }
1552
+ return s;
1553
+ }
1554
+ function buildStringDeclaration(parts, defaultValue) {
1555
+ const options = {};
1556
+ if (defaultValue !== void 0) options.default = defaultValue;
1557
+ const patterns = [];
1558
+ for (const p of parts) {
1559
+ const [key, raw] = p.split(":", 2);
1560
+ const n = raw !== void 0 ? Number(raw) : void 0;
1561
+ switch (key) {
1562
+ // tamanhos
1563
+ case "min":
1564
+ if (Number.isFinite(n)) options.minLength = n;
1565
+ break;
1566
+ case "max":
1567
+ if (Number.isFinite(n)) options.maxLength = n;
1568
+ break;
1569
+ case "between": {
1570
+ const [a, b] = (raw ?? "").split(",").map((x) => Number(x));
1571
+ if (Number.isFinite(a)) options.minLength = a;
1572
+ if (Number.isFinite(b)) options.maxLength = b;
1573
+ break;
1574
+ }
1575
+ case "size":
1576
+ case "len":
1577
+ case "length":
1578
+ if (Number.isFinite(n)) {
1579
+ options.minLength = n;
1580
+ options.maxLength = n;
1069
1581
  }
1582
+ break;
1583
+ // formatos comuns
1584
+ case "email":
1585
+ options.format = "email";
1586
+ break;
1587
+ case "url":
1588
+ case "uri":
1589
+ options.format = "uri";
1590
+ break;
1591
+ case "ipv4":
1592
+ options.format = "ipv4";
1593
+ break;
1594
+ case "ipv6":
1595
+ options.format = "ipv6";
1596
+ break;
1597
+ case "uuid":
1598
+ options.format = "uuid";
1599
+ break;
1600
+ // formatos extras "laravel-like"
1601
+ case "date":
1602
+ options.format = "date";
1603
+ break;
1604
+ case "date_time":
1605
+ case "datetime":
1606
+ options.format = "date-time";
1607
+ break;
1608
+ // regex
1609
+ case "regex": {
1610
+ const rx = parseRegexRule(raw);
1611
+ if (rx) patterns.push(rx);
1612
+ break;
1070
1613
  }
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];
1614
+ // letras/números
1615
+ case "alpha":
1616
+ patterns.push("^[A-Za-z]+$");
1617
+ break;
1618
+ case "alpha_num":
1619
+ patterns.push("^[A-Za-z0-9]+$");
1620
+ break;
1621
+ case "alpha_dash":
1622
+ patterns.push("^[A-Za-z0-9_-]+$");
1623
+ break;
1624
+ case "ascii":
1625
+ patterns.push("^[\\x00-\\x7F]*$");
1626
+ break;
1627
+ // start/end
1628
+ case "starts_with": {
1629
+ const list = (raw ?? "").split(",").map((x) => x.trim()).filter(Boolean).map(escapeRegexLiteral);
1630
+ if (list.length) patterns.push(`^(?:${list.join("|")})`);
1631
+ break;
1085
1632
  }
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];
1633
+ case "ends_with": {
1634
+ const list = (raw ?? "").split(",").map((x) => x.trim()).filter(Boolean).map(escapeRegexLiteral);
1635
+ if (list.length) patterns.push(`(?:${list.join("|")})$`);
1636
+ break;
1637
+ }
1638
+ // dígitos
1639
+ case "digits":
1640
+ if (Number.isFinite(n)) patterns.push(`^\\d{${n}}$`);
1641
+ break;
1642
+ case "digits_between": {
1643
+ const [a, b] = (raw ?? "").split(",").map((x) => Number(x));
1644
+ if (Number.isFinite(a) && Number.isFinite(b)) {
1645
+ patterns.push(`^\\d{${a},${b}}$`);
1095
1646
  }
1647
+ break;
1096
1648
  }
1097
- dataToSave = dataWithComments;
1649
+ // caixa
1650
+ case "lowercase":
1651
+ patterns.push("^[a-z0-9\\s\\p{P}]*$");
1652
+ break;
1653
+ case "uppercase":
1654
+ patterns.push("^[A-Z0-9\\s\\p{P}]*$");
1655
+ break;
1656
+ // utilitários comuns
1657
+ case "ulid":
1658
+ patterns.push("^[0-9A-HJKMNP-TV-Z]{26}$");
1659
+ break;
1660
+ case "slug":
1661
+ patterns.push("^[a-z0-9]+(?:-[a-z0-9]+)*$");
1662
+ break;
1663
+ // presence rules que não mudam o schema aqui
1664
+ case "required":
1665
+ case "filled":
1666
+ case "present":
1667
+ case "sometimes":
1668
+ case "string":
1669
+ case "text":
1670
+ break;
1098
1671
  }
1099
- const filePath = getFilePath2(this.config);
1100
- fs3.writeFileSync(filePath, JSON.stringify(dataToSave, null, 2));
1101
1672
  }
1102
- });
1673
+ const merged = mergePatterns(patterns);
1674
+ if (merged) options.pattern = merged;
1675
+ return import_typebox2.Type.String(options);
1676
+ }
1677
+ function applyNumericConstraints(parts, options) {
1678
+ for (const p of parts) {
1679
+ const [key, raw] = p.split(":", 2);
1680
+ const n = raw !== void 0 ? Number(raw) : void 0;
1681
+ switch (key) {
1682
+ case "min":
1683
+ if (Number.isFinite(n)) options.minimum = n;
1684
+ break;
1685
+ case "max":
1686
+ if (Number.isFinite(n)) options.maximum = n;
1687
+ break;
1688
+ case "between": {
1689
+ const [a, b] = (raw ?? "").split(",").map((x) => Number(x));
1690
+ if (Number.isFinite(a)) options.minimum = a;
1691
+ if (Number.isFinite(b)) options.maximum = b;
1692
+ break;
1693
+ }
1694
+ case "gt":
1695
+ if (Number.isFinite(n)) options.exclusiveMinimum = n;
1696
+ break;
1697
+ case "gte":
1698
+ if (Number.isFinite(n)) options.minimum = n;
1699
+ break;
1700
+ case "lt":
1701
+ if (Number.isFinite(n)) options.exclusiveMaximum = n;
1702
+ break;
1703
+ case "lte":
1704
+ if (Number.isFinite(n)) options.maximum = n;
1705
+ break;
1706
+ case "multiple_of":
1707
+ if (Number.isFinite(n) && n !== 0) options.multipleOf = n;
1708
+ break;
1709
+ // presence/type rules que não mudam constraints
1710
+ case "number":
1711
+ case "numeric":
1712
+ case "integer":
1713
+ case "int":
1714
+ case "required":
1715
+ case "filled":
1716
+ case "present":
1717
+ case "sometimes":
1718
+ break;
1719
+ }
1720
+ }
1721
+ }
1722
+ function buildNumberDeclaration(parts, defaultValue) {
1723
+ const options = {};
1724
+ if (defaultValue !== void 0) options.default = defaultValue;
1725
+ applyNumericConstraints(parts, options);
1726
+ return import_typebox2.Type.Number(options);
1727
+ }
1728
+ function buildIntegerDeclaration(parts, defaultValue) {
1729
+ const options = {};
1730
+ if (defaultValue !== void 0) options.default = defaultValue;
1731
+ applyNumericConstraints(parts, options);
1732
+ return import_typebox2.Type.Integer(options);
1733
+ }
1734
+ function buildBooleanDeclaration(defaultValue) {
1735
+ const options = {};
1736
+ if (defaultValue !== void 0) options.default = defaultValue;
1737
+ return import_typebox2.Type.Boolean(options);
1738
+ }
1739
+ function buildEnumDeclaration(values, defaultValue) {
1740
+ const options = {};
1741
+ if (defaultValue !== void 0) options.default = defaultValue;
1742
+ return import_typebox2.Type.Union(
1743
+ values.map((v) => import_typebox2.Type.Literal(v)),
1744
+ options
1745
+ );
1746
+ }
1747
+ function rule(rules, defaultValue) {
1748
+ const parts = rules.split("|").map((p) => p.trim()).filter(Boolean);
1749
+ const isOptional = parts.includes("optional") || parts.includes("nullable");
1750
+ const inRule = parts.find((p) => p.startsWith("in:"));
1751
+ let schema;
1752
+ if (inRule) {
1753
+ const values = inRule.slice(3).split(",").map((v) => v.trim()).filter(Boolean);
1754
+ schema = buildEnumDeclaration(values, defaultValue);
1755
+ } else if (parts.includes("boolean")) {
1756
+ schema = buildBooleanDeclaration(defaultValue);
1757
+ } else if (parts.includes("integer") || parts.includes("int")) {
1758
+ schema = buildIntegerDeclaration(parts, defaultValue);
1759
+ } else if (parts.includes("number") || parts.includes("numeric")) {
1760
+ schema = buildNumberDeclaration(parts, defaultValue);
1761
+ } else {
1762
+ schema = buildStringDeclaration(parts, defaultValue);
1763
+ }
1764
+ return isOptional ? import_typebox2.Type.Optional(schema) : schema;
1765
+ }
1103
1766
 
1104
1767
  // src/factory.ts
1105
- var import_typebox3 = require("@sinclair/typebox");
1106
1768
  function getEnumValues(values) {
1107
1769
  if (Array.isArray(values)) {
1108
1770
  return values;
@@ -1156,9 +1818,51 @@ var _c = {
1156
1818
  const { max = 100, ...rest } = options || {};
1157
1819
  return import_typebox3.Type.Number({
1158
1820
  ...rest,
1159
- [Symbol.for("isRandom")]: true,
1821
+ [/* @__PURE__ */ Symbol.for("isRandom")]: true,
1160
1822
  max
1161
1823
  });
1824
+ },
1825
+ /** Creates a Model relation schema. */
1826
+ Model: (model, resolver, options) => {
1827
+ return import_typebox3.Type.Any({
1828
+ ...options,
1829
+ model,
1830
+ resolver
1831
+ });
1832
+ },
1833
+ /** Creates a number schema that defaults to current timestamp in ms. */
1834
+ createms: (options) => {
1835
+ return import_typebox3.Type.Number({
1836
+ ...options,
1837
+ createms: true,
1838
+ default: Date.now()
1839
+ });
1840
+ },
1841
+ /**
1842
+ * Creates a schema based on a Laravel-like rule string.
1843
+ * @param rules The rule string (e.g., 'required|string|min:3').
1844
+ * @param defaultValue The default value.
1845
+ */
1846
+ rule,
1847
+ /**
1848
+ * Validates data against a schema definition.
1849
+ * @param schema The schema definition.
1850
+ * @param data The data to validate.
1851
+ * @returns The validated and coerced data.
1852
+ */
1853
+ validate: (schema, data) => {
1854
+ const compiledSchema = buildTypeBoxSchema(schema);
1855
+ addSmartDefaults(compiledSchema);
1856
+ const configWithDefaults = import_value2.Value.Default(compiledSchema, data);
1857
+ import_value2.Value.Convert(compiledSchema, configWithDefaults);
1858
+ if (!import_value2.Value.Check(compiledSchema, configWithDefaults)) {
1859
+ const errors = [...import_value2.Value.Errors(compiledSchema, configWithDefaults)];
1860
+ throw new Error(
1861
+ `Validation failed:
1862
+ ${errors.map((e) => `- ${e.path}: ${e.message}`).join("\n")}`
1863
+ );
1864
+ }
1865
+ return configWithDefaults;
1162
1866
  }
1163
1867
  };
1164
1868
  var c = {
@@ -1176,25 +1880,351 @@ var c = {
1176
1880
  url: _c.URL,
1177
1881
  any: _c.Any,
1178
1882
  optional: _c.Optional,
1179
- random: _c.Random
1883
+ random: _c.Random,
1884
+ model: _c.Model,
1885
+ createms: _c.createms,
1886
+ rule: _c.rule,
1887
+ validate: _c.validate
1180
1888
  };
1181
1889
  var k = c;
1890
+ var m = c;
1891
+
1892
+ // src/kfg.ts
1893
+ var import_value3 = require("@sinclair/typebox/value");
1894
+
1895
+ // src/store.ts
1896
+ var KfgStore = class {
1897
+ map = /* @__PURE__ */ new Map();
1898
+ get(key, defaultValue) {
1899
+ return this.map.get(key) ?? defaultValue;
1900
+ }
1901
+ set(key, value) {
1902
+ this.map.set(key, value);
1903
+ }
1904
+ merge(key, value) {
1905
+ const current = this.get(key, {});
1906
+ this.set(key, deepMerge(current, value));
1907
+ }
1908
+ insert(key, value) {
1909
+ const current = this.get(key, {});
1910
+ Object.assign(current, value);
1911
+ this.set(key, current);
1912
+ }
1913
+ };
1914
+
1915
+ // src/kfg.ts
1916
+ var Kfg = class {
1917
+ driver;
1918
+ schema;
1919
+ $store = new KfgStore();
1920
+ multimode = false;
1921
+ loaded = false;
1922
+ _lastOptions;
1923
+ hooks = {};
1924
+ /**
1925
+ * Creates a new instance of Kfg.
1926
+ * @param driver The driver instance.
1927
+ * @param schema The schema to use for validating the configuration.
1928
+ * @param options Options or multimode flag.
1929
+ */
1930
+ constructor(driver, schema, options) {
1931
+ this.driver = driver;
1932
+ this.schema = schema;
1933
+ if (typeof options === "boolean") {
1934
+ this.multimode = options;
1935
+ } else if (options) {
1936
+ this.multimode = options.multiple || false;
1937
+ }
1938
+ }
1939
+ /**
1940
+ * Returns the driver configuration from the store.
1941
+ */
1942
+ get $config() {
1943
+ return this.$store.get("~driver", this.driver.config);
1944
+ }
1945
+ /**
1946
+ * Registers a hook.
1947
+ */
1948
+ on(event, fn) {
1949
+ if (!this.hooks[event]) {
1950
+ this.hooks[event] = [];
1951
+ }
1952
+ this.hooks[event]?.push(fn);
1953
+ return this;
1954
+ }
1955
+ runHooks(event, ...args) {
1956
+ const hooks = this.hooks[event];
1957
+ if (!hooks || hooks.length === 0) {
1958
+ return this.driver.async ? Promise.resolve(args[0]) : args[0];
1959
+ }
1960
+ let currentData = args[0];
1961
+ const otherArgs = args.slice(1);
1962
+ if (this.driver.async) {
1963
+ return (async () => {
1964
+ for (const hook of hooks) {
1965
+ const result = await hook(currentData, ...otherArgs);
1966
+ if (result !== void 0 && event !== "ready") {
1967
+ currentData = result;
1968
+ }
1969
+ }
1970
+ return currentData;
1971
+ })();
1972
+ }
1973
+ for (const hook of hooks) {
1974
+ const result = hook(currentData, ...otherArgs);
1975
+ if (result !== void 0 && event !== "ready") {
1976
+ currentData = result;
1977
+ }
1978
+ }
1979
+ return currentData;
1980
+ }
1981
+ /**
1982
+ * Mounts the configuration using the driver.
1983
+ * @param options - The loading options.
1984
+ */
1985
+ mount(options) {
1986
+ this._lastOptions = options;
1987
+ let schemaToLoad = this.schema;
1988
+ if (options?.only_importants) {
1989
+ schemaToLoad = makeSchemaOptional(this.schema);
1990
+ }
1991
+ const processResult = (result2) => {
1992
+ this.validate(result2, schemaToLoad);
1993
+ this.loaded = true;
1994
+ this.runHooks("ready");
1995
+ };
1996
+ const result = this.driver.mount(this, options);
1997
+ if (this.driver.async) {
1998
+ return result.then(processResult);
1999
+ }
2000
+ processResult(result);
2001
+ return void 0;
2002
+ }
2003
+ /**
2004
+ * Alias for mount().
2005
+ */
2006
+ load(options) {
2007
+ return this.mount(options);
2008
+ }
2009
+ /**
2010
+ * Reloads the configuration.
2011
+ * @param options - The loading options.
2012
+ */
2013
+ reload(options) {
2014
+ this.loaded = false;
2015
+ return this.mount(options || this._lastOptions);
2016
+ }
2017
+ /**
2018
+ * Saves the configuration.
2019
+ * @param data Optional data to save.
2020
+ */
2021
+ save(data) {
2022
+ if (!this.loaded) {
2023
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2024
+ }
2025
+ return this.driver.saveTo(this, data);
2026
+ }
2027
+ /**
2028
+ * Gets a value from the configuration.
2029
+ * @param path The path to the value.
2030
+ * @returns The value at the given path.
2031
+ */
2032
+ get(path3) {
2033
+ if (!this.loaded) {
2034
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2035
+ }
2036
+ return this.driver.get(this, path3);
2037
+ }
2038
+ /**
2039
+ * Checks if a value exists in the configuration.
2040
+ * @param paths The paths to the values.
2041
+ * @returns True if all values exist, false otherwise.
2042
+ */
2043
+ has(...paths) {
2044
+ if (!this.loaded) {
2045
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2046
+ }
2047
+ return this.driver.has(this, ...paths);
2048
+ }
2049
+ /**
2050
+ * Sets a value in the configuration.
2051
+ */
2052
+ set(path3, value, options) {
2053
+ if (!this.loaded) {
2054
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2055
+ }
2056
+ const run = (processedValue) => {
2057
+ return this.driver.set(
2058
+ this,
2059
+ path3,
2060
+ processedValue,
2061
+ options
2062
+ );
2063
+ };
2064
+ if (this.multimode) {
2065
+ const parts = path3.split(".");
2066
+ const id = parts[0];
2067
+ if (parts.length === 1) {
2068
+ const getItem = this.get(id);
2069
+ const processHook = (oldItem2) => {
2070
+ if (oldItem2) {
2071
+ return this.runHooks("update", value, oldItem2);
2072
+ }
2073
+ return value;
2074
+ };
2075
+ if (this.driver.async) {
2076
+ return getItem.then((oldItem2) => processHook(oldItem2)).then((processed2) => run(processed2));
2077
+ }
2078
+ const oldItem = getItem;
2079
+ const processed = processHook(oldItem);
2080
+ return run(processed);
2081
+ }
2082
+ }
2083
+ return this.driver.set(this, path3, value, options);
2084
+ }
2085
+ /**
2086
+ * Inserts a partial value into an object in the configuration.
2087
+ */
2088
+ insert(path3, partial) {
2089
+ if (!this.loaded) {
2090
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2091
+ }
2092
+ return this.driver.insert(this, path3, partial);
2093
+ }
2094
+ /**
2095
+ * Injects a partial value directly into the root configuration object.
2096
+ */
2097
+ inject(data) {
2098
+ return this.driver.inject(this, data);
2099
+ }
2100
+ /**
2101
+ * Deletes a value from the configuration.
2102
+ */
2103
+ del(path3) {
2104
+ if (!this.loaded) {
2105
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2106
+ }
2107
+ const run = () => {
2108
+ return this.driver.del(this, path3);
2109
+ };
2110
+ if (this.multimode) {
2111
+ const parts = path3.split(".");
2112
+ if (parts.length === 1) {
2113
+ const id = parts[0];
2114
+ const getItem = this.get(id);
2115
+ const processHook = (oldItem2) => {
2116
+ if (oldItem2) {
2117
+ return this.runHooks("delete", oldItem2);
2118
+ }
2119
+ return null;
2120
+ };
2121
+ if (this.driver.async) {
2122
+ return getItem.then((oldItem2) => processHook(oldItem2)).then(() => run());
2123
+ }
2124
+ const oldItem = getItem;
2125
+ processHook(oldItem);
2126
+ return run();
2127
+ }
2128
+ }
2129
+ return run();
2130
+ }
2131
+ create(data) {
2132
+ if (!this.loaded) {
2133
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2134
+ }
2135
+ const run = (processedData) => {
2136
+ return this.driver.create(this, processedData);
2137
+ };
2138
+ const hookResult = this.runHooks("create", data);
2139
+ if (this.driver.async) {
2140
+ return hookResult.then(run);
2141
+ }
2142
+ return run(hookResult);
2143
+ }
2144
+ size() {
2145
+ return this.driver.size(this);
2146
+ }
2147
+ where(id) {
2148
+ const self = this;
2149
+ return {
2150
+ get(path3) {
2151
+ const fullPath = path3 ? `${id}.${path3}` : id;
2152
+ return self.get(fullPath);
2153
+ },
2154
+ set(path3, value) {
2155
+ const fullPath = `${id}.${path3}`;
2156
+ return self.set(fullPath, value);
2157
+ },
2158
+ del(path3) {
2159
+ const fullPath = path3 ? `${id}.${path3}` : id;
2160
+ return self.del(fullPath);
2161
+ },
2162
+ toJSON() {
2163
+ return void 0;
2164
+ }
2165
+ };
2166
+ }
2167
+ /**
2168
+ * Validates data against the schema.
2169
+ */
2170
+ validate(data, schema = this.schema) {
2171
+ if (this.multimode) {
2172
+ if (typeof data !== "object" || data === null) {
2173
+ return {};
2174
+ }
2175
+ for (const key in data) {
2176
+ data[key] = this.validateItem(data[key], schema);
2177
+ }
2178
+ return data;
2179
+ }
2180
+ return this.validateItem(data, schema);
2181
+ }
2182
+ validateItem(data, schema) {
2183
+ const compiledSchema = buildTypeBoxSchema(schema);
2184
+ addSmartDefaults(compiledSchema);
2185
+ const configWithDefaults = import_value3.Value.Default(compiledSchema, data);
2186
+ import_value3.Value.Convert(compiledSchema, configWithDefaults);
2187
+ if (!import_value3.Value.Check(compiledSchema, configWithDefaults)) {
2188
+ const errors = [...import_value3.Value.Errors(compiledSchema, configWithDefaults)];
2189
+ throw new Error(
2190
+ `[Kfg] Validation failed:
2191
+ ${errors.map((e) => `- ${e.path}: ${e.message}`).join("\n")}`
2192
+ );
2193
+ }
2194
+ return configWithDefaults;
2195
+ }
2196
+ /**
2197
+ * Unmounts the driver.
2198
+ */
2199
+ unmount() {
2200
+ this.driver.unmount?.(this);
2201
+ }
2202
+ /**
2203
+ * Returns cached data.
2204
+ */
2205
+ toJSON() {
2206
+ if (!this.loaded) {
2207
+ throw new Error("[Kfg] Config not loaded. Call mount() first.");
2208
+ }
2209
+ return this.driver.toJSON(this);
2210
+ }
2211
+ };
2212
+
2213
+ // src/old.ts
2214
+ var ConfigJS = Kfg;
2215
+ var ConfigJSDriver = KfgDriver;
1182
2216
  // Annotate the CommonJS export names for ESM import in node:
1183
2217
  0 && (module.exports = {
1184
- ConfigFS,
2218
+ AsyncJsonDriver,
2219
+ AsyncSqliteDriver,
1185
2220
  ConfigJS,
1186
2221
  ConfigJSDriver,
1187
- FileFSConfigJS,
1188
- KFS_JOIN_SYMBOL,
1189
- KFS_MANY_SYMBOL,
2222
+ EnvDriver,
2223
+ JsonDriver,
1190
2224
  Kfg,
1191
2225
  KfgDriver,
1192
- KfgFS,
1193
- KfgFileFS,
2226
+ SqliteDriver,
1194
2227
  c,
1195
- cfs,
1196
- envDriver,
1197
- jsonDriver,
1198
2228
  k,
1199
- kfs
2229
+ m
1200
2230
  });