trickle-observe 0.2.94 → 0.2.95
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/db-observer.d.ts +8 -0
- package/dist/db-observer.js +93 -0
- package/dist/observe-register.js +18 -2
- package/package.json +1 -1
- package/src/db-observer.ts +98 -0
- package/src/observe-register.ts +16 -2
package/dist/db-observer.d.ts
CHANGED
|
@@ -13,3 +13,11 @@
|
|
|
13
13
|
* Called from observe-register when pg is required.
|
|
14
14
|
*/
|
|
15
15
|
export declare function patchPg(pgModule: any, debug: boolean): void;
|
|
16
|
+
/**
|
|
17
|
+
* Patch mysql2 to capture queries.
|
|
18
|
+
*/
|
|
19
|
+
export declare function patchMysql2(mysqlModule: any, debug: boolean): void;
|
|
20
|
+
/**
|
|
21
|
+
* Patch better-sqlite3 to capture queries.
|
|
22
|
+
*/
|
|
23
|
+
export declare function patchBetterSqlite3(dbConstructor: any, debug: boolean): void;
|
package/dist/db-observer.js
CHANGED
|
@@ -44,6 +44,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
})();
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
46
|
exports.patchPg = patchPg;
|
|
47
|
+
exports.patchMysql2 = patchMysql2;
|
|
48
|
+
exports.patchBetterSqlite3 = patchBetterSqlite3;
|
|
47
49
|
const fs = __importStar(require("fs"));
|
|
48
50
|
const path = __importStar(require("path"));
|
|
49
51
|
let queriesFile = null;
|
|
@@ -197,3 +199,94 @@ function patchPg(pgModule, debug) {
|
|
|
197
199
|
console.log('[trickle/db] PostgreSQL query tracing enabled');
|
|
198
200
|
}
|
|
199
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Patch mysql2 to capture queries.
|
|
204
|
+
*/
|
|
205
|
+
function patchMysql2(mysqlModule, debug) {
|
|
206
|
+
debugMode = debug;
|
|
207
|
+
// Patch Connection.prototype.query and .execute
|
|
208
|
+
const Connection = mysqlModule.Connection;
|
|
209
|
+
if (!Connection || !Connection.prototype)
|
|
210
|
+
return;
|
|
211
|
+
for (const method of ['query', 'execute']) {
|
|
212
|
+
const original = Connection.prototype[method];
|
|
213
|
+
if (!original || original.__trickle_patched)
|
|
214
|
+
continue;
|
|
215
|
+
Connection.prototype[method] = function patchedMethod(...args) {
|
|
216
|
+
const startTime = performance.now();
|
|
217
|
+
let queryText = typeof args[0] === 'string' ? args[0] : args[0]?.sql || '';
|
|
218
|
+
const truncated = queryText.length > MAX_QUERY_LENGTH ? queryText.substring(0, MAX_QUERY_LENGTH) + '...' : queryText;
|
|
219
|
+
const result = original.apply(this, args);
|
|
220
|
+
if (result && typeof result.then === 'function') {
|
|
221
|
+
return result.then((res) => {
|
|
222
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
223
|
+
const rows = Array.isArray(res) ? res[0] : res;
|
|
224
|
+
writeQuery({
|
|
225
|
+
kind: 'query', query: truncated, durationMs,
|
|
226
|
+
rowCount: Array.isArray(rows) ? rows.length : 0,
|
|
227
|
+
columns: Array.isArray(rows) && rows[0] ? Object.keys(rows[0]) : undefined,
|
|
228
|
+
timestamp: Date.now(),
|
|
229
|
+
});
|
|
230
|
+
return res;
|
|
231
|
+
}, (err) => {
|
|
232
|
+
writeQuery({
|
|
233
|
+
kind: 'query', query: truncated,
|
|
234
|
+
durationMs: Math.round((performance.now() - startTime) * 100) / 100,
|
|
235
|
+
rowCount: 0, error: err.message?.substring(0, 200), timestamp: Date.now(),
|
|
236
|
+
});
|
|
237
|
+
throw err;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return result;
|
|
241
|
+
};
|
|
242
|
+
Connection.prototype[method].__trickle_patched = true;
|
|
243
|
+
}
|
|
244
|
+
if (debug)
|
|
245
|
+
console.log('[trickle/db] MySQL query tracing enabled');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Patch better-sqlite3 to capture queries.
|
|
249
|
+
*/
|
|
250
|
+
function patchBetterSqlite3(dbConstructor, debug) {
|
|
251
|
+
debugMode = debug;
|
|
252
|
+
// better-sqlite3 returns a Database constructor — patch its prototype
|
|
253
|
+
const origPrepare = dbConstructor.prototype?.prepare;
|
|
254
|
+
if (!origPrepare || origPrepare.__trickle_patched)
|
|
255
|
+
return;
|
|
256
|
+
dbConstructor.prototype.prepare = function patchedPrepare(sql) {
|
|
257
|
+
const stmt = origPrepare.call(this, sql);
|
|
258
|
+
const truncated = sql.length > MAX_QUERY_LENGTH ? sql.substring(0, MAX_QUERY_LENGTH) + '...' : sql;
|
|
259
|
+
// Patch run, get, all methods on the statement
|
|
260
|
+
for (const method of ['run', 'get', 'all']) {
|
|
261
|
+
const origMethod = stmt[method];
|
|
262
|
+
if (!origMethod)
|
|
263
|
+
continue;
|
|
264
|
+
stmt[method] = function (...args) {
|
|
265
|
+
const startTime = performance.now();
|
|
266
|
+
try {
|
|
267
|
+
const result = origMethod.apply(this, args);
|
|
268
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
269
|
+
const rowCount = method === 'all' ? (Array.isArray(result) ? result.length : 0)
|
|
270
|
+
: method === 'get' ? (result ? 1 : 0)
|
|
271
|
+
: (result?.changes || 0);
|
|
272
|
+
writeQuery({
|
|
273
|
+
kind: 'query', query: truncated, durationMs, rowCount, timestamp: Date.now(),
|
|
274
|
+
});
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
writeQuery({
|
|
279
|
+
kind: 'query', query: truncated,
|
|
280
|
+
durationMs: Math.round((performance.now() - startTime) * 100) / 100,
|
|
281
|
+
rowCount: 0, error: err.message?.substring(0, 200), timestamp: Date.now(),
|
|
282
|
+
});
|
|
283
|
+
throw err;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return stmt;
|
|
288
|
+
};
|
|
289
|
+
dbConstructor.prototype.prepare.__trickle_patched = true;
|
|
290
|
+
if (debug)
|
|
291
|
+
console.log('[trickle/db] SQLite query tracing enabled');
|
|
292
|
+
}
|
package/dist/observe-register.js
CHANGED
|
@@ -1123,14 +1123,30 @@ if (enabled) {
|
|
|
1123
1123
|
}
|
|
1124
1124
|
catch { /* fall through to normal processing */ }
|
|
1125
1125
|
}
|
|
1126
|
-
// ──
|
|
1126
|
+
// ── Database auto-detection: patch database drivers to capture SQL queries ──
|
|
1127
1127
|
if (request === 'pg' && !expressPatched.has('pg')) {
|
|
1128
1128
|
expressPatched.add('pg');
|
|
1129
1129
|
try {
|
|
1130
1130
|
const { patchPg } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1131
1131
|
patchPg(exports, debug);
|
|
1132
1132
|
}
|
|
1133
|
-
catch { /*
|
|
1133
|
+
catch { /* not critical */ }
|
|
1134
|
+
}
|
|
1135
|
+
if ((request === 'mysql2' || request === 'mysql2/promise') && !expressPatched.has('mysql2')) {
|
|
1136
|
+
expressPatched.add('mysql2');
|
|
1137
|
+
try {
|
|
1138
|
+
const { patchMysql2 } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1139
|
+
patchMysql2(exports, debug);
|
|
1140
|
+
}
|
|
1141
|
+
catch { /* not critical */ }
|
|
1142
|
+
}
|
|
1143
|
+
if (request === 'better-sqlite3' && !expressPatched.has('better-sqlite3')) {
|
|
1144
|
+
expressPatched.add('better-sqlite3');
|
|
1145
|
+
try {
|
|
1146
|
+
const { patchBetterSqlite3 } = require(path_1.default.join(__dirname, 'db-observer.js'));
|
|
1147
|
+
patchBetterSqlite3(exports, debug);
|
|
1148
|
+
}
|
|
1149
|
+
catch { /* not critical */ }
|
|
1134
1150
|
}
|
|
1135
1151
|
// Resolve to absolute path for dedup — do this FIRST since bundlers like
|
|
1136
1152
|
// tsx/esbuild may use path aliases (e.g., @config/env) that don't start
|
package/package.json
CHANGED
package/src/db-observer.ts
CHANGED
|
@@ -183,3 +183,101 @@ export function patchPg(pgModule: any, debug: boolean): void {
|
|
|
183
183
|
console.log('[trickle/db] PostgreSQL query tracing enabled');
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Patch mysql2 to capture queries.
|
|
189
|
+
*/
|
|
190
|
+
export function patchMysql2(mysqlModule: any, debug: boolean): void {
|
|
191
|
+
debugMode = debug;
|
|
192
|
+
|
|
193
|
+
// Patch Connection.prototype.query and .execute
|
|
194
|
+
const Connection = mysqlModule.Connection;
|
|
195
|
+
if (!Connection || !Connection.prototype) return;
|
|
196
|
+
|
|
197
|
+
for (const method of ['query', 'execute'] as const) {
|
|
198
|
+
const original = Connection.prototype[method];
|
|
199
|
+
if (!original || (original as any).__trickle_patched) continue;
|
|
200
|
+
|
|
201
|
+
Connection.prototype[method] = function patchedMethod(...args: any[]): any {
|
|
202
|
+
const startTime = performance.now();
|
|
203
|
+
let queryText = typeof args[0] === 'string' ? args[0] : args[0]?.sql || '';
|
|
204
|
+
const truncated = queryText.length > MAX_QUERY_LENGTH ? queryText.substring(0, MAX_QUERY_LENGTH) + '...' : queryText;
|
|
205
|
+
|
|
206
|
+
const result = original.apply(this, args);
|
|
207
|
+
if (result && typeof result.then === 'function') {
|
|
208
|
+
return result.then(
|
|
209
|
+
(res: any) => {
|
|
210
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
211
|
+
const rows = Array.isArray(res) ? res[0] : res;
|
|
212
|
+
writeQuery({
|
|
213
|
+
kind: 'query', query: truncated, durationMs,
|
|
214
|
+
rowCount: Array.isArray(rows) ? rows.length : 0,
|
|
215
|
+
columns: Array.isArray(rows) && rows[0] ? Object.keys(rows[0]) : undefined,
|
|
216
|
+
timestamp: Date.now(),
|
|
217
|
+
});
|
|
218
|
+
return res;
|
|
219
|
+
},
|
|
220
|
+
(err: any) => {
|
|
221
|
+
writeQuery({
|
|
222
|
+
kind: 'query', query: truncated,
|
|
223
|
+
durationMs: Math.round((performance.now() - startTime) * 100) / 100,
|
|
224
|
+
rowCount: 0, error: err.message?.substring(0, 200), timestamp: Date.now(),
|
|
225
|
+
});
|
|
226
|
+
throw err;
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
};
|
|
232
|
+
(Connection.prototype[method] as any).__trickle_patched = true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (debug) console.log('[trickle/db] MySQL query tracing enabled');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Patch better-sqlite3 to capture queries.
|
|
240
|
+
*/
|
|
241
|
+
export function patchBetterSqlite3(dbConstructor: any, debug: boolean): void {
|
|
242
|
+
debugMode = debug;
|
|
243
|
+
|
|
244
|
+
// better-sqlite3 returns a Database constructor — patch its prototype
|
|
245
|
+
const origPrepare = dbConstructor.prototype?.prepare;
|
|
246
|
+
if (!origPrepare || (origPrepare as any).__trickle_patched) return;
|
|
247
|
+
|
|
248
|
+
dbConstructor.prototype.prepare = function patchedPrepare(sql: string): any {
|
|
249
|
+
const stmt = origPrepare.call(this, sql);
|
|
250
|
+
const truncated = sql.length > MAX_QUERY_LENGTH ? sql.substring(0, MAX_QUERY_LENGTH) + '...' : sql;
|
|
251
|
+
|
|
252
|
+
// Patch run, get, all methods on the statement
|
|
253
|
+
for (const method of ['run', 'get', 'all'] as const) {
|
|
254
|
+
const origMethod = stmt[method];
|
|
255
|
+
if (!origMethod) continue;
|
|
256
|
+
stmt[method] = function (...args: any[]): any {
|
|
257
|
+
const startTime = performance.now();
|
|
258
|
+
try {
|
|
259
|
+
const result = origMethod.apply(this, args);
|
|
260
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
261
|
+
const rowCount = method === 'all' ? (Array.isArray(result) ? result.length : 0)
|
|
262
|
+
: method === 'get' ? (result ? 1 : 0)
|
|
263
|
+
: (result?.changes || 0);
|
|
264
|
+
writeQuery({
|
|
265
|
+
kind: 'query', query: truncated, durationMs, rowCount, timestamp: Date.now(),
|
|
266
|
+
});
|
|
267
|
+
return result;
|
|
268
|
+
} catch (err: any) {
|
|
269
|
+
writeQuery({
|
|
270
|
+
kind: 'query', query: truncated,
|
|
271
|
+
durationMs: Math.round((performance.now() - startTime) * 100) / 100,
|
|
272
|
+
rowCount: 0, error: err.message?.substring(0, 200), timestamp: Date.now(),
|
|
273
|
+
});
|
|
274
|
+
throw err;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return stmt;
|
|
279
|
+
};
|
|
280
|
+
(dbConstructor.prototype.prepare as any).__trickle_patched = true;
|
|
281
|
+
|
|
282
|
+
if (debug) console.log('[trickle/db] SQLite query tracing enabled');
|
|
283
|
+
}
|
package/src/observe-register.ts
CHANGED
|
@@ -1113,13 +1113,27 @@ if (enabled) {
|
|
|
1113
1113
|
} catch { /* fall through to normal processing */ }
|
|
1114
1114
|
}
|
|
1115
1115
|
|
|
1116
|
-
// ──
|
|
1116
|
+
// ── Database auto-detection: patch database drivers to capture SQL queries ──
|
|
1117
1117
|
if (request === 'pg' && !expressPatched.has('pg')) {
|
|
1118
1118
|
expressPatched.add('pg');
|
|
1119
1119
|
try {
|
|
1120
1120
|
const { patchPg } = require(path.join(__dirname, 'db-observer.js'));
|
|
1121
1121
|
patchPg(exports, debug);
|
|
1122
|
-
} catch { /*
|
|
1122
|
+
} catch { /* not critical */ }
|
|
1123
|
+
}
|
|
1124
|
+
if ((request === 'mysql2' || request === 'mysql2/promise') && !expressPatched.has('mysql2')) {
|
|
1125
|
+
expressPatched.add('mysql2');
|
|
1126
|
+
try {
|
|
1127
|
+
const { patchMysql2 } = require(path.join(__dirname, 'db-observer.js'));
|
|
1128
|
+
patchMysql2(exports, debug);
|
|
1129
|
+
} catch { /* not critical */ }
|
|
1130
|
+
}
|
|
1131
|
+
if (request === 'better-sqlite3' && !expressPatched.has('better-sqlite3')) {
|
|
1132
|
+
expressPatched.add('better-sqlite3');
|
|
1133
|
+
try {
|
|
1134
|
+
const { patchBetterSqlite3 } = require(path.join(__dirname, 'db-observer.js'));
|
|
1135
|
+
patchBetterSqlite3(exports, debug);
|
|
1136
|
+
} catch { /* not critical */ }
|
|
1123
1137
|
}
|
|
1124
1138
|
|
|
1125
1139
|
// Resolve to absolute path for dedup — do this FIRST since bundlers like
|