@checkstack/backend-api 0.12.0 → 0.13.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 CHANGED
@@ -1,5 +1,42 @@
1
1
  # @checkstack/backend-api
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8d1ef12: ## Infrastructure Configuration Shell & Cache System
8
+
9
+ ### New Packages
10
+
11
+ - **`@checkstack/cache-api`**: Core cache abstractions — `CacheProvider` interface, `createScopedCache` factory for plugin key isolation, `CachePlugin`/`CacheManager` lifecycle interfaces.
12
+ - **`@checkstack/cache-common`**: Shared cache types, RPC contract (`getPlugins`, `getConfiguration`, `updateConfiguration`), access rules, and plugin metadata.
13
+ - **`@checkstack/cache-backend`**: Cache settings RPC router — exposes plugin discovery, configuration read/write endpoints with access-gated authorization.
14
+ - **`@checkstack/cache-frontend`**: Cache configuration tab component for the Infrastructure Settings page.
15
+ - **`@checkstack/infrastructure-common`**: Infrastructure tab registry, routes, and shared types for the IDE-style configuration shell.
16
+ - **`@checkstack/infrastructure-frontend`**: Infrastructure Settings page with vertical tab bar, per-tab access control, and user menu integration.
17
+
18
+ ### Modified Packages
19
+
20
+ - **`@checkstack/backend-api`**: Added `cachePluginRegistry` and `cacheManager` to `RpcContext` and `coreServices`.
21
+ - **`@checkstack/backend`**: Registered cache services in boot sequence, added cache config loading, extended dependency sorter for cache plugin ordering.
22
+ - **`@checkstack/queue-frontend`**: Refactored from standalone `/queue/config` route to an infrastructure tab. Queue settings now live inside the Infrastructure Settings page.
23
+
24
+ ### Architecture
25
+
26
+ The former monolithic Queue Config page is replaced by a pluggable Infrastructure Settings shell (`/infrastructure/config`). Plugins register configuration tabs via `registerInfrastructureTab()` with their own access rules, icons, and components. The shell evaluates per-tab access and only renders tabs the user can see.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [8d1ef12]
31
+ - Updated dependencies [8d1ef12]
32
+ - Updated dependencies [8d1ef12]
33
+ - Updated dependencies [8d1ef12]
34
+ - @checkstack/healthcheck-common@0.12.0
35
+ - @checkstack/common@0.7.0
36
+ - @checkstack/cache-api@0.2.0
37
+ - @checkstack/signal-common@0.1.10
38
+ - @checkstack/queue-api@0.2.14
39
+
3
40
  ## 0.12.0
4
41
 
5
42
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend-api",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "scripts": {
@@ -10,8 +10,9 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@checkstack/common": "0.6.5",
13
- "@checkstack/healthcheck-common": "0.10.1",
14
- "@checkstack/queue-api": "0.2.12",
13
+ "@checkstack/healthcheck-common": "0.11.0",
14
+ "@checkstack/cache-api": "0.1.0",
15
+ "@checkstack/queue-api": "0.2.13",
15
16
  "@checkstack/signal-common": "0.1.9",
16
17
  "@orpc/client": "^1.13.14",
17
18
  "@orpc/contract": "^1.13.14",
@@ -13,6 +13,7 @@ describe("aggregatedAverage", () => {
13
13
  const field = aggregatedAverage({
14
14
  "x-chart-type": "line",
15
15
  "x-chart-label": "Avg Response Time",
16
+ "x-anomaly-enabled": false,
16
17
  });
17
18
 
18
19
  expect(field.type).toBe("average");
@@ -39,6 +40,7 @@ describe("aggregatedRate", () => {
39
40
  const field = aggregatedRate({
40
41
  "x-chart-type": "gauge",
41
42
  "x-chart-label": "Success Rate",
43
+ "x-anomaly-enabled": false,
42
44
  });
43
45
 
44
46
  expect(field.type).toBe("rate");
@@ -65,6 +67,7 @@ describe("aggregatedCounter", () => {
65
67
  it("creates field with correct type", () => {
66
68
  const field = aggregatedCounter({
67
69
  "x-chart-type": "counter",
70
+ "x-anomaly-enabled": false,
68
71
  });
69
72
 
70
73
  expect(field.type).toBe("counter");
@@ -81,6 +84,7 @@ describe("aggregatedMinMax", () => {
81
84
  it("creates field with correct type", () => {
82
85
  const field = aggregatedMinMax({
83
86
  "x-chart-type": "line",
87
+ "x-anomaly-enabled": false,
84
88
  });
85
89
 
86
90
  expect(field.type).toBe("minmax");
@@ -202,6 +206,7 @@ describe("chart metadata registration", () => {
202
206
  "x-chart-type": "line",
203
207
  "x-chart-label": "Avg Response Time",
204
208
  "x-chart-unit": "ms",
209
+ "x-anomaly-enabled": false,
205
210
  });
206
211
 
207
212
  // The stateSchema should have metadata registered via healthResultRegistry
@@ -219,6 +224,7 @@ describe("chart metadata registration", () => {
219
224
  "x-chart-type": "gauge",
220
225
  "x-chart-label": "Success Rate",
221
226
  "x-chart-unit": "%",
227
+ "x-anomaly-enabled": false,
222
228
  });
223
229
 
224
230
  const meta = getHealthResultMeta(field.stateSchema);
@@ -232,6 +238,7 @@ describe("chart metadata registration", () => {
232
238
  const field = aggregatedCounter({
233
239
  "x-chart-type": "counter",
234
240
  "x-chart-label": "Error Count",
241
+ "x-anomaly-enabled": false,
235
242
  });
236
243
 
237
244
  const meta = getHealthResultMeta(field.stateSchema);
@@ -245,6 +252,7 @@ describe("chart metadata registration", () => {
245
252
  const field = aggregatedMinMax({
246
253
  "x-chart-type": "line",
247
254
  "x-chart-label": "Latency Range",
255
+ "x-anomaly-enabled": false,
248
256
  });
249
257
 
250
258
  const meta = getHealthResultMeta(field.stateSchema);
@@ -258,11 +266,13 @@ describe("chart metadata registration", () => {
258
266
  const field1 = aggregatedAverage({
259
267
  "x-chart-type": "line",
260
268
  "x-chart-label": "First Field",
269
+ "x-anomaly-enabled": false,
261
270
  });
262
271
 
263
272
  const field2 = aggregatedAverage({
264
273
  "x-chart-type": "gauge",
265
274
  "x-chart-label": "Second Field",
275
+ "x-anomaly-enabled": false,
266
276
  });
267
277
 
268
278
  const meta1 = getHealthResultMeta(field1.stateSchema);
@@ -40,7 +40,8 @@ export type AggregationType = "average" | "rate" | "counter" | "minmax";
40
40
  // =============================================================================
41
41
 
42
42
  /** Base metadata for chart annotations (excludes x-jsonpath) */
43
- type ChartMeta = Omit<HealthResultMeta, "x-jsonpath">;
43
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
44
+ type ChartMeta = DistributiveOmit<HealthResultMeta, "x-jsonpath">;
44
45
 
45
46
  /**
46
47
  * Base interface for aggregated field definitions.
@@ -207,6 +208,7 @@ export function aggregatedRate(meta: ChartMeta): AggregatedRateField {
207
208
  * errorCount: aggregatedCounter({
208
209
  * "x-chart-type": "counter",
209
210
  * "x-chart-label": "Errors",
211
+ "x-anomaly-direction": "lower-is-better",
210
212
  * }),
211
213
  * };
212
214
  * ```
@@ -3,6 +3,10 @@ import type { RpcService } from "./rpc";
3
3
  import type { HealthCheckRegistry } from "./health-check";
4
4
  import type { CollectorRegistry } from "./collector-registry";
5
5
  import type { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
6
+ import type {
7
+ CachePluginRegistry,
8
+ CacheManager,
9
+ } from "@checkstack/cache-api";
6
10
  import type { ConfigService } from "./config-service";
7
11
  import type { SignalService } from "@checkstack/signal-common";
8
12
  import { SafeDatabase } from "./plugin-system";
@@ -45,4 +49,8 @@ export const coreServices = {
45
49
  eventBus: createServiceRef<EventBus>("core.eventBus"),
46
50
  signalService: createServiceRef<SignalService>("core.signalService"),
47
51
  wsRegistry: createServiceRef<WebSocketRouteRegistry>("core.wsRegistry"),
52
+ cachePluginRegistry: createServiceRef<CachePluginRegistry>(
53
+ "core.cachePluginRegistry",
54
+ ),
55
+ cacheManager: createServiceRef<CacheManager>("core.cacheManager"),
48
56
  };
package/src/rpc.ts CHANGED
@@ -3,6 +3,10 @@ import { AnyContractRouter } from "@orpc/contract";
3
3
  import { HealthCheckRegistry } from "./health-check";
4
4
  import { CollectorRegistry } from "./collector-registry";
5
5
  import { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
6
+ import {
7
+ CachePluginRegistry,
8
+ CacheManager,
9
+ } from "@checkstack/cache-api";
6
10
  import {
7
11
  ProcedureMetadata,
8
12
  qualifyAccessRuleId,
@@ -45,6 +49,8 @@ export interface RpcContext {
45
49
  collectorRegistry: CollectorRegistry;
46
50
  queuePluginRegistry: QueuePluginRegistry;
47
51
  queueManager: QueueManager;
52
+ cachePluginRegistry: CachePluginRegistry;
53
+ cacheManager: CacheManager;
48
54
  /** Emit a hook event for cross-plugin communication */
49
55
  emitHook: EmitHookFn;
50
56
  }
package/src/test-utils.ts CHANGED
@@ -4,6 +4,10 @@ import { SafeDatabase } from "./plugin-system";
4
4
  import { HealthCheckRegistry } from "./health-check";
5
5
  import { CollectorRegistry } from "./collector-registry";
6
6
  import { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
7
+ import {
8
+ CachePluginRegistry,
9
+ CacheManager,
10
+ } from "@checkstack/cache-api";
7
11
 
8
12
  /**
9
13
  * Creates a mocked oRPC context for testing.
@@ -69,6 +73,18 @@ export function createMockRpcContext(
69
73
  startPolling: mock(),
70
74
  shutdown: mock(),
71
75
  } as unknown as QueueManager,
76
+ cachePluginRegistry: {
77
+ register: mock(),
78
+ getPlugin: mock(),
79
+ getPlugins: mock().mockReturnValue([]),
80
+ } as unknown as CachePluginRegistry,
81
+ cacheManager: {
82
+ getProvider: mock(),
83
+ getActivePlugin: mock().mockReturnValue("memory"),
84
+ getActiveConfig: mock(),
85
+ setActiveBackend: mock(),
86
+ shutdown: mock(),
87
+ } as unknown as CacheManager,
72
88
  // Default: authenticated user with wildcard access for testing
73
89
  user: { type: "user" as const, id: "test-user", accessRules: ["*"] },
74
90
  emitHook: mock() as unknown as EmitHookFn,