Skip to content

Commit efcab80

Browse files
bloveclaude
andauthored
refactor(core): audit @pretable/core public surface; rename GridCore* → Pretable* (#110)
* docs(spec): tier 1 sub-project A — PR 2 (@pretable/core audit) design Renames GridCore* → Pretable* at source in @pretable-internal/grid-core and layout-core; collapses @pretable/core's types.ts; replaces PretableGrid's Omit<> leak with an explicit interface; bundledPackages update so api-extractor inlines the renamed types in core.api.md. Headless docs/examples are explicitly deferred per memory. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(plan): tier 1 sub-project A — PR 2 (core audit) implementation plan 9 tasks: layout-core rename, grid-core rename (+ renderer-dom), explicit PretableGrid interface, react import update, public_api.ts with TSDoc + @public, api-extractor config update, regenerate .api.md files, README, gates+PR. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(layout-core): rename LayoutSpan → PretableRowRange Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(grid-core): rename GridCore* → Pretable* at source Engine-level types now use Pretable* names directly. Internal-only GridCoreStore renamed to PretableEngine to distinguish from the public PretableGrid interface (defined separately in @pretable/core). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(core): explicit PretableGrid interface; collapse types.ts Replaces extends Omit<GridCoreStore<TRow>, 'options'> with an explicit interface listing every method/property pretable promises. types.ts becomes a re-export shell from the renamed engine types. Also re-exports PretableRowRange via grid-core (which already bridges layout-core), and updates core/index.ts to export PretableGrid from pretable-grid.ts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(react): rename import aliases for renamed core types types.ts imports core's PretableColumn as PretableBaseColumn to disambiguate from react's own PretableColumn. index.ts re-exports PretableColumn as PretableCoreColumn so react's public surface is unchanged — PR 3 will revisit the surface during the react audit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(core): hand-curated public_api.ts with @public TSDoc tags Adds @public TSDoc tags to every symbol exported through @pretable/core's public surface, plus @internal tags on engine-only symbols (PretableEngine, PretableFrame, deriveSelectedRows, rangeContainsCell, layout-core's planning types). index.ts collapses to a single re-export from public_api.ts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(api): bundle internal packages; flip missing-release-tag to warning bundledPackages now inlines @pretable-internal/grid-core and @pretable-internal/layout-core into each report so renamed types appear self-contained. ae-missing-release-tag was 'none' for the PR 1 baseline; flips to 'warning' now that core is fully tagged — provides coverage visibility for PRs 3-5 without failing CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore(api): regenerate .api.md; revert release-tag flip and silence unresolved-link core.api.md: zero ae-forgotten-export warnings; every public symbol annotated @public; PretableGrid is a flat interface. react.api.md: mechanical rename diff (Pretable* names inlined). api-extractor.base.json: ae-missing-release-tag stays at "none" (the spec's flip-to-warning broke api:check — non-local mode treats warnings as fatal, opposite of what the spec assumed). ae-unresolved-link silenced because cross-package {@link createGrid} can't resolve in bundled reports. Tag flip will land in PR 5 once react/ui/stream-adapter are tagged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(core): add per-package README; correct spec on api-extractor warnings README mentions headless support; full docs/examples deferred per project_headless_engine_docs_deferred.md memory. Spec correction: api-extractor's non-local mode treats warnings as fatal, so ae-missing-release-tag stays at "none" until PR 5. Also silences ae-unresolved-link for cross-package TSDoc refs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: prettier reformat spec/plan/grid-core barrel Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a63886d commit efcab80

23 files changed

Lines changed: 2102 additions & 377 deletions

api-extractor.base.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
},
2222
"ae-missing-release-tag": {
2323
"logLevel": "none"
24+
},
25+
"ae-unresolved-link": {
26+
"logLevel": "none"
2427
}
2528
}
2629
},
@@ -43,6 +46,8 @@
4346
"@pretable/core",
4447
"@pretable/react",
4548
"@pretable/ui",
46-
"@pretable/stream-adapter"
49+
"@pretable/stream-adapter",
50+
"@pretable-internal/grid-core",
51+
"@pretable-internal/layout-core"
4752
]
4853
}

docs/superpowers/plans/2026-05-08-tier1-api-stabilization-pr2-core.md

Lines changed: 1122 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Tier 1 Sub-project A — PR 2 (`@pretable/core` audit) Design
2+
3+
> Status: spec. 2026-05-08. PR 2 of 5 in [Tier 1 Sub-project A — Public API Stabilization](2026-05-07-tier1-public-api-stabilization-design.md).
4+
5+
## Goal
6+
7+
Lock `@pretable/core`'s public surface into a state appropriate for a future headless engine (long-term scope) while shipping only the type cleanup now (no docs/examples — that's deferred per `project_headless_engine_docs_deferred.md` memory). Eliminate the `GridCore*` naming leakage by renaming the engine-level types at source and re-exporting them under their natural `Pretable*` names.
8+
9+
## Non-goals
10+
11+
- No `@pretable/react` audit. PR 3 owns react's surface; this PR only touches react where the rename forces a mechanical import update.
12+
- No headless-mode website docs, no `apps/headless-demo`, no `examples/headless-*` content. Deferred per memory.
13+
- No `@pretable-internal/grid-core` package consolidation into `@pretable/core`. Engine package stays separate.
14+
- No new tests beyond rename ripple. Existing tests cover behavior; the audit is type-level.
15+
16+
## Architecture
17+
18+
### Public-surface layout
19+
20+
`@pretable/core/src/` becomes:
21+
22+
```
23+
src/
24+
index.ts // export * from './public_api';
25+
public_api.ts // hand-curated re-exports, TSDoc + @public on each
26+
types.ts // re-exports from @pretable-internal/grid-core
27+
create-grid.ts // unchanged
28+
```
29+
30+
`public_api.ts` is the only file whose contents are reviewed for public-API impact. `index.ts` is one line.
31+
32+
### Engine type rename (the structural change)
33+
34+
`@pretable-internal/grid-core` is the underlying engine; today its types use `GridCore*` naming and `@pretable/core` aliases them with `Pretable*` names. Aliasing creates `ae-forgotten-export` warnings and leaks the internal name through `extends` clauses.
35+
36+
**Resolution:** rename the engine types at source. The engine becomes `Pretable*`-named in `@pretable-internal/grid-core`'s own `src/types.ts`. `@pretable/core/types.ts` collapses from ~50 lines of aliases to a clean re-export shell.
37+
38+
`@pretable-internal/grid-core` and `@pretable-internal/layout-core` are added to `bundledPackages` in `api-extractor.base.json` so the renamed types appear inlined in `core.api.md` rather than as `import { … } from '@pretable-internal/…'` references. Each `.api.md` stays self-contained.
39+
40+
### `PretableGrid` interface
41+
42+
The current `PretableGrid extends Omit<GridCoreStore<TRow>, "options">` is replaced with an explicit interface in `@pretable/core` that lists every method/property pretable promises. After rename, `GridCoreStore` becomes `PretableEngine` at the engine source — but `PretableGrid` becomes a _new, narrower_ interface in `@pretable/core` that `createGrid` returns. The internal engine factory (`createGridCore`) returns `PretableEngine`; `createGrid` wraps/casts to the public `PretableGrid`.
43+
44+
The rationale: `Omit<>` inheritance leaks the engine type into the public surface and forces every `.api.md` reader to mentally subtract a key. An explicit interface is what users will read in IDE tooltips.
45+
46+
## Components
47+
48+
### Rename map
49+
50+
In `@pretable-internal/grid-core/src/types.ts` and `create-grid-core.ts`:
51+
52+
| Internal (today) | After rename | Notes |
53+
| -------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
54+
| `GridCoreCellAddress` | `PretableCellAddress` | Already aliased — absorb. |
55+
| `GridCoreCellRange` | `PretableCellRange` | Already aliased — absorb. |
56+
| `GridCoreColumn` | `PretableColumn` | Was `PretableCoreColumn`. React imports as `PretableBaseColumn` to disambiguate from its own `PretableColumn`. PR 3 fully resolves the naming. |
57+
| `GridCoreFocusDirection` | `PretableFocusDirection` | Already aliased — absorb. |
58+
| `GridCoreFocusState` | `PretableFocusState` | Already aliased — absorb. |
59+
| `GridCoreFormatInput` | `PretableFormatInput` | Was an alias — absorb. |
60+
| `GridCoreMoveFocusOptions` | `PretableMoveFocusOptions` | Already aliased — absorb. |
61+
| `GridCoreOptions` | `PretableGridOptions` | Was an alias — absorb. |
62+
| `GridCoreRow` | `PretableRow` | Was a `Record<string, unknown>` constraint; `PretableRow` already exists in `@pretable/core/types.ts`. Unify on the public name. |
63+
| `GridCoreRowModel` | `PretableVisibleRow` | Already aliased — absorb. |
64+
| `GridCoreSelectionState` | `PretableSelectionState` | Already aliased — absorb. |
65+
| `GridCoreSnapshot` | `PretableGridSnapshot` | Already aliased — absorb. |
66+
| `GridCoreSortDirection` | `PretableSortDirection` | Already aliased — absorb. |
67+
| `GridCoreSortState` | `PretableSortState` | Already aliased — absorb. |
68+
| `GridCoreStore` | `PretableEngine` | Internal engine handle returned by `createGridCore`. Distinct from public `PretableGrid` (see Architecture). |
69+
| `GridCoreTransaction` | `PretableTransaction` | Was an alias — absorb. |
70+
| `GridCoreViewportState` | `PretableViewportState` | Already aliased — absorb. |
71+
| `RowSelectionTriState` | `PretableRowSelectionTriState` | Already aliased — absorb. |
72+
73+
In `@pretable-internal/layout-core/src/types.ts`:
74+
75+
| Internal (today) | After rename | Notes |
76+
| ---------------- | ------------------ | ---------------------------------------------------------------------------------------------------------- |
77+
| `LayoutSpan` | `PretableRowRange` | Semantic name — it represents the visible-row index range exposed via `PretableGridSnapshot.visibleRange`. |
78+
79+
`AutosizeOptions` keeps its name (already pretable-friendly).
80+
81+
### `@pretable/core` public surface (post-rename)
82+
83+
`public_api.ts` re-exports, every symbol carrying `@public` + a one-line TSDoc summary:
84+
85+
```
86+
export { createGrid } from "./create-grid";
87+
export type { PretableGrid } from "./pretable-grid";
88+
export type {
89+
AutosizeOptions,
90+
PretableCellAddress,
91+
PretableCellRange,
92+
PretableColumn,
93+
PretableFocusDirection,
94+
PretableFocusState,
95+
PretableFormatInput,
96+
PretableGridOptions,
97+
PretableGridSnapshot,
98+
PretableMoveFocusOptions,
99+
PretableRow,
100+
PretableRowRange,
101+
PretableRowSelectionTriState,
102+
PretableSelectionState,
103+
PretableSortDirection,
104+
PretableSortState,
105+
PretableTransaction,
106+
PretableViewportState,
107+
PretableVisibleRow,
108+
} from "./types";
109+
```
110+
111+
`pretable-grid.ts` is the new file holding the explicit `PretableGrid` interface — defined locally, not re-exported from the engine. (`@pretable-internal/grid-core`'s engine handle is the renamed `PretableEngine`, separate from public `PretableGrid`.) `types.ts` does not re-export `PretableGrid`.
112+
113+
### TSDoc style
114+
115+
- One-line summary above every symbol.
116+
- `@example` blocks **only** for `createGrid` and the new `PretableGrid` interface.
117+
- Type aliases get one-line summaries; no examples.
118+
119+
### `ae-missing-release-tag` config
120+
121+
PR 1 set this rule's `logLevel` to `none` to allow undocumented baselines. PR 2 keeps it at `none` because — as discovered during implementation — api-extractor's non-local mode treats `warning`-level extractor messages as **fatal** (exit 1), opposite of what the spec originally assumed. Flipping to `warning` would break `api:check` for `@pretable/react`, `@pretable/ui`, and `@pretable/stream-adapter` (which still have untagged symbols pending PRs 3–5). The flip moves to PR 5 once every package is fully tagged.
122+
123+
Additionally, `ae-unresolved-link` is silenced (`logLevel: none`) because cross-package `{@link …}` references — e.g., a TSDoc in `@pretable/core`'s `pretable-grid.ts` linking to `createGrid` — cannot resolve when api-extractor processes a different package whose bundled report inlines the source. The links work fine in IDE tooltips; only the bundled-report resolver fails. Silencing avoids false-positive CI failures.
124+
125+
## Data flow
126+
127+
1. Author renames at engine source (`@pretable-internal/{grid-core,layout-core}`).
128+
2. Internal consumers (`@pretable-internal/renderer-dom`, `@pretable/react/src/types.ts`'s import alias) updated.
129+
3. `@pretable/core/types.ts` collapses to clean re-exports.
130+
4. New `@pretable/core/src/pretable-grid.ts` defines the explicit `PretableGrid` interface.
131+
5. `public_api.ts` re-exports with TSDoc.
132+
6. `api-extractor.base.json` updates `bundledPackages` to include the two internal packages.
133+
7. `pnpm api` regenerates `core.api.md` (large diff: zero `ae-forgotten-export` warnings, type aliases now show `@public` documented entries).
134+
8. `react.api.md` regenerated as a side effect of the rename + bundledPackages change. Verify the diff is mechanical (renames only, no shape change).
135+
136+
## Error handling
137+
138+
This PR's failure modes are tooling and process:
139+
140+
- **Internal compile errors after rename.** Caught by `pnpm -w typecheck`. The audit fixes them inline.
141+
- **`react.api.md` drifts unexpectedly.** PR's job to verify the drift is rename-only; if shapes change, regress.
142+
- **`ae-missing-release-tag` warnings remain in `core.api.md`.** Means a symbol slipped past tagging. Fix during the audit, not after.
143+
- **CI fails because `pnpm api:check` mismatches.** Run `pnpm api`, commit, retry.
144+
145+
## Testing
146+
147+
Repo-wide gates: `pnpm -w typecheck`, `pnpm -w test`, `pnpm -w lint`, `pnpm format`, `pnpm api:check`.
148+
149+
No new test categories. Existing test coverage already exercises the renamed types under their old names; renames cascade through fine. The single test file under `packages/grid-core/src/__tests__/selection-state.test.ts` that imports `GridCore*` types directly gets a mechanical update.
150+
151+
## Per-package README
152+
153+
`packages/core/README.md` ~80 lines:
154+
155+
- One-paragraph "what is `@pretable/core`" — the headless engine. Most users reach for `@pretable/react`; this package exists for users who want to drive their own UI from the engine state.
156+
- "When to use" — 2-3 sentences. Mention that headless usage is supported but full docs/examples/demos are forthcoming (links to the deferred-memory's "future task triggers" framing without naming the memory).
157+
- Install command and minimal `createGrid` example.
158+
- "See [`core.api.md`](./core.api.md) for the full public surface."
159+
- License/contributing footer matching repo style.
160+
161+
## PR shape
162+
163+
Single PR. The rename is mechanical and atomic — splitting would force `@pretable/react` to compile against half-renamed engine types, an unstable intermediate state.
164+
165+
Implementation tasks (subagent-driven):
166+
167+
1. Rename `LayoutSpan``PretableRowRange` in `@pretable-internal/layout-core` + update internal consumers.
168+
2. Rename `GridCore*``Pretable*` in `@pretable-internal/grid-core` + update internal consumers (including the test file).
169+
3. Update `@pretable-internal/renderer-dom` imports.
170+
4. Update `@pretable/react/src/types.ts` import (alias `PretableColumn as PretableBaseColumn`).
171+
5. Define explicit `PretableGrid` interface in `@pretable/core/src/pretable-grid.ts`; rewrite `create-grid.ts` to return it.
172+
6. Write `@pretable/core/src/public_api.ts` with TSDoc + `@public` tags; collapse `index.ts` to one line; collapse `types.ts` to clean re-exports.
173+
7. Add `@pretable-internal/grid-core` and `@pretable-internal/layout-core` to `bundledPackages` in `api-extractor.base.json`. Flip `ae-missing-release-tag` to `warning`.
174+
8. Regenerate `core.api.md` and `react.api.md`; verify react's diff is rename-only.
175+
9. Write `packages/core/README.md`.
176+
10. Repo-wide gates + PR.
177+
178+
## Success criteria
179+
180+
- `core.api.md` has zero `ae-forgotten-export` warnings.
181+
- Every public symbol in `core.api.md` shows `@public` (not `@public (undocumented)`).
182+
- `PretableGrid` is an explicit interface, not `extends Omit<…>`.
183+
- `@pretable-internal/grid-core/src/types.ts` and `@pretable-internal/layout-core/src/types.ts` use only `Pretable*` names.
184+
- `react.api.md` diff is mechanical rename only (no shape change).
185+
- `packages/core/README.md` exists with the structure above.
186+
- All repo-wide gates pass including `pnpm api:check`.

packages/core/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# @pretable/core
2+
3+
The headless engine for [pretable](https://pretable.dev/). Drives sort, filter, selection, focus, viewport, and streaming-transaction state for any table-shaped UI.
4+
5+
## When to reach for this
6+
7+
Most users want **[`@pretable/react`](../react)** instead. It bundles `@pretable/core` with a React surface that handles rendering, layout, and keyboard interaction.
8+
9+
`@pretable/core` is for users building their own UI from scratch — for example, plain DOM, a non-React framework, or a custom canvas/webgl renderer. Headless usage is supported (the `createGrid` factory returns a fully-typed `PretableGrid` handle), but **dedicated docs, examples, and demos for headless mode are forthcoming**. If headless is what you're after, the type definitions and [`core.api.md`](./core.api.md) are the source of truth today.
10+
11+
## Install
12+
13+
```sh
14+
npm install @pretable/core
15+
# or pnpm add @pretable/core, yarn add @pretable/core
16+
```
17+
18+
## Minimal example
19+
20+
```ts
21+
import { createGrid } from "@pretable/core";
22+
23+
const grid = createGrid({
24+
columns: [
25+
{ id: "name", header: "Name" },
26+
{ id: "age", header: "Age", sortable: true },
27+
],
28+
rows: [
29+
{ id: "1", name: "Ada", age: 36 },
30+
{ id: "2", name: "Grace", age: 85 },
31+
],
32+
});
33+
34+
grid.subscribe(() => {
35+
const { visibleRows, sort } = grid.getSnapshot();
36+
console.log("rows:", visibleRows.length, "sort:", sort);
37+
});
38+
39+
grid.setSort("age", "desc");
40+
```
41+
42+
## Full public surface
43+
44+
See **[`core.api.md`](./core.api.md)** for every exported type, interface, and function with their full signatures. The file is generated by [API Extractor](https://api-extractor.com/) and committed to the repo; CI fails if it drifts.
45+
46+
## License
47+
48+
MIT — see [LICENSE](../../LICENSE).

0 commit comments

Comments
 (0)