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.
- package/dist/index.cjs +1895 -865
- package/dist/index.d.ts +251 -356
- package/dist/index.js +1886 -843
- 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
|
-
|
|
33
|
+
AsyncJsonDriver: () => AsyncJsonDriver,
|
|
34
|
+
AsyncSqliteDriver: () => AsyncSqliteDriver,
|
|
34
35
|
ConfigJS: () => ConfigJS,
|
|
35
36
|
ConfigJSDriver: () => ConfigJSDriver,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
KFS_MANY_SYMBOL: () => KFS_MANY_SYMBOL,
|
|
37
|
+
EnvDriver: () => EnvDriver,
|
|
38
|
+
JsonDriver: () => JsonDriver,
|
|
39
39
|
Kfg: () => Kfg,
|
|
40
40
|
KfgDriver: () => KfgDriver,
|
|
41
|
-
|
|
42
|
-
KfgFileFS: () => KfgFileFS,
|
|
41
|
+
SqliteDriver: () => SqliteDriver,
|
|
43
42
|
c: () => c,
|
|
44
|
-
cfs: () => cfs,
|
|
45
|
-
envDriver: () => envDriver,
|
|
46
|
-
jsonDriver: () => jsonDriver,
|
|
47
43
|
k: () => k,
|
|
48
|
-
|
|
44
|
+
m: () => m
|
|
49
45
|
});
|
|
50
46
|
module.exports = __toCommonJS(index_exports);
|
|
51
47
|
|
|
52
|
-
// src/
|
|
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/
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
|
545
|
+
var EnvDriver = new KfgDriver({
|
|
942
546
|
identify: "env-driver",
|
|
943
547
|
async: false,
|
|
944
|
-
config: {
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
const
|
|
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 =
|
|
955
|
-
|
|
956
|
-
|
|
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
|
-
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
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
|
-
|
|
586
|
+
opts.value,
|
|
587
|
+
opts.description
|
|
967
588
|
);
|
|
968
|
-
|
|
589
|
+
fs.writeFileSync(filePath, newContent);
|
|
969
590
|
},
|
|
970
|
-
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
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 =
|
|
603
|
+
const currentContent = fs.readFileSync(filePath, "utf-8");
|
|
977
604
|
const newContent = removeEnvKey(currentContent, envKey);
|
|
978
|
-
|
|
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
|
|
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
|
|
1011
|
-
identify: "
|
|
1250
|
+
var SqliteDriver = new KfgDriver({
|
|
1251
|
+
identify: "sqlite-driver",
|
|
1012
1252
|
async: false,
|
|
1013
|
-
config: {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
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 (
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1045
|
-
|
|
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
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
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
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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
|
-
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (
|
|
1094
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2218
|
+
AsyncJsonDriver,
|
|
2219
|
+
AsyncSqliteDriver,
|
|
1185
2220
|
ConfigJS,
|
|
1186
2221
|
ConfigJSDriver,
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
KFS_MANY_SYMBOL,
|
|
2222
|
+
EnvDriver,
|
|
2223
|
+
JsonDriver,
|
|
1190
2224
|
Kfg,
|
|
1191
2225
|
KfgDriver,
|
|
1192
|
-
|
|
1193
|
-
KfgFileFS,
|
|
2226
|
+
SqliteDriver,
|
|
1194
2227
|
c,
|
|
1195
|
-
cfs,
|
|
1196
|
-
envDriver,
|
|
1197
|
-
jsonDriver,
|
|
1198
2228
|
k,
|
|
1199
|
-
|
|
2229
|
+
m
|
|
1200
2230
|
});
|