@agendajs/postgres-backend 2.0.0 → 3.0.1

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/README.md CHANGED
@@ -155,6 +155,44 @@ CREATE INDEX agenda_jobs_next_run_at_idx
155
155
  ON agenda_jobs (next_run_at) WHERE next_run_at IS NOT NULL;
156
156
  ```
157
157
 
158
+ ## Job Logging
159
+
160
+ PostgresBackend includes a built-in `PostgresJobLogger` that stores structured job lifecycle events in a dedicated table (`agenda_logs` by default). The logger is lightweight and only creates its table on first use.
161
+
162
+ ### Automatic Usage
163
+
164
+ When you enable logging in Agenda, the backend's built-in logger is used automatically:
165
+
166
+ ```typescript
167
+ const agenda = new Agenda({
168
+ backend: new PostgresBackend({ connectionString: 'postgresql://...' }),
169
+ logging: true // Uses PostgresJobLogger automatically
170
+ });
171
+ ```
172
+
173
+ ### Standalone Usage
174
+
175
+ You can also use `PostgresJobLogger` independently — for example, to log to PostgreSQL while using a different backend for storage:
176
+
177
+ ```typescript
178
+ import { PostgresJobLogger } from '@agendajs/postgres-backend';
179
+ import { Pool } from 'pg';
180
+
181
+ const logger = new PostgresJobLogger({
182
+ pool: new Pool({ connectionString: 'postgresql://...' }),
183
+ logTableName: 'agenda_logs' // Optional: table name (default: 'agenda_logs')
184
+ });
185
+
186
+ // Use with any backend
187
+ import { MongoBackend } from '@agendajs/mongo-backend';
188
+ const agenda = new Agenda({
189
+ backend: new MongoBackend({ mongo: db }),
190
+ logging: logger
191
+ });
192
+ ```
193
+
194
+ See the [Agenda README](https://github.com/agenda/agenda#persistent-job-logging) for full logging documentation.
195
+
158
196
  ## Manual Schema Management
159
197
 
160
198
  If you prefer to manage the schema yourself (set `ensureSchema: false`):
@@ -1,4 +1,4 @@
1
- import type { AgendaBackend, JobRepository, NotificationChannel } from 'agenda';
1
+ import type { AgendaBackend, JobRepository, NotificationChannel, JobLogger } from 'agenda';
2
2
  import type { PostgresBackendConfig } from './types.js';
3
3
  /**
4
4
  * PostgreSQL backend for Agenda
@@ -41,6 +41,7 @@ export declare class PostgresBackend implements AgendaBackend {
41
41
  readonly name = "PostgreSQL";
42
42
  private _repository;
43
43
  private _notificationChannel?;
44
+ private _logger;
44
45
  private config;
45
46
  private _ownsConnection;
46
47
  constructor(config: PostgresBackendConfig);
@@ -53,6 +54,11 @@ export declare class PostgresBackend implements AgendaBackend {
53
54
  * True if created from connectionString/poolConfig, false if pool was passed in.
54
55
  */
55
56
  get ownsConnection(): boolean;
57
+ /**
58
+ * The job logger for persistent event logging.
59
+ * Always available; Agenda decides whether to activate it via its `logging` config.
60
+ */
61
+ get logger(): JobLogger;
56
62
  /**
57
63
  * The notification channel for real-time notifications via LISTEN/NOTIFY
58
64
  * Returns undefined if notifications are disabled
@@ -1,5 +1,6 @@
1
1
  import debug from 'debug';
2
2
  import { PostgresJobRepository } from './PostgresJobRepository.js';
3
+ import { PostgresJobLogger } from './PostgresJobLogger.js';
3
4
  import { PostgresNotificationChannel } from './PostgresNotificationChannel.js';
4
5
  const log = debug('agenda:postgres:backend');
5
6
  /**
@@ -43,6 +44,7 @@ export class PostgresBackend {
43
44
  name = 'PostgreSQL';
44
45
  _repository;
45
46
  _notificationChannel;
47
+ _logger;
46
48
  config;
47
49
  _ownsConnection;
48
50
  constructor(config) {
@@ -60,6 +62,11 @@ export class PostgresBackend {
60
62
  }
61
63
  });
62
64
  }
65
+ // Always create the logger (lightweight; only initializes on first use when Agenda activates it)
66
+ this._logger = new PostgresJobLogger({
67
+ tableName: config.logTableName,
68
+ ensureSchema: config.ensureSchema
69
+ });
63
70
  log('PostgresBackend created with config: %O', {
64
71
  tableName: config.tableName || 'agenda_jobs',
65
72
  channelName: config.channelName || 'agenda_jobs',
@@ -81,6 +88,13 @@ export class PostgresBackend {
81
88
  get ownsConnection() {
82
89
  return this._ownsConnection;
83
90
  }
91
+ /**
92
+ * The job logger for persistent event logging.
93
+ * Always available; Agenda decides whether to activate it via its `logging` config.
94
+ */
95
+ get logger() {
96
+ return this._logger;
97
+ }
84
98
  /**
85
99
  * The notification channel for real-time notifications via LISTEN/NOTIFY
86
100
  * Returns undefined if notifications are disabled
@@ -103,6 +117,8 @@ export class PostgresBackend {
103
117
  if (this._notificationChannel) {
104
118
  this._notificationChannel.setPool(this._repository.getPool());
105
119
  }
120
+ // Initialize the job logger with the shared pool
121
+ await this._logger.setPool(this._repository.getPool());
106
122
  // Note: notification channel is connected by Agenda.start()
107
123
  // when it's needed, so we don't connect it here
108
124
  log('PostgresBackend connected');
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresBackend.js","sourceRoot":"","sources":["../src/PostgresBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAG/E,MAAM,GAAG,GAAG,KAAK,CAAC,yBAAyB,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,OAAO,eAAe;IAClB,IAAI,GAAG,YAAY,CAAC;IAErB,WAAW,CAAwB;IACnC,oBAAoB,CAA+B;IACnD,MAAM,CAAwB;IAC9B,eAAe,CAAU;IAEjC,YAAY,MAA6B;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,6DAA6D;QAC7D,IAAI,CAAC,eAAe,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAEpC,oBAAoB;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAErD,gDAAgD;QAChD,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAClC,IAAI,CAAC,oBAAoB,GAAG,IAAI,2BAA2B,CAAC;gBAC3D,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,SAAS,EAAE;oBACV,OAAO,EAAE,IAAI;iBACb;aACD,CAAC,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,yCAAyC,EAAE;YAC9C,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,aAAa;YAC5C,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,aAAa;YAChD,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,IAAI;YACzC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,IAAI,KAAK;YAC1D,cAAc,EAAE,IAAI,CAAC,eAAe;SACpC,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,IAAI,mBAAmB;QACtB,OAAO,IAAI,CAAC,oBAAoB,CAAC;IAClC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO;QACZ,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAElC,qDAAqD;QACrD,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QAEjC,wDAAwD;QACxD,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/B,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,4DAA4D;QAC5D,gDAAgD;QAEhD,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU;QACf,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAErC,wDAAwD;QACxD,mDAAmD;QACnD,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;QAEpC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IACrC,CAAC;CACD"}
1
+ {"version":3,"file":"PostgresBackend.js","sourceRoot":"","sources":["../src/PostgresBackend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { Pool } from 'pg';
2
+ import type { JobLogger, JobLogEntry, JobLogQuery, JobLogQueryResult } from 'agenda';
3
+ /**
4
+ * SQL schema for the job logs table.
5
+ */
6
+ export declare function getCreateLogsTableSQL(tableName: string): string;
7
+ /**
8
+ * SQL indexes for the job logs table.
9
+ */
10
+ export declare function getCreateLogsIndexesSQL(tableName: string): string[];
11
+ /**
12
+ * PostgreSQL implementation of JobLogger.
13
+ * Stores job lifecycle events in a dedicated PostgreSQL table.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Via backend (automatic connection sharing):
18
+ * import { PostgresBackend } from '@agendajs/postgres-backend';
19
+ *
20
+ * const backend = new PostgresBackend({
21
+ * connectionString: 'postgresql://...',
22
+ * logging: true // enables PostgresJobLogger with 'agenda_logs' table
23
+ * });
24
+ *
25
+ * // Standalone (e.g., log to Postgres while using Mongo for storage):
26
+ * import { PostgresJobLogger } from '@agendajs/postgres-backend';
27
+ * import { Pool } from 'pg';
28
+ *
29
+ * const logger = new PostgresJobLogger({ pool: new Pool({ connectionString: '...' }) });
30
+ * const agenda = new Agenda({
31
+ * backend: new MongoBackend({ address: 'mongodb://...' }),
32
+ * logging: logger
33
+ * });
34
+ * ```
35
+ */
36
+ export declare class PostgresJobLogger implements JobLogger {
37
+ private pool;
38
+ private readonly tableName;
39
+ private readonly ensureSchema;
40
+ private initialized;
41
+ constructor(options?: {
42
+ pool?: Pool;
43
+ tableName?: string;
44
+ ensureSchema?: boolean;
45
+ } | string);
46
+ /**
47
+ * Set the pool and initialize the table schema.
48
+ * Called by PostgresBackend after the repository connects,
49
+ * or automatically when a pool is provided in the constructor.
50
+ */
51
+ setPool(pool: Pool): Promise<void>;
52
+ private ensureInitialized;
53
+ log(entry: Omit<JobLogEntry, '_id'>): Promise<void>;
54
+ getLogs(query?: JobLogQuery): Promise<JobLogQueryResult>;
55
+ clearLogs(query?: JobLogQuery): Promise<number>;
56
+ private buildWhere;
57
+ }
@@ -0,0 +1,224 @@
1
+ import debug from 'debug';
2
+ const log = debug('agenda:postgres:logger');
3
+ /**
4
+ * SQL schema for the job logs table.
5
+ */
6
+ export function getCreateLogsTableSQL(tableName) {
7
+ return `
8
+ CREATE TABLE IF NOT EXISTS "${tableName}" (
9
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
10
+ timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11
+ level VARCHAR(10) NOT NULL,
12
+ event VARCHAR(30) NOT NULL,
13
+ job_id VARCHAR(255),
14
+ job_name VARCHAR(255) NOT NULL,
15
+ message TEXT NOT NULL,
16
+ duration INTEGER,
17
+ error TEXT,
18
+ fail_count INTEGER,
19
+ retry_delay INTEGER,
20
+ retry_attempt INTEGER,
21
+ agenda_name VARCHAR(255),
22
+ meta JSONB
23
+ );
24
+ `;
25
+ }
26
+ /**
27
+ * SQL indexes for the job logs table.
28
+ */
29
+ export function getCreateLogsIndexesSQL(tableName) {
30
+ return [
31
+ `CREATE INDEX IF NOT EXISTS "${tableName}_timestamp_idx"
32
+ ON "${tableName}" (timestamp DESC)`,
33
+ `CREATE INDEX IF NOT EXISTS "${tableName}_job_id_idx"
34
+ ON "${tableName}" (job_id, timestamp DESC)`,
35
+ `CREATE INDEX IF NOT EXISTS "${tableName}_job_name_idx"
36
+ ON "${tableName}" (job_name, timestamp DESC)`
37
+ ];
38
+ }
39
+ /**
40
+ * PostgreSQL implementation of JobLogger.
41
+ * Stores job lifecycle events in a dedicated PostgreSQL table.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // Via backend (automatic connection sharing):
46
+ * import { PostgresBackend } from '@agendajs/postgres-backend';
47
+ *
48
+ * const backend = new PostgresBackend({
49
+ * connectionString: 'postgresql://...',
50
+ * logging: true // enables PostgresJobLogger with 'agenda_logs' table
51
+ * });
52
+ *
53
+ * // Standalone (e.g., log to Postgres while using Mongo for storage):
54
+ * import { PostgresJobLogger } from '@agendajs/postgres-backend';
55
+ * import { Pool } from 'pg';
56
+ *
57
+ * const logger = new PostgresJobLogger({ pool: new Pool({ connectionString: '...' }) });
58
+ * const agenda = new Agenda({
59
+ * backend: new MongoBackend({ address: 'mongodb://...' }),
60
+ * logging: logger
61
+ * });
62
+ * ```
63
+ */
64
+ export class PostgresJobLogger {
65
+ pool;
66
+ tableName;
67
+ ensureSchema;
68
+ initialized = false;
69
+ constructor(options) {
70
+ if (typeof options === 'string') {
71
+ // Legacy: constructor(tableName)
72
+ this.tableName = options;
73
+ this.ensureSchema = true;
74
+ }
75
+ else {
76
+ this.tableName = options?.tableName ?? 'agenda_logs';
77
+ this.ensureSchema = options?.ensureSchema ?? true;
78
+ if (options?.pool) {
79
+ this.pool = options.pool;
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Set the pool and initialize the table schema.
85
+ * Called by PostgresBackend after the repository connects,
86
+ * or automatically when a pool is provided in the constructor.
87
+ */
88
+ async setPool(pool) {
89
+ this.pool = pool;
90
+ await this.ensureInitialized();
91
+ }
92
+ async ensureInitialized() {
93
+ if (this.initialized || !this.pool || !this.ensureSchema)
94
+ return;
95
+ this.initialized = true;
96
+ log('creating schema for %s table', this.tableName);
97
+ const client = await this.pool.connect();
98
+ try {
99
+ await client.query(getCreateLogsTableSQL(this.tableName));
100
+ for (const sql of getCreateLogsIndexesSQL(this.tableName)) {
101
+ await client.query(sql);
102
+ }
103
+ log('schema created for %s table', this.tableName);
104
+ }
105
+ finally {
106
+ client.release();
107
+ }
108
+ }
109
+ async log(entry) {
110
+ if (!this.pool) {
111
+ log('pool not initialized, skipping log entry');
112
+ return;
113
+ }
114
+ await this.ensureInitialized();
115
+ await this.pool.query(`INSERT INTO "${this.tableName}"
116
+ (timestamp, level, event, job_id, job_name, message, duration, error, fail_count, retry_delay, retry_attempt, agenda_name, meta)
117
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`, [
118
+ entry.timestamp,
119
+ entry.level,
120
+ entry.event,
121
+ entry.jobId ?? null,
122
+ entry.jobName,
123
+ entry.message,
124
+ entry.duration ?? null,
125
+ entry.error ?? null,
126
+ entry.failCount ?? null,
127
+ entry.retryDelay ?? null,
128
+ entry.retryAttempt ?? null,
129
+ entry.agendaName ?? null,
130
+ entry.meta ? JSON.stringify(entry.meta) : null
131
+ ]);
132
+ }
133
+ async getLogs(query) {
134
+ if (!this.pool) {
135
+ return { entries: [], total: 0 };
136
+ }
137
+ const { where, params } = this.buildWhere(query);
138
+ const sort = query?.sort === 'asc' ? 'ASC' : 'DESC';
139
+ const limit = query?.limit ?? 50;
140
+ const offset = query?.offset ?? 0;
141
+ const entriesQuery = `
142
+ SELECT * FROM "${this.tableName}"
143
+ ${where}
144
+ ORDER BY timestamp ${sort}
145
+ LIMIT $${params.length + 1} OFFSET $${params.length + 2}
146
+ `;
147
+ const countQuery = `SELECT COUNT(*)::int as total FROM "${this.tableName}" ${where}`;
148
+ const [entriesResult, countResult] = await Promise.all([
149
+ this.pool.query(entriesQuery, [...params, limit, offset]),
150
+ this.pool.query(countQuery, params)
151
+ ]);
152
+ const entries = entriesResult.rows.map(row => ({
153
+ _id: row.id,
154
+ timestamp: row.timestamp,
155
+ level: row.level,
156
+ event: row.event,
157
+ jobId: row.job_id ?? undefined,
158
+ jobName: row.job_name,
159
+ message: row.message,
160
+ duration: row.duration ?? undefined,
161
+ error: row.error ?? undefined,
162
+ failCount: row.fail_count ?? undefined,
163
+ retryDelay: row.retry_delay ?? undefined,
164
+ retryAttempt: row.retry_attempt ?? undefined,
165
+ agendaName: row.agenda_name ?? undefined,
166
+ meta: row.meta ?? undefined
167
+ }));
168
+ return { entries, total: countResult.rows[0].total };
169
+ }
170
+ async clearLogs(query) {
171
+ if (!this.pool) {
172
+ return 0;
173
+ }
174
+ const { where, params } = this.buildWhere(query);
175
+ const result = await this.pool.query(`DELETE FROM "${this.tableName}" ${where}`, params);
176
+ return result.rowCount ?? 0;
177
+ }
178
+ buildWhere(query) {
179
+ if (!query)
180
+ return { where: '', params: [] };
181
+ const conditions = [];
182
+ const params = [];
183
+ let paramIndex = 1;
184
+ if (query.jobId) {
185
+ conditions.push(`job_id = $${paramIndex++}`);
186
+ params.push(query.jobId);
187
+ }
188
+ if (query.jobName) {
189
+ conditions.push(`job_name = $${paramIndex++}`);
190
+ params.push(query.jobName);
191
+ }
192
+ if (query.level) {
193
+ if (Array.isArray(query.level)) {
194
+ conditions.push(`level = ANY($${paramIndex++})`);
195
+ params.push(query.level);
196
+ }
197
+ else {
198
+ conditions.push(`level = $${paramIndex++}`);
199
+ params.push(query.level);
200
+ }
201
+ }
202
+ if (query.event) {
203
+ if (Array.isArray(query.event)) {
204
+ conditions.push(`event = ANY($${paramIndex++})`);
205
+ params.push(query.event);
206
+ }
207
+ else {
208
+ conditions.push(`event = $${paramIndex++}`);
209
+ params.push(query.event);
210
+ }
211
+ }
212
+ if (query.from) {
213
+ conditions.push(`timestamp >= $${paramIndex++}`);
214
+ params.push(query.from);
215
+ }
216
+ if (query.to) {
217
+ conditions.push(`timestamp <= $${paramIndex++}`);
218
+ params.push(query.to);
219
+ }
220
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
221
+ return { where, params };
222
+ }
223
+ }
224
+ //# sourceMappingURL=PostgresJobLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresJobLogger.js","sourceRoot":"","sources":["../src/PostgresJobLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,GAAG,GAAG,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAE5C;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACtD,OAAO;gCACwB,SAAS;;;;;;;;;;;;;;;;EAgBvC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACxD,OAAO;QACN,+BAA+B,SAAS;SACjC,SAAS,oBAAoB;QACpC,+BAA+B,SAAS;SACjC,SAAS,4BAA4B;QAC5C,+BAA+B,SAAS;SACjC,SAAS,8BAA8B;KAC9C,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,iBAAiB;IACrB,IAAI,CAAQ;IACH,SAAS,CAAS;IAClB,YAAY,CAAU;IAC/B,WAAW,GAAG,KAAK,CAAC;IAE5B,YAAY,OAA8E;QACzF,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YACjC,iCAAiC;YACjC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC;YACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC1B,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,aAAa,CAAC;YACrD,IAAI,CAAC,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,IAAI,CAAC;YAClD,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;gBACnB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,IAAU;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAChC,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC9B,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QACjE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAExB,GAAG,CAAC,8BAA8B,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,GAAG,IAAI,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3D,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;YACD,GAAG,CAAC,6BAA6B,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;gBAAS,CAAC;YACV,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC;IACF,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAA+B;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,GAAG,CAAC,0CAA0C,CAAC,CAAC;YAChD,OAAO;QACR,CAAC;QAED,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CACpB,gBAAgB,IAAI,CAAC,SAAS;;oEAEmC,EACjE;YACC,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,KAAK;YACX,KAAK,CAAC,KAAK;YACX,KAAK,CAAC,KAAK,IAAI,IAAI;YACnB,KAAK,CAAC,OAAO;YACb,KAAK,CAAC,OAAO;YACb,KAAK,CAAC,QAAQ,IAAI,IAAI;YACtB,KAAK,CAAC,KAAK,IAAI,IAAI;YACnB,KAAK,CAAC,SAAS,IAAI,IAAI;YACvB,KAAK,CAAC,UAAU,IAAI,IAAI;YACxB,KAAK,CAAC,YAAY,IAAI,IAAI;YAC1B,KAAK,CAAC,UAAU,IAAI,IAAI;YACxB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;SAC9C,CACD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAmB;QAChC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,KAAK,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACpD,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;QAElC,MAAM,YAAY,GAAG;oBACH,IAAI,CAAC,SAAS;KAC7B,KAAK;wBACc,IAAI;YAChB,MAAM,CAAC,MAAM,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,GAAG,CAAC;GACvD,CAAC;QACF,MAAM,UAAU,GAAG,uCAAuC,IAAI,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QAErF,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;YACzD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC;SACnC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAkB,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC7D,GAAG,EAAE,GAAG,CAAC,EAAE;YACX,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,KAAK,EAAE,GAAG,CAAC,KAAiB;YAC5B,KAAK,EAAE,GAAG,CAAC,KAAoB;YAC/B,KAAK,EAAE,GAAG,CAAC,MAAM,IAAI,SAAS;YAC9B,OAAO,EAAE,GAAG,CAAC,QAAQ;YACrB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,SAAS;YACnC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,SAAS;YAC7B,SAAS,EAAE,GAAG,CAAC,UAAU,IAAI,SAAS;YACtC,UAAU,EAAE,GAAG,CAAC,WAAW,IAAI,SAAS;YACxC,YAAY,EAAE,GAAG,CAAC,aAAa,IAAI,SAAS;YAC5C,UAAU,EAAE,GAAG,CAAC,WAAW,IAAI,SAAS;YACxC,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS;SAC3B,CAAC,CAAC,CAAC;QAEJ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAmB;QAClC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC;QACV,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CACnC,gBAAgB,IAAI,CAAC,SAAS,KAAK,KAAK,EAAE,EAC1C,MAAM,CACN,CAAC;QACF,OAAO,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;IAC7B,CAAC;IAEO,UAAU,CAAC,KAAmB;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAE7C,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,UAAU,CAAC,IAAI,CAAC,aAAa,UAAU,EAAE,EAAE,CAAC,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,UAAU,CAAC,IAAI,CAAC,eAAe,UAAU,EAAE,EAAE,CAAC,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,gBAAgB,UAAU,EAAE,GAAG,CAAC,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACP,UAAU,CAAC,IAAI,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC5C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,gBAAgB,UAAU,EAAE,GAAG,CAAC,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACP,UAAU,CAAC,IAAI,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC,CAAC;gBAC5C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChB,UAAU,CAAC,IAAI,CAAC,iBAAiB,UAAU,EAAE,EAAE,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;YACd,UAAU,CAAC,IAAI,CAAC,iBAAiB,UAAU,EAAE,EAAE,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC;CACD"}
package/dist/index.d.ts CHANGED
@@ -26,7 +26,9 @@
26
26
  */
27
27
  export { PostgresBackend } from './PostgresBackend.js';
28
28
  export { PostgresJobRepository } from './PostgresJobRepository.js';
29
+ export { PostgresJobLogger } from './PostgresJobLogger.js';
29
30
  export { PostgresNotificationChannel } from './PostgresNotificationChannel.js';
30
31
  export type { PostgresBackendConfig, PostgresJobRow } from './types.js';
31
32
  export type { PostgresNotificationChannelConfig } from './PostgresNotificationChannel.js';
32
33
  export { getCreateTableSQL, getCreateIndexesSQL, getDropTableSQL, getUpdateTimestampTriggerSQL } from './schema.js';
34
+ export { getCreateLogsTableSQL, getCreateLogsIndexesSQL } from './PostgresJobLogger.js';
package/dist/index.js CHANGED
@@ -26,7 +26,9 @@
26
26
  */
27
27
  export { PostgresBackend } from './PostgresBackend.js';
28
28
  export { PostgresJobRepository } from './PostgresJobRepository.js';
29
+ export { PostgresJobLogger } from './PostgresJobLogger.js';
29
30
  export { PostgresNotificationChannel } from './PostgresNotificationChannel.js';
30
31
  // Re-export schema utilities for advanced use cases
31
32
  export { getCreateTableSQL, getCreateIndexesSQL, getDropTableSQL, getUpdateTimestampTriggerSQL } from './schema.js';
33
+ export { getCreateLogsTableSQL, getCreateLogsIndexesSQL } from './PostgresJobLogger.js';
32
34
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAK/E,oDAAoD;AACpD,OAAO,EACN,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,4BAA4B,EAC5B,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAK/E,oDAAoD;AACpD,OAAO,EACN,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,4BAA4B,EAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -23,6 +23,8 @@ export interface PostgresBackendConfig {
23
23
  };
24
24
  /** Disable LISTEN/NOTIFY notification channel (default: false) */
25
25
  disableNotifications?: boolean;
26
+ /** Table name for log entries (default: 'agenda_logs'). */
27
+ logTableName?: string;
26
28
  }
27
29
  /**
28
30
  * Internal row type matching PostgreSQL column names
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agendajs/postgres-backend",
3
- "version": "2.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "PostgreSQL backend for Agenda job scheduler with LISTEN/NOTIFY support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -43,7 +43,7 @@
43
43
  "pg": "^8.13.0"
44
44
  },
45
45
  "peerDependencies": {
46
- "agenda": "6.1.0"
46
+ "agenda": "6.2.1"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/debug": "^4.1.12",
@@ -52,7 +52,7 @@
52
52
  "@vitest/coverage-v8": "^3.1.0",
53
53
  "tsx": "^4.21.0",
54
54
  "vitest": "^3.1.0",
55
- "agenda": "6.1.0"
55
+ "agenda": "6.2.1"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsc -b",