@dudousxd/nestjs-telescope
Version:
Laravel Telescope-style observability console for NestJS — core: watchers, recorder, correlation, SQLite store, headless API.
92 lines • 3.63 kB
JavaScript
/**
* Resolves the registered extensions once at module init. Each multi-hook runs for
* every extension and the results accumulate; an id/name claimed twice is a hard
* error naming both owners (mirrors nestjs-codegen's `mergeExclusive`). Resolution is
* eager so collisions fail at boot, not on first request.
*/
export class ExtensionRegistry {
_watchers = [];
_entryTypes = [];
_dashboards = [];
_providers = new Map();
/** Provider name → the `name` of the extension that contributed it. */
_providerOwners = new Map();
_recordObservers = [];
_flushObservers = [];
constructor(extensions, ctx) {
const entryOwners = new Map();
const dashOwners = new Map();
const provOwners = this._providerOwners;
for (const ext of extensions) {
for (const w of ext.watchers?.(ctx) ?? [])
this._watchers.push(w);
for (const et of ext.entryTypes?.(ctx) ?? []) {
const prev = entryOwners.get(et.id);
if (prev !== undefined) {
throw new Error(`Telescope entry type "${et.id}" is contributed by both "${prev}" and "${ext.name}". Entry-type ids must be unique.`);
}
entryOwners.set(et.id, ext.name);
this._entryTypes.push(et);
}
for (const d of ext.dashboards?.(ctx) ?? []) {
const prev = dashOwners.get(d.id);
if (prev !== undefined) {
throw new Error(`Telescope dashboard "${d.id}" is contributed by both "${prev}" and "${ext.name}". Dashboard ids must be unique.`);
}
dashOwners.set(d.id, ext.name);
this._dashboards.push(d);
}
for (const p of ext.dataProviders?.(ctx) ?? []) {
const prev = provOwners.get(p.name);
if (prev !== undefined) {
throw new Error(`Telescope data provider "${p.name}" is contributed by both "${prev}" and "${ext.name}". Provider names must be unique.`);
}
provOwners.set(p.name, ext.name);
this._providers.set(p.name, p);
}
if (ext.observeRecord)
this._recordObservers.push(ext.observeRecord.bind(ext));
if (ext.observeFlush)
this._flushObservers.push(ext.observeFlush.bind(ext));
}
}
/** Fan out a recorded input to every observer; isolate throwers (hot path). */
notifyRecord(input) {
for (const observe of this._recordObservers) {
try {
observe(input);
}
catch {
// Best-effort; one observer's bug must not affect capture or the others.
}
}
}
/** Await every flush observer; isolate throwers/rejections (off the host path). */
async notifyFlush(entries) {
for (const observe of this._flushObservers) {
try {
await observe(entries);
}
catch {
// Best-effort; never break the flush.
}
}
}
watchers() {
return [...this._watchers];
}
entryTypes() {
return [...this._entryTypes];
}
dashboards() {
return [...this._dashboards];
}
findProvider(name) {
return this._providers.get(name);
}
/** The `name` of the extension that contributed the given provider, or undefined. */
providerOwner(name) {
return this._providerOwners.get(name);
}
}
//# sourceMappingURL=registry.js.map