elit 3.1.8 → 3.1.9

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/server.mjs CHANGED
@@ -2723,8 +2723,16 @@ var Database = class {
2723
2723
  acc[type] = (...args) => logs.push({ type, args });
2724
2724
  return acc;
2725
2725
  }, {});
2726
+ const systemBase = {
2727
+ update,
2728
+ remove,
2729
+ rename: rename2,
2730
+ read,
2731
+ create,
2732
+ save
2733
+ };
2726
2734
  this.register({
2727
- console: customConsole
2735
+ dbConsole: { ...customConsole, ...systemBase }
2728
2736
  });
2729
2737
  let stringCode;
2730
2738
  if (typeof code === "function") {
@@ -2819,6 +2827,287 @@ var Database = class {
2819
2827
  return await this.vmRun(code, options);
2820
2828
  }
2821
2829
  };
2830
+ function create(dbName, code) {
2831
+ const DIR = "databases";
2832
+ const basePath = process.cwd();
2833
+ const baseDir = path.resolve(basePath, DIR);
2834
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
2835
+ fs2.appendFileSync(dbPath, code.toString(), "utf8");
2836
+ }
2837
+ function read(dbName) {
2838
+ const DIR = "databases";
2839
+ const basePath = process.cwd();
2840
+ const baseDir = path.resolve(basePath, DIR);
2841
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
2842
+ if (!fs2.existsSync(dbPath)) {
2843
+ throw new Error(`Database '${dbName}' not found`);
2844
+ }
2845
+ return fs2.readFileSync(dbPath, "utf8");
2846
+ }
2847
+ function remove(dbName, fnName) {
2848
+ const DIR = "databases";
2849
+ const basePath = process.cwd();
2850
+ const baseDir = path.resolve(basePath, DIR);
2851
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
2852
+ if (!fs2.existsSync(dbPath)) return false;
2853
+ if (!fnName) {
2854
+ const bak2 = `${dbPath}.bak`;
2855
+ try {
2856
+ fs2.copyFileSync(dbPath, bak2);
2857
+ } catch (e) {
2858
+ }
2859
+ try {
2860
+ fs2.unlinkSync(dbPath);
2861
+ return "Removed successfully";
2862
+ } catch (e) {
2863
+ return "Removed failed";
2864
+ }
2865
+ }
2866
+ const bak = `${dbPath}.bak`;
2867
+ try {
2868
+ fs2.copyFileSync(dbPath, bak);
2869
+ } catch (e) {
2870
+ }
2871
+ let src = fs2.readFileSync(dbPath, "utf8");
2872
+ const escaped = fnName.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
2873
+ const startRe = new RegExp(
2874
+ `function\\s+${escaped}\\s*\\(|\\bclass\\s+${escaped}\\b|\\b(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:function\\b|class\\b|\\(|\\{|\\[)`,
2875
+ "m"
2876
+ );
2877
+ const startMatch = src.match(startRe);
2878
+ if (startMatch) {
2879
+ const startIdx = startMatch.index;
2880
+ const len = src.length;
2881
+ const idxCurly = src.indexOf("{", startIdx);
2882
+ const idxBracket = src.indexOf("[", startIdx);
2883
+ let braceOpen = -1;
2884
+ if (idxCurly === -1) braceOpen = idxBracket;
2885
+ else if (idxBracket === -1) braceOpen = idxCurly;
2886
+ else braceOpen = Math.min(idxCurly, idxBracket);
2887
+ if (braceOpen !== -1) {
2888
+ const openingChar = src[braceOpen];
2889
+ const closingChar = openingChar === "[" ? "]" : "}";
2890
+ let i = braceOpen + 1;
2891
+ let depth = 1;
2892
+ while (i < len && depth > 0) {
2893
+ const ch = src[i];
2894
+ if (ch === openingChar) depth++;
2895
+ else if (ch === closingChar) depth--;
2896
+ i++;
2897
+ }
2898
+ let braceClose = i;
2899
+ let endIdx = braceClose;
2900
+ if (src.slice(braceClose, braceClose + 1) === ";")
2901
+ endIdx = braceClose + 1;
2902
+ const before = src.slice(0, startIdx);
2903
+ const after = src.slice(endIdx);
2904
+ src = before + after;
2905
+ } else {
2906
+ const semi = src.indexOf(";", startIdx);
2907
+ let endIdx = semi !== -1 ? semi + 1 : src.indexOf("\n\n", startIdx);
2908
+ if (endIdx === -1) endIdx = len;
2909
+ src = src.slice(0, startIdx) + src.slice(endIdx);
2910
+ }
2911
+ }
2912
+ const exportRe = new RegExp(
2913
+ `export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`,
2914
+ "g"
2915
+ );
2916
+ src = src.replace(exportRe, "");
2917
+ src = src.replace(/\n{3,}/g, "\n\n");
2918
+ fs2.writeFileSync(dbPath, src, "utf8");
2919
+ return `Removed ${fnName} from database ${dbName}.`;
2920
+ }
2921
+ function rename2(oldName, newName) {
2922
+ const DIR = "databases";
2923
+ const basePath = process.cwd();
2924
+ const baseDir = path.resolve(basePath, DIR);
2925
+ const oldPath = path.join(baseDir, `${oldName}.ts`);
2926
+ const newPath = path.join(baseDir, `${newName}.ts`);
2927
+ if (!fs2.existsSync(oldPath)) {
2928
+ return `Error: File '${oldName}.ts' does not exist in the database`;
2929
+ }
2930
+ if (fs2.existsSync(newPath)) {
2931
+ return `Error: File '${newName}.ts' already exists in the database`;
2932
+ }
2933
+ try {
2934
+ fs2.renameSync(oldPath, newPath);
2935
+ return `Successfully renamed '${oldName}.ts' to '${newName}.ts'`;
2936
+ } catch (error) {
2937
+ return `Error renaming file: ${error instanceof Error ? error.message : String(error)}`;
2938
+ }
2939
+ }
2940
+ function save(dbName, code) {
2941
+ const DIR = "databases";
2942
+ const basePath = process.cwd();
2943
+ const baseDir = path.resolve(basePath, DIR);
2944
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
2945
+ let fileContent = typeof code === "function" ? code.toString() : code;
2946
+ fs2.writeFileSync(dbPath, fileContent, "utf8");
2947
+ }
2948
+ function update(dbName, fnName, code) {
2949
+ const DIR = "databases";
2950
+ const basePath = process.cwd();
2951
+ const baseDir = path.resolve(basePath, DIR);
2952
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
2953
+ let src;
2954
+ if (!fs2.existsSync(dbPath)) {
2955
+ try {
2956
+ fs2.writeFileSync(dbPath, "", "utf8");
2957
+ return `Created new database file: ${dbPath}`;
2958
+ } catch (e) {
2959
+ return `Failed to create dbPath file: ${dbPath}`;
2960
+ }
2961
+ }
2962
+ src = fs2.readFileSync(dbPath, "utf8");
2963
+ const originalSrc = src;
2964
+ const escaped = fnName.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
2965
+ const startRe = new RegExp(
2966
+ `function\\s+${escaped}\\s*\\(|\\bclass\\s+${escaped}\\b|\\b(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:function\\b|class\\b|\\(|\\{|\\[)`,
2967
+ "m"
2968
+ );
2969
+ const startMatch = src.match(startRe);
2970
+ let declKind = null;
2971
+ if (startMatch) {
2972
+ let startIdx = startMatch.index;
2973
+ const snippet = src.slice(startIdx, startIdx + 80);
2974
+ if (/^function\b/.test(snippet)) declKind = "functionDecl";
2975
+ else if (/^class\b/.test(snippet)) declKind = "classDecl";
2976
+ else if (/^\b(?:const|let|var)\b/.test(snippet)) declKind = "varAssign";
2977
+ }
2978
+ let newCode;
2979
+ if (typeof code === "function") {
2980
+ const fnStr = code.toString();
2981
+ if (declKind === "functionDecl") {
2982
+ if (/^function\s+\w+/.test(fnStr)) newCode = fnStr;
2983
+ else
2984
+ newCode = `function ${fnName}${fnStr.replace(
2985
+ /^function\s*\(/,
2986
+ "("
2987
+ )}`;
2988
+ } else if (declKind === "classDecl") {
2989
+ if (/^class\s+\w+/.test(fnStr)) newCode = fnStr;
2990
+ else if (/^class\s*\{/.test(fnStr))
2991
+ newCode = fnStr.replace(/^class\s*\{/, `class ${fnName} {`);
2992
+ else newCode = `const ${fnName} = ${fnStr};`;
2993
+ } else {
2994
+ newCode = `const ${fnName} = ${fnStr};`;
2995
+ }
2996
+ } else {
2997
+ newCode = `const ${fnName} = ${valueToCode(code, 0)};`;
2998
+ }
2999
+ if (startMatch) {
3000
+ const startIdx = startMatch.index;
3001
+ const idxCurly = src.indexOf("{", startIdx);
3002
+ const idxBracket = src.indexOf("[", startIdx);
3003
+ let braceOpen = -1;
3004
+ if (idxCurly === -1) braceOpen = idxBracket;
3005
+ else if (idxBracket === -1) braceOpen = idxCurly;
3006
+ else braceOpen = Math.min(idxCurly, idxBracket);
3007
+ if (braceOpen === -1) {
3008
+ const exportRe = new RegExp(
3009
+ `export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`,
3010
+ "m"
3011
+ );
3012
+ if (exportRe.test(src)) {
3013
+ src = src.replace(
3014
+ exportRe,
3015
+ `${newCode}
3016
+
3017
+ export const ${fnName}: any = ${fnName};`
3018
+ );
3019
+ } else {
3020
+ src = src + `
3021
+
3022
+ ${newCode}
3023
+
3024
+ export const ${fnName}: any = ${fnName};`;
3025
+ }
3026
+ } else {
3027
+ const openingChar = src[braceOpen];
3028
+ const closingChar = openingChar === "[" ? "]" : "}";
3029
+ let i = braceOpen + 1;
3030
+ let depth = 1;
3031
+ const len = src.length;
3032
+ while (i < len && depth > 0) {
3033
+ const ch = src[i];
3034
+ if (ch === openingChar) depth++;
3035
+ else if (ch === closingChar) depth--;
3036
+ i++;
3037
+ }
3038
+ let braceClose = i;
3039
+ let endIdx = braceClose;
3040
+ if (src.slice(braceClose, braceClose + 1) === ";")
3041
+ endIdx = braceClose + 1;
3042
+ const before = src.slice(0, startIdx);
3043
+ const after = src.slice(endIdx);
3044
+ src = before + newCode + after;
3045
+ }
3046
+ } else {
3047
+ const exportRe = new RegExp(
3048
+ `export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`,
3049
+ "m"
3050
+ );
3051
+ if (exportRe.test(src)) {
3052
+ src = src.replace(
3053
+ exportRe,
3054
+ `${newCode}
3055
+
3056
+ export const ${fnName}: any = ${fnName};`
3057
+ );
3058
+ } else {
3059
+ src = src + `
3060
+
3061
+ ${newCode}
3062
+
3063
+ export const ${fnName}: any = ${fnName};`;
3064
+ }
3065
+ }
3066
+ fs2.writeFileSync(dbPath, src, "utf8");
3067
+ if (src === originalSrc) {
3068
+ return `Saved ${fnName} to database ${dbName}.`;
3069
+ } else {
3070
+ return `Updated ${dbName} with ${fnName}.`;
3071
+ }
3072
+ }
3073
+ function valueToCode(val, depth = 0) {
3074
+ const indentUnit = " ";
3075
+ const indent = indentUnit.repeat(depth);
3076
+ const indentInner = indentUnit.repeat(depth + 1);
3077
+ if (val === null) return "null";
3078
+ const t = typeof val;
3079
+ if (t === "string") return JSON.stringify(val);
3080
+ if (t === "number" || t === "boolean") return String(val);
3081
+ if (t === "function") return val.toString();
3082
+ if (Array.isArray(val)) {
3083
+ if (val.length === 0) return "[]";
3084
+ const items = val.map((v) => valueToCode(v, depth + 1));
3085
+ return "[\n" + items.map((it) => indentInner + it).join(",\n") + "\n" + indent + "]";
3086
+ }
3087
+ if (t === "object") {
3088
+ const keys = Object.keys(val);
3089
+ if (keys.length === 0) return "{}";
3090
+ const entries = keys.map((k) => {
3091
+ const keyPart = isIdentifier(k) ? k : JSON.stringify(k);
3092
+ const v = valueToCode(val[k], depth + 1);
3093
+ return indentInner + keyPart + ": " + v;
3094
+ });
3095
+ return "{\n" + entries.join(",\n") + "\n" + indent + "}";
3096
+ }
3097
+ return String(val);
3098
+ }
3099
+ function isIdentifier(key) {
3100
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
3101
+ }
3102
+ var dbConsole = {
3103
+ create,
3104
+ read,
3105
+ remove,
3106
+ rename: rename2,
3107
+ save,
3108
+ update,
3109
+ ...console
3110
+ };
2822
3111
 
2823
3112
  // src/server.ts
2824
3113
  var ServerDatabase = class {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elit",
3
- "version": "3.1.8",
3
+ "version": "3.1.9",
4
4
  "description": "Optimized lightweight library for creating DOM elements with reactive state",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/database.ts CHANGED
@@ -90,7 +90,6 @@ export class Database {
90
90
  throw new Error(`Module ${specifier} is not allowed or not found.`);
91
91
  }
92
92
 
93
-
94
93
  private async vmRun(code: string | Function, _options?: vm.RunningCodeOptions | string) {
95
94
  const logs: any[] = [];
96
95
 
@@ -99,8 +98,19 @@ export class Database {
99
98
  return acc;
100
99
  }, {});
101
100
 
101
+ const systemBase = {
102
+ update: update,
103
+ remove: remove,
104
+ rename: rename,
105
+ read: read,
106
+ create: create,
107
+ save: save
108
+ }
109
+
110
+
111
+
102
112
  this.register({
103
- console: customConsole
113
+ dbConsole: { ...customConsole, ...systemBase }
104
114
  });
105
115
 
106
116
  let stringCode: string;
@@ -235,6 +245,340 @@ export class Database {
235
245
 
236
246
  }
237
247
 
248
+ function create(dbName: string, code: string | Function): void {
249
+ const DIR = "databases";
250
+ const basePath = process.cwd();
251
+ const baseDir: string = path.resolve(basePath, DIR);
252
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
253
+ // Prepare the export line
254
+ fs.appendFileSync(dbPath, code.toString(), 'utf8');
255
+ }
256
+
257
+ function read(dbName: string): string {
258
+ const DIR = "databases";
259
+ const basePath = process.cwd();
260
+ const baseDir: string = path.resolve(basePath, DIR);
261
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
262
+
263
+ if (!fs.existsSync(dbPath)) {
264
+ throw new Error(`Database '${dbName}' not found`);
265
+ }
266
+
267
+ return fs.readFileSync(dbPath, 'utf8');
268
+ }
269
+
270
+ function remove(dbName: string, fnName: string) {
271
+ const DIR = "databases";
272
+ const basePath = process.cwd();
273
+ const baseDir: string = path.resolve(basePath, DIR); // โฟลเดอร์ที่อนุญาต
274
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
275
+ if (!fs.existsSync(dbPath)) return false;
276
+
277
+ // if no functionName provided -> remove the whole file (after backup)
278
+ if (!fnName) {
279
+ const bak = `${dbPath}.bak`;
280
+ try {
281
+ fs.copyFileSync(dbPath, bak);
282
+ } catch (e) {
283
+ // ignore backup errors
284
+ }
285
+ try {
286
+ fs.unlinkSync(dbPath);
287
+ return "Removed successfully";
288
+ } catch (e) {
289
+ return "Removed failed";
290
+ }
291
+ }
292
+
293
+ // create a backup before editing the file in-place
294
+ const bak = `${dbPath}.bak`;
295
+ try {
296
+ fs.copyFileSync(dbPath, bak);
297
+ } catch (e) {
298
+ // ignore backup errors but continue carefully
299
+ }
300
+
301
+ let src = fs.readFileSync(dbPath, "utf8");
302
+ const escaped = fnName.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
303
+
304
+ // regex to find a declaration of the named symbol (function, class, or var/const/let assignment)
305
+ const startRe = new RegExp(
306
+ `function\\s+${escaped}\\s*\\(|\\bclass\\s+${escaped}\\b|\\b(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:function\\b|class\\b|\\(|\\{|\\[)`,
307
+ "m"
308
+ );
309
+
310
+ const startMatch = src.match(startRe);
311
+
312
+ if (startMatch) {
313
+ const startIdx = startMatch.index;
314
+
315
+ // find the first meaningful character after startIdx: {, [, or ; or newline
316
+ const len = src.length;
317
+ const idxCurly = src.indexOf("{", startIdx);
318
+ const idxBracket = src.indexOf("[", startIdx);
319
+ let braceOpen = -1;
320
+ if (idxCurly === -1) braceOpen = idxBracket;
321
+ else if (idxBracket === -1) braceOpen = idxCurly;
322
+ else braceOpen = Math.min(idxCurly, idxBracket);
323
+
324
+ if (braceOpen !== -1) {
325
+ const openingChar = src[braceOpen];
326
+ const closingChar = openingChar === "[" ? "]" : "}";
327
+ let i = braceOpen + 1;
328
+ let depth = 1;
329
+ while (i < len && depth > 0) {
330
+ const ch = src[i];
331
+ if (ch === openingChar) depth++;
332
+ else if (ch === closingChar) depth--;
333
+ i++;
334
+ }
335
+ let braceClose = i;
336
+ let endIdx = braceClose;
337
+ if (src.slice(braceClose, braceClose + 1) === ";")
338
+ endIdx = braceClose + 1;
339
+
340
+ const before = src.slice(0, startIdx);
341
+ const after = src.slice(endIdx);
342
+ src = before + after;
343
+ } else {
344
+ // fallback: remove until next semicolon or a blank line
345
+ const semi = src.indexOf(";", startIdx);
346
+ let endIdx = semi !== -1 ? semi + 1 : src.indexOf("\n\n", startIdx);
347
+ if (endIdx === -1) endIdx = len;
348
+ src = src.slice(0, startIdx) + src.slice(endIdx);
349
+ }
350
+ }
351
+
352
+ // remove any export const <name>: any = <name>; lines
353
+ const exportRe = new RegExp(
354
+ `export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`,
355
+ "g"
356
+ );
357
+ src = src.replace(exportRe, "");
358
+
359
+ // tidy up multiple blank lines
360
+ src = src.replace(/\n{3,}/g, "\n\n");
361
+
362
+ fs.writeFileSync(dbPath, src, "utf8");
363
+
364
+ return `Removed ${fnName} from database ${dbName}.`;
365
+ }
366
+
367
+ function rename(oldName: string, newName: string): string {
368
+ const DIR = "databases";
369
+ const basePath = process.cwd();
370
+ const baseDir: string = path.resolve(basePath, DIR); // โฟลเดอร์ที่อนุญาต
371
+ const oldPath = path.join(baseDir, `${oldName}.ts`);
372
+ const newPath = path.join(baseDir, `${newName}.ts`);
373
+
374
+ // Check if the source file exists
375
+ if (!fs.existsSync(oldPath)) {
376
+ return `Error: File '${oldName}.ts' does not exist in the database`;
377
+ }
378
+
379
+ // Check if the destination file already exists
380
+ if (fs.existsSync(newPath)) {
381
+ return `Error: File '${newName}.ts' already exists in the database`;
382
+ }
383
+
384
+ try {
385
+ // Rename the file
386
+ fs.renameSync(oldPath, newPath);
387
+ return `Successfully renamed '${oldName}.ts' to '${newName}.ts'`;
388
+ } catch (error) {
389
+ return `Error renaming file: ${error instanceof Error ? error.message : String(error)}`;
390
+ }
391
+ }
392
+
393
+ function save(dbName: string, code: string | Function | any): void {
394
+ const DIR = "databases";
395
+ const basePath = process.cwd();
396
+ const baseDir: string = path.resolve(basePath, DIR); // โฟลเดอร์ที่อนุญาต
397
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
398
+
399
+ let fileContent = typeof code === 'function' ? code.toString() : code;
400
+
401
+ fs.writeFileSync(dbPath, fileContent, 'utf8');
402
+ }
403
+
404
+ function update(dbName: string, fnName: string, code: string | Function) {
405
+ const DIR = "databases";
406
+ const basePath = process.cwd();
407
+ const baseDir: string = path.resolve(basePath, DIR); // โฟลเดอร์ที่อนุญาต
408
+ const dbPath = path.join(baseDir, `${dbName}.ts`);
409
+
410
+ let src;
411
+
412
+ if (!fs.existsSync(dbPath)) {
413
+ // If dbPath doesn't exist, create an empty module file so we can insert into it.
414
+ try {
415
+ fs.writeFileSync(dbPath, "", "utf8");
416
+ // console.log("Created new database file:", dbPath);
417
+ return `Created new database file: ${dbPath}`;
418
+ } catch (e) {
419
+ // console.error("Failed to create dbPath file:", dbPath, e && e.message ? e.message : e);
420
+ return `Failed to create dbPath file: ${dbPath}`;
421
+ }
422
+ }
423
+
424
+ src = fs.readFileSync(dbPath, "utf8");
425
+
426
+ const originalSrc = src;
427
+
428
+ const escaped = fnName.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
429
+ const startRe = new RegExp(
430
+ `function\\s+${escaped}\\s*\\(|\\bclass\\s+${escaped}\\b|\\b(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:function\\b|class\\b|\\(|\\{|\\[)`,
431
+ "m"
432
+ );
433
+ const startMatch = src.match(startRe);
434
+
435
+ // determine declKind in the current file (if present)
436
+ let declKind = null;
437
+
438
+ if (startMatch) {
439
+ let startIdx: any = startMatch.index;
440
+ const snippet = src.slice(startIdx, startIdx + 80);
441
+ if (/^function\b/.test(snippet)) declKind = "functionDecl";
442
+ else if (/^class\b/.test(snippet)) declKind = "classDecl";
443
+ else if (/^\b(?:const|let|var)\b/.test(snippet)) declKind = "varAssign";
444
+ }
445
+
446
+ // build replacement code for this value
447
+ let newCode;
448
+ if (typeof code === "function") {
449
+ const fnStr = code.toString();
450
+ // prefer const assignment for anonymous functions/classes or arrow functions
451
+ if (declKind === "functionDecl") {
452
+ if (/^function\s+\w+/.test(fnStr)) newCode = fnStr;
453
+ else
454
+ newCode = `function ${fnName}${fnStr.replace(
455
+ /^function\s*\(/,
456
+ "("
457
+ )}`;
458
+ } else if (declKind === "classDecl") {
459
+ if (/^class\s+\w+/.test(fnStr)) newCode = fnStr;
460
+ else if (/^class\s*\{/.test(fnStr))
461
+ newCode = fnStr.replace(/^class\s*\{/, `class ${fnName} {`);
462
+ else newCode = `const ${fnName} = ${fnStr};`;
463
+ } else {
464
+ newCode = `const ${fnName} = ${fnStr};`;
465
+ }
466
+ } else {
467
+ newCode = `const ${fnName} = ${valueToCode(code, 0)};`;
468
+ }
469
+
470
+ // replacement: if found startMatch, find block and replace between startIdx and endIdx
471
+ if (startMatch) {
472
+ const startIdx = startMatch.index;
473
+ // find first '{' or '[' after startIdx
474
+ const idxCurly = src.indexOf("{", startIdx);
475
+ const idxBracket = src.indexOf("[", startIdx);
476
+ let braceOpen = -1;
477
+ if (idxCurly === -1) braceOpen = idxBracket;
478
+ else if (idxBracket === -1) braceOpen = idxCurly;
479
+ else braceOpen = Math.min(idxCurly, idxBracket);
480
+
481
+ if (braceOpen === -1) {
482
+ // no block — fallback: replace export or append
483
+ const exportRe = new RegExp(
484
+ `export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`,
485
+ "m"
486
+ );
487
+ if (exportRe.test(src)) {
488
+ src = src.replace(
489
+ exportRe,
490
+ `${newCode}\n\nexport const ${fnName}: any = ${fnName};`
491
+ );
492
+ } else {
493
+ src =
494
+ src + `\n\n${newCode}\n\nexport const ${fnName}: any = ${fnName};`;
495
+ }
496
+ } else {
497
+ const openingChar = src[braceOpen];
498
+ const closingChar = openingChar === "[" ? "]" : "}";
499
+ let i = braceOpen + 1;
500
+ let depth = 1;
501
+ const len = src.length;
502
+ while (i < len && depth > 0) {
503
+ const ch = src[i];
504
+ if (ch === openingChar) depth++;
505
+ else if (ch === closingChar) depth--;
506
+ i++;
507
+ }
508
+ let braceClose = i;
509
+ let endIdx = braceClose;
510
+ if (src.slice(braceClose, braceClose + 1) === ";")
511
+ endIdx = braceClose + 1;
512
+
513
+ const before = src.slice(0, startIdx);
514
+ const after = src.slice(endIdx);
515
+ src = before + newCode + after;
516
+ }
517
+ } else {
518
+ // not found — try to insert before existing export or append
519
+ const exportRe = new RegExp(
520
+ `export\\s+const\\s+${escaped}\\s*:\\s*any\\s*=\\s*${escaped}\\s*;?`,
521
+ "m"
522
+ );
523
+ if (exportRe.test(src)) {
524
+ src = src.replace(
525
+ exportRe,
526
+ `${newCode}\n\nexport const ${fnName}: any = ${fnName};`
527
+ );
528
+ } else {
529
+ src =
530
+ src + `\n\n${newCode}\n\nexport const ${fnName}: any = ${fnName};`;
531
+ }
532
+ }
533
+
534
+ fs.writeFileSync(dbPath, src, "utf8");
535
+
536
+ if (src === originalSrc) {
537
+ return `Saved ${fnName} to database ${dbName}.`;
538
+ } else {
539
+ return `Updated ${dbName} with ${fnName}.`;
540
+ }
541
+ }
542
+
543
+ function valueToCode(val: any, depth: number = 0): string {
544
+ const indentUnit = " ";
545
+ const indent = indentUnit.repeat(depth);
546
+ const indentInner = indentUnit.repeat(depth + 1);
547
+
548
+ if (val === null) return "null";
549
+ const t = typeof val;
550
+ if (t === "string") return JSON.stringify(val);
551
+ if (t === "number" || t === "boolean") return String(val);
552
+ if (t === "function") return val.toString();
553
+ if (Array.isArray(val)) {
554
+ if (val.length === 0) return "[]";
555
+ const items = val.map((v) => valueToCode(v, depth + 1));
556
+ return (
557
+ "[\n" +
558
+ items.map((it) => indentInner + it).join(",\n") +
559
+ "\n" +
560
+ indent +
561
+ "]"
562
+ );
563
+ }
564
+ if (t === "object") {
565
+ const keys = Object.keys(val);
566
+ if (keys.length === 0) return "{}";
567
+ const entries = keys.map((k) => {
568
+ const keyPart = isIdentifier(k) ? k : JSON.stringify(k);
569
+ const v = valueToCode(val[k], depth + 1);
570
+ return indentInner + keyPart + ": " + v;
571
+ });
572
+ return "{\n" + entries.join(",\n") + "\n" + indent + "}";
573
+ }
574
+ return String(val);
575
+ }
576
+
577
+ function isIdentifier(key: any) {
578
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);
579
+ }
580
+
581
+
238
582
  // Default database instance
239
583
 
240
584
  export function database() {
@@ -243,4 +587,22 @@ export function database() {
243
587
  });
244
588
  }
245
589
 
590
+ export interface DatabaseConsole extends Console {
591
+ create?(dbName: string, code: string | Function): void;
592
+ read(dbName: string): string;
593
+ remove(dbName: string, fnName: string): any;
594
+ rename(oldName: string, newName: string): string;
595
+ save(dbName: string, code: string | Function | any): void;
596
+ update(dbName: string, fnName: string, code: string | Function): any;
597
+ }
598
+
599
+ export const dbConsole: DatabaseConsole = {
600
+ create: create,
601
+ read: read,
602
+ remove: remove,
603
+ rename: rename,
604
+ save: save,
605
+ update: update,
606
+ ...console
607
+ }
246
608
  export default database;