@checkstack/healthcheck-common 0.0.3 → 0.2.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,130 @@
1
1
  # @checkstack/healthcheck-common
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8e43507: # Teams and Resource-Level Access Control
8
+
9
+ This release introduces a comprehensive Teams system for organizing users and controlling access to resources at a granular level.
10
+
11
+ ## Features
12
+
13
+ ### Team Management
14
+
15
+ - Create, update, and delete teams with name and description
16
+ - Add/remove users from teams
17
+ - Designate team managers with elevated privileges
18
+ - View team membership and manager status
19
+
20
+ ### Resource-Level Access Control
21
+
22
+ - Grant teams access to specific resources (systems, health checks, incidents, maintenances)
23
+ - Configure read-only or manage permissions per team
24
+ - Resource-level "Team Only" mode that restricts access exclusively to team members
25
+ - Separate `resourceAccessSettings` table for resource-level settings (not per-grant)
26
+ - Automatic cleanup of grants when teams are deleted (database cascade)
27
+
28
+ ### Middleware Integration
29
+
30
+ - Extended `autoAuthMiddleware` to support resource access checks
31
+ - Single-resource pre-handler validation for detail endpoints
32
+ - Automatic list filtering for collection endpoints
33
+ - S2S endpoints for access verification
34
+
35
+ ### Frontend Components
36
+
37
+ - `TeamsTab` component for managing teams in Auth Settings
38
+ - `TeamAccessEditor` component for assigning team access to resources
39
+ - Resource-level "Team Only" toggle in `TeamAccessEditor`
40
+ - Integration into System, Health Check, Incident, and Maintenance editors
41
+
42
+ ## Breaking Changes
43
+
44
+ ### API Response Format Changes
45
+
46
+ List endpoints now return objects with named keys instead of arrays directly:
47
+
48
+ ```typescript
49
+ // Before
50
+ const systems = await catalogApi.getSystems();
51
+
52
+ // After
53
+ const { systems } = await catalogApi.getSystems();
54
+ ```
55
+
56
+ Affected endpoints:
57
+
58
+ - `catalog.getSystems` → `{ systems: [...] }`
59
+ - `healthcheck.getConfigurations` → `{ configurations: [...] }`
60
+ - `incident.listIncidents` → `{ incidents: [...] }`
61
+ - `maintenance.listMaintenances` → `{ maintenances: [...] }`
62
+
63
+ ### User Identity Enrichment
64
+
65
+ `RealUser` and `ApplicationUser` types now include `teamIds: string[]` field with team memberships.
66
+
67
+ ## Documentation
68
+
69
+ See `docs/backend/teams.md` for complete API reference and integration guide.
70
+
71
+ - 97c5a6b: Add UUID-based collector identification for better multiple collector support
72
+
73
+ **Breaking Change**: Existing health check configurations with collectors need to be recreated.
74
+
75
+ - Each collector instance now has a unique UUID assigned on creation
76
+ - Collector results are stored under the UUID key with `_collectorId` and `_assertionFailed` metadata
77
+ - Auto-charts correctly display separate charts for each collector instance
78
+ - Charts are now grouped by collector instance with clear headings
79
+ - Assertion status card shows pass/fail for each collector
80
+ - Renamed "Success" to "HTTP Success" to clarify it's about HTTP request success
81
+ - Fixed deletion of collectors not persisting to database
82
+ - Fixed duplicate React key warnings in auto-chart grid
83
+
84
+ ### Patch Changes
85
+
86
+ - Updated dependencies [8e43507]
87
+ - @checkstack/common@0.1.0
88
+ - @checkstack/signal-common@0.0.4
89
+
90
+ ## 0.1.0
91
+
92
+ ### Minor Changes
93
+
94
+ - f5b1f49: Extended health check system with per-collector assertion support.
95
+
96
+ - Added `collectors` column to `healthCheckConfigurations` schema for storing collector configs
97
+ - Updated queue-executor to run configured collectors and evaluate per-collector assertions
98
+ - Added `CollectorAssertionSchema` to healthcheck-common for assertion validation
99
+ - Results now stored with `metadata.collectors` containing per-collector result data
100
+
101
+ - f5b1f49: Added JSONPath assertions for response body validation and fully qualified strategy IDs.
102
+
103
+ **JSONPath Assertions:**
104
+
105
+ - Added `healthResultJSONPath()` factory in healthcheck-common for fields supporting JSONPath queries
106
+ - Extended AssertionBuilder with jsonpath field type showing path input (e.g., `$.data.status`)
107
+ - Added `jsonPath` field to `CollectorAssertionSchema` for persistence
108
+ - HTTP Request collector body field now supports JSONPath assertions
109
+
110
+ **Fully Qualified Strategy IDs:**
111
+
112
+ - HealthCheckRegistry now uses scoped factories like CollectorRegistry
113
+ - Strategies are stored with `pluginId.strategyId` format
114
+ - Added `getStrategiesWithMeta()` method to HealthCheckRegistry interface
115
+ - Router returns qualified IDs so frontend can correctly fetch collectors
116
+
117
+ **UI Improvements:**
118
+
119
+ - Save button disabled when collector configs have invalid required fields
120
+ - Fixed nested button warning in CollectorList accordion
121
+
122
+ ### Patch Changes
123
+
124
+ - Updated dependencies [f5b1f49]
125
+ - @checkstack/common@0.0.3
126
+ - @checkstack/signal-common@0.0.3
127
+
3
128
  ## 0.0.3
4
129
 
5
130
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-common",
3
- "version": "0.0.3",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@ export interface HealthCheckStrategyDto {
15
15
  configSchema: Record<string, unknown>;
16
16
  }
17
17
 
18
+ import type { CollectorConfigEntry } from "./schemas";
19
+
18
20
  /**
19
21
  * Represents a Health Check Configuration (the check definition/template).
20
22
  * NOTE: This is derived from Zod schema but kept as interface for explicit type documentation.
@@ -25,6 +27,7 @@ export interface HealthCheckConfiguration {
25
27
  strategyId: string;
26
28
  config: Record<string, unknown>;
27
29
  intervalSeconds: number;
30
+ collectors?: CollectorConfigEntry[];
28
31
  createdAt: Date;
29
32
  updatedAt: Date;
30
33
  }
@@ -1,6 +1,7 @@
1
1
  import { oc } from "@orpc/contract";
2
2
  import {
3
3
  createClientDefinition,
4
+ createResourceAccessList,
4
5
  type ProcedureMetadata,
5
6
  } from "@checkstack/common";
6
7
  import { pluginMetadata } from "./plugin-metadata";
@@ -8,6 +9,7 @@ import { z } from "zod";
8
9
  import { permissions } from "./permissions";
9
10
  import {
10
11
  HealthCheckStrategyDtoSchema,
12
+ CollectorDtoSchema,
11
13
  HealthCheckConfigurationSchema,
12
14
  CreateHealthCheckConfigurationSchema,
13
15
  UpdateHealthCheckConfigurationSchema,
@@ -24,6 +26,12 @@ import {
24
26
  // Base builder with full metadata support
25
27
  const _base = oc.$meta<ProcedureMetadata>({});
26
28
 
29
+ // Resource access configurations for team-based access control
30
+ const configListAccess = createResourceAccessList(
31
+ "configuration",
32
+ "configurations"
33
+ );
34
+
27
35
  // --- Response Schemas for Evaluated Status ---
28
36
 
29
37
  const SystemCheckStatusSchema = z.object({
@@ -53,6 +61,18 @@ export const healthCheckContract = {
53
61
  })
54
62
  .output(z.array(HealthCheckStrategyDtoSchema)),
55
63
 
64
+ /**
65
+ * Get available collectors for a specific strategy.
66
+ * Returns collectors that support the given strategy's transport.
67
+ */
68
+ getCollectors: _base
69
+ .meta({
70
+ userType: "authenticated",
71
+ permissions: [permissions.healthCheckRead.id],
72
+ })
73
+ .input(z.object({ strategyId: z.string() }))
74
+ .output(z.array(CollectorDtoSchema)),
75
+
56
76
  // ==========================================================================
57
77
  // CONFIGURATION MANAGEMENT (userType: "authenticated")
58
78
  // ==========================================================================
@@ -61,8 +81,11 @@ export const healthCheckContract = {
61
81
  .meta({
62
82
  userType: "authenticated",
63
83
  permissions: [permissions.healthCheckRead.id],
84
+ resourceAccess: [configListAccess],
64
85
  })
65
- .output(z.array(HealthCheckConfigurationSchema)),
86
+ .output(
87
+ z.object({ configurations: z.array(HealthCheckConfigurationSchema) })
88
+ ),
66
89
 
67
90
  createConfiguration: _base
68
91
  .meta({
package/src/schemas.ts CHANGED
@@ -13,21 +13,87 @@ export const HealthCheckStrategyDtoSchema = z.object({
13
13
  aggregatedResultSchema: z.record(z.string(), z.unknown()).optional(),
14
14
  });
15
15
 
16
+ export type HealthCheckStrategyDto = z.infer<
17
+ typeof HealthCheckStrategyDtoSchema
18
+ >;
19
+
20
+ /**
21
+ * Collector DTO for frontend discovery.
22
+ * ID is fully-qualified: `pluginId.collectorId` (e.g., `collector-hardware.cpu`)
23
+ */
24
+ export const CollectorDtoSchema = z.object({
25
+ /** Fully-qualified ID: pluginId.collectorId */
26
+ id: z.string(),
27
+ /** Human-readable name */
28
+ displayName: z.string(),
29
+ /** Optional description */
30
+ description: z.string().optional(),
31
+ /** JSON Schema for collector configuration */
32
+ configSchema: z.record(z.string(), z.unknown()),
33
+ /** JSON Schema for per-run result metadata (with chart annotations) */
34
+ resultSchema: z.record(z.string(), z.unknown()),
35
+ /** Whether multiple instances of this collector are allowed per config */
36
+ allowMultiple: z.boolean(),
37
+ });
38
+
39
+ export type CollectorDto = z.infer<typeof CollectorDtoSchema>;
40
+
41
+ /**
42
+ * A single collector assertion with field, operator, and optional value.
43
+ */
44
+ export const CollectorAssertionSchema = z.object({
45
+ /** Field path to assert on (from collector result schema) */
46
+ field: z.string(),
47
+ /** JSONPath expression for jsonpath-type assertions (e.g., $.status) */
48
+ jsonPath: z.string().optional(),
49
+ /** Comparison operator */
50
+ operator: z.string(),
51
+ /** Expected value (not needed for some operators like exists, isTrue) */
52
+ value: z.unknown().optional(),
53
+ });
54
+
55
+ export type CollectorAssertion = z.infer<typeof CollectorAssertionSchema>;
56
+
57
+ /**
58
+ * A collector configuration entry within a health check.
59
+ * Each entry includes a unique ID, the collector type ID, its config, and per-collector assertions.
60
+ */
61
+ export const CollectorConfigEntrySchema = z.object({
62
+ /** Unique ID for this collector instance (UUID) */
63
+ id: z.string(),
64
+ /** Fully-qualified collector ID (e.g., collector-hardware.cpu) */
65
+ collectorId: z.string(),
66
+ /** Collector-specific configuration */
67
+ config: z.record(z.string(), z.unknown()),
68
+ /** Per-collector assertions (schema derived from collector's resultSchema) */
69
+ assertions: z.array(CollectorAssertionSchema).optional(),
70
+ });
71
+
72
+ export type CollectorConfigEntry = z.infer<typeof CollectorConfigEntrySchema>;
73
+
16
74
  export const HealthCheckConfigurationSchema = z.object({
17
75
  id: z.string(),
18
76
  name: z.string(),
19
77
  strategyId: z.string(),
20
78
  config: z.record(z.string(), z.unknown()),
21
79
  intervalSeconds: z.number(),
80
+ /** Optional collector configurations */
81
+ collectors: z.array(CollectorConfigEntrySchema).optional(),
22
82
  createdAt: z.date(),
23
83
  updatedAt: z.date(),
24
84
  });
25
85
 
86
+ export type HealthCheckConfiguration = z.infer<
87
+ typeof HealthCheckConfigurationSchema
88
+ >;
89
+
26
90
  export const CreateHealthCheckConfigurationSchema = z.object({
27
91
  name: z.string().min(1),
28
92
  strategyId: z.string().min(1),
29
93
  config: z.record(z.string(), z.unknown()),
30
94
  intervalSeconds: z.number().min(1),
95
+ /** Optional collector configurations */
96
+ collectors: z.array(CollectorConfigEntrySchema).optional(),
31
97
  });
32
98
 
33
99
  export type CreateHealthCheckConfiguration = z.infer<
@@ -40,6 +40,8 @@ export interface HealthResultMeta {
40
40
  "x-chart-label"?: string;
41
41
  /** Unit suffix for values (e.g., 'ms', '%', 'req/s') */
42
42
  "x-chart-unit"?: string;
43
+ /** Whether this field supports JSONPath assertions */
44
+ "x-jsonpath"?: boolean;
43
45
  }
44
46
 
45
47
  /**
@@ -52,6 +54,9 @@ export const healthResultRegistry = z.registry<HealthResultMeta>();
52
54
  // TYPED HEALTH RESULT FACTORIES
53
55
  // ============================================================================
54
56
 
57
+ /** Chart metadata (excludes x-jsonpath, use healthResultJSONPath for that) */
58
+ type ChartMeta = Omit<HealthResultMeta, "x-jsonpath">;
59
+
55
60
  /**
56
61
  * Create a health result string field with typed chart metadata.
57
62
  *
@@ -64,7 +69,7 @@ export const healthResultRegistry = z.registry<HealthResultMeta>();
64
69
  * });
65
70
  * ```
66
71
  */
67
- export function healthResultString(meta: HealthResultMeta) {
72
+ export function healthResultString(meta: ChartMeta) {
68
73
  const schema = z.string();
69
74
  schema.register(healthResultRegistry, meta);
70
75
  return schema;
@@ -73,7 +78,7 @@ export function healthResultString(meta: HealthResultMeta) {
73
78
  /**
74
79
  * Create a health result number field with typed chart metadata.
75
80
  */
76
- export function healthResultNumber(meta: HealthResultMeta) {
81
+ export function healthResultNumber(meta: ChartMeta) {
77
82
  const schema = z.number();
78
83
  schema.register(healthResultRegistry, meta);
79
84
  return schema;
@@ -82,12 +87,31 @@ export function healthResultNumber(meta: HealthResultMeta) {
82
87
  /**
83
88
  * Create a health result boolean field with typed chart metadata.
84
89
  */
85
- export function healthResultBoolean(meta: HealthResultMeta) {
90
+ export function healthResultBoolean(meta: ChartMeta) {
86
91
  const schema = z.boolean();
87
92
  schema.register(healthResultRegistry, meta);
88
93
  return schema;
89
94
  }
90
95
 
96
+ /**
97
+ * Create a health result string field with JSONPath assertion support.
98
+ * The UI will show a JSONPath input field for this result.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * import { healthResultJSONPath } from "@checkstack/healthcheck-common";
103
+ *
104
+ * const resultSchema = z.object({
105
+ * body: healthResultJSONPath(),
106
+ * });
107
+ * ```
108
+ */
109
+ export function healthResultJSONPath(meta: ChartMeta) {
110
+ const schema = z.string();
111
+ schema.register(healthResultRegistry, { ...meta, "x-jsonpath": true });
112
+ return schema;
113
+ }
114
+
91
115
  // ============================================================================
92
116
  // METADATA RETRIEVAL - For toJsonSchema integration
93
117
  // ============================================================================