Skip to content

Commit 009e25d

Browse files
os-zhuangclaude
andauthored
refactor(types): burn ledger batch 1 — report/chart/query symbols stop wearing spec names (#3167)
Batch 1 of the objectstack#4115 debt ledger (objectui#3155): 11 `@object-ui/types` collisions, triaged one by one against `@objectstack/spec` 17.0.0-rc.0. Ledger 115 -> 105; ALLOW 5 -> 6. Six were pure name collisions — same name, different concept — so they are renamed to local dialects. Each new name was run past the guard first (objectui#3074's `PageComponentSchema` lesson) and is pinned absent from the spec's export set: ChartSeries -> ChartDataSeries (ours carries literal `data: number[]`; ChartSeriesSchema -> ChartDataSeriesSchema spec's is a dataset-bound descriptor) QueryAST -> SqlQueryAST (compiled SQL tree vs ObjectQL request) QuerySchema -> DriverQueryConfig (TS config vs the spec's zod value) DriverInterface -> SqlDriverInterface (SQL client vs runtime driver contract) DatasourceSchema -> DatasourceRegistration (live record vs authored document) Renames were done declaration-and-barrel first, letting tsc enumerate the break points, never by global string replace — half these names exist on both sides. Three are now DERIVED, converging on the `ListViewSchema` paradigm. Two carried live defects, both of the silent-stripping kind a `z.object()` produces: - DashboardWidgetSchema declared 10 of the spec's 22 keys, so `objectui validate` deleted the other 12 in silence — chartConfig, colorVariant, filter, responsive, aria, action{Url,Type,Icon}, compareTo, suppressWarnings and the requiresObject/requiresService capability gates the renderer honours at runtime. The TS interface declared most of them all along. - GlobalFilterSchema took `scope` as a free-form string; it now uses the spec's widget|dashboard vocabulary. - AppContextSelectorSchema was a full restatement. Every pinned divergence is one a runtime normalizer already backs, and each is asserted in both directions so it dies the day the spec adopts it. ListViewSchema resolves as a pair: the zod node now names the spec in its own initializer instead of one hop away through a local const (which is why the repo's own best-practice derivation was sitting in the ledger), and the TS twin moves to ALLOW — the spec's `ListViewSchema` is a zod VALUE, so there is no spec type to alias, and the objectui node intersects non-serialisable runtime props. JoinedReportBlock is NOT burnable and stays in the ledger: the spec declares `JoinedReportBlockSchema` as `z.ZodTypeAny`, so its exported type resolves to `unknown` — re-exporting it would delete the block type outright. This is one variant wider than the objectstack#4171 cases already pinned: those erase to `any`, which `0 extends (1 & T)` detects, while this erases to `unknown`, which that probe reports as safe. Guard header and an inverted pin now say so; it belongs to ledger batch 8. Verification: guard green; `--ledger` diff removes exactly these 10 and adds nothing, other packages byte-identical; type-check 78/78 after rebuilding the types dist; 9326 assertions across 798 files green; mutation-tested both ways (hand-copying GlobalFilterSchema back and un-renaming SqlQueryAST each fail by name and file, and the parity test fails alongside). Claude-Session: https://claude.ai/code/session_015Jdef6ZFhJmiNRhNCd4DW3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 10bead2 commit 009e25d

14 files changed

Lines changed: 666 additions & 209 deletions
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
"@object-ui/types": minor
3+
"@object-ui/core": minor
4+
---
5+
6+
Report / chart / query symbols stop wearing `@objectstack/spec`'s names
7+
(objectui#3155, objectstack#4115).
8+
9+
**Breaking for TypeScript imports** — six exported names change. Each was a
10+
different concept than the spec export it collided with, so an author reading
11+
the objectui declaration as "the spec's" was reading a false claim:
12+
13+
| was | now | why they were never the same thing |
14+
|:--|:--|:--|
15+
| `ChartSeries` | `ChartDataSeries` | ours is a display name plus literal `data: number[]`; the spec's is a dataset-bound series descriptor (`type`/`stack`/`yAxis`/`variant`) with no data at all |
16+
| `ChartSeriesSchema` | `ChartDataSeriesSchema` | zod twin of the above |
17+
| `QueryAST` | `SqlQueryAST` | ours is a compiled SQL syntax tree (`select`/`from`/`join`/`group_by`); the spec's is the ObjectQL request descriptor (`object`/`fields`/`where`/`expand`) |
18+
| `QuerySchema` | `DriverQueryConfig` | ours is the high-level config `QueryASTBuilder` compiles; the spec exports that name as a zod schema value |
19+
| `DriverInterface` | `SqlDriverInterface` | ours is objectui's SQL-oriented client abstraction (`query(sql, params)`); the spec's is the platform runtime driver contract |
20+
| `DatasourceSchema` | `DatasourceRegistration` | ours is the in-memory record `DatasourceManager` holds — its `driver` is a live instance; the spec's is the authored metadata document, where `driver` is a name |
21+
22+
Three more are now DERIVED from the spec instead of hand-restated, which fixes
23+
live silent-stripping defects, since a `z.object()` drops unknown keys:
24+
25+
- **`DashboardWidgetSchema`** declared 10 of the spec's 22 keys, so
26+
`objectui validate` deleted the other 12 without a word — `chartConfig`,
27+
`colorVariant`, `filter`, `responsive`, `aria`,
28+
`actionUrl`/`actionType`/`actionIcon`, `compareTo`, `suppressWarnings` and the
29+
`requiresObject` / `requiresService` capability gates the dashboard renderer
30+
honours at runtime. The TS interface had declared most of them all along, so a
31+
widget could type-check and still lose half its configuration on validation.
32+
Pinned divergences kept: `id` stays optional, `type` stays widened for the
33+
objectui-only `list` / `custom` families, and the legacy `component` envelope
34+
stays.
35+
- **`GlobalFilterSchema`** took `scope` as a free-form string (any typo
36+
validated); it now uses the spec's `widget | dashboard` vocabulary. The three
37+
objectui widenings that back a real runtime normalizer are kept and pinned:
38+
the bare-string `options` shorthand, the normalized `{ preset }` date default,
39+
and an optional `optionsFrom.labelField`.
40+
- **`AppContextSelectorSchema`** was a full restatement; spec keys and their
41+
defaults now flow in by reference, with `label` widened for objectui's i18n
42+
label envelope — which `AppContextSelectors` already renders.
43+
44+
`ListViewSchema`'s zod node now names the spec in its own initializer rather
45+
than one hop away through a local const, so its long-standing derivation is
46+
visible where it is declared.
47+
48+
Drift guard: `packages/types/src/__tests__/report-chart-query-spec-parity.test.ts`.

packages/core/src/query/__tests__/query-ast.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
import { describe, it, expect } from 'vitest';
66
import { QueryASTBuilder } from '../query-ast';
7-
import type { QuerySchema } from '@object-ui/types';
7+
import type { DriverQueryConfig } from '@object-ui/types';
88

99
describe('QueryASTBuilder', () => {
1010
const builder = new QueryASTBuilder();
1111

1212
describe('Basic Query Building', () => {
1313
it('should build simple SELECT query', () => {
14-
const query: QuerySchema = {
14+
const query: DriverQueryConfig = {
1515
object: 'users',
1616
fields: ['id', 'name', 'email'],
1717
};
@@ -24,7 +24,7 @@ describe('QueryASTBuilder', () => {
2424
});
2525

2626
it('should build SELECT * when no fields specified', () => {
27-
const query: QuerySchema = {
27+
const query: DriverQueryConfig = {
2828
object: 'users',
2929
};
3030

@@ -38,7 +38,7 @@ describe('QueryASTBuilder', () => {
3838
});
3939

4040
it('should build query with WHERE clause', () => {
41-
const query: QuerySchema = {
41+
const query: DriverQueryConfig = {
4242
object: 'users',
4343
fields: ['id', 'name'],
4444
filter: {
@@ -60,7 +60,7 @@ describe('QueryASTBuilder', () => {
6060
});
6161

6262
it('should build query with ORDER BY', () => {
63-
const query: QuerySchema = {
63+
const query: DriverQueryConfig = {
6464
object: 'users',
6565
fields: ['id', 'name'],
6666
sort: [
@@ -77,7 +77,7 @@ describe('QueryASTBuilder', () => {
7777
});
7878

7979
it('should build query with LIMIT and OFFSET', () => {
80-
const query: QuerySchema = {
80+
const query: DriverQueryConfig = {
8181
object: 'users',
8282
fields: ['id', 'name'],
8383
limit: 10,
@@ -95,7 +95,7 @@ describe('QueryASTBuilder', () => {
9595

9696
describe('Advanced Query Building', () => {
9797
it('should build query with JOIN', () => {
98-
const query: QuerySchema = {
98+
const query: DriverQueryConfig = {
9999
object: 'users',
100100
fields: ['id', 'name', 'orders.total'],
101101
joins: [
@@ -119,7 +119,7 @@ describe('QueryASTBuilder', () => {
119119
});
120120

121121
it('should build query with aggregations', () => {
122-
const query: QuerySchema = {
122+
const query: DriverQueryConfig = {
123123
object: 'orders',
124124
aggregations: [
125125
{
@@ -145,7 +145,7 @@ describe('QueryASTBuilder', () => {
145145
});
146146

147147
it('should build query with GROUP BY', () => {
148-
const query: QuerySchema = {
148+
const query: DriverQueryConfig = {
149149
object: 'orders',
150150
fields: ['user_id'],
151151
group_by: ['user_id'],
@@ -170,7 +170,7 @@ describe('QueryASTBuilder', () => {
170170

171171
describe('Complex Filters', () => {
172172
it('should build query with nested AND/OR filters', () => {
173-
const query: QuerySchema = {
173+
const query: DriverQueryConfig = {
174174
object: 'users',
175175
filter: {
176176
operator: 'and',

packages/core/src/query/query-ast.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* ObjectUI - Query AST Builder
3-
* Phase 3.3: QuerySchema AST implementation
3+
* Phase 3.3: DriverQueryConfig AST implementation
44
* ObjectStack Spec v2.0.1: Window functions support
55
*/
66

77
import type {
8-
QueryAST,
9-
QuerySchema,
8+
SqlQueryAST,
9+
DriverQueryConfig,
1010
SelectNode,
1111
FromNode,
1212
WhereNode,
@@ -32,11 +32,11 @@ import type {
3232
} from '@object-ui/types';
3333

3434
/**
35-
* Query AST Builder - Converts QuerySchema to AST
35+
* Query AST Builder - Converts DriverQueryConfig to AST
3636
*/
3737
export class QueryASTBuilder {
38-
build(query: QuerySchema): QueryAST {
39-
const ast: QueryAST = {
38+
build(query: DriverQueryConfig): SqlQueryAST {
39+
const ast: SqlQueryAST = {
4040
select: this.buildSelect(query),
4141
from: this.buildFrom(query),
4242
};
@@ -68,7 +68,7 @@ export class QueryASTBuilder {
6868
return ast;
6969
}
7070

71-
private buildSelect(query: QuerySchema): SelectNode {
71+
private buildSelect(query: DriverQueryConfig): SelectNode {
7272
const fields: (FieldNode | AggregateNode | WindowNode)[] = [];
7373

7474
if (query.fields && query.fields.length > 0) {
@@ -94,7 +94,7 @@ export class QueryASTBuilder {
9494
};
9595
}
9696

97-
private buildFrom(query: QuerySchema): FromNode {
97+
private buildFrom(query: DriverQueryConfig): FromNode {
9898
return {
9999
type: 'from',
100100
table: query.object,
@@ -329,13 +329,13 @@ export class QueryASTBuilder {
329329
return node;
330330
}
331331

332-
optimize(ast: QueryAST): QueryAST {
332+
optimize(ast: SqlQueryAST): SqlQueryAST {
333333
return ast;
334334
}
335335
}
336336

337337
export const defaultQueryASTBuilder = new QueryASTBuilder();
338338

339-
export function buildQueryAST(query: QuerySchema): QueryAST {
339+
export function buildQueryAST(query: DriverQueryConfig): SqlQueryAST {
340340
return defaultQueryASTBuilder.build(query);
341341
}

0 commit comments

Comments
 (0)