@checkstack/backend-api 0.0.2 → 0.1.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 +39 -0
- package/package.json +1 -1
- package/src/collector-registry.ts +42 -0
- package/src/collector-strategy.ts +83 -0
- package/src/core-services.ts +5 -4
- package/src/health-check.ts +74 -6
- package/src/index.ts +3 -0
- package/src/rpc.ts +4 -8
- package/src/test-utils.ts +10 -5
- package/src/transport-client.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# @checkstack/backend-api
|
|
2
2
|
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f5b1f49: Added collector registry lifecycle cleanup during plugin unloading.
|
|
8
|
+
|
|
9
|
+
- Added `unregisterByOwner(pluginId)` to remove collectors owned by unloading plugins
|
|
10
|
+
- Added `unregisterByMissingStrategies(loadedPluginIds)` for dependency-based pruning
|
|
11
|
+
- Integrated registry cleanup into `PluginManager.deregisterPlugin()`
|
|
12
|
+
- Updated `registerCoreServices` to return global registries for lifecycle management
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- f5b1f49: Added JSONPath assertions for response body validation and fully qualified strategy IDs.
|
|
17
|
+
|
|
18
|
+
**JSONPath Assertions:**
|
|
19
|
+
|
|
20
|
+
- Added `healthResultJSONPath()` factory in healthcheck-common for fields supporting JSONPath queries
|
|
21
|
+
- Extended AssertionBuilder with jsonpath field type showing path input (e.g., `$.data.status`)
|
|
22
|
+
- Added `jsonPath` field to `CollectorAssertionSchema` for persistence
|
|
23
|
+
- HTTP Request collector body field now supports JSONPath assertions
|
|
24
|
+
|
|
25
|
+
**Fully Qualified Strategy IDs:**
|
|
26
|
+
|
|
27
|
+
- HealthCheckRegistry now uses scoped factories like CollectorRegistry
|
|
28
|
+
- Strategies are stored with `pluginId.strategyId` format
|
|
29
|
+
- Added `getStrategiesWithMeta()` method to HealthCheckRegistry interface
|
|
30
|
+
- Router returns qualified IDs so frontend can correctly fetch collectors
|
|
31
|
+
|
|
32
|
+
**UI Improvements:**
|
|
33
|
+
|
|
34
|
+
- Save button disabled when collector configs have invalid required fields
|
|
35
|
+
- Fixed nested button warning in CollectorList accordion
|
|
36
|
+
|
|
37
|
+
- Updated dependencies [f5b1f49]
|
|
38
|
+
- @checkstack/common@0.0.3
|
|
39
|
+
- @checkstack/queue-api@0.0.3
|
|
40
|
+
- @checkstack/signal-common@0.0.3
|
|
41
|
+
|
|
3
42
|
## 0.0.2
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PluginMetadata } from "@checkstack/common";
|
|
2
|
+
import type { CollectorStrategy } from "./collector-strategy";
|
|
3
|
+
import type { TransportClient } from "./transport-client";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A registered collector with its owning plugin metadata.
|
|
7
|
+
*/
|
|
8
|
+
export interface RegisteredCollector {
|
|
9
|
+
/** The collector strategy */
|
|
10
|
+
collector: CollectorStrategy<TransportClient<unknown, unknown>>;
|
|
11
|
+
/** The plugin that registered this collector */
|
|
12
|
+
ownerPlugin: PluginMetadata;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Scoped collector registry interface.
|
|
17
|
+
* The owning plugin metadata is automatically injected via factory.
|
|
18
|
+
*/
|
|
19
|
+
export interface CollectorRegistry {
|
|
20
|
+
/**
|
|
21
|
+
* Register a collector strategy.
|
|
22
|
+
* The owning plugin metadata is automatically captured from the scoped context.
|
|
23
|
+
*/
|
|
24
|
+
register(
|
|
25
|
+
collector: CollectorStrategy<TransportClient<unknown, unknown>>
|
|
26
|
+
): void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get a collector by ID.
|
|
30
|
+
*/
|
|
31
|
+
getCollector(id: string): RegisteredCollector | undefined;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get all collectors that support a specific transport plugin.
|
|
35
|
+
*/
|
|
36
|
+
getCollectorsForPlugin(pluginMetadata: PluginMetadata): RegisteredCollector[];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get all registered collectors.
|
|
40
|
+
*/
|
|
41
|
+
getCollectors(): RegisteredCollector[];
|
|
42
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { PluginMetadata } from "@checkstack/common";
|
|
2
|
+
import type { TransportClient } from "./transport-client";
|
|
3
|
+
import type { Versioned } from "./config-versioning";
|
|
4
|
+
import type { HealthCheckRunForAggregation } from "./health-check";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Result from a collector execution.
|
|
8
|
+
*/
|
|
9
|
+
export interface CollectorResult<TResult> {
|
|
10
|
+
/** Collector-specific result data */
|
|
11
|
+
result: TResult;
|
|
12
|
+
/** Optional error message if collection partially failed */
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generic collector strategy interface.
|
|
18
|
+
*
|
|
19
|
+
* Collectors extend health check strategies by providing additional metrics
|
|
20
|
+
* collection capabilities. They receive a connected transport client and
|
|
21
|
+
* produce typed results with chart metadata.
|
|
22
|
+
*
|
|
23
|
+
* @template TClient - Transport client type (e.g., SshTransportClient)
|
|
24
|
+
* @template TConfig - Collector configuration schema
|
|
25
|
+
* @template TResult - Per-execution result type
|
|
26
|
+
* @template TAggregated - Aggregated result for buckets
|
|
27
|
+
*/
|
|
28
|
+
export interface CollectorStrategy<
|
|
29
|
+
TClient extends TransportClient<unknown, unknown>,
|
|
30
|
+
TConfig = unknown,
|
|
31
|
+
TResult = Record<string, unknown>,
|
|
32
|
+
TAggregated = Record<string, unknown>
|
|
33
|
+
> {
|
|
34
|
+
/** Unique identifier for this collector */
|
|
35
|
+
id: string;
|
|
36
|
+
|
|
37
|
+
/** Human-readable name */
|
|
38
|
+
displayName: string;
|
|
39
|
+
|
|
40
|
+
/** Optional description */
|
|
41
|
+
description?: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* PluginMetadata of transport strategies this collector supports.
|
|
45
|
+
* The registry uses this to match collectors to compatible strategies.
|
|
46
|
+
*/
|
|
47
|
+
supportedPlugins: PluginMetadata[];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether multiple instances of this collector can be added to one config.
|
|
51
|
+
* Default: false (one instance per collector type)
|
|
52
|
+
*/
|
|
53
|
+
allowMultiple?: boolean;
|
|
54
|
+
|
|
55
|
+
/** Collector configuration schema with versioning */
|
|
56
|
+
config: Versioned<TConfig>;
|
|
57
|
+
|
|
58
|
+
/** Per-execution result schema (with x-chart-* metadata) */
|
|
59
|
+
result: Versioned<TResult>;
|
|
60
|
+
|
|
61
|
+
/** Aggregated result schema for bucket storage */
|
|
62
|
+
aggregatedResult: Versioned<TAggregated>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Execute the collector using the provided transport client.
|
|
66
|
+
*
|
|
67
|
+
* @param params.config - Validated collector configuration
|
|
68
|
+
* @param params.client - Connected transport client
|
|
69
|
+
* @param params.pluginId - ID of the transport strategy invoking this collector
|
|
70
|
+
* @returns Collector result with typed metadata
|
|
71
|
+
*/
|
|
72
|
+
execute(params: {
|
|
73
|
+
config: TConfig;
|
|
74
|
+
client: TClient;
|
|
75
|
+
pluginId: string;
|
|
76
|
+
}): Promise<CollectorResult<TResult>>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Aggregate results from multiple runs into a summary.
|
|
80
|
+
* Called during retention processing.
|
|
81
|
+
*/
|
|
82
|
+
aggregateResult(runs: HealthCheckRunForAggregation<TResult>[]): TAggregated;
|
|
83
|
+
}
|
package/src/core-services.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { createServiceRef } from "./service-ref";
|
|
2
2
|
import type { RpcService } from "./rpc";
|
|
3
3
|
import type { HealthCheckRegistry } from "./health-check";
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
QueueManager,
|
|
7
|
-
} from "@checkstack/queue-api";
|
|
4
|
+
import type { CollectorRegistry } from "./collector-registry";
|
|
5
|
+
import type { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
|
|
8
6
|
import type { ConfigService } from "./config-service";
|
|
9
7
|
import type { SignalService } from "@checkstack/signal-common";
|
|
10
8
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
@@ -32,6 +30,9 @@ export const coreServices = {
|
|
|
32
30
|
healthCheckRegistry: createServiceRef<HealthCheckRegistry>(
|
|
33
31
|
"core.healthCheckRegistry"
|
|
34
32
|
),
|
|
33
|
+
collectorRegistry: createServiceRef<CollectorRegistry>(
|
|
34
|
+
"core.collectorRegistry"
|
|
35
|
+
),
|
|
35
36
|
pluginInstaller: createServiceRef<PluginInstaller>("core.pluginInstaller"),
|
|
36
37
|
rpc: createServiceRef<RpcService>("core.rpc"),
|
|
37
38
|
rpcClient: createServiceRef<RpcClient>("core.rpcClient"),
|
package/src/health-check.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Versioned } from "./config-versioning";
|
|
2
|
+
import type { TransportClient } from "./transport-client";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Health check result with typed metadata.
|
|
@@ -23,13 +24,35 @@ export interface HealthCheckRunForAggregation<
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
+
* Connected transport client with cleanup capability.
|
|
28
|
+
*/
|
|
29
|
+
export interface ConnectedClient<
|
|
30
|
+
TClient extends TransportClient<unknown, unknown>
|
|
31
|
+
> {
|
|
32
|
+
/** The connected transport client */
|
|
33
|
+
client: TClient;
|
|
34
|
+
/** Close the connection and release resources */
|
|
35
|
+
close(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Health check strategy definition with typed config and transport client.
|
|
40
|
+
*
|
|
41
|
+
* Strategies provide a `createClient` function that establishes a connection
|
|
42
|
+
* and returns a transport client. The platform executor handles running
|
|
43
|
+
* collectors and basic health check logic (connectivity test, latency measurement).
|
|
44
|
+
*
|
|
27
45
|
* @template TConfig - Configuration type for this strategy
|
|
28
|
-
* @template
|
|
46
|
+
* @template TClient - Transport client type (e.g., SshTransportClient)
|
|
47
|
+
* @template TResult - Per-run result type (for aggregation)
|
|
29
48
|
* @template TAggregatedResult - Aggregated result type for buckets
|
|
30
49
|
*/
|
|
31
50
|
export interface HealthCheckStrategy<
|
|
32
51
|
TConfig = unknown,
|
|
52
|
+
TClient extends TransportClient<unknown, unknown> = TransportClient<
|
|
53
|
+
unknown,
|
|
54
|
+
unknown
|
|
55
|
+
>,
|
|
33
56
|
TResult = Record<string, unknown>,
|
|
34
57
|
TAggregatedResult = Record<string, unknown>
|
|
35
58
|
> {
|
|
@@ -46,7 +69,15 @@ export interface HealthCheckStrategy<
|
|
|
46
69
|
/** Aggregated result schema for long-term bucket storage */
|
|
47
70
|
aggregatedResult: Versioned<TAggregatedResult>;
|
|
48
71
|
|
|
49
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Create a connected transport client from the configuration.
|
|
74
|
+
* The platform will use this client to execute collectors.
|
|
75
|
+
*
|
|
76
|
+
* @param config - Validated strategy configuration
|
|
77
|
+
* @returns Connected client wrapper with close() method
|
|
78
|
+
* @throws Error if connection fails (will be caught by executor)
|
|
79
|
+
*/
|
|
80
|
+
createClient(config: TConfig): Promise<ConnectedClient<TClient>>;
|
|
50
81
|
|
|
51
82
|
/**
|
|
52
83
|
* Aggregate results from multiple runs into a summary for bucket storage.
|
|
@@ -59,10 +90,47 @@ export interface HealthCheckStrategy<
|
|
|
59
90
|
): TAggregatedResult;
|
|
60
91
|
}
|
|
61
92
|
|
|
93
|
+
/**
|
|
94
|
+
* A registered strategy with its owning plugin metadata and qualified ID.
|
|
95
|
+
*/
|
|
96
|
+
export interface RegisteredStrategy {
|
|
97
|
+
strategy: HealthCheckStrategy<
|
|
98
|
+
unknown,
|
|
99
|
+
TransportClient<unknown, unknown>,
|
|
100
|
+
unknown,
|
|
101
|
+
unknown
|
|
102
|
+
>;
|
|
103
|
+
ownerPluginId: string;
|
|
104
|
+
qualifiedId: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
62
107
|
export interface HealthCheckRegistry {
|
|
63
|
-
register(
|
|
108
|
+
register(
|
|
109
|
+
strategy: HealthCheckStrategy<
|
|
110
|
+
unknown,
|
|
111
|
+
TransportClient<unknown, unknown>,
|
|
112
|
+
unknown,
|
|
113
|
+
unknown
|
|
114
|
+
>
|
|
115
|
+
): void;
|
|
64
116
|
getStrategy(
|
|
65
117
|
id: string
|
|
66
|
-
):
|
|
67
|
-
|
|
118
|
+
):
|
|
119
|
+
| HealthCheckStrategy<
|
|
120
|
+
unknown,
|
|
121
|
+
TransportClient<unknown, unknown>,
|
|
122
|
+
unknown,
|
|
123
|
+
unknown
|
|
124
|
+
>
|
|
125
|
+
| undefined;
|
|
126
|
+
getStrategies(): HealthCheckStrategy<
|
|
127
|
+
unknown,
|
|
128
|
+
TransportClient<unknown, unknown>,
|
|
129
|
+
unknown,
|
|
130
|
+
unknown
|
|
131
|
+
>[];
|
|
132
|
+
/**
|
|
133
|
+
* Get all registered strategies with their metadata (qualified ID, owner plugin).
|
|
134
|
+
*/
|
|
135
|
+
getStrategiesWithMeta(): RegisteredStrategy[];
|
|
68
136
|
}
|
package/src/index.ts
CHANGED
package/src/rpc.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { os as baseOs, ORPCError, Router } from "@orpc/server";
|
|
2
2
|
import { AnyContractRouter } from "@orpc/contract";
|
|
3
3
|
import { HealthCheckRegistry } from "./health-check";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "@checkstack/queue-api";
|
|
8
|
-
import {
|
|
9
|
-
ProcedureMetadata,
|
|
10
|
-
qualifyPermissionId,
|
|
11
|
-
} from "@checkstack/common";
|
|
4
|
+
import { CollectorRegistry } from "./collector-registry";
|
|
5
|
+
import { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
|
|
6
|
+
import { ProcedureMetadata, qualifyPermissionId } from "@checkstack/common";
|
|
12
7
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
13
8
|
import {
|
|
14
9
|
Logger,
|
|
@@ -43,6 +38,7 @@ export interface RpcContext {
|
|
|
43
38
|
auth: AuthService;
|
|
44
39
|
user?: AuthUser;
|
|
45
40
|
healthCheckRegistry: HealthCheckRegistry;
|
|
41
|
+
collectorRegistry: CollectorRegistry;
|
|
46
42
|
queuePluginRegistry: QueuePluginRegistry;
|
|
47
43
|
queueManager: QueueManager;
|
|
48
44
|
/** Emit a hook event for cross-plugin communication */
|
package/src/test-utils.ts
CHANGED
|
@@ -2,10 +2,8 @@ import { mock } from "bun:test";
|
|
|
2
2
|
import { RpcContext, EmitHookFn } from "./rpc";
|
|
3
3
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
4
4
|
import { HealthCheckRegistry } from "./health-check";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
QueueManager,
|
|
8
|
-
} from "@checkstack/queue-api";
|
|
5
|
+
import { CollectorRegistry } from "./collector-registry";
|
|
6
|
+
import { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* Creates a mocked oRPC context for testing.
|
|
@@ -39,10 +37,17 @@ export function createMockRpcContext(
|
|
|
39
37
|
getAnonymousPermissions: mock().mockResolvedValue([]),
|
|
40
38
|
},
|
|
41
39
|
healthCheckRegistry: {
|
|
42
|
-
|
|
40
|
+
register: mock(),
|
|
43
41
|
getStrategies: mock().mockReturnValue([]),
|
|
44
42
|
getStrategy: mock(),
|
|
43
|
+
getStrategiesWithMeta: mock().mockReturnValue([]),
|
|
45
44
|
} as unknown as HealthCheckRegistry,
|
|
45
|
+
collectorRegistry: {
|
|
46
|
+
register: mock(),
|
|
47
|
+
getCollector: mock(),
|
|
48
|
+
getCollectors: mock().mockReturnValue([]),
|
|
49
|
+
getCollectorsForPlugin: mock().mockReturnValue([]),
|
|
50
|
+
} as unknown as CollectorRegistry,
|
|
46
51
|
queuePluginRegistry: {
|
|
47
52
|
register: mock(),
|
|
48
53
|
getPlugin: mock(),
|