Skip to content

refactor: move buildRows to CsvImportEvent method with typed return#154

Open
IzumiSy wants to merge 3 commits intomainfrom
refactor-csv-import-event-build-rows
Open

refactor: move buildRows to CsvImportEvent method with typed return#154
IzumiSy wants to merge 3 commits intomainfrom
refactor-csv-import-event-build-rows

Conversation

@IzumiSy
Copy link
Copy Markdown
Contributor

@IzumiSy IzumiSy commented Apr 3, 2026

Summary

Improve the buildRows interface by moving it to a method on CsvImportEvent, with a fully typed return value inferred from the schema definition. Also extract buildSummary as a testable function and add unit tests for summary statistics.

Before

import { buildRows } from "@tailor-platform/app-shell";

onImport: async (event) => {
  const rows = await buildRows(event, csvSchema);
  // rows: Record<string, unknown>[] — no type information
};

After

onImport: async (event) => {
  const rows = await event.buildRows();
  // rows: { name: string; price: number; category: unknown; ... }[] — inferred from schema
};

Motivation

  • Untyped return value: The standalone buildRows returned Record<string, unknown>[], losing all type information that is derivable from the schema definition. Since AppShell is designed to work with Tailor Platform's GraphQL backend, integration with type-safe tools like typed-document-node requires that imported rows carry proper types — returning Record<string, unknown>[] forces users to manually cast or assert types, which defeats the purpose of end-to-end type safety.
  • Redundant schema argument: useCsvImporter already receives the schema, yet buildRows required it again as a second argument. By making it a method on the event object, the schema is captured in a closure and the redundant parameter is eliminated.

Changes

  • Make CsvImportEvent<T> generic with a buildRows() method
  • Add InferCsvRow<T> utility type — infers row shape from schema column keys and StandardSchemaV1 output types
  • Use const T extends CsvSchema in useCsvImporter to preserve literal types without as const
  • Make CsvSchema.columns / CsvColumn.aliases readonly (backward-compatible, required for const T)
  • Remove standalone buildRows from public API (replaced by event.buildRows())
  • Export InferCsvRow type from public API
  • Extract buildSummary into a testable function with unit tests for summary statistics (warnings, corrections, combined scenarios)
  • Add JSDoc comments to CsvImportEvent.summary fields
  • Update tests, docs, and example

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 3, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@tailor-platform/app-shell@154
npm i https://pkg.pr.new/@tailor-platform/app-shell-vite-plugin@154

commit: 2d35554

@IzumiSy
Copy link
Copy Markdown
Contributor Author

IzumiSy commented Apr 3, 2026

/review

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by API Design Review for issue #154

Comment on lines +24 to +26
validRows: totalRows - warnings.length,
correctedRows,
warningRows: warnings.length,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[1/1 — Medium] warningRows counts issues, not distinct rows — contradicts JSDoc

warnings.length is the count of CsvCellIssue objects, not distinct rows. If row 0 has two warning-level issues, warningRows = 2 and validRows = totalRows - 2, even though only one row has warnings. This contradicts the JSDoc added in types.ts:

/** Rows that have at least one warning-level issue. */
warningRows: number;

Note that correctedRows correctly uses new Set() to count distinct rows — warningRows should do the same.

The existing test suite only places warnings on different rows, which masks this inconsistency.

Suggested fix:

const warningRows = new Set(warnings.map((w) => w.rowIndex)).size;

return {
  totalRows,
  validRows: totalRows - warningRows,
  correctedRows,
  warningRows,
  skippedRows: 0,
};

And add a corresponding test case:

it("counts distinct warning rows (multiple issues on the same row count as one)", () => {
  const issues: CsvCellIssue[] = [
    { rowIndex: 0, columnKey: "name",  level: "warning", message: "too short" },
    { rowIndex: 0, columnKey: "price", level: "warning", message: "unusual"   },
  ];
  expect(buildSummary(3, issues, [])).toEqual({
    totalRows: 3,
    validRows: 2,   // 1 distinct warning row, not 2
    correctedRows: 0,
    skippedRows: 0,
    warningRows: 1, // 1 row with warnings, not 2 issues
  });
});

Verdict: Request Changes
One Medium issue — the JSDoc-implementation mismatch on warningRows/validRows semantics is a new inconsistency introduced by this PR (the JSDoc is new; the pre-existing code had no such documentation claim). Everything else looks well-structured: the const T extends CsvSchema inference, InferCsvRow(T), readonly narrowings, and removal of the redundant buildRows standalone export are all solid improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant