@builderbot/database-json 1.3.14 → 1.3.15-alpha.149

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 CHANGED
@@ -6,49 +6,141 @@ var path = require('path');
6
6
 
7
7
  class JsonFileDB extends bot.MemoryDB {
8
8
  constructor(options = {
9
- filename: 'de',
9
+ filename: 'db.json',
10
10
  }) {
11
11
  super();
12
12
  this.listHistory = [];
13
- this.options = { filename: 'db.json' };
13
+ this.options = { filename: 'db.json', debounceTime: 0 };
14
+ this.initPromise = null;
15
+ this.writeQueue = Promise.resolve();
16
+ this.debounceTimer = null;
17
+ this.pendingWrite = null;
18
+ this.pendingWriteResolvers = [];
14
19
  this.options = { ...this.options, ...options };
15
20
  this.pathFile = path.join(process.cwd(), this.options.filename);
16
- this.init().then();
21
+ this.tempPath = `${this.pathFile}.tmp`;
22
+ this.initPromise = this.init();
17
23
  }
18
24
  /**
19
- * Revisamos si existe o no el archivo JSON
25
+ * Revisamos si existe o no el archivo JSON y cargamos el historial
20
26
  */
21
27
  async init() {
22
- if (fs.existsSync(this.pathFile)) {
23
- return;
24
- }
25
28
  try {
26
- const parseData = JSON.stringify([], null, 2);
27
- await fs.promises.writeFile(this.pathFile, parseData, 'utf-8');
29
+ // Limpiar archivo temporal si existe (de crash anterior)
30
+ if (fs.existsSync(this.tempPath)) {
31
+ try {
32
+ await fs.promises.unlink(this.tempPath);
33
+ }
34
+ catch {
35
+ // Ignorar error al limpiar temp
36
+ }
37
+ }
38
+ if (!fs.existsSync(this.pathFile)) {
39
+ const parseData = JSON.stringify([], null, 2);
40
+ await fs.promises.writeFile(this.pathFile, parseData, 'utf-8');
41
+ this.listHistory = [];
42
+ }
43
+ else {
44
+ // Cargar historial existente del archivo
45
+ const data = await fs.promises.readFile(this.pathFile, 'utf-8');
46
+ this.listHistory = this.validateJson(data);
47
+ }
28
48
  }
29
49
  catch (e) {
30
- throw new Error(e.message);
50
+ console.error('[JsonFileDB] Error initializing database:', e.message);
51
+ this.listHistory = [];
52
+ }
53
+ }
54
+ /**
55
+ * Esperar a que la inicialización termine
56
+ */
57
+ async waitForInit() {
58
+ if (this.initPromise) {
59
+ await this.initPromise;
31
60
  }
32
61
  }
33
62
  /**
34
- * Validar JSON
63
+ * Validar JSON - retorna array vacío si el JSON es inválido
35
64
  * @param raw
36
65
  */
37
66
  validateJson(raw) {
38
67
  try {
39
- return JSON.parse(raw);
68
+ const parsed = JSON.parse(raw);
69
+ // Asegurar que sea un array
70
+ if (Array.isArray(parsed)) {
71
+ return parsed;
72
+ }
73
+ console.warn('[JsonFileDB] Database file contains invalid data (not an array), starting fresh');
74
+ return [];
40
75
  }
41
76
  catch (e) {
42
- return {};
77
+ console.warn('[JsonFileDB] Database file corrupted, starting fresh:', e.message);
78
+ return [];
43
79
  }
44
80
  }
45
81
  /**
46
- * Leer archivo y parsear
82
+ * Leer archivo y parsear (siempre desde memoria después de init)
47
83
  */
48
84
  async readFileAndParse() {
49
- const data = await fs.promises.readFile(this.pathFile, 'utf-8');
50
- const parseData = this.validateJson(data);
51
- return parseData;
85
+ await this.waitForInit();
86
+ return this.listHistory;
87
+ }
88
+ /**
89
+ * Escribir al archivo de forma atómica (write to temp, then rename)
90
+ * Esto previene corrupción si el proceso se interrumpe durante la escritura
91
+ */
92
+ async atomicWrite() {
93
+ try {
94
+ const parseData = JSON.stringify(this.listHistory, null, 2);
95
+ // Escribir a archivo temporal
96
+ await fs.promises.writeFile(this.tempPath, parseData, 'utf-8');
97
+ // Renombrar atómicamente (esto es una operación atómica en la mayoría de sistemas)
98
+ await fs.promises.rename(this.tempPath, this.pathFile);
99
+ }
100
+ catch (e) {
101
+ console.error('[JsonFileDB] Error writing to database:', e.message);
102
+ // Intentar limpiar archivo temporal
103
+ try {
104
+ if (fs.existsSync(this.tempPath)) {
105
+ await fs.promises.unlink(this.tempPath);
106
+ }
107
+ }
108
+ catch {
109
+ // Ignorar error de limpieza
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * Escribir al archivo de forma segura con cola y debounce opcional
115
+ */
116
+ async safeWrite() {
117
+ const debounceTime = this.options.debounceTime || 0;
118
+ if (debounceTime > 0) {
119
+ // Con debounce: agrupar múltiples escrituras
120
+ return new Promise((resolve) => {
121
+ this.pendingWriteResolvers.push(resolve);
122
+ if (this.debounceTimer) {
123
+ clearTimeout(this.debounceTimer);
124
+ }
125
+ this.debounceTimer = setTimeout(async () => {
126
+ this.debounceTimer = null;
127
+ const resolvers = [...this.pendingWriteResolvers];
128
+ this.pendingWriteResolvers = [];
129
+ this.writeQueue = this.writeQueue.then(async () => {
130
+ await this.atomicWrite();
131
+ resolvers.forEach((r) => r());
132
+ });
133
+ await this.writeQueue;
134
+ }, debounceTime);
135
+ });
136
+ }
137
+ else {
138
+ // Sin debounce: escritura inmediata en cola
139
+ this.writeQueue = this.writeQueue.then(async () => {
140
+ await this.atomicWrite();
141
+ });
142
+ return this.writeQueue;
143
+ }
52
144
  }
53
145
  /**
54
146
  * Buscar el último mensaje por número
@@ -70,9 +162,9 @@ class JsonFileDB extends bot.MemoryDB {
70
162
  * @param ctx
71
163
  */
72
164
  async save(ctx) {
165
+ await this.waitForInit();
73
166
  this.listHistory.push(ctx);
74
- const parseData = JSON.stringify(this.listHistory, null, 2);
75
- await fs.promises.writeFile(this.pathFile, parseData, 'utf-8');
167
+ await this.safeWrite();
76
168
  }
77
169
  }
78
170
 
package/dist/index.d.ts CHANGED
@@ -2,22 +2,41 @@ import { MemoryDB } from '@builderbot/bot';
2
2
  import type { HistoryEntry, JsonFileAdapterOptions } from './types';
3
3
  declare class JsonFileDB extends MemoryDB {
4
4
  private pathFile;
5
+ private tempPath;
5
6
  listHistory: HistoryEntry[];
6
7
  private options;
8
+ private initPromise;
9
+ private writeQueue;
10
+ private debounceTimer;
11
+ private pendingWrite;
12
+ private pendingWriteResolvers;
7
13
  constructor(options?: JsonFileAdapterOptions);
8
14
  /**
9
- * Revisamos si existe o no el archivo JSON
15
+ * Revisamos si existe o no el archivo JSON y cargamos el historial
10
16
  */
11
17
  private init;
12
18
  /**
13
- * Validar JSON
19
+ * Esperar a que la inicialización termine
20
+ */
21
+ private waitForInit;
22
+ /**
23
+ * Validar JSON - retorna array vacío si el JSON es inválido
14
24
  * @param raw
15
25
  */
16
26
  private validateJson;
17
27
  /**
18
- * Leer archivo y parsear
28
+ * Leer archivo y parsear (siempre desde memoria después de init)
19
29
  */
20
30
  private readFileAndParse;
31
+ /**
32
+ * Escribir al archivo de forma atómica (write to temp, then rename)
33
+ * Esto previene corrupción si el proceso se interrumpe durante la escritura
34
+ */
35
+ private atomicWrite;
36
+ /**
37
+ * Escribir al archivo de forma segura con cola y debounce opcional
38
+ */
39
+ private safeWrite;
21
40
  /**
22
41
  * Buscar el último mensaje por número
23
42
  * @param from
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAI1C,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAEnE,cAAM,UAAW,SAAQ,QAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAQ;IACxB,WAAW,EAAE,YAAY,EAAE,CAAK;IAChC,OAAO,CAAC,OAAO,CAAkD;gBAG7D,OAAO,GAAE,sBAER;IAQL;;OAEG;YACW,IAAI;IAYlB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;YACW,gBAAgB;IAM9B;;;OAGG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAatE;;;OAGG;IACG,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAK/C;AAED,OAAO,EAAE,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAI1C,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAEnE,cAAM,UAAW,SAAQ,QAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAQ;IACxB,WAAW,EAAE,YAAY,EAAE,CAAK;IAChC,OAAO,CAAC,OAAO,CAAmE;IAClF,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,qBAAqB,CAAwB;gBAGjD,OAAO,GAAE,sBAER;IASL;;OAEG;YACW,IAAI;IA0BlB;;OAEG;YACW,WAAW;IAMzB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAepB;;OAEG;YACW,gBAAgB;IAK9B;;;OAGG;YACW,WAAW;IAoBzB;;OAEG;YACW,SAAS;IAkCvB;;;OAGG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAatE;;;OAGG;IACG,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAK/C;AAED,OAAO,EAAE,UAAU,EAAE,CAAA"}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  export interface JsonFileAdapterOptions {
2
2
  filename: string;
3
+ /**
4
+ * Tiempo en ms para agrupar escrituras (debounce).
5
+ * Mejora performance cuando hay muchas escrituras simultáneas.
6
+ * Default: 0 (sin debounce, escritura inmediata)
7
+ */
8
+ debounceTime?: number;
3
9
  }
4
10
  export interface HistoryEntry {
5
11
  ref: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,GAAG,CAAA;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,GAAG,CAAA;CACf"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,YAAY;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,GAAG,CAAA;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,GAAG,CAAA;CACf"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builderbot/database-json",
3
- "version": "1.3.14",
3
+ "version": "1.3.15-alpha.149",
4
4
  "description": "Esto es el conector a json",
5
5
  "keywords": [],
6
6
  "author": "Leifer Mendez <leifer33@gmail.com>",
@@ -29,7 +29,7 @@
29
29
  "url": "https://github.com/codigoencasa/bot-whatsapp/issues"
30
30
  },
31
31
  "dependencies": {
32
- "@builderbot/bot": "1.3.14"
32
+ "@builderbot/bot": "1.3.15-alpha.149"
33
33
  },
34
34
  "homepage": "https://github.com/codigoencasa/bot-whatsapp#readme",
35
35
  "devDependencies": {
@@ -43,5 +43,5 @@
43
43
  "tslib": "^2.6.2",
44
44
  "tsm": "^2.3.0"
45
45
  },
46
- "gitHead": "b9bacfa917364b27d22fe7398c1219cbd28b2a22"
46
+ "gitHead": "4936e5cb0486b776e0175e7aa8d1807f7ee62dfb"
47
47
  }