@dudousxd/nestjs-telescope-mikro-orm 1.0.0
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/CHANGELOG.md +110 -0
- package/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/mikro-orm-query.watcher.d.ts +43 -0
- package/dist/mikro-orm-query.watcher.d.ts.map +1 -0
- package/dist/mikro-orm-query.watcher.js +62 -0
- package/dist/mikro-orm-query.watcher.js.map +1 -0
- package/dist/mikro-orm-storage.provider.d.ts +51 -0
- package/dist/mikro-orm-storage.provider.d.ts.map +1 -0
- package/dist/mikro-orm-storage.provider.js +348 -0
- package/dist/mikro-orm-storage.provider.js.map +1 -0
- package/dist/n-plus-one.d.ts +9 -0
- package/dist/n-plus-one.d.ts.map +1 -0
- package/dist/n-plus-one.js +28 -0
- package/dist/n-plus-one.js.map +1 -0
- package/dist/query-family-hash.d.ts +3 -0
- package/dist/query-family-hash.d.ts.map +1 -0
- package/dist/query-family-hash.js +26 -0
- package/dist/query-family-hash.js.map +1 -0
- package/dist/telescope-entry.entity.d.ts +19 -0
- package/dist/telescope-entry.entity.d.ts.map +1 -0
- package/dist/telescope-entry.entity.js +37 -0
- package/dist/telescope-entry.entity.js.map +1 -0
- package/dist/telescope-mikro-orm.logger.d.ts +28 -0
- package/dist/telescope-mikro-orm.logger.d.ts.map +1 -0
- package/dist/telescope-mikro-orm.logger.js +49 -0
- package/dist/telescope-mikro-orm.logger.js.map +1 -0
- package/dist/telescope-rollup.entity.d.ts +12 -0
- package/dist/telescope-rollup.entity.d.ts.map +1 -0
- package/dist/telescope-rollup.entity.js +37 -0
- package/dist/telescope-rollup.entity.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { decodeCursor, encodeCursor, isBatchOrigin, mergeHistograms, normalizeHistogram, } from '@dudousxd/nestjs-telescope';
|
|
2
|
+
import { MikroORM, raw } from '@mikro-orm/core';
|
|
3
|
+
import { TelescopeEntry } from './telescope-entry.entity.js';
|
|
4
|
+
import { TelescopeRollup } from './telescope-rollup.entity.js';
|
|
5
|
+
const DEFAULT_LIMIT = 50;
|
|
6
|
+
/**
|
|
7
|
+
* Every persisted column EXCEPT `content`, used as the MikroORM `fields`
|
|
8
|
+
* projection for content-less aggregate scans (omitContent). Selecting these
|
|
9
|
+
* keeps the heavy `content` JSON blob out of the result set entirely — the main
|
|
10
|
+
* perf win on MySQL where pulse/timeseries otherwise read every content blob.
|
|
11
|
+
*/
|
|
12
|
+
const CONTENTLESS_FIELDS = [
|
|
13
|
+
'id',
|
|
14
|
+
'batchId',
|
|
15
|
+
'type',
|
|
16
|
+
'familyHash',
|
|
17
|
+
'tags',
|
|
18
|
+
'tagsText',
|
|
19
|
+
'sequence',
|
|
20
|
+
'durationMs',
|
|
21
|
+
'origin',
|
|
22
|
+
'instanceId',
|
|
23
|
+
'traceId',
|
|
24
|
+
'spanId',
|
|
25
|
+
'createdAt',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* The owned ORM runs under a dedicated MikroORM `contextName` so it is isolated
|
|
29
|
+
* from the HOST app's `RequestContext`. Without this, a host that wraps requests
|
|
30
|
+
* in its own MikroORM context (e.g. `@mikro-orm/nestjs`'s `MikroOrmMiddleware`,
|
|
31
|
+
* which runs on every route) would make our `em.find()` resolve against the
|
|
32
|
+
* HOST's EntityManager — the host ORM doesn't know `TelescopeEntry`, so MikroORM
|
|
33
|
+
* crashes looking up its (missing) metadata. A unique context name means our em
|
|
34
|
+
* never collides with the host's entry in the shared `RequestContext` map.
|
|
35
|
+
*/
|
|
36
|
+
const TELESCOPE_CONTEXT_NAME = 'nestjs-telescope';
|
|
37
|
+
function padTags(tags) {
|
|
38
|
+
return tags.length ? ` ${tags.join(' ')} ` : ' ';
|
|
39
|
+
}
|
|
40
|
+
function rowToEntry(row, omitContent = false) {
|
|
41
|
+
return {
|
|
42
|
+
id: row.id,
|
|
43
|
+
batchId: row.batchId,
|
|
44
|
+
type: row.type,
|
|
45
|
+
familyHash: row.familyHash,
|
|
46
|
+
content: omitContent ? null : (row.content ?? null),
|
|
47
|
+
tags: row.tags,
|
|
48
|
+
sequence: row.sequence,
|
|
49
|
+
durationMs: row.durationMs,
|
|
50
|
+
origin: isBatchOrigin(row.origin) ? row.origin : 'manual',
|
|
51
|
+
instanceId: row.instanceId,
|
|
52
|
+
traceId: row.traceId ?? null,
|
|
53
|
+
spanId: row.spanId ?? null,
|
|
54
|
+
createdAt: row.createdAt,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function entryToRowData(entry) {
|
|
58
|
+
return { ...entry, tagsText: padTags(entry.tags) };
|
|
59
|
+
}
|
|
60
|
+
function resolveLimit(limit) {
|
|
61
|
+
return typeof limit === 'number' && Number.isInteger(limit) && limit > 0 ? limit : DEFAULT_LIMIT;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Connection-relevant keys copied from a host MikroORM's resolved config when
|
|
65
|
+
* deriving the owned ORM's connection. Deliberately EXCLUDES entities,
|
|
66
|
+
* migrations, seeder, subscribers, extensions, and discovery so the owned ORM
|
|
67
|
+
* sees only `TelescopeEntry` and never inherits the host's metadata or wiring.
|
|
68
|
+
*/
|
|
69
|
+
function pickHostConnection(hostConfig) {
|
|
70
|
+
const connection = {};
|
|
71
|
+
if (hostConfig.driver !== undefined)
|
|
72
|
+
connection.driver = hostConfig.driver;
|
|
73
|
+
if (hostConfig.dbName !== undefined)
|
|
74
|
+
connection.dbName = hostConfig.dbName;
|
|
75
|
+
if (hostConfig.clientUrl !== undefined)
|
|
76
|
+
connection.clientUrl = hostConfig.clientUrl;
|
|
77
|
+
if (hostConfig.host !== undefined)
|
|
78
|
+
connection.host = hostConfig.host;
|
|
79
|
+
if (hostConfig.port !== undefined)
|
|
80
|
+
connection.port = hostConfig.port;
|
|
81
|
+
if (hostConfig.user !== undefined)
|
|
82
|
+
connection.user = hostConfig.user;
|
|
83
|
+
if (hostConfig.password !== undefined)
|
|
84
|
+
connection.password = hostConfig.password;
|
|
85
|
+
if (hostConfig.schema !== undefined)
|
|
86
|
+
connection.schema = hostConfig.schema;
|
|
87
|
+
if (hostConfig.driverOptions !== undefined)
|
|
88
|
+
connection.driverOptions = hostConfig.driverOptions;
|
|
89
|
+
if (hostConfig.pool !== undefined)
|
|
90
|
+
connection.pool = hostConfig.pool;
|
|
91
|
+
if (hostConfig.charset !== undefined)
|
|
92
|
+
connection.charset = hostConfig.charset;
|
|
93
|
+
if (hostConfig.collate !== undefined)
|
|
94
|
+
connection.collate = hostConfig.collate;
|
|
95
|
+
if (hostConfig.forceUtcTimezone !== undefined) {
|
|
96
|
+
connection.forceUtcTimezone = hostConfig.forceUtcTimezone;
|
|
97
|
+
}
|
|
98
|
+
if (hostConfig.timezone !== undefined)
|
|
99
|
+
connection.timezone = hostConfig.timezone;
|
|
100
|
+
return connection;
|
|
101
|
+
}
|
|
102
|
+
export class MikroOrmStorageProvider {
|
|
103
|
+
source;
|
|
104
|
+
ensureSchema;
|
|
105
|
+
orm = null;
|
|
106
|
+
constructor(source, opts) {
|
|
107
|
+
this.source = source;
|
|
108
|
+
if (opts?.ensureSchema !== undefined) {
|
|
109
|
+
this.ensureSchema = opts.ensureSchema;
|
|
110
|
+
}
|
|
111
|
+
else if (!(source instanceof MikroORM) && source.ensureSchema !== undefined) {
|
|
112
|
+
this.ensureSchema = source.ensureSchema;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
this.ensureSchema = true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
buildOrmOptions() {
|
|
119
|
+
if (this.source instanceof MikroORM) {
|
|
120
|
+
const connection = pickHostConnection(this.source.config.getAll());
|
|
121
|
+
return {
|
|
122
|
+
...connection,
|
|
123
|
+
entities: [TelescopeEntry, TelescopeRollup],
|
|
124
|
+
contextName: TELESCOPE_CONTEXT_NAME,
|
|
125
|
+
allowGlobalContext: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const { ensureSchema: _ensureSchema, entities: _entities, ...rest } = this.source;
|
|
129
|
+
return {
|
|
130
|
+
...rest,
|
|
131
|
+
entities: [TelescopeEntry, TelescopeRollup],
|
|
132
|
+
contextName: TELESCOPE_CONTEXT_NAME,
|
|
133
|
+
allowGlobalContext: true,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async init() {
|
|
137
|
+
if (this.orm)
|
|
138
|
+
return;
|
|
139
|
+
this.orm = await MikroORM.init(this.buildOrmOptions());
|
|
140
|
+
if (this.ensureSchema) {
|
|
141
|
+
await this.orm.schema.ensureDatabase();
|
|
142
|
+
// `{ safe: true }` disables BOTH table and column dropping, so the update
|
|
143
|
+
// is additive-only: it creates the table and adds missing columns/indexes
|
|
144
|
+
// but never drops anything that already exists.
|
|
145
|
+
await this.orm.schema.update({ safe: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async close() {
|
|
149
|
+
if (this.orm) {
|
|
150
|
+
await this.orm.close(true);
|
|
151
|
+
this.orm = null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
forkEm() {
|
|
155
|
+
if (!this.orm) {
|
|
156
|
+
throw new Error('MikroOrmStorageProvider: init() must run before use');
|
|
157
|
+
}
|
|
158
|
+
return this.orm.em.fork();
|
|
159
|
+
}
|
|
160
|
+
async store(entries) {
|
|
161
|
+
if (entries.length === 0)
|
|
162
|
+
return;
|
|
163
|
+
const em = this.forkEm();
|
|
164
|
+
for (const entry of entries) {
|
|
165
|
+
em.create(TelescopeEntry, entryToRowData(entry));
|
|
166
|
+
}
|
|
167
|
+
await em.flush();
|
|
168
|
+
}
|
|
169
|
+
async update(id, patch) {
|
|
170
|
+
const em = this.forkEm();
|
|
171
|
+
const row = await em.findOne(TelescopeEntry, { id });
|
|
172
|
+
if (!row)
|
|
173
|
+
return;
|
|
174
|
+
const { id: _ignoredId, ...rest } = patch;
|
|
175
|
+
const patchData = { ...rest };
|
|
176
|
+
if (patch.tags !== undefined) {
|
|
177
|
+
patchData.tagsText = padTags(patch.tags);
|
|
178
|
+
}
|
|
179
|
+
em.assign(row, patchData);
|
|
180
|
+
await em.flush();
|
|
181
|
+
}
|
|
182
|
+
async find(id) {
|
|
183
|
+
const em = this.forkEm();
|
|
184
|
+
const row = await em.findOne(TelescopeEntry, { id });
|
|
185
|
+
if (!row)
|
|
186
|
+
return null;
|
|
187
|
+
const batch = await this.batch(row.batchId);
|
|
188
|
+
return { ...rowToEntry(row), batch };
|
|
189
|
+
}
|
|
190
|
+
async get(query) {
|
|
191
|
+
const em = this.forkEm();
|
|
192
|
+
const where = {};
|
|
193
|
+
if (query.ids !== undefined) {
|
|
194
|
+
// Empty id set ⇒ no rows (an `id IN ()` would match nothing anyway, but we
|
|
195
|
+
// skip the round-trip entirely).
|
|
196
|
+
if (query.ids.length === 0) {
|
|
197
|
+
return { data: [], nextCursor: null };
|
|
198
|
+
}
|
|
199
|
+
where.id = { $in: query.ids };
|
|
200
|
+
}
|
|
201
|
+
if (query.type)
|
|
202
|
+
where.type = query.type;
|
|
203
|
+
if (query.familyHash)
|
|
204
|
+
where.familyHash = query.familyHash;
|
|
205
|
+
if (query.batchId)
|
|
206
|
+
where.batchId = query.batchId;
|
|
207
|
+
if (query.traceId !== undefined)
|
|
208
|
+
where.traceId = query.traceId;
|
|
209
|
+
if (query.tag)
|
|
210
|
+
where.tagsText = { $like: `% ${query.tag} %` };
|
|
211
|
+
if (query.before || query.after) {
|
|
212
|
+
where.createdAt = {
|
|
213
|
+
...(query.after && { $gt: query.after }),
|
|
214
|
+
...(query.before && { $lt: query.before }),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const cursor = query.cursor ? decodeCursor(query.cursor) : null;
|
|
218
|
+
if (cursor) {
|
|
219
|
+
const cursorDate = new Date(cursor.createdAt);
|
|
220
|
+
where.$or = [
|
|
221
|
+
{ createdAt: { $lt: cursorDate } },
|
|
222
|
+
{ createdAt: cursorDate, id: { $lt: cursor.id } },
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
// Free-text scan over the stored content JSON, ANDed with every other filter
|
|
226
|
+
// (and the cursor keyset). `content` is a JSON property, so a plain
|
|
227
|
+
// `{ content: { $like } }` would JSON-encode the operand to `'"%term%"'` and
|
|
228
|
+
// never match; `raw('content')` references the underlying text column directly,
|
|
229
|
+
// yielding `content LIKE '%term%'`. The LIKE runs in SQL over the column, so it
|
|
230
|
+
// is independent of the omitContent field projection. Wrapped in `$and` so it
|
|
231
|
+
// composes with the cursor's own `$or` clause without clobbering it.
|
|
232
|
+
const finalWhere = query.search !== undefined
|
|
233
|
+
? { $and: [where, { [raw('content')]: { $like: `%${query.search}%` } }] }
|
|
234
|
+
: where;
|
|
235
|
+
const limit = resolveLimit(query.limit);
|
|
236
|
+
const rows = await em.find(TelescopeEntry, finalWhere, {
|
|
237
|
+
orderBy: { createdAt: 'desc', id: 'desc' },
|
|
238
|
+
limit: limit + 1,
|
|
239
|
+
// omitContent: project every column except the heavy content blob so the
|
|
240
|
+
// driver never SELECTs/materializes it.
|
|
241
|
+
...(query.omitContent ? { fields: [...CONTENTLESS_FIELDS] } : {}),
|
|
242
|
+
});
|
|
243
|
+
const hasMore = rows.length > limit;
|
|
244
|
+
const slice = rows.slice(0, limit);
|
|
245
|
+
const last = slice.at(-1);
|
|
246
|
+
const nextCursor = hasMore && last ? encodeCursor(last.createdAt.getTime(), last.id) : null;
|
|
247
|
+
return { data: slice.map((row) => rowToEntry(row, query.omitContent === true)), nextCursor };
|
|
248
|
+
}
|
|
249
|
+
async batch(batchId) {
|
|
250
|
+
const em = this.forkEm();
|
|
251
|
+
const rows = await em.find(TelescopeEntry, { batchId }, { orderBy: { sequence: 'asc' } });
|
|
252
|
+
return rows.map((row) => rowToEntry(row));
|
|
253
|
+
}
|
|
254
|
+
async tags(prefix) {
|
|
255
|
+
const em = this.forkEm();
|
|
256
|
+
const rows = await em.find(TelescopeEntry, {}, { orderBy: { createdAt: 'desc', id: 'desc' }, limit: 10000, fields: ['tags'] });
|
|
257
|
+
const counts = new Map();
|
|
258
|
+
for (const row of rows) {
|
|
259
|
+
for (const tag of row.tags) {
|
|
260
|
+
if (prefix && !tag.startsWith(prefix))
|
|
261
|
+
continue;
|
|
262
|
+
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return [...counts.entries()].map(([tag, count]) => ({ tag, count }));
|
|
266
|
+
}
|
|
267
|
+
async prune(olderThan, keepLast) {
|
|
268
|
+
const em = this.forkEm();
|
|
269
|
+
if (keepLast === undefined) {
|
|
270
|
+
return em.nativeDelete(TelescopeEntry, { createdAt: { $lt: olderThan } });
|
|
271
|
+
}
|
|
272
|
+
const older = await em.find(TelescopeEntry, { createdAt: { $lt: olderThan } }, { orderBy: { createdAt: 'desc', id: 'desc' }, fields: ['id'] });
|
|
273
|
+
const toDelete = older.slice(keepLast).map((row) => row.id);
|
|
274
|
+
if (toDelete.length === 0)
|
|
275
|
+
return 0;
|
|
276
|
+
return em.nativeDelete(TelescopeEntry, { id: { $in: toDelete } });
|
|
277
|
+
}
|
|
278
|
+
async clear() {
|
|
279
|
+
const em = this.forkEm();
|
|
280
|
+
await em.nativeDelete(TelescopeEntry, {});
|
|
281
|
+
await em.nativeDelete(TelescopeRollup, {});
|
|
282
|
+
}
|
|
283
|
+
// ── RollupStore SPI ────────────────────────────────────────────────────────
|
|
284
|
+
/**
|
|
285
|
+
* Additively merge deltas into `telescope_rollups`, matching the sqlite
|
|
286
|
+
* provider's `on conflict do update set count = count + excluded.count, ...`
|
|
287
|
+
* semantics: accumulate count/sum and keep a running max per
|
|
288
|
+
* (metric, bucketStart).
|
|
289
|
+
*
|
|
290
|
+
* Implemented as a read-modify-write inside ONE transaction rather than a
|
|
291
|
+
* native upsert. MikroORM's `em.upsert` / `qb.onConflict().merge()` REPLACE
|
|
292
|
+
* conflicting columns; neither expresses a portable `count = count +
|
|
293
|
+
* excluded.count` self-referential increment across MySQL and SQLite without
|
|
294
|
+
* driver-specific raw SQL. The transactional read-modify-write is portable,
|
|
295
|
+
* cast-free, and — because the whole batch commits atomically with row locks
|
|
296
|
+
* on the touched PKs — safe to call repeatedly and concurrently: concurrent
|
|
297
|
+
* writers serialize on the same (metric, bucketStart) rows, so no increment is
|
|
298
|
+
* lost. New buckets are inserted; existing ones accumulate.
|
|
299
|
+
*/
|
|
300
|
+
async recordRollups(deltas) {
|
|
301
|
+
if (deltas.length === 0)
|
|
302
|
+
return;
|
|
303
|
+
const em = this.forkEm();
|
|
304
|
+
await em.transactional(async (tx) => {
|
|
305
|
+
for (const delta of deltas) {
|
|
306
|
+
const existing = await tx.findOne(TelescopeRollup, {
|
|
307
|
+
metric: delta.metric,
|
|
308
|
+
bucketStart: delta.bucketStart,
|
|
309
|
+
});
|
|
310
|
+
if (existing) {
|
|
311
|
+
existing.count += delta.count;
|
|
312
|
+
existing.sum += delta.sum;
|
|
313
|
+
existing.max = Math.max(existing.max, delta.max);
|
|
314
|
+
// Element-wise histogram merge; legacy null reads back as zeros.
|
|
315
|
+
existing.histogram = mergeHistograms(normalizeHistogram(existing.histogram), delta.histogram);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
tx.create(TelescopeRollup, {
|
|
319
|
+
metric: delta.metric,
|
|
320
|
+
bucketStart: delta.bucketStart,
|
|
321
|
+
count: delta.count,
|
|
322
|
+
sum: delta.sum,
|
|
323
|
+
max: delta.max,
|
|
324
|
+
histogram: normalizeHistogram(delta.histogram),
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
async queryRollups(metrics, fromBucket, toBucket) {
|
|
331
|
+
if (metrics.length === 0)
|
|
332
|
+
return [];
|
|
333
|
+
const em = this.forkEm();
|
|
334
|
+
const rows = await em.find(TelescopeRollup, {
|
|
335
|
+
metric: { $in: metrics },
|
|
336
|
+
bucketStart: { $gte: fromBucket, $lte: toBucket },
|
|
337
|
+
});
|
|
338
|
+
return rows.map((row) => ({
|
|
339
|
+
metric: row.metric,
|
|
340
|
+
bucketStart: row.bucketStart,
|
|
341
|
+
count: row.count,
|
|
342
|
+
sum: row.sum,
|
|
343
|
+
max: row.max,
|
|
344
|
+
histogram: normalizeHistogram(row.histogram),
|
|
345
|
+
}));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=mikro-orm-storage.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mikro-orm-storage.provider.js","sourceRoot":"","sources":["../src/mikro-orm-storage.provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Entry } from '@dudousxd/nestjs-telescope';
|
|
2
|
+
export interface NPlusOneInsight {
|
|
3
|
+
familyHash: string;
|
|
4
|
+
count: number;
|
|
5
|
+
sql: string;
|
|
6
|
+
}
|
|
7
|
+
/** Group query entries by familyHash; report templates run >= threshold times. */
|
|
8
|
+
export declare function detectNPlusOne(entries: Entry[], threshold: number): NPlusOneInsight[];
|
|
9
|
+
//# sourceMappingURL=n-plus-one.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n-plus-one.d.ts","sourceRoot":"","sources":["../src/n-plus-one.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,4BAA4B,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CAsBrF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// packages/mikro-orm/src/n-plus-one.ts
|
|
2
|
+
import { EntryType } from '@dudousxd/nestjs-telescope';
|
|
3
|
+
/** Group query entries by familyHash; report templates run >= threshold times. */
|
|
4
|
+
export function detectNPlusOne(entries, threshold) {
|
|
5
|
+
const groups = new Map();
|
|
6
|
+
for (const entry of entries) {
|
|
7
|
+
if (entry.type !== EntryType.Query || entry.familyHash === null)
|
|
8
|
+
continue;
|
|
9
|
+
const sql = typeof entry.content.sql === 'string'
|
|
10
|
+
? entry.content.sql
|
|
11
|
+
: '';
|
|
12
|
+
const existing = groups.get(entry.familyHash);
|
|
13
|
+
if (existing) {
|
|
14
|
+
existing.count += 1;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
groups.set(entry.familyHash, { count: 1, sql });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const insights = [];
|
|
21
|
+
for (const [familyHash, group] of groups) {
|
|
22
|
+
if (group.count >= threshold) {
|
|
23
|
+
insights.push({ familyHash, count: group.count, sql: group.sql });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return insights;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=n-plus-one.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n-plus-one.js","sourceRoot":"","sources":["../src/n-plus-one.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,EAAc,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAQnE,kFAAkF;AAClF,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,SAAiB;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0C,CAAC;IACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;YAAE,SAAS;QAC1E,MAAM,GAAG,GACP,OAAQ,KAAK,CAAC,OAA6B,CAAC,GAAG,KAAK,QAAQ;YAC1D,CAAC,CAAE,KAAK,CAAC,OAA2B,CAAC,GAAG;YACxC,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACzC,IAAI,KAAK,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-family-hash.d.ts","sourceRoot":"","sources":["../src/query-family-hash.ts"],"names":[],"mappings":"AAwBA,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// packages/mikro-orm/src/query-family-hash.ts
|
|
2
|
+
/** Normalize an SQL string into a template: lowercase, collapse whitespace,
|
|
3
|
+
* replace string/number literals and bound-param placeholders with '?'. */
|
|
4
|
+
function normalizeSql(sql) {
|
|
5
|
+
return sql
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/'(?:[^']|'')*'/g, '?') // string literals
|
|
8
|
+
.replace(/\b\d+(?:\.\d+)?\b/g, '?') // numeric literals
|
|
9
|
+
.replace(/\?/g, '?') // already-parameterized (no-op, keeps idempotency explicit)
|
|
10
|
+
.replace(/\s+/g, ' ')
|
|
11
|
+
.trim();
|
|
12
|
+
}
|
|
13
|
+
/** Stable 32-bit FNV-1a hash, hex-encoded. Deterministic across processes. */
|
|
14
|
+
function fnv1a(input) {
|
|
15
|
+
let hash = 0x811c9dc5;
|
|
16
|
+
for (let i = 0; i < input.length; i++) {
|
|
17
|
+
hash ^= input.charCodeAt(i);
|
|
18
|
+
hash = Math.imul(hash, 0x01000193);
|
|
19
|
+
}
|
|
20
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
21
|
+
}
|
|
22
|
+
/** Grouping key for "the same query" — same template, any bound values. */
|
|
23
|
+
export function queryFamilyHash(sql) {
|
|
24
|
+
return fnv1a(normalizeSql(sql));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=query-family-hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-family-hash.js","sourceRoot":"","sources":["../src/query-family-hash.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C;4EAC4E;AAC5E,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG;SACP,WAAW,EAAE;SACb,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,kBAAkB;SAClD,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC,mBAAmB;SACtD,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,4DAA4D;SAChF,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,SAAS,KAAK,CAAC,KAAa;IAC1B,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACpD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { EntitySchema } from '@mikro-orm/core';
|
|
2
|
+
export interface TelescopeEntryRow {
|
|
3
|
+
id: string;
|
|
4
|
+
batchId: string;
|
|
5
|
+
type: string;
|
|
6
|
+
familyHash: string | null;
|
|
7
|
+
content: unknown;
|
|
8
|
+
tags: string[];
|
|
9
|
+
tagsText: string;
|
|
10
|
+
sequence: number;
|
|
11
|
+
durationMs: number | null;
|
|
12
|
+
origin: string;
|
|
13
|
+
instanceId: string;
|
|
14
|
+
traceId: string | null;
|
|
15
|
+
spanId: string | null;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
}
|
|
18
|
+
export declare const TelescopeEntry: EntitySchema<TelescopeEntryRow, never, import("@mikro-orm/core").EntityCtor<TelescopeEntryRow>>;
|
|
19
|
+
//# sourceMappingURL=telescope-entry.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-entry.entity.d.ts","sourceRoot":"","sources":["../src/telescope-entry.entity.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,eAAO,MAAM,cAAc,iGAuBzB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// packages/mikro-orm/src/telescope-entry.entity.ts
|
|
2
|
+
//
|
|
3
|
+
// Telescope entry persistence schema, defined via EntitySchema (NOT decorators)
|
|
4
|
+
// so the host does not need emitDecoratorMetadata. The host registers
|
|
5
|
+
// `TelescopeEntry` in their MikroORM `entities` array.
|
|
6
|
+
//
|
|
7
|
+
// `tagsText` is a space-padded join of the entry's tags (` tag1 tag2 `) used as a
|
|
8
|
+
// cross-driver tag filter index: JSON-array querying is not portable across
|
|
9
|
+
// MySQL and SQLite, so we filter on `tagsText` with `LIKE '% <tag> %'`. The
|
|
10
|
+
// `tags` JSON column remains the source of truth for retrieval; `tagsText` is
|
|
11
|
+
// only an index for filtering.
|
|
12
|
+
import { EntitySchema } from '@mikro-orm/core';
|
|
13
|
+
export const TelescopeEntry = new EntitySchema({
|
|
14
|
+
name: 'TelescopeEntry',
|
|
15
|
+
tableName: 'telescope_entries',
|
|
16
|
+
properties: {
|
|
17
|
+
id: { type: 'string', primary: true, length: 64 },
|
|
18
|
+
batchId: { type: 'string', length: 64, index: true },
|
|
19
|
+
type: { type: 'string', length: 32, index: true },
|
|
20
|
+
familyHash: { type: 'string', nullable: true, length: 64, index: true },
|
|
21
|
+
content: { type: 'json' },
|
|
22
|
+
tags: { type: 'json' },
|
|
23
|
+
tagsText: { type: 'text' },
|
|
24
|
+
sequence: { type: 'integer' },
|
|
25
|
+
durationMs: { type: 'integer', nullable: true },
|
|
26
|
+
origin: { type: 'string', length: 16 },
|
|
27
|
+
instanceId: { type: 'string', length: 128 },
|
|
28
|
+
// Indexed: the #/traces/:id view filters entries by traceId. Without this,
|
|
29
|
+
// that lookup is a full table scan (every other filterable column — type,
|
|
30
|
+
// batchId, familyHash, createdAt — is already indexed). `schema.update`
|
|
31
|
+
// adds the index additively on the next boot (no migration).
|
|
32
|
+
traceId: { type: 'string', nullable: true, length: 32, index: true },
|
|
33
|
+
spanId: { type: 'string', nullable: true, length: 16 },
|
|
34
|
+
createdAt: { type: 'datetime', index: true },
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=telescope-entry.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-entry.entity.js","sourceRoot":"","sources":["../src/telescope-entry.entity.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,gFAAgF;AAChF,sEAAsE;AACtE,uDAAuD;AACvD,EAAE;AACF,kFAAkF;AAClF,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAC9E,+BAA+B;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmB/C,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,YAAY,CAAoB;IAChE,IAAI,EAAE,gBAAgB;IACtB,SAAS,EAAE,mBAAmB;IAC9B,UAAU,EAAE;QACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QACjD,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACpD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACjD,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACvE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzB,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACtB,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QAC1B,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QAC7B,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC/C,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;QACtC,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE;QAC3C,2EAA2E;QAC3E,0EAA0E;QAC1E,wEAAwE;QACxE,6DAA6D;QAC7D,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACpE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QACtD,SAAS,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE;KAC7C;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type RecordInput } from '@dudousxd/nestjs-telescope';
|
|
2
|
+
import { DefaultLogger, type LogContext, type LoggerOptions } from '@mikro-orm/core';
|
|
3
|
+
export interface TelescopeLoggerOptions {
|
|
4
|
+
/** Queries at/above this many ms get a 'slow' tag. Default 100. */
|
|
5
|
+
slowMs?: number;
|
|
6
|
+
}
|
|
7
|
+
/** A MikroORM DefaultLogger subclass that tees every executed query into
|
|
8
|
+
* `record`, while preserving MikroORM's own logging behavior via super. */
|
|
9
|
+
export declare class TelescopeMikroOrmLogger extends DefaultLogger {
|
|
10
|
+
private readonly _record;
|
|
11
|
+
private readonly slowMs;
|
|
12
|
+
constructor(options: LoggerOptions, _record: (input: RecordInput) => void, slowMs: number);
|
|
13
|
+
logQuery(context: {
|
|
14
|
+
query: string;
|
|
15
|
+
} & LogContext): void;
|
|
16
|
+
}
|
|
17
|
+
/** Builds a `loggerFactory`-compatible function that records every query.
|
|
18
|
+
*
|
|
19
|
+
* Host-wired usage:
|
|
20
|
+
* ```ts
|
|
21
|
+
* MikroORM.init({
|
|
22
|
+
* debug: ['query'],
|
|
23
|
+
* loggerFactory: telescopeMikroOrmLogger(record, { slowMs: 100 }),
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function telescopeMikroOrmLogger(record: (input: RecordInput) => void, options?: TelescopeLoggerOptions): (loggerOptions: LoggerOptions) => TelescopeMikroOrmLogger;
|
|
28
|
+
//# sourceMappingURL=telescope-mikro-orm.logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-mikro-orm.logger.d.ts","sourceRoot":"","sources":["../src/telescope-mikro-orm.logger.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,WAAW,EAAmB,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErF,MAAM,WAAW,sBAAsB;IACrC,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;4EAC4E;AAC5E,qBAAa,uBAAwB,SAAQ,aAAa;IAGtD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFvB,OAAO,EAAE,aAAa,EACL,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,EACrC,MAAM,EAAE,MAAM;IAMxB,QAAQ,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,GAAG,IAAI;CAmBjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,EACpC,OAAO,GAAE,sBAA2B,GACnC,CAAC,aAAa,EAAE,aAAa,KAAK,uBAAuB,CAI3D"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// packages/mikro-orm/src/telescope-mikro-orm.logger.ts
|
|
2
|
+
import { EntryType, queryFamilyHash } from '@dudousxd/nestjs-telescope';
|
|
3
|
+
import { DefaultLogger } from '@mikro-orm/core';
|
|
4
|
+
/** A MikroORM DefaultLogger subclass that tees every executed query into
|
|
5
|
+
* `record`, while preserving MikroORM's own logging behavior via super. */
|
|
6
|
+
export class TelescopeMikroOrmLogger extends DefaultLogger {
|
|
7
|
+
_record;
|
|
8
|
+
slowMs;
|
|
9
|
+
constructor(options, _record, slowMs) {
|
|
10
|
+
super(options);
|
|
11
|
+
this._record = _record;
|
|
12
|
+
this.slowMs = slowMs;
|
|
13
|
+
}
|
|
14
|
+
// Matches DefaultLogger.logQuery's concrete signature exactly.
|
|
15
|
+
logQuery(context) {
|
|
16
|
+
const sql = context.query;
|
|
17
|
+
if (sql) {
|
|
18
|
+
const took = typeof context.took === 'number' ? context.took : null;
|
|
19
|
+
const tags = took !== null && took >= this.slowMs ? ['slow'] : [];
|
|
20
|
+
this._record({
|
|
21
|
+
type: EntryType.Query,
|
|
22
|
+
content: {
|
|
23
|
+
sql,
|
|
24
|
+
bindings: Array.isArray(context.params) ? [...context.params] : [],
|
|
25
|
+
took,
|
|
26
|
+
},
|
|
27
|
+
familyHash: queryFamilyHash(sql),
|
|
28
|
+
durationMs: took,
|
|
29
|
+
...(tags.length > 0 ? { tags } : {}),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
super.logQuery(context);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Builds a `loggerFactory`-compatible function that records every query.
|
|
36
|
+
*
|
|
37
|
+
* Host-wired usage:
|
|
38
|
+
* ```ts
|
|
39
|
+
* MikroORM.init({
|
|
40
|
+
* debug: ['query'],
|
|
41
|
+
* loggerFactory: telescopeMikroOrmLogger(record, { slowMs: 100 }),
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function telescopeMikroOrmLogger(record, options = {}) {
|
|
46
|
+
const slowMs = options.slowMs ?? 100;
|
|
47
|
+
return (loggerOptions) => new TelescopeMikroOrmLogger(loggerOptions, record, slowMs);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=telescope-mikro-orm.logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-mikro-orm.logger.js","sourceRoot":"","sources":["../src/telescope-mikro-orm.logger.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,SAAS,EAAoB,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAuC,MAAM,iBAAiB,CAAC;AAOrF;4EAC4E;AAC5E,MAAM,OAAO,uBAAwB,SAAQ,aAAa;IAGrC;IACA;IAHnB,YACE,OAAsB,EACL,OAAqC,EACrC,MAAc;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHE,YAAO,GAAP,OAAO,CAA8B;QACrC,WAAM,GAAN,MAAM,CAAQ;IAGjC,CAAC;IAED,+DAA+D;IACtD,QAAQ,CAAC,OAAuC;QACvD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC;QAC1B,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACpE,MAAM,IAAI,GAAa,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,IAAI,CAAC,OAAO,CAAC;gBACX,IAAI,EAAE,SAAS,CAAC,KAAK;gBACrB,OAAO,EAAE;oBACP,GAAG;oBACH,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;oBAClE,IAAI;iBACL;gBACD,UAAU,EAAE,eAAe,CAAC,GAAG,CAAC;gBAChC,UAAU,EAAE,IAAI;gBAChB,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAoC,EACpC,UAAkC,EAAE;IAEpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;IACrC,OAAO,CAAC,aAA4B,EAAE,EAAE,CACtC,IAAI,uBAAuB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EntitySchema } from '@mikro-orm/core';
|
|
2
|
+
export interface TelescopeRollupRow {
|
|
3
|
+
metric: string;
|
|
4
|
+
bucketStart: number;
|
|
5
|
+
count: number;
|
|
6
|
+
sum: number;
|
|
7
|
+
max: number;
|
|
8
|
+
/** Fixed-length latency histogram (JSON column); null on legacy rows. */
|
|
9
|
+
histogram: number[] | null;
|
|
10
|
+
}
|
|
11
|
+
export declare const TelescopeRollup: EntitySchema<TelescopeRollupRow, never, import("@mikro-orm/core").EntityCtor<TelescopeRollupRow>>;
|
|
12
|
+
//# sourceMappingURL=telescope-rollup.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telescope-rollup.entity.d.ts","sourceRoot":"","sources":["../src/telescope-rollup.entity.ts"],"names":[],"mappings":"AAYA,OAAO,EAAc,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAWD,eAAO,MAAM,eAAe,mGAc1B,CAAC"}
|