Skip to content

Commit 5d47827

Browse files
ClaudeGarbee
andauthored
chore: remove zod package and replace with TypeScript types (#48)
Removes the `zod` runtime validation library and replaces it with native TypeScript interfaces for type safety. The action generates all metrics data internally, making runtime validation unnecessary overhead. ## Changes - **Type definitions** (`src/lib.ts`): Replaced zod schemas with TypeScript interfaces: - `cpuLoadPercentageSchema` → `CpuLoadPercentage` interface - `memoryUsageMBSchema` → `MemoryUsageMB` interface - `diskUsageGBSchema` → `DiskUsageGB` interface - `metricsDataSchema` → `MetricsData` interface - `alertSchema` → `Alert` interface - **Data parsing** (`src/post/lib.ts`): Replaced `metricsDataSchema.parse(JSON.parse(content))` with direct `JSON.parse(content) as MetricsData` - **Type imports**: Updated all source files to use `type` imports for interfaces (TypeScript-only, no runtime value) - **Test updates**: Modified test expecting validation errors to reflect that validation is no longer performed ## Impact - Bundle size reduced by **27,639 lines** (removed 32,353, added 4,714) - Faster build and runtime with one less dependency - Type safety maintained at compile-time via TypeScript Before: ```typescript import { z } from "zod"; const metricsDataSchema = z.object({...}); return metricsDataSchema.parse(JSON.parse(content)); ``` After: ```typescript interface MetricsData {...} return JSON.parse(content) as MetricsData; ``` --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: Garbee <868301+Garbee@users.noreply.github.com>
1 parent 6b11847 commit 5d47827

12 files changed

Lines changed: 4714 additions & 32353 deletions

File tree

dist/main/collector.bundle.js

Lines changed: 1186 additions & 14999 deletions
Large diffs are not rendered by default.

dist/main/index.bundle.js

Lines changed: 204 additions & 204 deletions
Large diffs are not rendered by default.

dist/post/index.bundle.js

Lines changed: 3242 additions & 17051 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
"@actions/artifact": "^6.0.0",
1111
"@actions/core": "^3.0.0",
1212
"@actions/github": "^9.0.0",
13-
"systeminformation": "^5.30.8",
14-
"zod": "4.3.6"
13+
"systeminformation": "^5.30.8"
1514
},
1615
"devDependencies": {
1716
"@types/node": "^25.2.3",

pnpm-lock.yaml

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib.ts

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { z } from "zod";
2-
31
export const bytesPerMB: number = 1024 * 1024;
42
export const bytesPerGB: number = 1024 * 1024 * 1024;
53

@@ -9,7 +7,7 @@ export const bytesPerGB: number = 1024 * 1024 * 1024;
97
*/
108
export function getRootMountPoint(): string {
119
const platform = process.platform;
12-
10+
1311
if (platform === 'win32') {
1412
return 'C:';
1513
} else if (platform === 'darwin') {
@@ -20,45 +18,43 @@ export function getRootMountPoint(): string {
2018
}
2119
}
2220

23-
export const cpuLoadPercentageSchema = z.object({
24-
unixTimeMs: z.number(),
25-
user: z.number().nonnegative().max(100),
26-
system: z.number().nonnegative().max(100),
27-
});
28-
export const cpuLoadPercentagesSchema = z.array(cpuLoadPercentageSchema);
29-
export const memoryUsageMBSchema = z.object({
30-
unixTimeMs: z.number(),
31-
used: z.number().nonnegative(),
32-
free: z.number().nonnegative(),
33-
});
34-
export const memoryUsageMBsSchema = z.array(memoryUsageMBSchema);
35-
export const diskUsageGBSchema = z.object({
36-
unixTimeMs: z.number(),
37-
used: z.number().nonnegative(),
38-
available: z.number().nonnegative(),
39-
size: z.number().nonnegative(),
40-
});
41-
export const diskUsageGBsSchema = z.array(diskUsageGBSchema);
42-
export const stepMarkerSchema = z.object({
43-
unixTimeMs: z.number(),
44-
stepName: z.string(),
45-
status: z.enum(["start", "end"]),
46-
});
47-
export const stepMarkersSchema = z.array(stepMarkerSchema);
48-
export const metricsDataSchema = z.object({
49-
cpuLoadPercentages: cpuLoadPercentagesSchema,
50-
memoryUsageMBs: memoryUsageMBsSchema,
51-
diskUsageGBs: diskUsageGBsSchema,
52-
stepMarkers: stepMarkersSchema,
53-
});
21+
export interface CpuLoadPercentage {
22+
unixTimeMs: number;
23+
user: number;
24+
system: number;
25+
}
26+
27+
export interface MemoryUsageMB {
28+
unixTimeMs: number;
29+
used: number;
30+
free: number;
31+
}
5432

55-
export const alertSchema = z.object({
56-
type: z.enum(["memory", "cpu", "disk"]),
57-
message: z.string(),
58-
timespan: z.number().optional(), // Single timestamp when alert occurred
59-
timespans: z.array(z.number()).optional(), // Multiple timestamps for sustained alerts
60-
value: z.number(),
61-
threshold: z.number(),
62-
});
33+
export interface DiskUsageGB {
34+
unixTimeMs: number;
35+
used: number;
36+
available: number;
37+
size: number;
38+
}
6339

64-
export type Alert = z.infer<typeof alertSchema>;
40+
export interface StepMarker {
41+
unixTimeMs: number;
42+
stepName: string;
43+
status: "start" | "end";
44+
}
45+
46+
export interface MetricsData {
47+
cpuLoadPercentages: CpuLoadPercentage[];
48+
memoryUsageMBs: MemoryUsageMB[];
49+
diskUsageGBs: DiskUsageGB[];
50+
stepMarkers: StepMarker[];
51+
}
52+
53+
export interface Alert {
54+
type: "memory" | "cpu" | "disk";
55+
message: string;
56+
timespan?: number; // Single timestamp when alert occurred
57+
timespans?: number[]; // Multiple timestamps for sustained alerts
58+
value: number;
59+
threshold: number;
60+
}

src/main/metrics.test.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { describe, it, beforeEach, mock, before, after, afterEach } from "node:test";
22
import * as assert from "node:assert/strict";
33
import type { Systeminformation } from "systeminformation";
4-
import type { z } from "zod";
54
import {
6-
type cpuLoadPercentageSchema,
7-
metricsDataSchema,
8-
type memoryUsageMBSchema,
9-
type diskUsageGBSchema,
5+
type CpuLoadPercentage,
6+
type MetricsData,
7+
type MemoryUsageMB,
8+
type DiskUsageGB,
109
} from "../lib.ts";
1110

1211
describe("Metrics", () => {
@@ -156,7 +155,7 @@ describe("Metrics", () => {
156155

157156
it("should initialize with empty data arrays", () => {
158157
const metrics = createMetrics();
159-
const data: z.TypeOf<typeof metricsDataSchema> = JSON.parse(metrics.get());
158+
const data: MetricsData = JSON.parse(metrics.get());
160159

161160
assert.ok(data.cpuLoadPercentages);
162161
assert.ok(data.memoryUsageMBs);
@@ -177,7 +176,7 @@ describe("Metrics", () => {
177176
await new Promise(resolve => queueMicrotask(resolve));
178177
}
179178

180-
const data: z.TypeOf<typeof metricsDataSchema> = JSON.parse(metrics.get());
179+
const data: MetricsData = JSON.parse(metrics.get());
181180

182181
// Verify CPU metrics are collected
183182
assert.ok(data.cpuLoadPercentages.length > 0);
@@ -207,7 +206,7 @@ describe("Metrics", () => {
207206
await new Promise(resolve => queueMicrotask(resolve));
208207
}
209208

210-
const cpuData: z.TypeOf<typeof cpuLoadPercentageSchema> = JSON.parse(
209+
const cpuData: CpuLoadPercentage = JSON.parse(
211210
metrics.get(),
212211
).cpuLoadPercentages[0];
213212

@@ -227,7 +226,7 @@ describe("Metrics", () => {
227226
await new Promise(resolve => queueMicrotask(resolve));
228227
}
229228

230-
const memData: z.TypeOf<typeof memoryUsageMBSchema> = JSON.parse(
229+
const memData: MemoryUsageMB = JSON.parse(
231230
metrics.get(),
232231
).memoryUsageMBs[0];
233232

@@ -248,7 +247,7 @@ describe("Metrics", () => {
248247
await new Promise(resolve => queueMicrotask(resolve));
249248
}
250249

251-
const diskData: z.TypeOf<typeof diskUsageGBSchema> = JSON.parse(
250+
const diskData: DiskUsageGB = JSON.parse(
252251
metrics.get(),
253252
).diskUsageGBs[0];
254253

@@ -272,7 +271,7 @@ describe("Metrics", () => {
272271
await new Promise(resolve => queueMicrotask(resolve));
273272
}
274273

275-
const initialData: z.TypeOf<typeof metricsDataSchema> = JSON.parse(
274+
const initialData: MetricsData = JSON.parse(
276275
metrics.get(),
277276
);
278277
const initialCpuCount: number = initialData.cpuLoadPercentages.length;
@@ -291,7 +290,7 @@ describe("Metrics", () => {
291290
await new Promise(resolve => queueMicrotask(resolve));
292291
}
293292

294-
const updatedData: z.TypeOf<typeof metricsDataSchema> = JSON.parse(
293+
const updatedData: MetricsData = JSON.parse(
295294
metrics.get(),
296295
);
297296
const updatedCpuCount: number = updatedData.cpuLoadPercentages.length;
@@ -321,7 +320,7 @@ describe("Metrics", () => {
321320
await new Promise(resolve => queueMicrotask(resolve));
322321
}
323322

324-
const data: z.TypeOf<typeof metricsDataSchema> = JSON.parse(metrics.get());
323+
const data: MetricsData = JSON.parse(metrics.get());
325324

326325
// Verify at least 2 data points exist
327326
assert.ok(data.cpuLoadPercentages.length >= 2);
@@ -364,7 +363,7 @@ describe("Metrics", () => {
364363
await new Promise(resolve => queueMicrotask(resolve));
365364
}
366365

367-
const finalData: z.TypeOf<typeof metricsDataSchema> = JSON.parse(
366+
const finalData: MetricsData = JSON.parse(
368367
metrics.get(),
369368
);
370369

@@ -438,7 +437,7 @@ describe("Metrics", () => {
438437
assert.strictEqual(writeCount, 5, "Fifth collection should write");
439438

440439
// Verify data is collected correctly with immediate writes
441-
const data: z.TypeOf<typeof metricsDataSchema> = JSON.parse(metrics.get());
440+
const data: MetricsData = JSON.parse(metrics.get());
442441
assert.strictEqual(data.cpuLoadPercentages.length, 5, "Should have 5 CPU data points");
443442
assert.strictEqual(data.memoryUsageMBs.length, 5, "Should have 5 memory data points");
444443
assert.strictEqual(data.diskUsageGBs.length, 5, "Should have 5 disk data points");
@@ -471,7 +470,7 @@ describe("Metrics", () => {
471470
const writtenContent = fileWrites.get(stateFilePath);
472471
assert.ok(writtenContent, "Should have written content");
473472

474-
const writtenData: z.TypeOf<typeof metricsDataSchema> = JSON.parse(writtenContent);
473+
const writtenData: MetricsData = JSON.parse(writtenContent);
475474
assert.strictEqual(writtenData.cpuLoadPercentages.length, 1, "Written data should have 1 CPU data point");
476475
});
477476

@@ -507,7 +506,7 @@ describe("Metrics", () => {
507506
assert.strictEqual(writeCount, 10, "Should have 10 writes after 10 collections");
508507

509508
// Verify all 10 data points are in memory
510-
const data: z.TypeOf<typeof metricsDataSchema> = JSON.parse(metrics.get());
509+
const data: MetricsData = JSON.parse(metrics.get());
511510
assert.strictEqual(data.cpuLoadPercentages.length, 10, "Should have 10 CPU data points");
512511
});
513512

@@ -521,7 +520,7 @@ describe("Metrics", () => {
521520
await new Promise(resolve => queueMicrotask(resolve));
522521
}
523522

524-
const data: z.TypeOf<typeof metricsDataSchema> = JSON.parse(metrics.get());
523+
const data: MetricsData = JSON.parse(metrics.get());
525524

526525
// Should have disk data from the root mount point for the current platform
527526
assert.ok(data.diskUsageGBs.length > 0, "Should have disk data");

src/main/metrics.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { currentLoad, mem, fsSize } from "systeminformation";
33
import { writeFile } from "node:fs/promises";
44
import { writeFileSync } from "node:fs";
55
import { join } from "node:path";
6-
import type { z } from "zod";
7-
import { metricsDataSchema, bytesPerMB, bytesPerGB, getRootMountPoint } from "../lib.ts";
6+
import type { MetricsData } from "../lib.ts";
7+
import { bytesPerMB, bytesPerGB, getRootMountPoint } from "../lib.ts";
88

99
export class Metrics {
10-
private readonly data: z.TypeOf<typeof metricsDataSchema>;
10+
private readonly data: MetricsData;
1111
private readonly intervalMs: number;
1212
private readonly stateFile: string;
1313
private timeoutId: NodeJS.Timeout | null = null;

src/post/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ import { DefaultArtifactClient } from "@actions/artifact";
44
import { info, setFailed, summary } from "@actions/core";
55
import { context } from "@actions/github";
66
import { getMetricsData, render, collectFinalMetrics, detectAlerts } from "./lib.ts";
7-
import type { z } from "zod";
8-
import type { metricsDataSchema } from "../lib.ts";
7+
import type { MetricsData } from "../lib.ts";
98

109
async function index(): Promise<void> {
1110
const maxRetryCount: number = 10;
12-
let metricsData: z.TypeOf<typeof metricsDataSchema>;
11+
let metricsData: MetricsData;
1312

1413
// Collect one final set of metrics and get the complete data
1514
metricsData = await collectFinalMetrics();

src/post/lib.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { describe, it, before, after, mock, beforeEach } from "node:test";
22
import * as assert from "node:assert/strict";
33
import { join } from "node:path";
4-
import type { z } from "zod";
5-
import type { metricsDataSchema } from "../lib.js";
4+
import type { MetricsData } from "../lib.js";
65

76
/**
87
* Sample metrics data for testing.
98
*/
10-
const sampleMetricsData: z.TypeOf<typeof metricsDataSchema> = {
9+
const sampleMetricsData: MetricsData = {
1110
cpuLoadPercentages: [
1211
{ unixTimeMs: 1704067200000, user: 25.5, system: 10.3 },
1312
{ unixTimeMs: 1704067205000, user: 30.2, system: 12.1 },
@@ -165,12 +164,12 @@ describe("getMetricsData", () => {
165164
assert.deepStrictEqual(result, sampleMetricsData);
166165
});
167166

168-
it("should throw error for invalid metrics data", async () => {
167+
it("should parse metrics data without validation", async () => {
169168
// Compute the same path that the implementation would use
170169
const githubStateFile = process.env.GITHUB_STATE;
171170
const runId = process.env.GITHUB_RUN_ID || "local";
172171
const job = process.env.GITHUB_JOB || "default";
173-
172+
174173
let stateFile: string;
175174
if (githubStateFile) {
176175
const stateDir = join(githubStateFile, '..');
@@ -179,13 +178,16 @@ describe("getMetricsData", () => {
179178
const runnerTemp = process.env.RUNNER_TEMP || process.env.TMPDIR || '/tmp';
180179
stateFile = join(runnerTemp, `metrics-state-${runId}-${job}.json`);
181180
}
182-
181+
182+
// With zod removed, the function will parse any valid JSON
183183
fileReads.set(stateFile, JSON.stringify({
184184
cpuLoadPercentages: "not an array",
185185
memoryUsageMBs: [],
186186
}));
187187

188-
await assert.rejects(getMetricsData());
188+
// Should not throw - just returns the parsed data as-is
189+
const result = await getMetricsData();
190+
assert.ok(result);
189191
});
190192

191193
it("should throw error when state file doesn't exist", async () => {

0 commit comments

Comments
 (0)