@checkstack/healthcheck-common 0.2.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 +84 -0
- package/package.json +1 -1
- package/src/access.ts +43 -0
- package/src/index.ts +1 -1
- package/src/rpc-contract.ts +24 -32
- package/src/schemas.ts +2 -2
- package/src/zod-health-result.ts +113 -45
- package/src/permissions.ts +0 -39
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,89 @@
|
|
|
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
|
+
|
|
3
87
|
## 0.2.0
|
|
4
88
|
|
|
5
89
|
### Minor Changes
|
package/package.json
CHANGED
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
package/src/rpc-contract.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { oc } from "@orpc/contract";
|
|
2
2
|
import {
|
|
3
3
|
createClientDefinition,
|
|
4
|
-
createResourceAccessList,
|
|
5
4
|
type ProcedureMetadata,
|
|
6
5
|
} from "@checkstack/common";
|
|
7
6
|
import { pluginMetadata } from "./plugin-metadata";
|
|
8
7
|
import { z } from "zod";
|
|
9
|
-
import {
|
|
8
|
+
import { healthCheckAccess } from "./access";
|
|
10
9
|
import {
|
|
11
10
|
HealthCheckStrategyDtoSchema,
|
|
12
11
|
CollectorDtoSchema,
|
|
@@ -26,12 +25,6 @@ import {
|
|
|
26
25
|
// Base builder with full metadata support
|
|
27
26
|
const _base = oc.$meta<ProcedureMetadata>({});
|
|
28
27
|
|
|
29
|
-
// Resource access configurations for team-based access control
|
|
30
|
-
const configListAccess = createResourceAccessList(
|
|
31
|
-
"configuration",
|
|
32
|
-
"configurations"
|
|
33
|
-
);
|
|
34
|
-
|
|
35
28
|
// --- Response Schemas for Evaluated Status ---
|
|
36
29
|
|
|
37
30
|
const SystemCheckStatusSchema = z.object({
|
|
@@ -51,13 +44,13 @@ const SystemHealthStatusResponseSchema = z.object({
|
|
|
51
44
|
// Health Check RPC Contract using oRPC's contract-first pattern
|
|
52
45
|
export const healthCheckContract = {
|
|
53
46
|
// ==========================================================================
|
|
54
|
-
// STRATEGY MANAGEMENT (userType: "authenticated" with read
|
|
47
|
+
// STRATEGY MANAGEMENT (userType: "authenticated" with read access)
|
|
55
48
|
// ==========================================================================
|
|
56
49
|
|
|
57
50
|
getStrategies: _base
|
|
58
51
|
.meta({
|
|
59
52
|
userType: "authenticated",
|
|
60
|
-
|
|
53
|
+
access: [healthCheckAccess.configuration.read],
|
|
61
54
|
})
|
|
62
55
|
.output(z.array(HealthCheckStrategyDtoSchema)),
|
|
63
56
|
|
|
@@ -68,7 +61,7 @@ export const healthCheckContract = {
|
|
|
68
61
|
getCollectors: _base
|
|
69
62
|
.meta({
|
|
70
63
|
userType: "authenticated",
|
|
71
|
-
|
|
64
|
+
access: [healthCheckAccess.configuration.read],
|
|
72
65
|
})
|
|
73
66
|
.input(z.object({ strategyId: z.string() }))
|
|
74
67
|
.output(z.array(CollectorDtoSchema)),
|
|
@@ -80,8 +73,7 @@ export const healthCheckContract = {
|
|
|
80
73
|
getConfigurations: _base
|
|
81
74
|
.meta({
|
|
82
75
|
userType: "authenticated",
|
|
83
|
-
|
|
84
|
-
resourceAccess: [configListAccess],
|
|
76
|
+
access: [healthCheckAccess.configuration.read],
|
|
85
77
|
})
|
|
86
78
|
.output(
|
|
87
79
|
z.object({ configurations: z.array(HealthCheckConfigurationSchema) })
|
|
@@ -90,7 +82,7 @@ export const healthCheckContract = {
|
|
|
90
82
|
createConfiguration: _base
|
|
91
83
|
.meta({
|
|
92
84
|
userType: "authenticated",
|
|
93
|
-
|
|
85
|
+
access: [healthCheckAccess.configuration.manage],
|
|
94
86
|
})
|
|
95
87
|
.input(CreateHealthCheckConfigurationSchema)
|
|
96
88
|
.output(HealthCheckConfigurationSchema),
|
|
@@ -98,7 +90,7 @@ export const healthCheckContract = {
|
|
|
98
90
|
updateConfiguration: _base
|
|
99
91
|
.meta({
|
|
100
92
|
userType: "authenticated",
|
|
101
|
-
|
|
93
|
+
access: [healthCheckAccess.configuration.manage],
|
|
102
94
|
})
|
|
103
95
|
.input(
|
|
104
96
|
z.object({
|
|
@@ -111,7 +103,7 @@ export const healthCheckContract = {
|
|
|
111
103
|
deleteConfiguration: _base
|
|
112
104
|
.meta({
|
|
113
105
|
userType: "authenticated",
|
|
114
|
-
|
|
106
|
+
access: [healthCheckAccess.configuration.manage],
|
|
115
107
|
})
|
|
116
108
|
.input(z.string())
|
|
117
109
|
.output(z.void()),
|
|
@@ -123,7 +115,7 @@ export const healthCheckContract = {
|
|
|
123
115
|
getSystemConfigurations: _base
|
|
124
116
|
.meta({
|
|
125
117
|
userType: "authenticated",
|
|
126
|
-
|
|
118
|
+
access: [healthCheckAccess.configuration.read],
|
|
127
119
|
})
|
|
128
120
|
.input(z.string())
|
|
129
121
|
.output(z.array(HealthCheckConfigurationSchema)),
|
|
@@ -135,7 +127,7 @@ export const healthCheckContract = {
|
|
|
135
127
|
getSystemAssociations: _base
|
|
136
128
|
.meta({
|
|
137
129
|
userType: "authenticated",
|
|
138
|
-
|
|
130
|
+
access: [healthCheckAccess.configuration.read],
|
|
139
131
|
})
|
|
140
132
|
.input(z.object({ systemId: z.string() }))
|
|
141
133
|
.output(
|
|
@@ -152,7 +144,7 @@ export const healthCheckContract = {
|
|
|
152
144
|
associateSystem: _base
|
|
153
145
|
.meta({
|
|
154
146
|
userType: "authenticated",
|
|
155
|
-
|
|
147
|
+
access: [healthCheckAccess.configuration.manage],
|
|
156
148
|
})
|
|
157
149
|
.input(
|
|
158
150
|
z.object({
|
|
@@ -165,7 +157,7 @@ export const healthCheckContract = {
|
|
|
165
157
|
disassociateSystem: _base
|
|
166
158
|
.meta({
|
|
167
159
|
userType: "authenticated",
|
|
168
|
-
|
|
160
|
+
access: [healthCheckAccess.configuration.manage],
|
|
169
161
|
})
|
|
170
162
|
.input(
|
|
171
163
|
z.object({
|
|
@@ -176,13 +168,13 @@ export const healthCheckContract = {
|
|
|
176
168
|
.output(z.void()),
|
|
177
169
|
|
|
178
170
|
// ==========================================================================
|
|
179
|
-
// RETENTION CONFIGURATION (userType: "authenticated" with manage
|
|
171
|
+
// RETENTION CONFIGURATION (userType: "authenticated" with manage access)
|
|
180
172
|
// ==========================================================================
|
|
181
173
|
|
|
182
174
|
getRetentionConfig: _base
|
|
183
175
|
.meta({
|
|
184
176
|
userType: "authenticated",
|
|
185
|
-
|
|
177
|
+
access: [healthCheckAccess.configuration.read],
|
|
186
178
|
})
|
|
187
179
|
.input(
|
|
188
180
|
z.object({
|
|
@@ -199,7 +191,7 @@ export const healthCheckContract = {
|
|
|
199
191
|
updateRetentionConfig: _base
|
|
200
192
|
.meta({
|
|
201
193
|
userType: "authenticated",
|
|
202
|
-
|
|
194
|
+
access: [healthCheckAccess.configuration.manage],
|
|
203
195
|
})
|
|
204
196
|
.input(
|
|
205
197
|
z.object({
|
|
@@ -211,13 +203,13 @@ export const healthCheckContract = {
|
|
|
211
203
|
.output(z.void()),
|
|
212
204
|
|
|
213
205
|
// ==========================================================================
|
|
214
|
-
// HISTORY & STATUS (userType: "user" with read
|
|
206
|
+
// HISTORY & STATUS (userType: "user" with read access)
|
|
215
207
|
// ==========================================================================
|
|
216
208
|
|
|
217
209
|
getHistory: _base
|
|
218
210
|
.meta({
|
|
219
211
|
userType: "public",
|
|
220
|
-
|
|
212
|
+
access: [healthCheckAccess.status],
|
|
221
213
|
})
|
|
222
214
|
.input(
|
|
223
215
|
z.object({
|
|
@@ -238,12 +230,12 @@ export const healthCheckContract = {
|
|
|
238
230
|
|
|
239
231
|
/**
|
|
240
232
|
* Get detailed health check run history with full result data.
|
|
241
|
-
* Requires
|
|
233
|
+
* Requires access to view detailed run data including metadata.
|
|
242
234
|
*/
|
|
243
235
|
getDetailedHistory: _base
|
|
244
236
|
.meta({
|
|
245
237
|
userType: "authenticated",
|
|
246
|
-
|
|
238
|
+
access: [healthCheckAccess.details],
|
|
247
239
|
})
|
|
248
240
|
.input(
|
|
249
241
|
z.object({
|
|
@@ -270,7 +262,7 @@ export const healthCheckContract = {
|
|
|
270
262
|
getAggregatedHistory: _base
|
|
271
263
|
.meta({
|
|
272
264
|
userType: "public",
|
|
273
|
-
|
|
265
|
+
access: [healthCheckAccess.status],
|
|
274
266
|
})
|
|
275
267
|
.input(
|
|
276
268
|
z.object({
|
|
@@ -290,12 +282,12 @@ export const healthCheckContract = {
|
|
|
290
282
|
/**
|
|
291
283
|
* Get detailed aggregated health check history including strategy-specific data.
|
|
292
284
|
* Returns buckets with core metrics AND aggregatedResult from strategy.
|
|
293
|
-
* Requires healthCheckDetailsRead
|
|
285
|
+
* Requires healthCheckDetailsRead access rule.
|
|
294
286
|
*/
|
|
295
287
|
getDetailedAggregatedHistory: _base
|
|
296
288
|
.meta({
|
|
297
289
|
userType: "public",
|
|
298
|
-
|
|
290
|
+
access: [healthCheckAccess.details],
|
|
299
291
|
})
|
|
300
292
|
.input(
|
|
301
293
|
z.object({
|
|
@@ -319,7 +311,7 @@ export const healthCheckContract = {
|
|
|
319
311
|
getSystemHealthStatus: _base
|
|
320
312
|
.meta({
|
|
321
313
|
userType: "public",
|
|
322
|
-
|
|
314
|
+
access: [healthCheckAccess.status],
|
|
323
315
|
})
|
|
324
316
|
.input(z.object({ systemId: z.string() }))
|
|
325
317
|
.output(SystemHealthStatusResponseSchema),
|
|
@@ -331,7 +323,7 @@ export const healthCheckContract = {
|
|
|
331
323
|
getSystemHealthOverview: _base
|
|
332
324
|
.meta({
|
|
333
325
|
userType: "public",
|
|
334
|
-
|
|
326
|
+
access: [healthCheckAccess.status],
|
|
335
327
|
})
|
|
336
328
|
.input(z.object({ systemId: z.string() }))
|
|
337
329
|
.output(
|
package/src/schemas.ts
CHANGED
|
@@ -272,7 +272,7 @@ export const DEFAULT_RETENTION_CONFIG: RetentionConfig = {
|
|
|
272
272
|
/**
|
|
273
273
|
* Base schema for aggregated health check data buckets.
|
|
274
274
|
* Contains core metrics only (no strategy-specific data).
|
|
275
|
-
* Used by getAggregatedHistory endpoint (healthCheckStatusRead
|
|
275
|
+
* Used by getAggregatedHistory endpoint (healthCheckStatusRead access).
|
|
276
276
|
*/
|
|
277
277
|
export const AggregatedBucketBaseSchema = z.object({
|
|
278
278
|
bucketStart: z.date(),
|
|
@@ -292,7 +292,7 @@ export type AggregatedBucketBase = z.infer<typeof AggregatedBucketBaseSchema>;
|
|
|
292
292
|
|
|
293
293
|
/**
|
|
294
294
|
* Extended schema with strategy-specific aggregated result.
|
|
295
|
-
* Used by getDetailedAggregatedHistory endpoint (healthCheckDetailsRead
|
|
295
|
+
* Used by getDetailedAggregatedHistory endpoint (healthCheckDetailsRead access).
|
|
296
296
|
*/
|
|
297
297
|
export const AggregatedBucketSchema = AggregatedBucketBaseSchema.extend({
|
|
298
298
|
aggregatedResult: z.record(z.string(), z.unknown()).optional(),
|
package/src/zod-health-result.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
*
|
|
34
|
-
*
|
|
19
|
+
* Unique symbol for branding health result fields.
|
|
20
|
+
* This enables compile-time enforcement that developers use factory functions.
|
|
35
21
|
*/
|
|
36
|
-
|
|
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
|
-
*
|
|
49
|
-
*
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
// ============================================================================
|
package/src/permissions.ts
DELETED
|
@@ -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);
|