@wabot-dev/framework 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/README.md +9 -9
  2. package/dist/src/addon/auth/jwt/JwtGuardMiddleware.js +4 -1
  3. package/dist/src/addon/chat-bot/google/GoogleChatAdapter.js +5 -1
  4. package/dist/src/addon/chat-controller/whatsapp/WhatsAppChannel.js +1 -1
  5. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppReceiverByCloudApi.js +3 -3
  6. package/dist/src/addon/chat-controller/whatsapp/cloud-api/WhatsAppSenderByCloudApi.js +1 -1
  7. package/dist/src/addon/chat-controller/whatsapp/proxy/WhatsAppSenderByWabotProxy.js +1 -1
  8. package/dist/src/core/description/metadata/@description.js +1 -1
  9. package/dist/src/core/env/Env.js +1 -1
  10. package/dist/src/core/logger/Logger.js +30 -0
  11. package/dist/src/core/validation/metadata/@isArray.js +1 -1
  12. package/dist/src/core/validation/metadata/@isModel.js +1 -1
  13. package/dist/src/core/validation/validators/is-date/@isDate.js +1 -1
  14. package/dist/src/core/validation/validators/is-number/@isNumber.js +1 -1
  15. package/dist/src/core/validation/validators/is-string/@isString.js +1 -1
  16. package/dist/src/feature/async/CronScheduler.js +3 -3
  17. package/dist/src/feature/async/JobRunner.js +1 -1
  18. package/dist/src/feature/async/JobScheduler.js +9 -6
  19. package/dist/src/feature/chat-bot/ChatMemory.js +2 -2
  20. package/dist/src/feature/http/HttpServerProvider.js +1 -1
  21. package/dist/src/feature/pg/PgLockKey.js +4 -4
  22. package/dist/src/feature/rest-controller/runRestControllers.js +1 -1
  23. package/dist/src/feature/socket-controller/runSocketControllers.js +2 -2
  24. package/dist/src/index.d.ts +30 -0
  25. package/package.json +1 -1
package/README.md CHANGED
@@ -6,10 +6,9 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
7
7
  [![Documentation](https://img.shields.io/badge/docs-wabot.dev-blue.svg?style=flat-square)](https://docs.wabot.dev)
8
8
 
9
-
10
9
  **Un framework moderno y flexible para crear bots con TypeScript e Inteligencia Artificial**
11
10
 
12
- [Documentación](https://docs.wabot.dev) • [Inicio Rápido](https://docs.wabot.dev/guides/start-new-project/)
11
+ [Documentación](https://docs.wabot.dev) • [Inicio Rápido](https://docs.wabot.dev/guides/start-new-project/)
13
12
 
14
13
  </div>
15
14
 
@@ -43,11 +42,11 @@ visita nuestra documentación 📘 **[Ver guía completa de inicio →](https://
43
42
 
44
43
  Wabot se integra nativamente con las principales plataformas de mensajería:
45
44
 
46
- | Plataforma | Estado |
47
- |------------|--------|
45
+ | Plataforma | Estado |
46
+ | --------------- | ------------ |
48
47
  | 📱 **WhatsApp** | ✅ Soportado |
49
48
  | ✈️ **Telegram** | ✅ Soportado |
50
- | 🔌 **Socket** | ✅ Soportado |
49
+ | 🔌 **Socket** | ✅ Soportado |
51
50
 
52
51
  ---
53
52
 
@@ -55,11 +54,12 @@ Wabot se integra nativamente con las principales plataformas de mensajería:
55
54
 
56
55
  Potencia tu bot con los principales proveedores de inteligencia artificial:
57
56
 
58
- | Proveedor | Soporte en Wabot |
59
- |-----------|------------------|
60
- | 🟢 **OpenAI** | ✅ Integración completa |
61
- | 🔵 **Google** | ✅ Integración completa |
57
+ | Proveedor | Soporte en Wabot |
58
+ | ---------------- | ----------------------- |
59
+ | 🟢 **OpenAI** | ✅ Integración completa |
60
+ | 🔵 **Google** | ✅ Integración completa |
62
61
  | 🟣 **Anthropic** | ✅ Integración completa |
62
+
63
63
  ---
64
64
 
65
65
  ## 📚 Documentación
@@ -20,7 +20,10 @@ let JwtGuardMiddleware = class JwtGuardMiddleware {
20
20
  }
21
21
  const parts = authorization.split(' ');
22
22
  if (parts.length !== 2) {
23
- throw new CustomError({ httpCode: 401, message: 'Authorization header must be: Bearer <token>' });
23
+ throw new CustomError({
24
+ httpCode: 401,
25
+ message: 'Authorization header must be: Bearer <token>',
26
+ });
24
27
  }
25
28
  const [bearer, token] = parts;
26
29
  if (bearer.toLowerCase() !== 'bearer') {
@@ -124,7 +124,11 @@ let GoogleChatAdapter = class GoogleChatAdapter {
124
124
  }
125
125
  nextItems.push({
126
126
  type: 'functionCall',
127
- functionCall: { id: id ?? Random.alphaNumericLowerCase(10), name, arguments: args && JSON.stringify(args) },
127
+ functionCall: {
128
+ id: id ?? Random.alphaNumericLowerCase(10),
129
+ name,
130
+ arguments: args && JSON.stringify(args),
131
+ },
128
132
  });
129
133
  }
130
134
  }
@@ -33,7 +33,7 @@ let WhatsAppChannel = class WhatsAppChannel {
33
33
  });
34
34
  }
35
35
  catch (err) {
36
- this.logger.error(err);
36
+ this.logger.error('Failed to handle WhatsApp message', err);
37
37
  }
38
38
  },
39
39
  });
@@ -33,7 +33,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
33
33
  res.status(200).send(challenge);
34
34
  }
35
35
  catch (e) {
36
- this.logger.error(e);
36
+ this.logger.error('WhatsApp webhook verification failed', e);
37
37
  res.sendStatus(500);
38
38
  return;
39
39
  }
@@ -66,7 +66,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
66
66
  }
67
67
  }
68
68
  catch (err) {
69
- this.logger.error(err);
69
+ this.logger.error('Failed to handle WhatsApp webhook payload', err);
70
70
  }
71
71
  }
72
72
  async emmitMessage(metadata, message, contact) {
@@ -75,7 +75,7 @@ class WhatsAppReceiverByCloudApi extends WhatsAppReceiver {
75
75
  return;
76
76
  }
77
77
  if (message.type !== 'text') {
78
- this.logger.error(`message type ${message.type} is not supported yet`);
78
+ this.logger.warn(`Message type '${message.type}' is not supported yet`);
79
79
  return;
80
80
  }
81
81
  const channelName = 'WhatsAppChannel';
@@ -114,7 +114,7 @@ let WhatsAppSenderByCloudApi = class WhatsAppSenderByCloudApi extends WhatsAppSe
114
114
  return template;
115
115
  }
116
116
  catch (error) {
117
- this.logger.error(error);
117
+ this.logger.error('Failed to fetch WhatsApp template', error);
118
118
  throw error;
119
119
  }
120
120
  }
@@ -43,7 +43,7 @@ let WhatsAppSenderByWabotProxy = class WhatsAppSenderByWabotProxy extends WhatsA
43
43
  }
44
44
  }
45
45
  catch (err) {
46
- this.logger.error(err);
46
+ this.logger.error(`Failed to send WhatsApp from '${request.from}' to '${request.to}'`, err);
47
47
  throw new Error(undefined, { cause: err });
48
48
  }
49
49
  }
@@ -13,7 +13,7 @@ function description(description) {
13
13
  propertyType,
14
14
  functionArgsTypes,
15
15
  functionReturnType: undefined,
16
- description
16
+ description,
17
17
  });
18
18
  };
19
19
  }
@@ -17,7 +17,7 @@ let Env = class Env {
17
17
  }
18
18
  requireString(varName, options) {
19
19
  const value = process.env[varName] ?? options?.default;
20
- if (!value)
20
+ if (value == null)
21
21
  throw new Error(`Env Variable ${varName} is required`);
22
22
  return value;
23
23
  }
@@ -13,6 +13,30 @@ const levelToSeverity = {
13
13
  error: 'error',
14
14
  fatal: 'fatal',
15
15
  };
16
+ /**
17
+ * Logger with 6 severity levels. Uses the `debug` library for output.
18
+ *
19
+ * ## Level verbosity contract
20
+ *
21
+ * - **fatal** — The process cannot continue. Something is critically broken
22
+ * (uncaught exceptions, unhandled rejections). Investigate immediately.
23
+ *
24
+ * - **error** — An operation failed unexpectedly. Always include: what failed,
25
+ * why (the Error), and enough context to locate the problem (IDs, names).
26
+ *
27
+ * - **warn** — Something unusual happened but the system handled it gracefully.
28
+ * Known limitations, safety guards triggered, recoverable issues.
29
+ *
30
+ * - **info** — Key lifecycle events the user cares about: systems starting or
31
+ * stopping, configuration applied, significant state changes. Should read
32
+ * like a high-level audit log.
33
+ *
34
+ * - **debug** — Internal operational details for developers troubleshooting.
35
+ * Step-by-step flow, lock acquisition, query execution, reconciliation steps.
36
+ *
37
+ * - **trace** — Very fine-grained: every HTTP request, every socket event,
38
+ * every message sent or received.
39
+ */
16
40
  class Logger {
17
41
  static monitor = null;
18
42
  debuggers;
@@ -32,21 +56,27 @@ class Logger {
32
56
  static getMonitor() {
33
57
  return Logger.monitor;
34
58
  }
59
+ /** Very fine-grained: every HTTP request, socket event, message sent/received. */
35
60
  trace(...args) {
36
61
  this.log('trace', args);
37
62
  }
63
+ /** Internal operational details for developers: step-by-step flow, lock acquisition, queries. */
38
64
  debug(...args) {
39
65
  this.log('debug', args);
40
66
  }
67
+ /** Key lifecycle events: systems start/stop, configuration applied, significant state changes. */
41
68
  info(...args) {
42
69
  this.log('info', args);
43
70
  }
71
+ /** Something unusual happened but the system recovered. Known limitations, safety guards. */
44
72
  warn(...args) {
45
73
  this.log('warn', args);
46
74
  }
75
+ /** Operation failed unexpectedly. Always include: what failed + why (Error) + identifiers. */
47
76
  error(...args) {
48
77
  this.log('error', args);
49
78
  }
79
+ /** Process cannot continue. Uncaught exceptions, unhandled rejections. Investigate immediately. */
50
80
  fatal(...args) {
51
81
  this.log('fatal', args);
52
82
  }
@@ -11,7 +11,7 @@ function isArray(options) {
11
11
  propertyName,
12
12
  validator: validateArray,
13
13
  validatorOptions: options,
14
- typeDescriptor: 'array'
14
+ typeDescriptor: 'array',
15
15
  });
16
16
  };
17
17
  }
@@ -11,7 +11,7 @@ function isModel(model) {
11
11
  propertyName,
12
12
  validator: validateModel,
13
13
  validatorOptions: store.getModelValidatorsInfo(model),
14
- typeDescriptor: 'object'
14
+ typeDescriptor: 'object',
15
15
  });
16
16
  };
17
17
  }
@@ -10,7 +10,7 @@ function isDate() {
10
10
  modelConstructor: target.constructor,
11
11
  propertyName,
12
12
  validator: validateIsDate,
13
- typeDescriptor: 'date'
13
+ typeDescriptor: 'date',
14
14
  });
15
15
  };
16
16
  }
@@ -10,7 +10,7 @@ function isNumber() {
10
10
  modelConstructor: target.constructor,
11
11
  propertyName,
12
12
  validator: validateIsNumber,
13
- typeDescriptor: 'number'
13
+ typeDescriptor: 'number',
14
14
  });
15
15
  };
16
16
  }
@@ -10,7 +10,7 @@ function isString() {
10
10
  modelConstructor: target.constructor,
11
11
  propertyName,
12
12
  validator: validateIsString,
13
- typeDescriptor: 'string'
13
+ typeDescriptor: 'string',
14
14
  });
15
15
  };
16
16
  }
@@ -60,12 +60,12 @@ let CronScheduler = class CronScheduler {
60
60
  this.logger.debug(`start reconciliation of crons ${configToReconciliate.map((x) => x.name).join(', ')}`);
61
61
  try {
62
62
  const cronJobs = await Promise.all(configToReconciliate.map((x) => this.reconciliateConfig(x)));
63
- this.logger.info(`success reconciliation of reconciliation of crons ${configToReconciliate.map((x) => x.name).join(', ')}`);
63
+ this.logger.info(`Cron reconciliation succeeded for ${configToReconciliate.map((x) => x.name).join(', ')}`);
64
64
  this.jobScheduler.start(cronJobs.map((x) => x.commandName));
65
65
  this.jobWatchdog.start(cronJobs.map((x) => x.commandName));
66
66
  }
67
67
  catch (e) {
68
- this.logger.error(e);
68
+ this.logger.error('Cron reconciliation failed', e);
69
69
  this.stop(configToReconciliate);
70
70
  this.logger.error(`reconciliation fail - stopped crons ${configToReconciliate.map((x) => x.name).join(', ')}`);
71
71
  throw e;
@@ -88,7 +88,7 @@ let CronScheduler = class CronScheduler {
88
88
  });
89
89
  }
90
90
  catch (e) {
91
- this.logger.error(e);
91
+ this.logger.error('Cron tick failed', e);
92
92
  }
93
93
  finally {
94
94
  this.timeout = setTimeout(() => this.tick(), interval * 1000);
@@ -45,7 +45,7 @@ let JobRunner = class JobRunner {
45
45
  job.setAsSuccess();
46
46
  }
47
47
  catch (e) {
48
- this.logger.error(e);
48
+ this.logger.error(`Failed to run command '${job.commandName}'`, e);
49
49
  job.setAsFailed(e instanceof Error ? e : new Error('Invalid Job error'));
50
50
  }
51
51
  finally {
@@ -22,16 +22,15 @@ let JobScheduler = class JobScheduler {
22
22
  this.env = env;
23
23
  }
24
24
  start(commands) {
25
- this.logger.info(`starting handlers for commands ${commands}`);
26
25
  commands.forEach((x) => this.commands.add(x));
27
- this.logger.info(`starting job handlers for commands ${commands.join(', ')}`);
26
+ this.logger.info(`Starting job handlers for commands ${commands.join(', ')}`);
28
27
  if (this.commands.size > 0 && !this.running) {
29
28
  this.running = true;
30
29
  this.tick();
31
30
  }
32
31
  }
33
32
  stop(commands) {
34
- this.logger.info(`stoping handlers for commands ${commands}`);
33
+ this.logger.info(`Stopping job handlers for commands ${commands.join(', ')}`);
35
34
  commands.forEach((x) => this.commands.delete(x));
36
35
  if (this.commands.size === 0) {
37
36
  this.running = false;
@@ -42,7 +41,9 @@ let JobScheduler = class JobScheduler {
42
41
  }
43
42
  tryExecuteNow(job) {
44
43
  if (this.commands.has(job.commandName))
45
- this.executor.execute(job).catch((e) => this.logger.error(e));
44
+ this.executor
45
+ .execute(job)
46
+ .catch((e) => this.logger.error(`Failed to execute job ${job.id}`, e));
46
47
  }
47
48
  async tick() {
48
49
  if (!this.running)
@@ -57,12 +58,14 @@ let JobScheduler = class JobScheduler {
57
58
  await this.locker.withKey('wabot-job-scheduler-loop').run(async () => {
58
59
  const jobs = await this.repo.findPendingForRunFrom(new Date(), this.executor.remainingSlots());
59
60
  const readyToRunJobs = jobs.filter((job) => this.commands.has(job.commandName) && job.isScheduleReady());
60
- readyToRunJobs.forEach((j) => this.executor.execute(j).catch((e) => this.logger.error(e)));
61
+ readyToRunJobs.forEach((j) => this.executor
62
+ .execute(j)
63
+ .catch((e) => this.logger.error(`Failed to execute job ${j.id}`, e)));
61
64
  await new Promise((r) => setTimeout(r, 10));
62
65
  });
63
66
  }
64
67
  catch (e) {
65
- this.logger.error(e);
68
+ this.logger.error('Job scheduler tick failed', e);
66
69
  }
67
70
  finally {
68
71
  this.timeout = setTimeout(() => this.tick(), interval * 1000);
@@ -1,9 +1,9 @@
1
1
  class ChatMemory {
2
2
  findLastItems(count) {
3
- throw new Error("Method not implemented.");
3
+ throw new Error('Method not implemented.');
4
4
  }
5
5
  create(item) {
6
- throw new Error("Method not implemented.");
6
+ throw new Error('Method not implemented.');
7
7
  }
8
8
  }
9
9
 
@@ -20,7 +20,7 @@ let HttpServerProvider = class HttpServerProvider {
20
20
  this.listening = true;
21
21
  const PORT = process.env.PORT || 3000;
22
22
  this.server.listen(PORT, () => {
23
- this.logger.info(`server listenig on port ${PORT}`);
23
+ this.logger.info(`Server listening on port ${PORT}`);
24
24
  });
25
25
  }
26
26
  };
@@ -16,9 +16,9 @@ class PgLockKey {
16
16
  return withPgClient(this.pool, async (client) => {
17
17
  let locked = false;
18
18
  try {
19
- this.logger.debug(`try to adquire ${this.key}`);
19
+ this.logger.debug(`try to acquire ${this.key}`);
20
20
  await client.query('SELECT pg_advisory_lock($1)', [this.value]);
21
- this.logger.debug(`adquired ${this.key}`);
21
+ this.logger.debug(`acquired ${this.key}`);
22
22
  locked = true;
23
23
  return await fn();
24
24
  }
@@ -27,7 +27,7 @@ class PgLockKey {
27
27
  this.logger.debug(`try to release ${this.key}`);
28
28
  const res = await client.query('SELECT pg_advisory_unlock($1) AS unlocked', [this.value]);
29
29
  if (!res.rows[0]?.unlocked) {
30
- this.logger.error('error - no unlock');
30
+ this.logger.error(`Failed to release lock '${this.key}'`);
31
31
  }
32
32
  else {
33
33
  this.logger.debug(`released ${this.key}`);
@@ -50,7 +50,7 @@ class PgLockKey {
50
50
  if (locked) {
51
51
  const res = await client.query('SELECT pg_advisory_unlock($1) AS unlocked', [this.value]);
52
52
  if (!res.rows[0]?.unlocked) {
53
- this.logger.error('error - no unlock');
53
+ this.logger.error(`Failed to release lock '${this.key}'`);
54
54
  }
55
55
  }
56
56
  }
@@ -68,7 +68,7 @@ function runRestControllers(controllers) {
68
68
  res.status(200).json(response ?? null);
69
69
  }
70
70
  catch (err) {
71
- logger.error(err);
71
+ logger.error(`${method.toUpperCase()} ${route} failed`, err);
72
72
  if (err instanceof Error) {
73
73
  const keys = Object.keys(err).filter((key) => !['message', 'stack'].includes(key));
74
74
  const { httpCode, ...info } = keys.reduce((acc, key) => {
@@ -70,7 +70,7 @@ function runSocketControllers(controllers) {
70
70
  }
71
71
  }
72
72
  catch (err) {
73
- logger.error(err);
73
+ logger.error(`Event '${event.config.event}' on '${namespace}' failed`, err);
74
74
  if (err instanceof Error) {
75
75
  const keys = Object.keys(err).filter((key) => !['message', 'stack'].includes(key));
76
76
  const { httpCode, ...info } = keys.reduce((acc, key) => {
@@ -108,7 +108,7 @@ function runSocketControllers(controllers) {
108
108
  }
109
109
  }
110
110
  catch (err) {
111
- logger.error(err);
111
+ logger.error(`Connection setup on '${namespace}' failed`, err);
112
112
  socket.disconnect();
113
113
  connectionContainer.dispose();
114
114
  }
@@ -129,6 +129,30 @@ interface IErrorMonitor {
129
129
  captureMessage(message: string, context: IErrorMonitorContext): void;
130
130
  }
131
131
 
132
+ /**
133
+ * Logger with 6 severity levels. Uses the `debug` library for output.
134
+ *
135
+ * ## Level verbosity contract
136
+ *
137
+ * - **fatal** — The process cannot continue. Something is critically broken
138
+ * (uncaught exceptions, unhandled rejections). Investigate immediately.
139
+ *
140
+ * - **error** — An operation failed unexpectedly. Always include: what failed,
141
+ * why (the Error), and enough context to locate the problem (IDs, names).
142
+ *
143
+ * - **warn** — Something unusual happened but the system handled it gracefully.
144
+ * Known limitations, safety guards triggered, recoverable issues.
145
+ *
146
+ * - **info** — Key lifecycle events the user cares about: systems starting or
147
+ * stopping, configuration applied, significant state changes. Should read
148
+ * like a high-level audit log.
149
+ *
150
+ * - **debug** — Internal operational details for developers troubleshooting.
151
+ * Step-by-step flow, lock acquisition, query execution, reconciliation steps.
152
+ *
153
+ * - **trace** — Very fine-grained: every HTTP request, every socket event,
154
+ * every message sent or received.
155
+ */
132
156
  declare class Logger {
133
157
  private static monitor;
134
158
  private debuggers;
@@ -136,11 +160,17 @@ declare class Logger {
136
160
  constructor(name: string);
137
161
  static setMonitor(monitor: IErrorMonitor): void;
138
162
  static getMonitor(): IErrorMonitor | null;
163
+ /** Very fine-grained: every HTTP request, socket event, message sent/received. */
139
164
  trace(...args: any[]): void;
165
+ /** Internal operational details for developers: step-by-step flow, lock acquisition, queries. */
140
166
  debug(...args: any[]): void;
167
+ /** Key lifecycle events: systems start/stop, configuration applied, significant state changes. */
141
168
  info(...args: any[]): void;
169
+ /** Something unusual happened but the system recovered. Known limitations, safety guards. */
142
170
  warn(...args: any[]): void;
171
+ /** Operation failed unexpectedly. Always include: what failed + why (Error) + identifiers. */
143
172
  error(...args: any[]): void;
173
+ /** Process cannot continue. Uncaught exceptions, unhandled rejections. Investigate immediately. */
144
174
  fatal(...args: any[]): void;
145
175
  private log;
146
176
  private sendToMonitor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wabot-dev/framework",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Framework for IA Chat Bots",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",