@checkstack/backend-api 0.11.0 → 0.12.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,81 @@
1
1
  # @checkstack/backend-api
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 26d8bae: Distributed satellite health checks and Assignment IDE page
8
+
9
+ **Satellite System**
10
+
11
+ - New `satellite-backend`, `satellite-common`, `satellite-frontend`, and `satellite` agent packages for distributed health check execution
12
+ - WebSocket-based satellite connectivity with authentication, heartbeats, and live configuration push
13
+ - Satellite management UI with create dialog, status badges, and list page
14
+
15
+ **Live Configuration Updates**
16
+
17
+ - Added `assignmentChanged` hook to `healthcheck-backend` for cross-plugin communication
18
+ - `satellite-backend` subscribes to assignment changes and pushes config updates to connected satellites in real-time
19
+
20
+ **Assignment IDE Page**
21
+
22
+ - Replaced the 1028-line modal-based `SystemHealthCheckAssignment` component with a full-page IDE layout
23
+ - New modular components: `AssignmentTree`, `GeneralPanel`, `ThresholdsPanel`, `RetentionPanel`, `ExecutionPanel`
24
+ - Added unassign capability and sorted assignment lists for stable ordering
25
+
26
+ **Shared IDE Primitives**
27
+
28
+ - Extracted `IDETreeNode`, `IDETreeSection`, `IDEStatusBar`, `IDELayout` to `@checkstack/ui` for cross-plugin reuse
29
+ - Migrated existing health check IDE editor to use shared primitives
30
+
31
+ **Infrastructure**
32
+
33
+ - Added `Dockerfile.satellite` for containerized satellite deployment
34
+ - WebSocket route registry in `@checkstack/backend` and `@checkstack/backend-api`
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies [26d8bae]
39
+ - Updated dependencies [26d8bae]
40
+ - @checkstack/healthcheck-common@0.11.0
41
+ - @checkstack/queue-api@0.2.13
42
+
43
+ ## 0.11.1
44
+
45
+ ### Patch Changes
46
+
47
+ - d1a2796: Enforce stricter code quality standards and eliminate AI slop anti-patterns.
48
+
49
+ **New utility**
50
+
51
+ - `extractErrorMessage(error, fallback?)` in `@checkstack/common` for consistent error extraction
52
+
53
+ **ESLint rules**
54
+
55
+ - `react-hooks/rules-of-hooks` and `exhaustive-deps` for hook correctness
56
+ - `no-console` in frontend packages — forces `toast` over silent `console.error`
57
+ - `no-restricted-syntax` banning `instanceof Error` — forces `extractErrorMessage`
58
+ - Custom `no-eslint-disable-any` rule preventing `@typescript-eslint/no-explicit-any` circumvention
59
+
60
+ **Refactoring**
61
+
62
+ - Replace 141 `instanceof Error` boilerplate patterns across the codebase
63
+ - Replace swallowed `console.error` with user-visible `toast.error()` feedback
64
+ - Remove 15 redundant `as` type casts in IntegrationsPage and ProviderConnectionsPage
65
+ - Consolidate 3 identical callback handlers into `handleDialogClose`
66
+ - Fix conditional React hook call in `FormField.tsx`
67
+ - Fix unstable useMemo deps in `Dashboard.tsx`
68
+ - Replace `useEffect`→`setState` with derived `useMemo` in `RegisterPage.tsx`
69
+ - Rewrite `keystore.test.ts` with typed `DrizzleMockChain` (eliminating 7 `any` suppressions)
70
+ - Delete obvious comments in `encryption.ts` and Teams `provider.ts`
71
+
72
+ - Updated dependencies [d1a2796]
73
+ - Updated dependencies [3c34b07]
74
+ - @checkstack/common@0.6.5
75
+ - @checkstack/healthcheck-common@0.10.1
76
+ - @checkstack/signal-common@0.1.9
77
+ - @checkstack/queue-api@0.2.12
78
+
3
79
  ## 0.11.0
4
80
 
5
81
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend-api",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "scripts": {
@@ -9,10 +9,10 @@
9
9
  "lint:code": "eslint . --max-warnings 0"
10
10
  },
11
11
  "dependencies": {
12
- "@checkstack/common": "0.6.4",
13
- "@checkstack/healthcheck-common": "0.8.4",
14
- "@checkstack/queue-api": "0.2.9",
15
- "@checkstack/signal-common": "0.1.8",
12
+ "@checkstack/common": "0.6.5",
13
+ "@checkstack/healthcheck-common": "0.10.1",
14
+ "@checkstack/queue-api": "0.2.12",
15
+ "@checkstack/signal-common": "0.1.9",
16
16
  "@orpc/client": "^1.13.14",
17
17
  "@orpc/contract": "^1.13.14",
18
18
  "@orpc/openapi": "^1.13.2",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/bun": "latest",
28
- "@checkstack/tsconfig": "0.0.4",
28
+ "@checkstack/tsconfig": "0.0.5",
29
29
  "@checkstack/scripts": "0.1.2"
30
30
  },
31
31
  "peerDependencies": {
@@ -66,8 +66,7 @@ export class MigrationBuilder<TCurrent> {
66
66
  migration: Migration<TCurrent, TNext>
67
67
  ): MigrationBuilder<TNext> {
68
68
  this.migrations.push(migration);
69
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
- return this as any as MigrationBuilder<TNext>;
69
+ return this as unknown as MigrationBuilder<TNext>;
71
70
  }
72
71
 
73
72
  /**
@@ -14,6 +14,7 @@ import {
14
14
  RpcClient,
15
15
  } from "./types";
16
16
  import type { EventBus } from "./event-bus-types";
17
+ import type { WebSocketRouteRegistry } from "./ws-registry";
17
18
 
18
19
  export * from "./types";
19
20
 
@@ -43,4 +44,5 @@ export const coreServices = {
43
44
  config: createServiceRef<ConfigService>("core.config"),
44
45
  eventBus: createServiceRef<EventBus>("core.eventBus"),
45
46
  signalService: createServiceRef<SignalService>("core.signalService"),
47
+ wsRegistry: createServiceRef<WebSocketRouteRegistry>("core.wsRegistry"),
46
48
  };
package/src/encryption.ts CHANGED
@@ -30,22 +30,18 @@ const getMasterKey = (): Buffer => {
30
30
  export const encrypt = (plaintext: string): string => {
31
31
  const key = getMasterKey();
32
32
 
33
- // Generate random IV (12 bytes for GCM)
34
33
  const iv = crypto.randomBytes(12);
35
34
 
36
- // Create cipher
37
35
  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
38
36
 
39
- // Encrypt
40
37
  const encrypted = Buffer.concat([
41
38
  cipher.update(plaintext, "utf8"),
42
39
  cipher.final(),
43
40
  ]);
44
41
 
45
- // Get auth tag
46
42
  const authTag = cipher.getAuthTag();
47
43
 
48
- // Return base64-encoded iv:authTag:ciphertext
44
+
49
45
  return `${iv.toString("base64")}:${authTag.toString(
50
46
  "base64"
51
47
  )}:${encrypted.toString("base64")}`;
@@ -58,7 +54,6 @@ export const encrypt = (plaintext: string): string => {
58
54
  export const decrypt = (encrypted: string): string => {
59
55
  const key = getMasterKey();
60
56
 
61
- // Parse the encrypted string
62
57
  const parts = encrypted.split(":");
63
58
  if (parts.length !== 3) {
64
59
  throw new Error("Invalid encrypted format");
@@ -68,11 +63,9 @@ export const decrypt = (encrypted: string): string => {
68
63
  const authTag = Buffer.from(parts[1], "base64");
69
64
  const ciphertext = Buffer.from(parts[2], "base64");
70
65
 
71
- // Create decipher
72
66
  const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
73
67
  decipher.setAuthTag(authTag);
74
68
 
75
- // Decrypt
76
69
  const decrypted = Buffer.concat([
77
70
  decipher.update(ciphertext),
78
71
  decipher.final(),
package/src/index.ts CHANGED
@@ -27,3 +27,4 @@ export * from "./collector-strategy";
27
27
  export * from "./collector-registry";
28
28
  export * from "./incremental-aggregation";
29
29
  export * from "./aggregated-result";
30
+ export * from "./ws-registry";
@@ -7,6 +7,8 @@
7
7
  * @module @checkstack/backend-api/oauth-handler
8
8
  */
9
9
 
10
+ import { extractErrorMessage } from "@checkstack/common";
11
+
10
12
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
11
13
  // OAuth Configuration Interface
12
14
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -416,7 +418,7 @@ export function createOAuthHandler(
416
418
  return Response.json(result);
417
419
  } catch (error) {
418
420
  return Response.json(
419
- { error: error instanceof Error ? error.message : "Refresh failed" },
421
+ { error: extractErrorMessage(error, "Refresh failed") },
420
422
  { status: 500 }
421
423
  );
422
424
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * A generic interface for a WebSocket connection managed by the core server.
3
+ * Plugins interact with WebSocket clients through this adapter, insulating
4
+ * them from the underlying Bun WebSocket API.
5
+ */
6
+ export interface WsConnection {
7
+ /** Send a string message to the client */
8
+ send(data: string): void;
9
+ /** Close the connection */
10
+ close(): void;
11
+ }
12
+
13
+ /**
14
+ * Per-connection handlers returned by a route handler's `onConnection` callback.
15
+ * These closures capture the connection's state (e.g., auth state, session data).
16
+ */
17
+ export interface WsConnectionHandlers {
18
+ onMessage: (message: string) => void | Promise<void>;
19
+ onClose: () => void;
20
+ }
21
+
22
+ /**
23
+ * A WebSocket route handler registered by a plugin.
24
+ * The core server calls `onConnection` when a new WebSocket is opened on
25
+ * the handler's registered path, and the handler returns per-connection
26
+ * event callbacks.
27
+ */
28
+ export interface WebSocketRouteHandler {
29
+ /**
30
+ * Called when a new WebSocket connection opens on this route.
31
+ * Return per-connection message/close handlers.
32
+ */
33
+ onConnection(ws: WsConnection): WsConnectionHandlers;
34
+ }
35
+
36
+ /**
37
+ * Scoped registry for plugin WebSocket route handlers.
38
+ * Each plugin receives a scoped instance (via factory) that auto-prefixes
39
+ * the pluginId — just like the RPC service.
40
+ *
41
+ * Plugins register handlers with a local path segment, and the core server
42
+ * makes them available under `/api/ws/{pluginId}{path}`.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * // In satellite-backend init (pluginId "satellite" is auto-prefixed):
47
+ * wsRegistry.register("/", handler);
48
+ * // → Available at /api/ws/satellite
49
+ *
50
+ * wsRegistry.register("/connect", handler);
51
+ * // → Available at /api/ws/satellite/connect
52
+ * ```
53
+ */
54
+ export interface WebSocketRouteRegistry {
55
+ /**
56
+ * Register a WebSocket route handler at the given path.
57
+ * The pluginId prefix is applied automatically by the scoped factory.
58
+ */
59
+ register(path: string, handler: WebSocketRouteHandler): void;
60
+ }
61
+
62
+ /**
63
+ * Core-level store for all registered WS routes.
64
+ * The backend server uses this to look up handlers during WebSocket upgrade.
65
+ * Not exposed to plugins — they interact via the scoped `WebSocketRouteRegistry`.
66
+ */
67
+ export interface WebSocketRouteStore {
68
+ /** Look up a handler by full path (e.g., "satellite" or "satellite/connect"). */
69
+ getHandler(fullPath: string): WebSocketRouteHandler | undefined;
70
+ }