@checkstack/healthcheck-ping-backend 0.2.23 → 0.3.1

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,44 @@
1
1
  # @checkstack/healthcheck-ping-backend
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [13373ce]
8
+ - @checkstack/common@0.14.0
9
+ - @checkstack/backend-api@0.21.1
10
+ - @checkstack/healthcheck-common@1.5.1
11
+
12
+ ## 0.3.0
13
+
14
+ ### Minor Changes
15
+
16
+ - 9dcc848: Health-check strategy and collector configs now migrate-then-validate when loaded, instead of being cast/rendered raw.
17
+
18
+ These configs declared `version: 2`/`3` migrations but the load path never ran them: stored values are persisted UNVERSIONED, and the executor cast them straight to the strategy/collector type. Both the execution path (`queue-executor`) and the read API (`mapConfig`, feeding router / frontend / gitops `getConfiguration`) now use assume-v1-on-read (`Versioned.parseAssumingV1`): wrap as version 1, run the declared chain, then validate. Order is preserved: migrate -> secret resolve -> template render -> execute. An unregistered strategy/collector or a failed migrate falls back to the raw stored blob rather than dropping the configuration. Every reshaper migration is now IDEMPOTENT, guarding on its legacy discriminator so already-current data passes through untouched.
19
+
20
+ BREAKING CHANGE: for any config GENUINELY at version 1 in the database (e.g. an HTTP strategy still carrying `url`/`method`, or an execute collector still carrying `command`/`args`), the declared migrations now actually RUN on load, so the loaded/returned shape changes for such rows. This is the intended fix - those fields were already supposed to have been migrated away. Configs already at the current shape are unaffected. No data backfill is performed; migration is applied on every read.
21
+
22
+ This is a beta minor.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [9dcc848]
27
+ - Updated dependencies [9dcc848]
28
+ - Updated dependencies [9dcc848]
29
+ - Updated dependencies [9dcc848]
30
+ - Updated dependencies [9dcc848]
31
+ - Updated dependencies [9dcc848]
32
+ - Updated dependencies [9dcc848]
33
+ - Updated dependencies [9dcc848]
34
+ - Updated dependencies [9dcc848]
35
+ - Updated dependencies [9dcc848]
36
+ - Updated dependencies [9dcc848]
37
+ - Updated dependencies [9dcc848]
38
+ - @checkstack/backend-api@0.21.0
39
+ - @checkstack/healthcheck-common@1.5.0
40
+ - @checkstack/common@0.13.0
41
+
3
42
  ## 0.2.23
4
43
 
5
44
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-ping-backend",
3
- "version": "0.2.23",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "checkstack": {
@@ -14,15 +14,15 @@
14
14
  "pack": "bunx @checkstack/scripts plugin-pack"
15
15
  },
16
16
  "dependencies": {
17
- "@checkstack/backend-api": "0.18.0",
18
- "@checkstack/common": "0.12.0",
19
- "@checkstack/healthcheck-common": "1.3.0"
17
+ "@checkstack/backend-api": "0.21.0",
18
+ "@checkstack/common": "0.13.0",
19
+ "@checkstack/healthcheck-common": "1.5.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/bun": "^1.0.0",
23
23
  "typescript": "^5.0.0",
24
24
  "@checkstack/tsconfig": "0.0.7",
25
- "@checkstack/scripts": "0.3.4"
25
+ "@checkstack/scripts": "0.4.0"
26
26
  },
27
27
  "description": "Checkstack healthcheck-ping-backend plugin",
28
28
  "author": {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Contract test: every Ping health-check Versioned schema stored UNVERSIONED
3
+ * and read back via assume-v1-on-read MUST have a COMPLETE, contiguous
4
+ * migration chain from version 1 to its current `version`. Pure STRUCTURAL
5
+ * check (`validateMigrationChainFromV1`); enumerates every Versioned field
6
+ * (config + result + aggregatedResult) of this plugin's strategy + collectors
7
+ * so a new collector or a bumped result schema is covered automatically. See
8
+ * the HTTP plugin's equivalent test for the full rationale.
9
+ */
10
+ import { describe, expect, it } from "bun:test";
11
+ import { PingHealthCheckStrategy } from "./strategy";
12
+ import { PingCollector } from "./ping-collector";
13
+
14
+ describe("ping health-check migration-chain contract", () => {
15
+ const strategy = new PingHealthCheckStrategy();
16
+ const collector = new PingCollector();
17
+ const configs = [
18
+ { name: "ping strategy config", config: strategy.config },
19
+ { name: "ping strategy result", config: strategy.result },
20
+ { name: "ping strategy aggregatedResult", config: strategy.aggregatedResult },
21
+ { name: "ping collector config", config: collector.config },
22
+ { name: "ping collector result", config: collector.result },
23
+ {
24
+ name: "ping collector aggregatedResult",
25
+ config: collector.aggregatedResult,
26
+ },
27
+ ];
28
+
29
+ it("every registered Versioned schema has a complete v1->version chain", () => {
30
+ for (const { name, config } of configs) {
31
+ const problem = config.validateMigrationChainFromV1();
32
+ expect(
33
+ problem,
34
+ `${name} (version ${config.version}) has a broken migration chain: ${problem}`,
35
+ ).toBeUndefined();
36
+ }
37
+ });
38
+ });
@@ -249,4 +249,26 @@ describe("PingHealthCheckStrategy", () => {
249
249
  connectedClient.close();
250
250
  });
251
251
  });
252
+
253
+ describe("config migration (assume-v1-on-read)", () => {
254
+ const strategy = new PingHealthCheckStrategy();
255
+
256
+ it("migrates a genuine v1 blob (host/count/...) down to {timeout}", async () => {
257
+ const migrated = await strategy.config.parseAssumingV1({
258
+ host: "8.8.8.8",
259
+ count: 4,
260
+ timeout: 7000,
261
+ });
262
+ expect(migrated).toEqual({ timeout: 7000 });
263
+ });
264
+
265
+ it("is idempotent: an already-current {timeout} blob is unchanged", async () => {
266
+ const migrated = await strategy.config.parseAssumingV1({ timeout: 3000 });
267
+ expect(migrated).toEqual({ timeout: 3000 });
268
+ });
269
+
270
+ it("has a complete v1->version migration chain", () => {
271
+ expect(strategy.config.validateMigrationChainFromV1()).toBeUndefined();
272
+ });
273
+ });
252
274
  });
package/src/strategy.ts CHANGED
@@ -39,11 +39,19 @@ export const pingConfigSchema = baseStrategyConfigSchema.extend({});
39
39
 
40
40
  export type PingConfig = z.infer<typeof pingConfigSchema>;
41
41
 
42
- // Legacy config type for migrations
43
- interface PingConfigV1 {
44
- host: string;
45
- count: number;
46
- timeout: number;
42
+ // The migrate input is `unknown` per the versioning chain, so narrowing is
43
+ // done with `typeof`/`in` guards (no casts).
44
+
45
+ /** Type guard: the migrate input is a plain object whose keys can be probed. */
46
+ function isRecord(data: unknown): data is Record<string, unknown> {
47
+ return typeof data === "object" && data !== null;
48
+ }
49
+
50
+ /** Read a numeric `timeout` field from a legacy/current config blob. */
51
+ function readTimeout(data: unknown): number | undefined {
52
+ if (!isRecord(data)) return undefined;
53
+ const value = data.timeout;
54
+ return typeof value === "number" ? value : undefined;
47
55
  }
48
56
 
49
57
  /**
@@ -166,9 +174,14 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
166
174
  fromVersion: 1,
167
175
  toVersion: 2,
168
176
  description: "Remove host/count (moved to PingCollector)",
169
- migrate: (data: PingConfigV1): PingConfig => ({
170
- timeout: data.timeout,
171
- }),
177
+ // IDEMPOTENT: only a genuine v1 blob still carries host/count. An
178
+ // already-v2 blob (just `{ timeout }`) passes through untouched.
179
+ migrate: (data: unknown): unknown => {
180
+ if (isRecord(data) && ("host" in data || "count" in data)) {
181
+ return { timeout: readTimeout(data) };
182
+ }
183
+ return data;
184
+ },
172
185
  },
173
186
  ],
174
187
  });