From 383ab74dfa1650a2b5a8b8ecf5a30328166030d8 Mon Sep 17 00:00:00 2001 From: adityaaa-IIT-BHU Date: Tue, 4 Aug 2026 10:44:45 +0900 Subject: [PATCH] fix: key fuse connections on pin labels (#754) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fuseProps built its connections schema by hand with z.record(z.string(), ...), so any key passed validation — pin99, "not a pin at all", even "". Every other two-pin passive builds it from its own pin labels via createConnectionsProp, which keys the record on z.enum(labels). Use createConnectionsProp(fusePinLabels), and narrow the interface to Connections to match capacitor and resistor. Fuse was also the only component in lib/components/ exporting both an interface and an Inferred* type without calling expectTypesMatch, which is why the schema and FuseProps were able to drift apart. Add the assertion so it can't happen again. --- README.md | 2 +- generated/COMPONENT_TYPES.md | 13 ++-------- generated/PROPS_OVERVIEW.md | 2 +- lib/components/fuse.ts | 17 +++++-------- tests/fuse.test.ts | 48 ++++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 tests/fuse.test.ts diff --git a/README.md b/README.md index 6236788e..242c5149 100644 --- a/README.md +++ b/README.md @@ -941,7 +941,7 @@ export interface FuseProps< /** * Connections to other components */ - connections?: Connections; + connections?: Connections; } ``` diff --git a/generated/COMPONENT_TYPES.md b/generated/COMPONENT_TYPES.md index 58a0e324..00211f8a 100644 --- a/generated/COMPONENT_TYPES.md +++ b/generated/COMPONENT_TYPES.md @@ -2155,7 +2155,7 @@ export interface FuseProps schOrientation?: SchematicOrientation - connections?: Connections + connections?: Connections } /** * Schema for validating fuse props @@ -2165,16 +2165,7 @@ export const fuseProps = commonComponentProps.extend({ voltageRating: z.union([z.number(), z.string()]).optional(), schShowRatings: z.boolean().optional(), schOrientation: schematicOrientation.optional(), - connections: z - .record( - z.string(), - z.union([ - z.string(), - z.array(z.string()).readonly(), - z.array(z.string()), - ]), - ) - .optional(), + connections: createConnectionsProp(fusePinLabels).optional(), }) ``` diff --git a/generated/PROPS_OVERVIEW.md b/generated/PROPS_OVERVIEW.md index cfdfde4a..45d09789 100644 --- a/generated/PROPS_OVERVIEW.md +++ b/generated/PROPS_OVERVIEW.md @@ -1119,7 +1119,7 @@ export interface FuseProps /** * Connections to other components */ - connections?: Connections + connections?: Connections } diff --git a/lib/components/fuse.ts b/lib/components/fuse.ts index 1a4798b8..86797407 100644 --- a/lib/components/fuse.ts +++ b/lib/components/fuse.ts @@ -1,4 +1,5 @@ import { z } from "zod" +import { createConnectionsProp } from "lib/common/connectionsProp" import { type CommonComponentProps, commonComponentProps, @@ -7,6 +8,7 @@ import { schematicOrientation, type SchematicOrientation, } from "lib/common/schematicOrientation" +import { expectTypesMatch } from "lib/typecheck" import type { Connections } from "lib/utility-types/connections-and-selectors" /** @@ -38,7 +40,7 @@ export interface FuseProps /** * Connections to other components */ - connections?: Connections + connections?: Connections } /** @@ -49,16 +51,9 @@ export const fuseProps = commonComponentProps.extend({ voltageRating: z.union([z.number(), z.string()]).optional(), schShowRatings: z.boolean().optional(), schOrientation: schematicOrientation.optional(), - connections: z - .record( - z.string(), - z.union([ - z.string(), - z.array(z.string()).readonly(), - z.array(z.string()), - ]), - ) - .optional(), + connections: createConnectionsProp(fusePinLabels).optional(), }) export type InferredFuseProps = z.input + +expectTypesMatch(true) diff --git a/tests/fuse.test.ts b/tests/fuse.test.ts new file mode 100644 index 00000000..7703bb7d --- /dev/null +++ b/tests/fuse.test.ts @@ -0,0 +1,48 @@ +import { expect, test } from "bun:test" +import { fuseProps, type FuseProps } from "lib/components/fuse" + +test("should parse fuse connections keyed by pin labels", () => { + const raw: FuseProps = { + name: "F1", + currentRating: "1A", + connections: { + pin1: "net.VCC", + pin2: ["net.GND"], + }, + } + + const parsed = fuseProps.parse(raw) + expect(parsed.connections).toEqual({ + pin1: "net.VCC", + pin2: ["net.GND"], + }) +}) + +test("should reject fuse connections keyed by an unknown pin", () => { + const parsed = fuseProps.safeParse({ + name: "F1", + currentRating: "1A", + connections: { pin99: ".R1 > .pin1" }, + }) + + expect(parsed.success).toBe(false) +}) + +test("should reject fuse connections keyed by an empty string", () => { + const parsed = fuseProps.safeParse({ + name: "F1", + currentRating: "1A", + connections: { "": ".R1 > .pin1" }, + }) + + expect(parsed.success).toBe(false) +}) + +test("should allow optional fuse connections", () => { + const parsed = fuseProps.parse({ + name: "F1", + currentRating: "1A", + }) + + expect(parsed.connections).toBeUndefined() +})