@checkstack/satellite-common 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 +40 -0
- package/package.json +30 -0
- package/src/access.ts +29 -0
- package/src/constants.ts +28 -0
- package/src/index.ts +45 -0
- package/src/plugin-metadata.ts +9 -0
- package/src/protocol.ts +141 -0
- package/src/routes.ts +20 -0
- package/src/rpc-contract.ts +78 -0
- package/src/schemas.ts +50 -0
- package/src/signals.ts +28 -0
- package/tsconfig.json +6 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @checkstack/satellite-common
|
|
2
|
+
|
|
3
|
+
## 0.2.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
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@checkstack/satellite-common",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./src/index.ts"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@checkstack/common": "0.6.5",
|
|
12
|
+
"@checkstack/healthcheck-common": "0.10.1",
|
|
13
|
+
"@checkstack/signal-common": "0.1.9",
|
|
14
|
+
"@orpc/contract": "^1.13.14",
|
|
15
|
+
"zod": "^4.2.1"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.7.2",
|
|
19
|
+
"@checkstack/tsconfig": "0.0.5",
|
|
20
|
+
"@checkstack/scripts": "0.1.2"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"lint": "bun run lint:code",
|
|
25
|
+
"lint:code": "eslint . --max-warnings 0"
|
|
26
|
+
},
|
|
27
|
+
"checkstack": {
|
|
28
|
+
"type": "common"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/access.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { accessPair } from "@checkstack/common";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Access rules for the Satellite plugin.
|
|
5
|
+
*/
|
|
6
|
+
export const satelliteAccess = {
|
|
7
|
+
/**
|
|
8
|
+
* Satellite management access with read and manage levels.
|
|
9
|
+
* Read allows viewing satellite list and status.
|
|
10
|
+
* Manage allows creating, deleting, and managing satellites.
|
|
11
|
+
*/
|
|
12
|
+
satellite: accessPair("satellite", {
|
|
13
|
+
read: {
|
|
14
|
+
description: "View satellite list and status",
|
|
15
|
+
},
|
|
16
|
+
manage: {
|
|
17
|
+
description:
|
|
18
|
+
"Manage satellites - create, delete, and configure satellite nodes",
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* All access rules for registration with the plugin system.
|
|
25
|
+
*/
|
|
26
|
+
export const satelliteAccessRules = [
|
|
27
|
+
satelliteAccess.satellite.read,
|
|
28
|
+
satelliteAccess.satellite.manage,
|
|
29
|
+
];
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How often satellites send heartbeats to the core (in milliseconds).
|
|
3
|
+
*/
|
|
4
|
+
export const HEARTBEAT_INTERVAL_MS = 15_000;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* How long the core waits before considering a satellite offline (in milliseconds).
|
|
8
|
+
* Set to 3× the heartbeat interval to tolerate brief network hiccups.
|
|
9
|
+
*/
|
|
10
|
+
export const OFFLINE_THRESHOLD_MS = 45_000;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maximum number of health check results to buffer in-memory
|
|
14
|
+
* on the satellite when the WebSocket connection is lost.
|
|
15
|
+
* Oldest results are dropped when the buffer is full (FIFO ring buffer).
|
|
16
|
+
*/
|
|
17
|
+
export const RESULT_BUFFER_CAPACITY = 100;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Base delay for reconnection backoff (in milliseconds).
|
|
21
|
+
* Actual delay uses exponential backoff with jitter.
|
|
22
|
+
*/
|
|
23
|
+
export const RECONNECT_BASE_MS = 1000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Maximum delay between reconnection attempts (in milliseconds).
|
|
27
|
+
*/
|
|
28
|
+
export const RECONNECT_MAX_MS = 30_000;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export { satelliteAccess, satelliteAccessRules } from "./access";
|
|
2
|
+
export {
|
|
3
|
+
satelliteContract,
|
|
4
|
+
SatelliteApi,
|
|
5
|
+
type SatelliteContract,
|
|
6
|
+
} from "./rpc-contract";
|
|
7
|
+
export {
|
|
8
|
+
SatelliteStatusSchema,
|
|
9
|
+
SatelliteSchema,
|
|
10
|
+
SatelliteWithStatusSchema,
|
|
11
|
+
CreateSatelliteSchema,
|
|
12
|
+
type SatelliteStatus,
|
|
13
|
+
type Satellite,
|
|
14
|
+
type SatelliteWithStatus,
|
|
15
|
+
type CreateSatellite,
|
|
16
|
+
} from "./schemas";
|
|
17
|
+
export {
|
|
18
|
+
SatelliteAssignmentSchema,
|
|
19
|
+
SatelliteToCoreMessageSchema,
|
|
20
|
+
CoreToSatelliteMessageSchema,
|
|
21
|
+
type SatelliteAssignment,
|
|
22
|
+
type SatelliteToCoreMessage,
|
|
23
|
+
type CoreToSatelliteMessage,
|
|
24
|
+
type AuthenticateMessage,
|
|
25
|
+
type HeartbeatMessage,
|
|
26
|
+
type ResultMessage,
|
|
27
|
+
type StrategyErrorMessage,
|
|
28
|
+
type AuthenticatedMessage,
|
|
29
|
+
type AuthFailedMessage,
|
|
30
|
+
type ConfigUpdatedMessage,
|
|
31
|
+
type ShutdownMessage,
|
|
32
|
+
} from "./protocol";
|
|
33
|
+
export {
|
|
34
|
+
SATELLITE_STATUS_CHANGED,
|
|
35
|
+
SATELLITE_CONFIG_CHANGED,
|
|
36
|
+
} from "./signals";
|
|
37
|
+
export {
|
|
38
|
+
HEARTBEAT_INTERVAL_MS,
|
|
39
|
+
OFFLINE_THRESHOLD_MS,
|
|
40
|
+
RESULT_BUFFER_CAPACITY,
|
|
41
|
+
RECONNECT_BASE_MS,
|
|
42
|
+
RECONNECT_MAX_MS,
|
|
43
|
+
} from "./constants";
|
|
44
|
+
export * from "./plugin-metadata";
|
|
45
|
+
export { satelliteRoutes } from "./routes";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { definePluginMetadata } from "@checkstack/common";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plugin metadata for the satellite plugin.
|
|
5
|
+
* Exported from the common package so both backend and frontend can reference it.
|
|
6
|
+
*/
|
|
7
|
+
export const pluginMetadata = definePluginMetadata({
|
|
8
|
+
pluginId: "satellite",
|
|
9
|
+
});
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
HealthCheckStatusSchema,
|
|
4
|
+
HealthCheckRunResultSchema,
|
|
5
|
+
} from "@checkstack/healthcheck-common";
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// SATELLITE ASSIGNMENT (Core → Satellite configuration payload)
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A single collector configuration sent to the satellite for execution.
|
|
13
|
+
*/
|
|
14
|
+
const SatelliteCollectorConfigSchema = z.object({
|
|
15
|
+
id: z.string(),
|
|
16
|
+
collectorId: z.string(),
|
|
17
|
+
config: z.record(z.string(), z.unknown()),
|
|
18
|
+
assertions: z
|
|
19
|
+
.array(
|
|
20
|
+
z.object({
|
|
21
|
+
field: z.string(),
|
|
22
|
+
jsonPath: z.string().optional(),
|
|
23
|
+
operator: z.string(),
|
|
24
|
+
value: z.unknown().optional(),
|
|
25
|
+
}),
|
|
26
|
+
)
|
|
27
|
+
.optional(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A health check assignment sent from the core to a satellite.
|
|
32
|
+
* Contains everything the satellite needs to execute the check.
|
|
33
|
+
*/
|
|
34
|
+
export const SatelliteAssignmentSchema = z.object({
|
|
35
|
+
configId: z.string(),
|
|
36
|
+
systemId: z.string(),
|
|
37
|
+
strategyId: z.string(),
|
|
38
|
+
config: z.record(z.string(), z.unknown()),
|
|
39
|
+
collectors: z.array(SatelliteCollectorConfigSchema).optional(),
|
|
40
|
+
intervalSeconds: z.number(),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export type SatelliteAssignment = z.infer<typeof SatelliteAssignmentSchema>;
|
|
44
|
+
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// SATELLITE → CORE MESSAGES
|
|
47
|
+
// =============================================================================
|
|
48
|
+
|
|
49
|
+
const AuthenticateMessageSchema = z.object({
|
|
50
|
+
type: z.literal("authenticate"),
|
|
51
|
+
clientId: z.string(),
|
|
52
|
+
token: z.string(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const HeartbeatMessageSchema = z.object({
|
|
56
|
+
type: z.literal("heartbeat"),
|
|
57
|
+
version: z.string(),
|
|
58
|
+
uptimeSeconds: z.number(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const ResultMessageSchema = z.object({
|
|
62
|
+
type: z.literal("result"),
|
|
63
|
+
configId: z.string(),
|
|
64
|
+
systemId: z.string(),
|
|
65
|
+
status: HealthCheckStatusSchema,
|
|
66
|
+
latencyMs: z.number().optional(),
|
|
67
|
+
/** Structured run result — typed to enforce parity with the local executor */
|
|
68
|
+
result: HealthCheckRunResultSchema.optional(),
|
|
69
|
+
executedAt: z.string(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const StrategyErrorMessageSchema = z.object({
|
|
73
|
+
type: z.literal("strategy_error"),
|
|
74
|
+
strategyId: z.string(),
|
|
75
|
+
message: z.string(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Discriminated union of all messages that a satellite can send to the core.
|
|
80
|
+
*/
|
|
81
|
+
export const SatelliteToCoreMessageSchema = z.discriminatedUnion("type", [
|
|
82
|
+
AuthenticateMessageSchema,
|
|
83
|
+
HeartbeatMessageSchema,
|
|
84
|
+
ResultMessageSchema,
|
|
85
|
+
StrategyErrorMessageSchema,
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
export type SatelliteToCoreMessage = z.infer<
|
|
89
|
+
typeof SatelliteToCoreMessageSchema
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
// Re-export individual message types for use in handler type narrowing
|
|
93
|
+
export type AuthenticateMessage = z.infer<typeof AuthenticateMessageSchema>;
|
|
94
|
+
export type HeartbeatMessage = z.infer<typeof HeartbeatMessageSchema>;
|
|
95
|
+
export type ResultMessage = z.infer<typeof ResultMessageSchema>;
|
|
96
|
+
export type StrategyErrorMessage = z.infer<typeof StrategyErrorMessageSchema>;
|
|
97
|
+
|
|
98
|
+
// =============================================================================
|
|
99
|
+
// CORE → SATELLITE MESSAGES
|
|
100
|
+
// =============================================================================
|
|
101
|
+
|
|
102
|
+
const AuthenticatedMessageSchema = z.object({
|
|
103
|
+
type: z.literal("authenticated"),
|
|
104
|
+
satelliteId: z.string(),
|
|
105
|
+
assignments: z.array(SatelliteAssignmentSchema),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const AuthFailedMessageSchema = z.object({
|
|
109
|
+
type: z.literal("auth_failed"),
|
|
110
|
+
reason: z.string(),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const ConfigUpdatedMessageSchema = z.object({
|
|
114
|
+
type: z.literal("config_updated"),
|
|
115
|
+
assignments: z.array(SatelliteAssignmentSchema),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const ShutdownMessageSchema = z.object({
|
|
119
|
+
type: z.literal("shutdown"),
|
|
120
|
+
reason: z.string(),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Discriminated union of all messages that the core can send to a satellite.
|
|
125
|
+
*/
|
|
126
|
+
export const CoreToSatelliteMessageSchema = z.discriminatedUnion("type", [
|
|
127
|
+
AuthenticatedMessageSchema,
|
|
128
|
+
AuthFailedMessageSchema,
|
|
129
|
+
ConfigUpdatedMessageSchema,
|
|
130
|
+
ShutdownMessageSchema,
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
export type CoreToSatelliteMessage = z.infer<
|
|
134
|
+
typeof CoreToSatelliteMessageSchema
|
|
135
|
+
>;
|
|
136
|
+
|
|
137
|
+
// Re-export individual message types
|
|
138
|
+
export type AuthenticatedMessage = z.infer<typeof AuthenticatedMessageSchema>;
|
|
139
|
+
export type AuthFailedMessage = z.infer<typeof AuthFailedMessageSchema>;
|
|
140
|
+
export type ConfigUpdatedMessage = z.infer<typeof ConfigUpdatedMessageSchema>;
|
|
141
|
+
export type ShutdownMessage = z.infer<typeof ShutdownMessageSchema>;
|
package/src/routes.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createRoutes } from "@checkstack/common";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Route definitions for the satellite plugin.
|
|
5
|
+
* Import and use these routes in both frontend plugins and for link generation.
|
|
6
|
+
*
|
|
7
|
+
* @example Frontend plugin usage
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { satelliteRoutes } from "@checkstack/satellite-common";
|
|
10
|
+
*
|
|
11
|
+
* createFrontendPlugin({
|
|
12
|
+
* routes: [
|
|
13
|
+
* { route: satelliteRoutes.routes.list, element: <SatelliteListPage /> },
|
|
14
|
+
* ],
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export const satelliteRoutes = createRoutes("satellite", {
|
|
19
|
+
list: "/",
|
|
20
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createClientDefinition, proc } from "@checkstack/common";
|
|
3
|
+
import { satelliteAccess } from "./access";
|
|
4
|
+
import { pluginMetadata } from "./plugin-metadata";
|
|
5
|
+
import { SatelliteWithStatusSchema, CreateSatelliteSchema } from "./schemas";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* RPC contract for satellite management.
|
|
9
|
+
* Consumed by satellite-frontend for the management UI.
|
|
10
|
+
*/
|
|
11
|
+
export const satelliteContract = {
|
|
12
|
+
/** List all satellites with computed online/offline status */
|
|
13
|
+
listSatellites: proc({
|
|
14
|
+
operationType: "query",
|
|
15
|
+
userType: "authenticated",
|
|
16
|
+
access: [satelliteAccess.satellite.read],
|
|
17
|
+
}).output(
|
|
18
|
+
z.object({ satellites: z.array(SatelliteWithStatusSchema) }),
|
|
19
|
+
),
|
|
20
|
+
|
|
21
|
+
/** Get a single satellite by ID */
|
|
22
|
+
getSatellite: proc({
|
|
23
|
+
operationType: "query",
|
|
24
|
+
userType: "authenticated",
|
|
25
|
+
access: [satelliteAccess.satellite.read],
|
|
26
|
+
})
|
|
27
|
+
.input(z.object({ id: z.string() }))
|
|
28
|
+
.output(SatelliteWithStatusSchema.nullable()),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a new satellite.
|
|
32
|
+
* Returns the satellite record AND the plaintext token (shown once).
|
|
33
|
+
* The clientId is the satellite's UUID (satellite.id).
|
|
34
|
+
*/
|
|
35
|
+
createSatellite: proc({
|
|
36
|
+
operationType: "mutation",
|
|
37
|
+
userType: "authenticated",
|
|
38
|
+
access: [satelliteAccess.satellite.manage],
|
|
39
|
+
})
|
|
40
|
+
.input(CreateSatelliteSchema)
|
|
41
|
+
.output(
|
|
42
|
+
z.object({
|
|
43
|
+
satellite: SatelliteWithStatusSchema,
|
|
44
|
+
/** Plaintext token — shown once, never stored */
|
|
45
|
+
token: z.string(),
|
|
46
|
+
}),
|
|
47
|
+
),
|
|
48
|
+
|
|
49
|
+
/** Delete a satellite by ID */
|
|
50
|
+
deleteSatellite: proc({
|
|
51
|
+
operationType: "mutation",
|
|
52
|
+
userType: "authenticated",
|
|
53
|
+
access: [satelliteAccess.satellite.manage],
|
|
54
|
+
})
|
|
55
|
+
.input(z.object({ id: z.string() }))
|
|
56
|
+
.output(z.void()),
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the list of online satellite IDs.
|
|
60
|
+
* Used internally by healthcheck-backend for stale source exclusion
|
|
61
|
+
* during health state evaluation.
|
|
62
|
+
*/
|
|
63
|
+
getOnlineSatelliteIds: proc({
|
|
64
|
+
operationType: "query",
|
|
65
|
+
userType: "service",
|
|
66
|
+
access: [],
|
|
67
|
+
}).output(z.object({ satelliteIds: z.array(z.string()) })),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Export contract type
|
|
71
|
+
export type SatelliteContract = typeof satelliteContract;
|
|
72
|
+
|
|
73
|
+
// Export client definition for type-safe forPlugin usage
|
|
74
|
+
// Use: const client = rpcApi.forPlugin(SatelliteApi);
|
|
75
|
+
export const SatelliteApi = createClientDefinition(
|
|
76
|
+
satelliteContract,
|
|
77
|
+
pluginMetadata,
|
|
78
|
+
);
|
package/src/schemas.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// SATELLITE ENTITY SCHEMAS
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Satellite status derived from lastHeartbeatAt vs OFFLINE_THRESHOLD_MS.
|
|
9
|
+
*/
|
|
10
|
+
export const SatelliteStatusSchema = z.enum(["online", "offline"]);
|
|
11
|
+
|
|
12
|
+
export type SatelliteStatus = z.infer<typeof SatelliteStatusSchema>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Full satellite record as stored in the database.
|
|
16
|
+
* tokenHash is intentionally excluded from the schema —
|
|
17
|
+
* it should never leave the backend.
|
|
18
|
+
*/
|
|
19
|
+
export const SatelliteSchema = z.object({
|
|
20
|
+
id: z.string().uuid(),
|
|
21
|
+
name: z.string(),
|
|
22
|
+
region: z.string(),
|
|
23
|
+
tags: z.record(z.string(), z.string()),
|
|
24
|
+
lastHeartbeatAt: z.date().optional(),
|
|
25
|
+
version: z.string().optional(),
|
|
26
|
+
createdAt: z.date(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export type Satellite = z.infer<typeof SatelliteSchema>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Satellite with computed online/offline status.
|
|
33
|
+
* Used in API responses — the status is derived from lastHeartbeatAt.
|
|
34
|
+
*/
|
|
35
|
+
export const SatelliteWithStatusSchema = SatelliteSchema.extend({
|
|
36
|
+
status: SatelliteStatusSchema,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export type SatelliteWithStatus = z.infer<typeof SatelliteWithStatusSchema>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Input schema for creating a new satellite.
|
|
43
|
+
*/
|
|
44
|
+
export const CreateSatelliteSchema = z.object({
|
|
45
|
+
name: z.string().min(1, "Name is required"),
|
|
46
|
+
region: z.string().min(1, "Region is required"),
|
|
47
|
+
tags: z.record(z.string(), z.string()).default({}),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export type CreateSatellite = z.infer<typeof CreateSatelliteSchema>;
|
package/src/signals.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createSignal } from "@checkstack/signal-common";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { SatelliteStatusSchema } from "./schemas";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Broadcast when a satellite's connection status changes (online ↔ offline).
|
|
7
|
+
* Frontend components listening to this signal can update status badges in real-time.
|
|
8
|
+
*/
|
|
9
|
+
export const SATELLITE_STATUS_CHANGED = createSignal(
|
|
10
|
+
"satellite.status.changed",
|
|
11
|
+
z.object({
|
|
12
|
+
satelliteId: z.string(),
|
|
13
|
+
status: SatelliteStatusSchema,
|
|
14
|
+
name: z.string(),
|
|
15
|
+
region: z.string(),
|
|
16
|
+
}),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Broadcast when satellite configuration changes (assignments updated).
|
|
21
|
+
* Frontend components listening to this signal can refetch satellite data.
|
|
22
|
+
*/
|
|
23
|
+
export const SATELLITE_CONFIG_CHANGED = createSignal(
|
|
24
|
+
"satellite.config.changed",
|
|
25
|
+
z.object({
|
|
26
|
+
satelliteId: z.string(),
|
|
27
|
+
}),
|
|
28
|
+
);
|