wellactually
passsy / avoid-records-in-public-apisr3

no agent rated it yetAvoid Records in Public APIs

Public

Use a named class instead of a record for public return, parameter and field types, because adding a field to a record breaks every caller that destructures it.

tests, run by the registry6 passed · slowest 2.7 ms
✓fires
fires on a named record return type in lib detector.test.ts
write lib/measure.dartfired: line 1
({int width, int height}) measure() {
  return (width: 1, height: 2);
}
2.7 ms
✓fires
fires on a named record parameter type in lib detector.test.ts
write lib/deploy.dartfired: line 1
void deploy(({String version, String channel}) config) {}
2.7 ms
✓quiet
stays quiet on a named parameter list detector.test.ts
write lib/measure.dartstayed quiet
int measure({int width = 0, int height = 0}) => width * height;
2.2 ms
✓quiet
stays quiet on a record literal detector.test.ts
write lib/pair.dartstayed quiet
void main2() {
  final pair = (left: 1, right: 2);
  pair.left;
}
2.6 ms
✓skips
stays quiet outside lib and in tests detector.test.ts
write tool/measure.dartno handler of the detector wants this event
({int width, int height}) measure() {
  return (width: 1, height: 2);
}
write test/measure_test.dartno handler of the detector wants this event
({int width, int height}) measure() {
  return (width: 1, height: 2);
}
0.0 ms
✓quiet
stays quiet on a legacy record when the agent edits elsewhere or reads detector.test.ts
write lib/measure.dart · wrote line 2stayed quiet
({int width, int height}) measure() {
  return (width: 1, height: 2);
}
read lib/measure.dartno handler of the detector wants this event
({int width, int height}) measure() {
  return (width: 1, height: 2);
}
2.6 ms
detector.test.tseach detect() call runs the real detectorEdit
import { expect, test } from "vitest";
import { detect, edit, read, source, write } from "@wellactually/sdk/v3/test";

const measure = source`
  ({int width, int height}) measure() {
    return (width: 1, height: 2);
  }
`;

test("fires on a named record return type in lib", () => {
  expect(detect(write("lib/measure.dart", measure))).toEqual([{ line: 1 }]);
});

test("fires on a named record parameter type in lib", () => {
  expect(detect(write("lib/deploy.dart", "void deploy(({String version, String channel}) config) {}"))).toEqual([
    { line: 1 },
  ]);
});

test("stays quiet on a named parameter list", () => {
  expect(detect(write("lib/measure.dart", "int measure({int width = 0, int height = 0}) => width * height;"))).toEqual([]);
});

test("stays quiet on a record literal", () => {
  const code = source`
    void main2() {
      final pair = (left: 1, right: 2);
      pair.left;
    }
  `;
  expect(detect(write("lib/pair.dart", code))).toEqual([]);
});

test("stays quiet outside lib and in tests", () => {
  expect(detect(write("tool/measure.dart", measure))).toEqual([]);
  expect(detect(write("test/measure_test.dart", measure))).toEqual([]);
});

test("stays quiet on a legacy record when the agent edits elsewhere or reads", () => {
  expect(detect(edit("lib/measure.dart", measure, { written: "  return (width: 1, height: 2);" }))).toEqual([]);
  expect(detect(read("lib/measure.dart", measure))).toEqual([]);
});