@checkstack/healthcheck-common 0.1.0 → 0.3.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,176 @@
1
1
  # @checkstack/healthcheck-common
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9faec1f: # Unified AccessRule Terminology Refactoring
8
+
9
+ This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
10
+
11
+ ## Changes
12
+
13
+ ### Core Infrastructure (`@checkstack/common`)
14
+
15
+ - Introduced `AccessRule` interface as the primary access control type
16
+ - Added `accessPair()` helper for creating read/manage access rule pairs
17
+ - Added `access()` builder for individual access rules
18
+ - Replaced `Permission` type with `AccessRule` throughout
19
+
20
+ ### API Changes
21
+
22
+ - `env.registerPermissions()` → `env.registerAccessRules()`
23
+ - `meta.permissions` → `meta.access` in RPC contracts
24
+ - `usePermission()` → `useAccess()` in frontend hooks
25
+ - Route `permission:` field → `accessRule:` field
26
+
27
+ ### UI Changes
28
+
29
+ - "Roles & Permissions" tab → "Roles & Access Rules"
30
+ - "You don't have permission..." → "You don't have access..."
31
+ - All permission-related UI text updated
32
+
33
+ ### Documentation & Templates
34
+
35
+ - Updated 18 documentation files with AccessRule terminology
36
+ - Updated 7 scaffolding templates with `accessPair()` pattern
37
+ - All code examples use new AccessRule API
38
+
39
+ ## Migration Guide
40
+
41
+ ### Backend Plugins
42
+
43
+ ```diff
44
+ - import { permissionList } from "./permissions";
45
+ - env.registerPermissions(permissionList);
46
+ + import { accessRules } from "./access";
47
+ + env.registerAccessRules(accessRules);
48
+ ```
49
+
50
+ ### RPC Contracts
51
+
52
+ ```diff
53
+ - .meta({ userType: "user", permissions: [permissions.read.id] })
54
+ + .meta({ userType: "user", access: [access.read] })
55
+ ```
56
+
57
+ ### Frontend Hooks
58
+
59
+ ```diff
60
+ - const canRead = accessApi.usePermission(permissions.read.id);
61
+ + const canRead = accessApi.useAccess(access.read);
62
+ ```
63
+
64
+ ### Routes
65
+
66
+ ```diff
67
+ - permission: permissions.entityRead.id,
68
+ + accessRule: access.read,
69
+ ```
70
+
71
+ - f533141: Enforce health result factory function usage via branded types
72
+
73
+ - Added `healthResultSchema()` builder that enforces the use of factory functions at compile-time
74
+ - Added `healthResultArray()` factory for array fields (e.g., DNS resolved values)
75
+ - Added branded `HealthResultField<T>` type to mark schemas created by factory functions
76
+ - Consolidated `ChartType` and `HealthResultMeta` into `@checkstack/common` as single source of truth
77
+ - Updated all 12 health check strategies and 11 collectors to use `healthResultSchema()`
78
+ - Using raw `z.number()` etc. inside `healthResultSchema()` now causes a TypeScript error
79
+
80
+ ### Patch Changes
81
+
82
+ - Updated dependencies [9faec1f]
83
+ - Updated dependencies [f533141]
84
+ - @checkstack/common@0.2.0
85
+ - @checkstack/signal-common@0.1.0
86
+
87
+ ## 0.2.0
88
+
89
+ ### Minor Changes
90
+
91
+ - 8e43507: # Teams and Resource-Level Access Control
92
+
93
+ This release introduces a comprehensive Teams system for organizing users and controlling access to resources at a granular level.
94
+
95
+ ## Features
96
+
97
+ ### Team Management
98
+
99
+ - Create, update, and delete teams with name and description
100
+ - Add/remove users from teams
101
+ - Designate team managers with elevated privileges
102
+ - View team membership and manager status
103
+
104
+ ### Resource-Level Access Control
105
+
106
+ - Grant teams access to specific resources (systems, health checks, incidents, maintenances)
107
+ - Configure read-only or manage permissions per team
108
+ - Resource-level "Team Only" mode that restricts access exclusively to team members
109
+ - Separate `resourceAccessSettings` table for resource-level settings (not per-grant)
110
+ - Automatic cleanup of grants when teams are deleted (database cascade)
111
+
112
+ ### Middleware Integration
113
+
114
+ - Extended `autoAuthMiddleware` to support resource access checks
115
+ - Single-resource pre-handler validation for detail endpoints
116
+ - Automatic list filtering for collection endpoints
117
+ - S2S endpoints for access verification
118
+
119
+ ### Frontend Components
120
+
121
+ - `TeamsTab` component for managing teams in Auth Settings
122
+ - `TeamAccessEditor` component for assigning team access to resources
123
+ - Resource-level "Team Only" toggle in `TeamAccessEditor`
124
+ - Integration into System, Health Check, Incident, and Maintenance editors
125
+
126
+ ## Breaking Changes
127
+
128
+ ### API Response Format Changes
129
+
130
+ List endpoints now return objects with named keys instead of arrays directly:
131
+
132
+ ```typescript
133
+ // Before
134
+ const systems = await catalogApi.getSystems();
135
+
136
+ // After
137
+ const { systems } = await catalogApi.getSystems();
138
+ ```
139
+
140
+ Affected endpoints:
141
+
142
+ - `catalog.getSystems` → `{ systems: [...] }`
143
+ - `healthcheck.getConfigurations` → `{ configurations: [...] }`
144
+ - `incident.listIncidents` → `{ incidents: [...] }`
145
+ - `maintenance.listMaintenances` → `{ maintenances: [...] }`
146
+
147
+ ### User Identity Enrichment
148
+
149
+ `RealUser` and `ApplicationUser` types now include `teamIds: string[]` field with team memberships.
150
+
151
+ ## Documentation
152
+
153
+ See `docs/backend/teams.md` for complete API reference and integration guide.
154
+
155
+ - 97c5a6b: Add UUID-based collector identification for better multiple collector support
156
+
157
+ **Breaking Change**: Existing health check configurations with collectors need to be recreated.
158
+
159
+ - Each collector instance now has a unique UUID assigned on creation
160
+ - Collector results are stored under the UUID key with `_collectorId` and `_assertionFailed` metadata
161
+ - Auto-charts correctly display separate charts for each collector instance
162
+ - Charts are now grouped by collector instance with clear headings
163
+ - Assertion status card shows pass/fail for each collector
164
+ - Renamed "Success" to "HTTP Success" to clarify it's about HTTP request success
165
+ - Fixed deletion of collectors not persisting to database
166
+ - Fixed duplicate React key warnings in auto-chart grid
167
+
168
+ ### Patch Changes
169
+
170
+ - Updated dependencies [8e43507]
171
+ - @checkstack/common@0.1.0
172
+ - @checkstack/signal-common@0.0.4
173
+
3
174
  ## 0.1.0
4
175
 
5
176
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-common",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/access.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { access, accessPair } from "@checkstack/common";
2
+
3
+ /**
4
+ * Access rules for the Health Check plugin.
5
+ */
6
+ export const healthCheckAccess = {
7
+ /**
8
+ * Status-only access for viewing health check status.
9
+ * Enabled by default for anonymous and authenticated users.
10
+ */
11
+ status: access("healthcheck.status", "read", "View Health Check Status", {
12
+ isDefault: true,
13
+ isPublic: true,
14
+ }),
15
+
16
+ /**
17
+ * Configuration access for viewing and managing health check configurations.
18
+ */
19
+ configuration: accessPair("healthcheck", {
20
+ read: "Read Health Check Configurations",
21
+ manage: "Full management of Health Check Configurations",
22
+ }),
23
+
24
+ /**
25
+ * Access for viewing detailed health check run data including metadata.
26
+ * Allows access to extended visualizations without full management access.
27
+ */
28
+ details: access(
29
+ "healthcheck.details",
30
+ "read",
31
+ "View Detailed Health Check Run Data (Warning: This may expose sensitive data, depending on the health check strategy)"
32
+ ),
33
+ };
34
+
35
+ /**
36
+ * All access rules for registration with the plugin system.
37
+ */
38
+ export const healthCheckAccessRules = [
39
+ healthCheckAccess.status,
40
+ healthCheckAccess.configuration.read,
41
+ healthCheckAccess.configuration.manage,
42
+ healthCheckAccess.details,
43
+ ];
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./permissions";
1
+ export * from "./access";
2
2
  export * from "./schemas";
3
3
  export * from "./zod-health-result";
4
4
 
@@ -5,7 +5,7 @@ import {
5
5
  } from "@checkstack/common";
6
6
  import { pluginMetadata } from "./plugin-metadata";
7
7
  import { z } from "zod";
8
- import { permissions } from "./permissions";
8
+ import { healthCheckAccess } from "./access";
9
9
  import {
10
10
  HealthCheckStrategyDtoSchema,
11
11
  CollectorDtoSchema,
@@ -44,13 +44,13 @@ const SystemHealthStatusResponseSchema = z.object({
44
44
  // Health Check RPC Contract using oRPC's contract-first pattern
45
45
  export const healthCheckContract = {
46
46
  // ==========================================================================
47
- // STRATEGY MANAGEMENT (userType: "authenticated" with read permission)
47
+ // STRATEGY MANAGEMENT (userType: "authenticated" with read access)
48
48
  // ==========================================================================
49
49
 
50
50
  getStrategies: _base
51
51
  .meta({
52
52
  userType: "authenticated",
53
- permissions: [permissions.healthCheckRead.id],
53
+ access: [healthCheckAccess.configuration.read],
54
54
  })
55
55
  .output(z.array(HealthCheckStrategyDtoSchema)),
56
56
 
@@ -61,7 +61,7 @@ export const healthCheckContract = {
61
61
  getCollectors: _base
62
62
  .meta({
63
63
  userType: "authenticated",
64
- permissions: [permissions.healthCheckRead.id],
64
+ access: [healthCheckAccess.configuration.read],
65
65
  })
66
66
  .input(z.object({ strategyId: z.string() }))
67
67
  .output(z.array(CollectorDtoSchema)),
@@ -73,14 +73,16 @@ export const healthCheckContract = {
73
73
  getConfigurations: _base
74
74
  .meta({
75
75
  userType: "authenticated",
76
- permissions: [permissions.healthCheckRead.id],
76
+ access: [healthCheckAccess.configuration.read],
77
77
  })
78
- .output(z.array(HealthCheckConfigurationSchema)),
78
+ .output(
79
+ z.object({ configurations: z.array(HealthCheckConfigurationSchema) })
80
+ ),
79
81
 
80
82
  createConfiguration: _base
81
83
  .meta({
82
84
  userType: "authenticated",
83
- permissions: [permissions.healthCheckManage.id],
85
+ access: [healthCheckAccess.configuration.manage],
84
86
  })
85
87
  .input(CreateHealthCheckConfigurationSchema)
86
88
  .output(HealthCheckConfigurationSchema),
@@ -88,7 +90,7 @@ export const healthCheckContract = {
88
90
  updateConfiguration: _base
89
91
  .meta({
90
92
  userType: "authenticated",
91
- permissions: [permissions.healthCheckManage.id],
93
+ access: [healthCheckAccess.configuration.manage],
92
94
  })
93
95
  .input(
94
96
  z.object({
@@ -101,7 +103,7 @@ export const healthCheckContract = {
101
103
  deleteConfiguration: _base
102
104
  .meta({
103
105
  userType: "authenticated",
104
- permissions: [permissions.healthCheckManage.id],
106
+ access: [healthCheckAccess.configuration.manage],
105
107
  })
106
108
  .input(z.string())
107
109
  .output(z.void()),
@@ -113,7 +115,7 @@ export const healthCheckContract = {
113
115
  getSystemConfigurations: _base
114
116
  .meta({
115
117
  userType: "authenticated",
116
- permissions: [permissions.healthCheckRead.id],
118
+ access: [healthCheckAccess.configuration.read],
117
119
  })
118
120
  .input(z.string())
119
121
  .output(z.array(HealthCheckConfigurationSchema)),
@@ -125,7 +127,7 @@ export const healthCheckContract = {
125
127
  getSystemAssociations: _base
126
128
  .meta({
127
129
  userType: "authenticated",
128
- permissions: [permissions.healthCheckRead.id],
130
+ access: [healthCheckAccess.configuration.read],
129
131
  })
130
132
  .input(z.object({ systemId: z.string() }))
131
133
  .output(
@@ -142,7 +144,7 @@ export const healthCheckContract = {
142
144
  associateSystem: _base
143
145
  .meta({
144
146
  userType: "authenticated",
145
- permissions: [permissions.healthCheckManage.id],
147
+ access: [healthCheckAccess.configuration.manage],
146
148
  })
147
149
  .input(
148
150
  z.object({
@@ -155,7 +157,7 @@ export const healthCheckContract = {
155
157
  disassociateSystem: _base
156
158
  .meta({
157
159
  userType: "authenticated",
158
- permissions: [permissions.healthCheckManage.id],
160
+ access: [healthCheckAccess.configuration.manage],
159
161
  })
160
162
  .input(
161
163
  z.object({
@@ -166,13 +168,13 @@ export const healthCheckContract = {
166
168
  .output(z.void()),
167
169
 
168
170
  // ==========================================================================
169
- // RETENTION CONFIGURATION (userType: "authenticated" with manage permission)
171
+ // RETENTION CONFIGURATION (userType: "authenticated" with manage access)
170
172
  // ==========================================================================
171
173
 
172
174
  getRetentionConfig: _base
173
175
  .meta({
174
176
  userType: "authenticated",
175
- permissions: [permissions.healthCheckRead.id],
177
+ access: [healthCheckAccess.configuration.read],
176
178
  })
177
179
  .input(
178
180
  z.object({
@@ -189,7 +191,7 @@ export const healthCheckContract = {
189
191
  updateRetentionConfig: _base
190
192
  .meta({
191
193
  userType: "authenticated",
192
- permissions: [permissions.healthCheckManage.id],
194
+ access: [healthCheckAccess.configuration.manage],
193
195
  })
194
196
  .input(
195
197
  z.object({
@@ -201,13 +203,13 @@ export const healthCheckContract = {
201
203
  .output(z.void()),
202
204
 
203
205
  // ==========================================================================
204
- // HISTORY & STATUS (userType: "user" with read permission)
206
+ // HISTORY & STATUS (userType: "user" with read access)
205
207
  // ==========================================================================
206
208
 
207
209
  getHistory: _base
208
210
  .meta({
209
211
  userType: "public",
210
- permissions: [permissions.healthCheckStatusRead.id],
212
+ access: [healthCheckAccess.status],
211
213
  })
212
214
  .input(
213
215
  z.object({
@@ -228,12 +230,12 @@ export const healthCheckContract = {
228
230
 
229
231
  /**
230
232
  * Get detailed health check run history with full result data.
231
- * Requires permission to view detailed run data including metadata.
233
+ * Requires access to view detailed run data including metadata.
232
234
  */
233
235
  getDetailedHistory: _base
234
236
  .meta({
235
237
  userType: "authenticated",
236
- permissions: [permissions.healthCheckDetailsRead.id],
238
+ access: [healthCheckAccess.details],
237
239
  })
238
240
  .input(
239
241
  z.object({
@@ -260,7 +262,7 @@ export const healthCheckContract = {
260
262
  getAggregatedHistory: _base
261
263
  .meta({
262
264
  userType: "public",
263
- permissions: [permissions.healthCheckStatusRead.id],
265
+ access: [healthCheckAccess.status],
264
266
  })
265
267
  .input(
266
268
  z.object({
@@ -280,12 +282,12 @@ export const healthCheckContract = {
280
282
  /**
281
283
  * Get detailed aggregated health check history including strategy-specific data.
282
284
  * Returns buckets with core metrics AND aggregatedResult from strategy.
283
- * Requires healthCheckDetailsRead permission.
285
+ * Requires healthCheckDetailsRead access rule.
284
286
  */
285
287
  getDetailedAggregatedHistory: _base
286
288
  .meta({
287
289
  userType: "public",
288
- permissions: [permissions.healthCheckDetailsRead.id],
290
+ access: [healthCheckAccess.details],
289
291
  })
290
292
  .input(
291
293
  z.object({
@@ -309,7 +311,7 @@ export const healthCheckContract = {
309
311
  getSystemHealthStatus: _base
310
312
  .meta({
311
313
  userType: "public",
312
- permissions: [permissions.healthCheckStatusRead.id],
314
+ access: [healthCheckAccess.status],
313
315
  })
314
316
  .input(z.object({ systemId: z.string() }))
315
317
  .output(SystemHealthStatusResponseSchema),
@@ -321,7 +323,7 @@ export const healthCheckContract = {
321
323
  getSystemHealthOverview: _base
322
324
  .meta({
323
325
  userType: "public",
324
- permissions: [permissions.healthCheckStatusRead.id],
326
+ access: [healthCheckAccess.status],
325
327
  })
326
328
  .input(z.object({ systemId: z.string() }))
327
329
  .output(
package/src/schemas.ts CHANGED
@@ -56,9 +56,11 @@ export type CollectorAssertion = z.infer<typeof CollectorAssertionSchema>;
56
56
 
57
57
  /**
58
58
  * A collector configuration entry within a health check.
59
- * Each entry includes the collector ID, its config, and per-collector assertions.
59
+ * Each entry includes a unique ID, the collector type ID, its config, and per-collector assertions.
60
60
  */
61
61
  export const CollectorConfigEntrySchema = z.object({
62
+ /** Unique ID for this collector instance (UUID) */
63
+ id: z.string(),
62
64
  /** Fully-qualified collector ID (e.g., collector-hardware.cpu) */
63
65
  collectorId: z.string(),
64
66
  /** Collector-specific configuration */
@@ -270,7 +272,7 @@ export const DEFAULT_RETENTION_CONFIG: RetentionConfig = {
270
272
  /**
271
273
  * Base schema for aggregated health check data buckets.
272
274
  * Contains core metrics only (no strategy-specific data).
273
- * Used by getAggregatedHistory endpoint (healthCheckStatusRead permission).
275
+ * Used by getAggregatedHistory endpoint (healthCheckStatusRead access).
274
276
  */
275
277
  export const AggregatedBucketBaseSchema = z.object({
276
278
  bucketStart: z.date(),
@@ -290,7 +292,7 @@ export type AggregatedBucketBase = z.infer<typeof AggregatedBucketBaseSchema>;
290
292
 
291
293
  /**
292
294
  * Extended schema with strategy-specific aggregated result.
293
- * Used by getDetailedAggregatedHistory endpoint (healthCheckDetailsRead permission).
295
+ * Used by getDetailedAggregatedHistory endpoint (healthCheckDetailsRead access).
294
296
  */
295
297
  export const AggregatedBucketSchema = AggregatedBucketBaseSchema.extend({
296
298
  aggregatedResult: z.record(z.string(), z.unknown()).optional(),
@@ -1,54 +1,95 @@
1
1
  import { z } from "zod";
2
+ import type { HealthResultMeta } from "@checkstack/common";
2
3
 
3
4
  // ============================================================================
4
5
  // HEALTH RESULT REGISTRY - Typed metadata for chart annotations
5
6
  // ============================================================================
6
7
 
7
8
  /**
8
- * Chart types for auto-generated health check visualizations.
9
- *
10
- * Numeric types:
11
- * - line: Time series line chart for numeric metrics over time
12
- * - bar: Bar chart for distributions (record of string to number)
13
- * - pie: Pie chart for category distributions (record of string to number)
14
- * - counter: Simple count display with trend indicator
15
- * - gauge: Percentage gauge for rates/percentages (0-100)
16
- *
17
- * Non-numeric types:
18
- * - boolean: Boolean indicator (success/failure, connected/disconnected)
19
- * - text: Text display for string values
20
- * - status: Status badge for error/warning states
9
+ * Registry for health result schema metadata.
10
+ * Used by auto-chart components for visualization inference.
21
11
  */
22
- export type ChartType =
23
- | "line"
24
- | "bar"
25
- | "pie"
26
- | "counter"
27
- | "gauge"
28
- | "boolean"
29
- | "text"
30
- | "status";
12
+ export const healthResultRegistry = z.registry<HealthResultMeta>();
13
+
14
+ // ============================================================================
15
+ // BRANDED TYPES FOR COMPILE-TIME ENFORCEMENT
16
+ // ============================================================================
31
17
 
32
18
  /**
33
- * Metadata type for health check result schemas.
34
- * Provides autocompletion for `.meta()` calls on result fields.
19
+ * Unique symbol for branding health result fields.
20
+ * This enables compile-time enforcement that developers use factory functions.
35
21
  */
36
- export interface HealthResultMeta {
37
- /** The type of chart to render for this field */
38
- "x-chart-type"?: ChartType;
39
- /** Human-readable label for the chart (defaults to field name) */
40
- "x-chart-label"?: string;
41
- /** Unit suffix for values (e.g., 'ms', '%', 'req/s') */
42
- "x-chart-unit"?: string;
43
- /** Whether this field supports JSONPath assertions */
44
- "x-jsonpath"?: boolean;
45
- }
22
+ declare const HealthResultBrand: unique symbol;
46
23
 
47
24
  /**
48
- * Registry for health result schema metadata.
49
- * Used by auto-chart components for visualization inference.
25
+ * A branded Zod schema type that marks it as a health result field.
26
+ * Only schemas created via factory functions (healthResultNumber, healthResultString, etc.)
27
+ * carry this brand.
50
28
  */
51
- export const healthResultRegistry = z.registry<HealthResultMeta>();
29
+ export type HealthResultField<T extends z.ZodTypeAny> = T & {
30
+ [HealthResultBrand]: true;
31
+ };
32
+
33
+ /**
34
+ * Allowed field types in a health result schema.
35
+ * Supports direct fields, optional fields, and fields with defaults.
36
+ */
37
+ export type HealthResultFieldType =
38
+ | HealthResultField<z.ZodTypeAny>
39
+ | z.ZodOptional<HealthResultField<z.ZodTypeAny>>
40
+ | z.ZodDefault<HealthResultField<z.ZodTypeAny>>
41
+ | z.ZodNullable<HealthResultField<z.ZodTypeAny>>;
42
+
43
+ /**
44
+ * Constraint for health result schema shapes.
45
+ * All fields must use factory functions (healthResultNumber, healthResultString, etc.)
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // ✅ Valid - uses factory functions
50
+ * const validSchema = z.object({
51
+ * value: healthResultNumber({ "x-chart-type": "line" }),
52
+ * error: healthResultString({ "x-chart-type": "status" }).optional(),
53
+ * });
54
+ *
55
+ * // ❌ Invalid - uses raw z.number()
56
+ * const invalidSchema = z.object({
57
+ * value: z.number(), // Type error!
58
+ * });
59
+ * ```
60
+ */
61
+ export type HealthResultShape = Record<string, HealthResultFieldType>;
62
+
63
+ // ============================================================================
64
+ // HEALTH RESULT SCHEMA BUILDER
65
+ // ============================================================================
66
+
67
+ /**
68
+ * Create a health result schema that enforces the use of factory functions.
69
+ *
70
+ * This builder ensures all fields use healthResultNumber(), healthResultString(),
71
+ * healthResultBoolean(), or healthResultJSONPath() - raw Zod schemas like z.number()
72
+ * will cause a compile-time error.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // ✅ Valid - all fields use factory functions
77
+ * const schema = healthResultSchema({
78
+ * responseTime: healthResultNumber({ "x-chart-type": "line", "x-chart-label": "Response Time" }),
79
+ * error: healthResultString({ "x-chart-type": "status" }).optional(),
80
+ * });
81
+ *
82
+ * // ❌ Invalid - raw z.number() causes type error
83
+ * const schema = healthResultSchema({
84
+ * value: z.number(), // Type error: not assignable to HealthResultFieldType
85
+ * });
86
+ * ```
87
+ */
88
+ export function healthResultSchema<T extends HealthResultShape>(
89
+ shape: T
90
+ ): z.ZodObject<T> {
91
+ return z.object(shape);
92
+ }
52
93
 
53
94
  // ============================================================================
54
95
  // TYPED HEALTH RESULT FACTORIES
@@ -69,28 +110,53 @@ type ChartMeta = Omit<HealthResultMeta, "x-jsonpath">;
69
110
  * });
70
111
  * ```
71
112
  */
72
- export function healthResultString(meta: ChartMeta) {
113
+ export function healthResultString(
114
+ meta: ChartMeta
115
+ ): HealthResultField<z.ZodString> {
73
116
  const schema = z.string();
74
117
  schema.register(healthResultRegistry, meta);
75
- return schema;
118
+ return schema as HealthResultField<z.ZodString>;
76
119
  }
77
120
 
78
121
  /**
79
122
  * Create a health result number field with typed chart metadata.
80
123
  */
81
- export function healthResultNumber(meta: ChartMeta) {
124
+ export function healthResultNumber(
125
+ meta: ChartMeta
126
+ ): HealthResultField<z.ZodNumber> {
82
127
  const schema = z.number();
83
128
  schema.register(healthResultRegistry, meta);
84
- return schema;
129
+ return schema as HealthResultField<z.ZodNumber>;
85
130
  }
86
131
 
87
132
  /**
88
133
  * Create a health result boolean field with typed chart metadata.
89
134
  */
90
- export function healthResultBoolean(meta: ChartMeta) {
135
+ export function healthResultBoolean(
136
+ meta: ChartMeta
137
+ ): HealthResultField<z.ZodBoolean> {
91
138
  const schema = z.boolean();
92
139
  schema.register(healthResultRegistry, meta);
93
- return schema;
140
+ return schema as HealthResultField<z.ZodBoolean>;
141
+ }
142
+
143
+ /**
144
+ * Create a health result array field with typed chart metadata.
145
+ * For arrays of strings (e.g., DNS resolved values, list of hosts).
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const resultSchema = healthResultSchema({
150
+ * resolvedValues: healthResultArray({ "x-chart-type": "text", "x-chart-label": "Values" }),
151
+ * });
152
+ * ```
153
+ */
154
+ export function healthResultArray(
155
+ meta: ChartMeta
156
+ ): HealthResultField<z.ZodArray<z.ZodString>> {
157
+ const schema = z.array(z.string());
158
+ schema.register(healthResultRegistry, meta);
159
+ return schema as HealthResultField<z.ZodArray<z.ZodString>>;
94
160
  }
95
161
 
96
162
  /**
@@ -106,10 +172,12 @@ export function healthResultBoolean(meta: ChartMeta) {
106
172
  * });
107
173
  * ```
108
174
  */
109
- export function healthResultJSONPath(meta: ChartMeta) {
175
+ export function healthResultJSONPath(
176
+ meta: ChartMeta
177
+ ): HealthResultField<z.ZodString> {
110
178
  const schema = z.string();
111
179
  schema.register(healthResultRegistry, { ...meta, "x-jsonpath": true });
112
- return schema;
180
+ return schema as HealthResultField<z.ZodString>;
113
181
  }
114
182
 
115
183
  // ============================================================================
@@ -1,39 +0,0 @@
1
- import { createPermission } from "@checkstack/common";
2
-
3
- export const permissions = {
4
- /**
5
- * Status-only permission for viewing health check status.
6
- * Enabled by default for anonymous and authenticated users.
7
- */
8
- healthCheckStatusRead: createPermission(
9
- "healthcheck.status",
10
- "read",
11
- "View Health Check Status",
12
- { isAuthenticatedDefault: true, isPublicDefault: true }
13
- ),
14
- /**
15
- * Configuration read permission for viewing health check configurations.
16
- * Restricted to users with explicit grant.
17
- */
18
- healthCheckRead: createPermission(
19
- "healthcheck",
20
- "read",
21
- "Read Health Check Configurations"
22
- ),
23
- /**
24
- * Permission for viewing detailed health check run data including metadata.
25
- * Allows access to extended visualizations without full management access.
26
- */
27
- healthCheckDetailsRead: createPermission(
28
- "healthcheck.details",
29
- "read",
30
- "View Detailed Health Check Run Data (Warning: This may expose sensitive data, depending on the health check strategy)"
31
- ),
32
- healthCheckManage: createPermission(
33
- "healthcheck",
34
- "manage",
35
- "Full management of Health Check Configurations"
36
- ),
37
- };
38
-
39
- export const permissionList = Object.values(permissions);