@hazeljs/inspector 0.2.0-rc.5
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/LICENSE +192 -0
- package/README.md +97 -0
- package/dist/config/inspector.config.d.ts +8 -0
- package/dist/config/inspector.config.d.ts.map +1 -0
- package/dist/config/inspector.config.js +38 -0
- package/dist/contracts/types.d.ts +253 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/inspector.module.d.ts +25 -0
- package/dist/inspector.module.d.ts.map +1 -0
- package/dist/inspector.module.js +489 -0
- package/dist/plugins/agent.inspector.d.ts +8 -0
- package/dist/plugins/agent.inspector.d.ts.map +1 -0
- package/dist/plugins/agent.inspector.js +51 -0
- package/dist/plugins/ai.inspector.d.ts +8 -0
- package/dist/plugins/ai.inspector.d.ts.map +1 -0
- package/dist/plugins/ai.inspector.js +87 -0
- package/dist/plugins/core.inspector.d.ts +8 -0
- package/dist/plugins/core.inspector.d.ts.map +1 -0
- package/dist/plugins/core.inspector.js +203 -0
- package/dist/plugins/cron.inspector.d.ts +8 -0
- package/dist/plugins/cron.inspector.d.ts.map +1 -0
- package/dist/plugins/cron.inspector.js +74 -0
- package/dist/plugins/data.inspector.d.ts +8 -0
- package/dist/plugins/data.inspector.d.ts.map +1 -0
- package/dist/plugins/data.inspector.js +50 -0
- package/dist/plugins/event-emitter.inspector.d.ts +8 -0
- package/dist/plugins/event-emitter.inspector.d.ts.map +1 -0
- package/dist/plugins/event-emitter.inspector.js +53 -0
- package/dist/plugins/flow.inspector.d.ts +8 -0
- package/dist/plugins/flow.inspector.d.ts.map +1 -0
- package/dist/plugins/flow.inspector.js +112 -0
- package/dist/plugins/graphql.inspector.d.ts +8 -0
- package/dist/plugins/graphql.inspector.d.ts.map +1 -0
- package/dist/plugins/graphql.inspector.js +75 -0
- package/dist/plugins/grpc.inspector.d.ts +8 -0
- package/dist/plugins/grpc.inspector.d.ts.map +1 -0
- package/dist/plugins/grpc.inspector.js +53 -0
- package/dist/plugins/kafka.inspector.d.ts +8 -0
- package/dist/plugins/kafka.inspector.d.ts.map +1 -0
- package/dist/plugins/kafka.inspector.js +59 -0
- package/dist/plugins/ml.inspector.d.ts +8 -0
- package/dist/plugins/ml.inspector.d.ts.map +1 -0
- package/dist/plugins/ml.inspector.js +78 -0
- package/dist/plugins/prompts.inspector.d.ts +7 -0
- package/dist/plugins/prompts.inspector.d.ts.map +1 -0
- package/dist/plugins/prompts.inspector.js +41 -0
- package/dist/plugins/queue.inspector.d.ts +8 -0
- package/dist/plugins/queue.inspector.d.ts.map +1 -0
- package/dist/plugins/queue.inspector.js +55 -0
- package/dist/plugins/rag.inspector.d.ts +8 -0
- package/dist/plugins/rag.inspector.d.ts.map +1 -0
- package/dist/plugins/rag.inspector.js +72 -0
- package/dist/plugins/serverless.inspector.d.ts +8 -0
- package/dist/plugins/serverless.inspector.d.ts.map +1 -0
- package/dist/plugins/serverless.inspector.js +53 -0
- package/dist/plugins/websocket.inspector.d.ts +8 -0
- package/dist/plugins/websocket.inspector.d.ts.map +1 -0
- package/dist/plugins/websocket.inspector.js +73 -0
- package/dist/registry/registry.d.ts +12 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +43 -0
- package/dist/runtime/inspector-runtime.d.ts +66 -0
- package/dist/runtime/inspector-runtime.d.ts.map +1 -0
- package/dist/runtime/inspector-runtime.js +97 -0
- package/dist/service/inspector.service.d.ts +37 -0
- package/dist/service/inspector.service.d.ts.map +1 -0
- package/dist/service/inspector.service.js +146 -0
- package/package.json +124 -0
- package/ui-dist/assets/index-BN8Zr7QX.css +1 -0
- package/ui-dist/assets/index-DLS5TpZI.js +80 -0
- package/ui-dist/index.html +316 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HazelInspectorRegistry - Pluggable inspector plugin registration and aggregation
|
|
3
|
+
*/
|
|
4
|
+
import type { InspectorContext, InspectorEntry, HazelInspectorPlugin } from '../contracts/types';
|
|
5
|
+
export type { HazelInspectorPlugin };
|
|
6
|
+
export declare class HazelInspectorRegistry {
|
|
7
|
+
private plugins;
|
|
8
|
+
register(plugin: HazelInspectorPlugin): void;
|
|
9
|
+
getPlugins(): HazelInspectorPlugin[];
|
|
10
|
+
runAll(context: InspectorContext): Promise<InspectorEntry[]>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEjG,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAErC,qBAAa,sBAAsB;IACjC,OAAO,CAAC,OAAO,CAA8B;IAE7C,QAAQ,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAM5C,UAAU,IAAI,oBAAoB,EAAE;IAI9B,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CAwBnE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HazelInspectorRegistry - Pluggable inspector plugin registration and aggregation
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HazelInspectorRegistry = void 0;
|
|
7
|
+
class HazelInspectorRegistry {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.plugins = [];
|
|
10
|
+
}
|
|
11
|
+
register(plugin) {
|
|
12
|
+
if (!this.plugins.some((p) => p.name === plugin.name)) {
|
|
13
|
+
this.plugins.push(plugin);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
getPlugins() {
|
|
17
|
+
return [...this.plugins];
|
|
18
|
+
}
|
|
19
|
+
async runAll(context) {
|
|
20
|
+
const results = [];
|
|
21
|
+
const seenIds = new Set();
|
|
22
|
+
for (const plugin of this.plugins) {
|
|
23
|
+
try {
|
|
24
|
+
if (!plugin.supports(context)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const entries = await plugin.inspect(context);
|
|
28
|
+
const list = Array.isArray(entries) ? entries : [];
|
|
29
|
+
for (const entry of list) {
|
|
30
|
+
if (!seenIds.has(entry.id)) {
|
|
31
|
+
seenIds.add(entry.id);
|
|
32
|
+
results.push(entry);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error(`[HazelInspector] Plugin ${plugin.name} failed:`, error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.HazelInspectorRegistry = HazelInspectorRegistry;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InspectorRuntime - Global registry for gateway, discovery, resilience
|
|
3
|
+
* Packages register their instances here so the Inspector can display overview data.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* // In your app when creating a gateway:
|
|
7
|
+
* import { InspectorRuntime } from '@hazeljs/inspector';
|
|
8
|
+
* const gateway = GatewayServer.fromConfig(config);
|
|
9
|
+
* InspectorRuntime.registerGateway(gateway);
|
|
10
|
+
*/
|
|
11
|
+
export interface GatewayOverview {
|
|
12
|
+
routes: string[];
|
|
13
|
+
totalRoutes: number;
|
|
14
|
+
metrics?: {
|
|
15
|
+
totalCalls: number;
|
|
16
|
+
successCalls: number;
|
|
17
|
+
failureCalls: number;
|
|
18
|
+
failureRate: number;
|
|
19
|
+
averageResponseTime: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface DiscoveryOverview {
|
|
23
|
+
services: string[];
|
|
24
|
+
totalServices: number;
|
|
25
|
+
totalInstances: number;
|
|
26
|
+
instancesByService: Record<string, number>;
|
|
27
|
+
}
|
|
28
|
+
export interface ResilienceOverview {
|
|
29
|
+
circuitBreakers: number;
|
|
30
|
+
circuitBreakerStates: {
|
|
31
|
+
name: string;
|
|
32
|
+
state: string;
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
declare class InspectorRuntimeImpl {
|
|
36
|
+
private gateway;
|
|
37
|
+
private discovery;
|
|
38
|
+
registerGateway(gw: {
|
|
39
|
+
getRoutes: () => string[];
|
|
40
|
+
getMetrics?: () => {
|
|
41
|
+
getSnapshot?: () => {
|
|
42
|
+
aggregated?: {
|
|
43
|
+
totalCalls?: number;
|
|
44
|
+
successCalls?: number;
|
|
45
|
+
failureCalls?: number;
|
|
46
|
+
failureRate?: number;
|
|
47
|
+
averageResponseTime?: number;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}): void;
|
|
52
|
+
registerDiscovery(client: {
|
|
53
|
+
getAllServices: () => Promise<string[]>;
|
|
54
|
+
getInstances: (s: string) => Promise<unknown[]>;
|
|
55
|
+
}): void;
|
|
56
|
+
getGateway(): typeof this.gateway;
|
|
57
|
+
getDiscovery(): typeof this.discovery;
|
|
58
|
+
/** Reset for testing */
|
|
59
|
+
reset(): void;
|
|
60
|
+
getGatewayOverview(): Promise<GatewayOverview | null>;
|
|
61
|
+
getDiscoveryOverview(): Promise<DiscoveryOverview | null>;
|
|
62
|
+
getResilienceOverview(): Promise<ResilienceOverview | null>;
|
|
63
|
+
}
|
|
64
|
+
export declare const InspectorRuntime: InspectorRuntimeImpl;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=inspector-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspector-runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/inspector-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzD;AAED,cAAM,oBAAoB;IACxB,OAAO,CAAC,OAAO,CAaC;IAChB,OAAO,CAAC,SAAS,CAGD;IAEhB,eAAe,CAAC,EAAE,EAAE;QAClB,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM;YACjB,WAAW,CAAC,EAAE,MAAM;gBAClB,UAAU,CAAC,EAAE;oBACX,UAAU,CAAC,EAAE,MAAM,CAAC;oBACpB,YAAY,CAAC,EAAE,MAAM,CAAC;oBACtB,YAAY,CAAC,EAAE,MAAM,CAAC;oBACtB,WAAW,CAAC,EAAE,MAAM,CAAC;oBACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;iBAC9B,CAAC;aACH,CAAC;SACH,CAAC;KACH,GAAG,IAAI;IAIR,iBAAiB,CAAC,MAAM,EAAE;QACxB,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACxC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;KACjD,GAAG,IAAI;IAIR,UAAU,IAAI,OAAO,IAAI,CAAC,OAAO;IAIjC,YAAY,IAAI,OAAO,IAAI,CAAC,SAAS;IAIrC,wBAAwB;IACxB,KAAK,IAAI,IAAI;IAKP,kBAAkB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAuBrD,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAkBzD,qBAAqB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;CAelE;AAED,eAAO,MAAM,gBAAgB,sBAA6B,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* InspectorRuntime - Global registry for gateway, discovery, resilience
|
|
4
|
+
* Packages register their instances here so the Inspector can display overview data.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // In your app when creating a gateway:
|
|
8
|
+
* import { InspectorRuntime } from '@hazeljs/inspector';
|
|
9
|
+
* const gateway = GatewayServer.fromConfig(config);
|
|
10
|
+
* InspectorRuntime.registerGateway(gateway);
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.InspectorRuntime = void 0;
|
|
14
|
+
class InspectorRuntimeImpl {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.gateway = null;
|
|
17
|
+
this.discovery = null;
|
|
18
|
+
}
|
|
19
|
+
registerGateway(gw) {
|
|
20
|
+
this.gateway = gw;
|
|
21
|
+
}
|
|
22
|
+
registerDiscovery(client) {
|
|
23
|
+
this.discovery = client;
|
|
24
|
+
}
|
|
25
|
+
getGateway() {
|
|
26
|
+
return this.gateway;
|
|
27
|
+
}
|
|
28
|
+
getDiscovery() {
|
|
29
|
+
return this.discovery;
|
|
30
|
+
}
|
|
31
|
+
/** Reset for testing */
|
|
32
|
+
reset() {
|
|
33
|
+
this.gateway = null;
|
|
34
|
+
this.discovery = null;
|
|
35
|
+
}
|
|
36
|
+
async getGatewayOverview() {
|
|
37
|
+
if (!this.gateway)
|
|
38
|
+
return null;
|
|
39
|
+
try {
|
|
40
|
+
const routes = this.gateway.getRoutes();
|
|
41
|
+
let metrics;
|
|
42
|
+
if (typeof this.gateway.getMetrics === 'function') {
|
|
43
|
+
const snap = this.gateway.getMetrics()?.getSnapshot?.()?.aggregated;
|
|
44
|
+
if (snap) {
|
|
45
|
+
metrics = {
|
|
46
|
+
totalCalls: snap.totalCalls ?? 0,
|
|
47
|
+
successCalls: snap.successCalls ?? 0,
|
|
48
|
+
failureCalls: snap.failureCalls ?? 0,
|
|
49
|
+
failureRate: snap.failureRate ?? 0,
|
|
50
|
+
averageResponseTime: snap.averageResponseTime ?? 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { routes, totalRoutes: routes.length, metrics: metrics ?? undefined };
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async getDiscoveryOverview() {
|
|
61
|
+
if (!this.discovery)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
const services = await this.discovery.getAllServices();
|
|
65
|
+
const instancesByService = {};
|
|
66
|
+
let totalInstances = 0;
|
|
67
|
+
for (const svc of services) {
|
|
68
|
+
const instances = await this.discovery.getInstances(svc);
|
|
69
|
+
const count = Array.isArray(instances) ? instances.length : 0;
|
|
70
|
+
instancesByService[svc] = count;
|
|
71
|
+
totalInstances += count;
|
|
72
|
+
}
|
|
73
|
+
return { services, totalServices: services.length, totalInstances, instancesByService };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async getResilienceOverview() {
|
|
80
|
+
try {
|
|
81
|
+
const { CircuitBreakerRegistry } = require('@hazeljs/resilience');
|
|
82
|
+
const breakers = CircuitBreakerRegistry.getAll?.();
|
|
83
|
+
if (!breakers || !(breakers instanceof Map))
|
|
84
|
+
return null;
|
|
85
|
+
const circuitBreakerStates = [];
|
|
86
|
+
for (const [name, breaker] of breakers) {
|
|
87
|
+
const state = breaker.getState?.() ?? 'unknown';
|
|
88
|
+
circuitBreakerStates.push({ name, state });
|
|
89
|
+
}
|
|
90
|
+
return { circuitBreakers: breakers.size, circuitBreakerStates };
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.InspectorRuntime = new InspectorRuntimeImpl();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HazelInspectorService - Aggregates inspector plugin results with caching
|
|
3
|
+
*/
|
|
4
|
+
import type { InspectorContext, InspectorEntry, InspectorSnapshot, InspectorEntryKind, RouteInspectorEntry, ModuleInspectorEntry, ProviderInspectorEntry, DecoratorInspectorEntry, CronInspectorEntry, QueueInspectorEntry, WebSocketInspectorEntry, AgentInspectorEntry, RagInspectorEntry, PromptInspectorEntry, AIFunctionInspectorEntry, EventInspectorEntry, GraphQLInspectorEntry, GrpcInspectorEntry, KafkaInspectorEntry, FlowInspectorEntry, DataPipelineInspectorEntry, ServerlessInspectorEntry, MLModelInspectorEntry, GroupedSnapshot } from '../contracts/types';
|
|
5
|
+
import type { HazelInspectorRegistry } from '../registry/registry';
|
|
6
|
+
import type { InspectorModuleOptions } from '../contracts/types';
|
|
7
|
+
export declare class HazelInspectorService {
|
|
8
|
+
private registry;
|
|
9
|
+
private config;
|
|
10
|
+
private cache;
|
|
11
|
+
private cacheTime;
|
|
12
|
+
constructor(registry: HazelInspectorRegistry, config: Required<InspectorModuleOptions>);
|
|
13
|
+
collectSnapshot(context: InspectorContext): Promise<InspectorSnapshot>;
|
|
14
|
+
refresh(context: InspectorContext): Promise<InspectorSnapshot>;
|
|
15
|
+
getRoutes(entries: InspectorEntry[]): RouteInspectorEntry[];
|
|
16
|
+
getModules(entries: InspectorEntry[]): ModuleInspectorEntry[];
|
|
17
|
+
getProviders(entries: InspectorEntry[]): ProviderInspectorEntry[];
|
|
18
|
+
getDecorators(entries: InspectorEntry[]): DecoratorInspectorEntry[];
|
|
19
|
+
getJobs(entries: InspectorEntry[]): CronInspectorEntry[];
|
|
20
|
+
getQueues(entries: InspectorEntry[]): QueueInspectorEntry[];
|
|
21
|
+
getWebSockets(entries: InspectorEntry[]): WebSocketInspectorEntry[];
|
|
22
|
+
getAgents(entries: InspectorEntry[]): AgentInspectorEntry[];
|
|
23
|
+
getRag(entries: InspectorEntry[]): RagInspectorEntry[];
|
|
24
|
+
getPrompts(entries: InspectorEntry[]): PromptInspectorEntry[];
|
|
25
|
+
getAIFunctions(entries: InspectorEntry[]): AIFunctionInspectorEntry[];
|
|
26
|
+
getEvents(entries: InspectorEntry[]): EventInspectorEntry[];
|
|
27
|
+
getGraphQL(entries: InspectorEntry[]): GraphQLInspectorEntry[];
|
|
28
|
+
getGrpc(entries: InspectorEntry[]): GrpcInspectorEntry[];
|
|
29
|
+
getKafka(entries: InspectorEntry[]): KafkaInspectorEntry[];
|
|
30
|
+
getFlows(entries: InspectorEntry[]): FlowInspectorEntry[];
|
|
31
|
+
getDataPipelines(entries: InspectorEntry[]): DataPipelineInspectorEntry[];
|
|
32
|
+
getServerless(entries: InspectorEntry[]): ServerlessInspectorEntry[];
|
|
33
|
+
getMLModels(entries: InspectorEntry[]): MLModelInspectorEntry[];
|
|
34
|
+
getByKind(entries: InspectorEntry[], kind: InspectorEntryKind): InspectorEntry[];
|
|
35
|
+
getGroupedSnapshot(entries: InspectorEntry[]): GroupedSnapshot;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=inspector.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspector.service.d.ts","sourceRoot":"","sources":["../../src/service/inspector.service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,qBAAa,qBAAqB;IAK9B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,SAAS,CAAK;gBAGZ,QAAQ,EAAE,sBAAsB,EAChC,MAAM,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAK5C,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAuBtE,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAKpE,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,mBAAmB,EAAE;IAI3D,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,oBAAoB,EAAE;IAI7D,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,sBAAsB,EAAE;IAIjE,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,uBAAuB,EAAE;IAInE,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,kBAAkB,EAAE;IAIxD,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,mBAAmB,EAAE;IAI3D,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,uBAAuB,EAAE;IAInE,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,mBAAmB,EAAE;IAI3D,MAAM,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,iBAAiB,EAAE;IAItD,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,oBAAoB,EAAE;IAI7D,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,wBAAwB,EAAE;IAIrE,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,mBAAmB,EAAE;IAI3D,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,qBAAqB,EAAE;IAI9D,OAAO,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,kBAAkB,EAAE;IAIxD,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,mBAAmB,EAAE;IAI1D,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,kBAAkB,EAAE;IAIzD,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,0BAA0B,EAAE;IAIzE,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,wBAAwB,EAAE;IAIpE,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,qBAAqB,EAAE;IAI/D,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,kBAAkB,GAAG,cAAc,EAAE;IAIhF,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,eAAe;CA6C/D"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HazelInspectorService - Aggregates inspector plugin results with caching
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HazelInspectorService = void 0;
|
|
7
|
+
const inspector_config_1 = require("../config/inspector.config");
|
|
8
|
+
class HazelInspectorService {
|
|
9
|
+
constructor(registry, config) {
|
|
10
|
+
this.registry = registry;
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.cache = null;
|
|
13
|
+
this.cacheTime = 0;
|
|
14
|
+
this.config = (0, inspector_config_1.mergeInspectorConfig)(config);
|
|
15
|
+
}
|
|
16
|
+
async collectSnapshot(context) {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const maxAge = this.config.maxSnapshotCacheAgeMs ?? 5000;
|
|
19
|
+
if (this.cache && now - this.cacheTime < maxAge) {
|
|
20
|
+
return this.cache;
|
|
21
|
+
}
|
|
22
|
+
const rawEntries = await this.registry.runAll(context);
|
|
23
|
+
const entries = Array.isArray(rawEntries) ? rawEntries : [];
|
|
24
|
+
const summary = {};
|
|
25
|
+
for (const e of entries) {
|
|
26
|
+
summary[e.kind] = (summary[e.kind] ?? 0) + 1;
|
|
27
|
+
}
|
|
28
|
+
this.cache = {
|
|
29
|
+
collectedAt: new Date().toISOString(),
|
|
30
|
+
entries,
|
|
31
|
+
summary,
|
|
32
|
+
};
|
|
33
|
+
this.cacheTime = now;
|
|
34
|
+
return this.cache;
|
|
35
|
+
}
|
|
36
|
+
async refresh(context) {
|
|
37
|
+
this.cache = null;
|
|
38
|
+
return this.collectSnapshot(context);
|
|
39
|
+
}
|
|
40
|
+
getRoutes(entries) {
|
|
41
|
+
return entries.filter((e) => e.kind === 'route');
|
|
42
|
+
}
|
|
43
|
+
getModules(entries) {
|
|
44
|
+
return entries.filter((e) => e.kind === 'module');
|
|
45
|
+
}
|
|
46
|
+
getProviders(entries) {
|
|
47
|
+
return entries.filter((e) => e.kind === 'provider');
|
|
48
|
+
}
|
|
49
|
+
getDecorators(entries) {
|
|
50
|
+
return entries.filter((e) => e.kind === 'decorator');
|
|
51
|
+
}
|
|
52
|
+
getJobs(entries) {
|
|
53
|
+
return entries.filter((e) => e.kind === 'cron');
|
|
54
|
+
}
|
|
55
|
+
getQueues(entries) {
|
|
56
|
+
return entries.filter((e) => e.kind === 'queue');
|
|
57
|
+
}
|
|
58
|
+
getWebSockets(entries) {
|
|
59
|
+
return entries.filter((e) => e.kind === 'websocket');
|
|
60
|
+
}
|
|
61
|
+
getAgents(entries) {
|
|
62
|
+
return entries.filter((e) => e.kind === 'agent');
|
|
63
|
+
}
|
|
64
|
+
getRag(entries) {
|
|
65
|
+
return entries.filter((e) => e.kind === 'rag');
|
|
66
|
+
}
|
|
67
|
+
getPrompts(entries) {
|
|
68
|
+
return entries.filter((e) => e.kind === 'prompt');
|
|
69
|
+
}
|
|
70
|
+
getAIFunctions(entries) {
|
|
71
|
+
return entries.filter((e) => e.kind === 'aifunction');
|
|
72
|
+
}
|
|
73
|
+
getEvents(entries) {
|
|
74
|
+
return entries.filter((e) => e.kind === 'event');
|
|
75
|
+
}
|
|
76
|
+
getGraphQL(entries) {
|
|
77
|
+
return entries.filter((e) => e.kind === 'graphql');
|
|
78
|
+
}
|
|
79
|
+
getGrpc(entries) {
|
|
80
|
+
return entries.filter((e) => e.kind === 'grpc');
|
|
81
|
+
}
|
|
82
|
+
getKafka(entries) {
|
|
83
|
+
return entries.filter((e) => e.kind === 'kafka');
|
|
84
|
+
}
|
|
85
|
+
getFlows(entries) {
|
|
86
|
+
return entries.filter((e) => e.kind === 'flow');
|
|
87
|
+
}
|
|
88
|
+
getDataPipelines(entries) {
|
|
89
|
+
return entries.filter((e) => e.kind === 'data');
|
|
90
|
+
}
|
|
91
|
+
getServerless(entries) {
|
|
92
|
+
return entries.filter((e) => e.kind === 'serverless');
|
|
93
|
+
}
|
|
94
|
+
getMLModels(entries) {
|
|
95
|
+
return entries.filter((e) => e.kind === 'ml');
|
|
96
|
+
}
|
|
97
|
+
getByKind(entries, kind) {
|
|
98
|
+
return entries.filter((e) => e.kind === kind);
|
|
99
|
+
}
|
|
100
|
+
getGroupedSnapshot(entries) {
|
|
101
|
+
const knownKinds = [
|
|
102
|
+
'route',
|
|
103
|
+
'decorator',
|
|
104
|
+
'module',
|
|
105
|
+
'provider',
|
|
106
|
+
'websocket',
|
|
107
|
+
'cron',
|
|
108
|
+
'queue',
|
|
109
|
+
'agent',
|
|
110
|
+
'rag',
|
|
111
|
+
'prompt',
|
|
112
|
+
'aifunction',
|
|
113
|
+
'event',
|
|
114
|
+
'graphql',
|
|
115
|
+
'grpc',
|
|
116
|
+
'kafka',
|
|
117
|
+
'flow',
|
|
118
|
+
'data',
|
|
119
|
+
'serverless',
|
|
120
|
+
'ml',
|
|
121
|
+
];
|
|
122
|
+
return {
|
|
123
|
+
routes: this.getRoutes(entries),
|
|
124
|
+
decorators: this.getDecorators(entries),
|
|
125
|
+
modules: this.getModules(entries),
|
|
126
|
+
providers: this.getProviders(entries),
|
|
127
|
+
websockets: this.getWebSockets(entries),
|
|
128
|
+
jobs: this.getJobs(entries),
|
|
129
|
+
queues: this.getQueues(entries),
|
|
130
|
+
agents: this.getAgents(entries),
|
|
131
|
+
rag: this.getRag(entries),
|
|
132
|
+
prompts: this.getPrompts(entries),
|
|
133
|
+
aifunctions: this.getAIFunctions(entries),
|
|
134
|
+
events: this.getEvents(entries),
|
|
135
|
+
graphql: this.getGraphQL(entries),
|
|
136
|
+
grpc: this.getGrpc(entries),
|
|
137
|
+
kafka: this.getKafka(entries),
|
|
138
|
+
flows: this.getFlows(entries),
|
|
139
|
+
dataPipelines: this.getDataPipelines(entries),
|
|
140
|
+
serverless: this.getServerless(entries),
|
|
141
|
+
mlModels: this.getMLModels(entries),
|
|
142
|
+
other: entries.filter((e) => !knownKinds.includes(e.kind)),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.HazelInspectorService = HazelInspectorService;
|
package/package.json
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hazeljs/inspector",
|
|
3
|
+
"version": "0.2.0-rc.5",
|
|
4
|
+
"description": "Framework-aware runtime inspector for HazelJS - metadata explorer and DevTools UI",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"ui-dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && npm run build:ui",
|
|
13
|
+
"build:ui": "vite build",
|
|
14
|
+
"test": "jest --coverage --passWithNoTests",
|
|
15
|
+
"test:ci": "jest --coverage --coverageReporters=text --coverageReporters=lcov --no-coverage-threshold",
|
|
16
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
17
|
+
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
|
|
18
|
+
"clean": "rm -rf dist ui-dist"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"cron-parser": "^4.9.0",
|
|
22
|
+
"reflect-metadata": "^0.2.2"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@hazeljs/agent": ">=0.2.0-beta.0",
|
|
26
|
+
"@hazeljs/ai": ">=0.2.0-beta.0",
|
|
27
|
+
"@hazeljs/core": ">=0.2.0-beta.0",
|
|
28
|
+
"@hazeljs/cron": ">=0.2.0-beta.0",
|
|
29
|
+
"@hazeljs/data": ">=0.2.0-beta.0",
|
|
30
|
+
"@hazeljs/event-emitter": ">=0.2.0-beta.0",
|
|
31
|
+
"@hazeljs/flow": ">=0.2.0-beta.0",
|
|
32
|
+
"@hazeljs/graphql": ">=0.2.0-beta.0",
|
|
33
|
+
"@hazeljs/grpc": ">=0.2.0-beta.0",
|
|
34
|
+
"@hazeljs/kafka": ">=0.2.0-beta.0",
|
|
35
|
+
"@hazeljs/ml": ">=0.2.0-beta.0",
|
|
36
|
+
"@hazeljs/prompts": ">=0.2.0-beta.0",
|
|
37
|
+
"@hazeljs/queue": ">=0.2.0-beta.0",
|
|
38
|
+
"@hazeljs/rag": ">=0.2.0-beta.0",
|
|
39
|
+
"@hazeljs/serverless": ">=0.2.0-beta.0",
|
|
40
|
+
"@hazeljs/websocket": ">=0.2.0-beta.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"@hazeljs/cron": {
|
|
44
|
+
"optional": true
|
|
45
|
+
},
|
|
46
|
+
"@hazeljs/queue": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"@hazeljs/websocket": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"@hazeljs/agent": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"@hazeljs/ai": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"@hazeljs/rag": {
|
|
59
|
+
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"@hazeljs/prompts": {
|
|
62
|
+
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"@hazeljs/event-emitter": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@hazeljs/graphql": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"@hazeljs/grpc": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"@hazeljs/kafka": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"@hazeljs/flow": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"@hazeljs/data": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"@hazeljs/serverless": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@hazeljs/ml": {
|
|
86
|
+
"optional": true
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"devDependencies": {
|
|
90
|
+
"@hazeljs/core": "^0.2.0-rc.5",
|
|
91
|
+
"@types/jest": "^29.5.14",
|
|
92
|
+
"@types/node": "^20.17.50",
|
|
93
|
+
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
|
94
|
+
"@typescript-eslint/parser": "^8.18.2",
|
|
95
|
+
"eslint": "^8.56.0",
|
|
96
|
+
"jest": "^29.7.0",
|
|
97
|
+
"preact": "^10.19.0",
|
|
98
|
+
"ts-jest": "^29.1.2",
|
|
99
|
+
"typescript": "^5.3.3",
|
|
100
|
+
"vite": "^5.0.0"
|
|
101
|
+
},
|
|
102
|
+
"publishConfig": {
|
|
103
|
+
"access": "public"
|
|
104
|
+
},
|
|
105
|
+
"repository": {
|
|
106
|
+
"type": "git",
|
|
107
|
+
"url": "git+https://github.com/hazel-js/hazeljs.git",
|
|
108
|
+
"directory": "packages/inspector"
|
|
109
|
+
},
|
|
110
|
+
"keywords": [
|
|
111
|
+
"hazeljs",
|
|
112
|
+
"inspector",
|
|
113
|
+
"devtools",
|
|
114
|
+
"metadata",
|
|
115
|
+
"debugging"
|
|
116
|
+
],
|
|
117
|
+
"author": "Muhammad Arslan <muhammad.arslan@hazeljs.ai>",
|
|
118
|
+
"license": "Apache-2.0",
|
|
119
|
+
"bugs": {
|
|
120
|
+
"url": "https://github.com/hazeljs/hazel-js/issues"
|
|
121
|
+
},
|
|
122
|
+
"homepage": "https://hazeljs.com",
|
|
123
|
+
"gitHead": "84ad3db0f24f75a9d55c6eec5997849fed3aa00e"
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=DM+Sans:wght@400;500;600;700&display=swap";:root{--hazel-primary: #1a365d;--hazel-primary-hover: #2c5282;--hazel-accent: #1a365d;--hazel-accent-hover: #2c5282;--hazel-primary-muted: rgba(26, 54, 93, .25);--hazel-accent-muted: rgba(26, 54, 93, .25);--bg-primary: #0c0e12;--bg-secondary: #14171d;--bg-tertiary: #1a1e26;--bg-elevated: #1f242b;--bg-hover: #252a33;--border: #2d333b;--border-subtle: #21262d;--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #6e7681;--accent: var(--hazel-accent);--accent-hover: var(--hazel-accent-hover);--accent-muted: var(--hazel-accent-muted);--success: #3fb950;--warning: #d29922;--error: #f85149;--radius-sm: 6px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);--shadow-md: 0 4px 12px rgba(0, 0, 0, .4);--shadow-lg: 0 8px 24px rgba(0, 0, 0, .5);--shadow-glow: 0 0 24px rgba(26, 54, 93, .2);--transition: .2s cubic-bezier(.4, 0, .2, 1)}*{box-sizing:border-box}body{margin:0;font-family:DM Sans,-apple-system,BlinkMacSystemFont,sans-serif;background:var(--bg-primary);color:var(--text-primary);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}#app{display:flex;min-height:100vh}.sidebar{width:240px;min-width:240px;background:var(--bg-secondary);border-right:1px solid var(--border-subtle);display:flex;flex-direction:column;padding:0}.logo{font-size:13px;font-weight:600;letter-spacing:.02em;padding:1.25rem 1.25rem 1rem;margin:0;color:var(--text-primary);border-bottom:1px solid var(--border-subtle);display:flex;align-items:center;gap:.625rem}.logo-img{width:24px;height:24px;object-fit:contain;flex-shrink:0}.logo-svg{width:24px;height:24px;color:var(--accent);flex-shrink:0}.logo-fallback{display:flex;align-items:center}.logo-text{color:var(--text-primary);font-weight:700}.sidebar nav{flex:1;padding:.75rem 0;overflow-y:auto;min-height:0;display:flex;flex-direction:column;gap:.25rem}.nav-section{display:flex;flex-direction:column;gap:.125rem}.nav-section-title{font-size:10px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;padding:.5rem 1.25rem .25rem;margin:.25rem .5rem 0 0;border-top:1px solid var(--border-subtle)}.sidebar nav a{display:flex;align-items:center;gap:.625rem;padding:.5rem 1.25rem;margin:0 .5rem;color:var(--text-secondary);text-decoration:none;border-radius:var(--radius-sm);font-weight:500;transition:color var(--transition),background var(--transition)}.sidebar nav a:hover{color:var(--text-primary);background:var(--bg-hover)}.sidebar nav a.active{color:#60a5fa;background:#3b82f633}.sidebar nav a .nav-icon{width:3px;height:14px;border-radius:2px;background:var(--text-muted);flex-shrink:0}.sidebar nav a:hover .nav-icon{background:var(--text-secondary)}.sidebar nav a.active .nav-icon{background:#60a5fa}.sidebar-footer{padding:1rem 1.25rem;border-top:1px solid var(--border-subtle)}.btn{width:100%;padding:.5rem 1rem;font-family:inherit;font-size:13px;font-weight:500;background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;transition:background var(--transition),border-color var(--transition)}.btn:hover{background:var(--bg-hover);border-color:var(--text-muted)}.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:#fff}.content{flex:1;padding:1rem 1.5rem;overflow:auto;position:relative;background:var(--bg-primary);isolation:isolate}.content:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:radial-gradient(ellipse 80% 50% at 50% -20%,rgba(26,54,93,.35),transparent),radial-gradient(ellipse 60% 40% at 100% 50%,rgba(26,54,93,.08),transparent),radial-gradient(ellipse 40% 30% at 0% 80%,rgba(26,54,93,.15),transparent);pointer-events:none;z-index:0}.content>*{position:relative;z-index:1}.toolbar{margin-bottom:1rem;display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;padding:.5rem .75rem;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md)}.toolbar-actions{display:flex;align-items:center;gap:.75rem}.toolbar-toggle{display:flex;align-items:center;gap:.5rem;font-size:13px;color:var(--text-secondary);cursor:pointer}.toolbar-toggle input{accent-color:var(--accent)}.btn-sm{width:auto;padding:.4rem .75rem}.overview-status-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;padding:.75rem 1rem;margin-bottom:1rem;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md)}.status-bar-left{display:flex;align-items:center;gap:.75rem}.status-dot{width:8px;height:8px;border-radius:50%;background:var(--text-muted);animation:pulse-dot 2s ease-in-out infinite}.status-dot.healthy{background:var(--success);box-shadow:0 0 8px #3fb95080}.status-dot.unhealthy{background:var(--error);box-shadow:0 0 8px #f8514980}.status-text{font-weight:600;font-size:14px;color:var(--text-primary)}.status-uptime{font-size:12px;font-family:JetBrains Mono,monospace}.quick-stats{display:flex;align-items:center;gap:1.5rem;font-size:12px;color:var(--text-secondary)}.quick-stat{display:flex;align-items:center;gap:.375rem}.quick-stat strong{color:var(--accent);font-weight:600}.quick-stat.muted{color:var(--text-muted)}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.6}}.loading-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0c0e12e6;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1.25rem;z-index:1000;color:var(--text-secondary)}.loading-spinner{width:44px;height:44px;border:3px solid var(--border-subtle);border-top-color:var(--accent);border-radius:50%;animation:spin .7s cubic-bezier(.5,0,.5,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.health-container{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:1rem;margin-bottom:1.5rem}.health-card{background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md);padding:1rem 1.25rem}.health-card.healthy{border-left:4px solid var(--success);background:linear-gradient(90deg,rgba(63,185,80,.06) 0%,transparent 100%)}.health-card.unhealthy{border-left:4px solid var(--error);background:linear-gradient(90deg,rgba(248,81,73,.06) 0%,transparent 100%)}.health-card.unknown{border-left:4px solid var(--warning);background:linear-gradient(90deg,rgba(210,153,34,.06) 0%,transparent 100%)}.health-card h3{margin:0 0 .5rem;font-size:14px}.health-card .status{font-weight:600}.health-card.healthy .status{color:var(--success)}.health-card.unhealthy .status{color:var(--error)}.health-card.unknown .status{color:var(--warning)}.health-card .endpoint{font-size:10px;color:var(--text-muted);margin-top:.375rem;font-family:JetBrains Mono,monospace}.overview-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr));gap:1rem;margin-bottom:1.5rem;min-height:340px;align-items:stretch}@media (max-width: 900px){.overview-grid{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(3,minmax(0,1fr));min-height:510px}}@media (max-width: 600px){.overview-grid{grid-template-columns:1fr;grid-template-rows:repeat(6,minmax(0,1fr));min-height:720px}}.overview-block{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md);padding:1rem 1.25rem;overflow-y:auto;transition:border-color var(--transition),box-shadow var(--transition)}.overview-block:before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,var(--accent) 0%,transparent 70%);opacity:.5}.overview-block:hover{border-color:var(--border);box-shadow:var(--shadow-sm)}.overview-block-title{margin:0 0 .75rem;font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.1em}.overview-env .overview-block-title,.overview-health .overview-block-title,.overview-runtime .overview-block-title,.overview-gateway .overview-block-title,.overview-discovery .overview-block-title,.overview-resilience .overview-block-title{color:var(--text-secondary)}.env-info{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:1rem;margin-bottom:1.5rem}.env-info-compact{margin-bottom:0;gap:.5rem}.env-info .env-item{font-size:12px;color:var(--text-secondary);padding:.35rem 0;border-bottom:1px solid var(--border-subtle)}.env-info .env-item:last-child{border-bottom:none}.env-info .env-item strong{color:var(--text-primary);font-weight:600;display:block;margin-bottom:.125rem}.health-cards-inline{display:flex;flex-wrap:wrap;gap:.5rem}.health-cards-inline .health-card{flex:1;min-width:0;padding:.75rem 1rem;border-radius:var(--radius-sm);transition:box-shadow var(--transition)}.health-cards-inline .health-card:hover{box-shadow:var(--shadow-sm)}.health-cards-inline .health-card h3{font-size:11px;font-weight:600;margin-bottom:.375rem;color:var(--text-secondary)}.health-cards-inline .health-card .status{font-size:13px;font-weight:700}.runtime-cards-inline{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.75rem}.runtime-cards-inline .card{min-width:0;padding:.75rem 1rem;background:var(--bg-tertiary);border-radius:var(--radius-sm);border:1px solid var(--border-subtle);transition:border-color var(--transition),background var(--transition)}.runtime-cards-inline .card:hover{background:var(--bg-elevated);border-color:var(--border)}.runtime-cards-inline .card .card-icon{width:22px;height:22px;color:var(--accent);margin-bottom:.35rem}.runtime-cards-inline .card .card-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:.2rem}.runtime-cards-inline .card .card-value{font-size:1.1rem;font-weight:600;font-family:JetBrains Mono,monospace}.runtime-cards-inline .card .card-value.highlight{color:var(--accent)}.chart-inline{height:120px;margin-bottom:1rem}.overview-gateway-stats,.overview-discovery-stats,.overview-resilience-stats{display:flex;flex-wrap:wrap;gap:.75rem;margin-bottom:.75rem}.overview-stat{display:flex;flex-direction:column;gap:.125rem;padding:.4rem .6rem;background:var(--bg-tertiary);border-radius:var(--radius-sm);border:1px solid var(--border-subtle);min-width:0}.overview-stat .stat-value{font-size:1.1rem;font-weight:700;font-family:JetBrains Mono,monospace;color:var(--accent)}.overview-stat .stat-label{font-size:9px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em}.overview-gateway-routes,.overview-discovery-services,.overview-resilience-states{font-size:11px;color:var(--text-secondary);line-height:1.5;min-width:0;overflow-wrap:break-word;word-break:break-word}.overview-resilience-states{display:flex;flex-wrap:wrap;gap:.375rem}.overview-gateway-routes code,.overview-discovery-services code,.overview-resilience-states code{font-size:10px;padding:.15rem .35rem;background:var(--bg-tertiary);border-radius:4px;border:1px solid var(--border-subtle);font-family:JetBrains Mono,monospace}.discovery-service{margin-right:.5rem}.cb-state{display:inline-block;padding:.15rem .4rem;border-radius:4px;font-size:11px;font-weight:600}.cb-state.closed{color:var(--success);background:#3fb9501f}.cb-state.open{color:var(--error);background:#f851491f}.cb-state.half-open{color:var(--warning);background:#d299221f}.muted{color:var(--text-muted);font-size:11px}.detail-actions{display:flex;gap:.5rem}.modal{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:flex;align-items:center;justify-content:center;z-index:200}.modal-content{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-lg);padding:1.5rem;max-width:480px;width:90%}.modal-content.modal-wide{max-width:720px}.playground-output{background:var(--bg-tertiary);border:1px solid var(--border);border-radius:var(--radius-sm);padding:1rem;font-size:12px;max-height:200px;overflow:auto;white-space:pre-wrap;word-break:break-word}.playground-meta{font-size:12px;color:var(--text-muted);margin-top:.5rem}.playground-actions{margin:.5rem 0}.diff-sources{display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem}.diff-hint{font-size:12px;color:var(--text-muted);margin:.25rem 0}.diff-result{margin-top:1rem;padding:1rem;background:var(--bg-tertiary);border-radius:var(--radius-sm);max-height:300px;overflow:auto}.diff-result .added{color:var(--success)}.diff-result .removed{color:var(--error)}.diff-result .changed{color:var(--warning)}.module-graph-container{margin-bottom:1rem;padding:1rem;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md);min-height:200px}.module-graph-svg{width:100%;height:300px}.rag-explorer{margin-bottom:1rem;padding:1rem;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md)}.rag-search-row{display:flex;gap:.5rem;margin-top:.5rem}.rag-search-row input{flex:1}.rag-results{margin-top:1rem;max-height:300px;overflow:auto}.rag-result-item{padding:.75rem;border-bottom:1px solid var(--border-subtle);font-size:12px}.rag-result-item .score{color:var(--accent);font-weight:600}.modal-content h3{margin:0 0 1rem;font-size:16px}.modal-body,.form-group{margin-bottom:1rem}.form-group label{display:block;font-size:12px;font-weight:600;color:var(--text-muted);margin-bottom:.5rem}.form-group input,.form-group select,.form-group textarea{width:100%;padding:.5rem;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--bg-tertiary);color:var(--text-primary);font-family:JetBrains Mono,monospace}.modal-footer{display:flex;gap:.5rem;justify-content:flex-end}.test-result{margin-top:1rem;padding:1rem;border-radius:var(--radius-sm);font-family:JetBrains Mono,monospace;font-size:12px;max-height:200px;overflow:auto}.test-result.success{background:#3fb95026;color:var(--success)}.test-result.error{background:#f8514926;color:var(--error)}.chart-container{margin-bottom:1.5rem;height:200px;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md);padding:1rem}.flow-diagram{--flow-edge: var(--text-muted);display:flex;flex-direction:column;gap:.5rem;padding:.5rem;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md);margin-bottom:.5rem}.flow-diagram-card{display:flex;flex-direction:column;gap:.35rem;background:var(--bg-tertiary);border:1px solid var(--border-subtle);border-radius:var(--radius-sm);padding:.35rem .5rem;overflow:hidden}.flow-diagram-badge{display:inline-flex;align-items:center;font-size:.65rem;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.04em}.flow-diagram-svg-wrap{height:64px;overflow:hidden;border-radius:var(--radius-sm);background:var(--bg-primary)}.flow-diagram-svg{display:block;width:100%;height:100%;object-fit:contain;object-position:left center}.flow-runtime-hint{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem .5rem;font-size:11px;color:var(--text-muted);margin-bottom:.5rem;padding:.4rem .6rem;background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-sm)}.flow-runtime-label{font-weight:500;color:var(--text-secondary)}.flow-runtime-hint code{font-size:10px;padding:.1rem .3rem;background:var(--bg-tertiary);border-radius:4px;font-family:JetBrains Mono,monospace}.flow-runtime-sep{color:var(--border);font-weight:300}#flows .section-title{margin-bottom:.5rem}.flow-diagram-card .flow-node-group{cursor:default}.flow-diagram-card .flow-node-group:hover .flow-diagram-node{filter:brightness(1.08)}.flow-diagram-node{fill:var(--bg-elevated);stroke:var(--border-subtle);stroke-width:1;transition:filter .15s ease}.flow-diagram-node.entry{fill:var(--accent-muted);stroke:var(--accent)}.flow-group{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.flow-node{padding:.5rem 1rem;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:var(--radius-sm);font-size:12px;font-family:JetBrains Mono,monospace}.flow-node.entry{border-color:var(--accent);background:var(--accent-muted)}.offline-banner{background:var(--error);color:#fff;padding:1rem 1.5rem;border-radius:var(--radius-md);margin-bottom:1rem;display:flex;align-items:center;justify-content:space-between;gap:1rem}.offline-banner button{padding:.5rem 1rem;background:#fff3;border:none;border-radius:var(--radius-sm);color:#fff;cursor:pointer;font-weight:600}.offline-banner button:hover{background:#ffffff4d}th.sortable{cursor:pointer;-webkit-user-select:none;user-select:none}th.sortable:hover{color:var(--accent)}th.sortable:after{content:" ▾";opacity:.5;font-size:10px}th.sortable.asc:after{content:" ▴";opacity:1}th.sortable.desc:after{content:" ▾";opacity:1}.search{flex:1;max-width:360px;padding:.5rem 1rem .5rem 2.5rem;font-family:JetBrains Mono,monospace;font-size:13px;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--bg-secondary);color:var(--text-primary);transition:border-color var(--transition),box-shadow var(--transition)}.search::placeholder{color:var(--text-muted)}.search:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-muted)}.search-wrapper{position:relative}.search-wrapper:before{content:"⌕";position:absolute;left:.875rem;top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:14px;pointer-events:none}.search-wrapper .search{padding-left:2.25rem}.tab-panel{display:none;animation:fadeIn .2s ease}.tab-panel.active{display:block}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}#summary-cards{display:flex;flex-direction:column;gap:2rem;margin-top:.5rem}#summary-cards:before{content:"Application Summary";display:block;font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.12em;margin-bottom:1rem;padding-bottom:.75rem;border-bottom:1px solid var(--border-subtle)}.overview-section{display:flex;flex-direction:column;gap:1rem}.overview-section-title{margin:0;font-size:12px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;padding-bottom:.5rem;border-bottom:1px solid var(--border-subtle)}.overview-section-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:1rem}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:1rem}.overview-section .card{cursor:pointer;background:#14171d99;transition:border-color var(--transition),box-shadow var(--transition),transform var(--transition),background var(--transition)}.overview-section .card:hover{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent-muted);background:#1f242bcc;transform:translate(4px)}.overview-section .card .card-icon{color:var(--text-muted);transition:color var(--transition)}.overview-section .card:hover .card-icon{color:var(--accent)}.card{background:var(--bg-secondary);border:1px solid var(--border-subtle);border-radius:var(--radius-md);padding:1.25rem;transition:border-color var(--transition),box-shadow var(--transition);display:flex;flex-direction:column;gap:.5rem;cursor:default}.card:hover{border-color:var(--border);box-shadow:var(--shadow-sm)}.card .card-icon{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:var(--accent-muted);color:var(--accent);border-radius:var(--radius-sm);flex-shrink:0}.card .card-icon svg{flex-shrink:0}.card .card-label{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:0}.card .card-value{font-size:1.75rem;font-weight:600;font-family:JetBrains Mono,monospace;color:var(--text-primary)}.card .card-value.highlight{color:var(--accent)}.table-container{overflow-x:auto;border:1px solid var(--border-subtle);border-radius:var(--radius-md);background:var(--bg-secondary)}table{width:100%;border-collapse:collapse;font-size:13px}th,td{padding:.75rem 1rem;text-align:left;border-bottom:1px solid var(--border-subtle)}th{background:var(--bg-tertiary);color:var(--text-muted);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.05em}tr{transition:background var(--transition)}tr:hover{background:var(--bg-hover)}tr:last-child td{border-bottom:none}tr[data-id]{cursor:pointer}td{font-family:JetBrains Mono,monospace;font-size:12px}.badge{display:inline-block;padding:.2rem .5rem;font-size:10px;font-weight:600;text-transform:uppercase;border-radius:4px}.badge-get{background:#3fb95033;color:var(--success)}.badge-post{background:#d2992233;color:var(--warning)}.badge-put{background:#388bfd33;color:#389bfd}.badge-patch{background:#a371f733;color:#a371f7}.badge-delete{background:#f8514933;color:var(--error)}.badge-default{background:var(--bg-tertiary);color:var(--text-muted)}.detail-panel{position:fixed;top:0;right:0;width:420px;max-width:90vw;height:100%;background:var(--bg-secondary);border-left:1px solid var(--border);box-shadow:-8px 0 24px #0006;padding:0;overflow:hidden;z-index:100;display:flex;flex-direction:column;animation:slideIn .2s ease}.hidden{display:none!important}.info-card{background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius-md);padding:1rem 1.25rem;margin-bottom:1rem}.info-card-title{margin:0 0 .5rem;font-size:14px;font-weight:600;color:var(--text-primary)}.info-card-text{margin:.25rem 0;font-size:13px;color:var(--text-secondary);line-height:1.5}.info-card code{background:var(--bg-tertiary);padding:.15rem .4rem;border-radius:4px;font-family:JetBrains Mono,monospace;font-size:12px}.overview-error{background:var(--bg-secondary);border:1px solid var(--error);border-radius:var(--radius-md);padding:1.5rem;color:var(--text-secondary)}.overview-error a{color:var(--accent)}@keyframes slideIn{0%{transform:translate(100%)}to{transform:translate(0)}}.detail-header{display:flex;justify-content:space-between;align-items:center;padding:1rem 1.25rem;border-bottom:1px solid var(--border-subtle);background:var(--bg-tertiary)}.detail-header h2{margin:0;font-size:14px;font-weight:600;color:var(--text-primary)}#detail-content{flex:1;padding:1.25rem;margin:0;font-family:JetBrains Mono,monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-word;overflow:auto;color:var(--text-secondary)}#detail-content .json-key{color:#7ee787}#detail-content .json-string{color:#a5d6ff}#detail-content .json-number{color:#79c0ff}#detail-content .json-boolean{color:#ff7b72}#detail-content .json-null{color:var(--text-muted)}td.empty-state{text-align:center;padding:3rem 2rem!important;color:var(--text-muted)}.empty-state p{margin:.5rem 0;font-size:14px}.content a{color:var(--accent);text-decoration:none}.content a:hover{text-decoration:underline}.section-title{font-size:16px;font-weight:600;margin:0 0 1rem;color:var(--text-primary)}
|