Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .changeset/spec-symbol-ledger-batch-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
"@object-ui/types": minor
"@object-ui/core": minor
---

Report / chart / query symbols stop wearing `@objectstack/spec`'s names
(objectui#3155, objectstack#4115).

**Breaking for TypeScript imports** — six exported names change. Each was a
different concept than the spec export it collided with, so an author reading
the objectui declaration as "the spec's" was reading a false claim:

| was | now | why they were never the same thing |
|:--|:--|:--|
| `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 |
| `ChartSeriesSchema` | `ChartDataSeriesSchema` | zod twin of the above |
| `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`) |
| `QuerySchema` | `DriverQueryConfig` | ours is the high-level config `QueryASTBuilder` compiles; the spec exports that name as a zod schema value |
| `DriverInterface` | `SqlDriverInterface` | ours is objectui's SQL-oriented client abstraction (`query(sql, params)`); the spec's is the platform runtime driver contract |
| `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 |

Three more are now DERIVED from the spec instead of hand-restated, which fixes
live silent-stripping defects, since a `z.object()` drops unknown keys:

- **`DashboardWidgetSchema`** declared 10 of the spec's 22 keys, so
`objectui validate` deleted the other 12 without a word — `chartConfig`,
`colorVariant`, `filter`, `responsive`, `aria`,
`actionUrl`/`actionType`/`actionIcon`, `compareTo`, `suppressWarnings` and the
`requiresObject` / `requiresService` capability gates the dashboard renderer
honours at runtime. The TS interface had declared most of them all along, so a
widget could type-check and still lose half its configuration on validation.
Pinned divergences kept: `id` stays optional, `type` stays widened for the
objectui-only `list` / `custom` families, and the legacy `component` envelope
stays.
- **`GlobalFilterSchema`** took `scope` as a free-form string (any typo
validated); it now uses the spec's `widget | dashboard` vocabulary. The three
objectui widenings that back a real runtime normalizer are kept and pinned:
the bare-string `options` shorthand, the normalized `{ preset }` date default,
and an optional `optionsFrom.labelField`.
- **`AppContextSelectorSchema`** was a full restatement; spec keys and their
defaults now flow in by reference, with `label` widened for objectui's i18n
label envelope — which `AppContextSelectors` already renders.

`ListViewSchema`'s zod node now names the spec in its own initializer rather
than one hop away through a local const, so its long-standing derivation is
visible where it is declared.

Drift guard: `packages/types/src/__tests__/report-chart-query-spec-parity.test.ts`.
20 changes: 10 additions & 10 deletions packages/core/src/query/__tests__/query-ast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

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

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

describe('Basic Query Building', () => {
it('should build simple SELECT query', () => {
const query: QuerySchema = {
const query: DriverQueryConfig = {
object: 'users',
fields: ['id', 'name', 'email'],
};
Expand All @@ -24,7 +24,7 @@ describe('QueryASTBuilder', () => {
});

it('should build SELECT * when no fields specified', () => {
const query: QuerySchema = {
const query: DriverQueryConfig = {
object: 'users',
};

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

it('should build query with WHERE clause', () => {
const query: QuerySchema = {
const query: DriverQueryConfig = {
object: 'users',
fields: ['id', 'name'],
filter: {
Expand All @@ -60,7 +60,7 @@ describe('QueryASTBuilder', () => {
});

it('should build query with ORDER BY', () => {
const query: QuerySchema = {
const query: DriverQueryConfig = {
object: 'users',
fields: ['id', 'name'],
sort: [
Expand All @@ -77,7 +77,7 @@ describe('QueryASTBuilder', () => {
});

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

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

it('should build query with aggregations', () => {
const query: QuerySchema = {
const query: DriverQueryConfig = {
object: 'orders',
aggregations: [
{
Expand All @@ -145,7 +145,7 @@ describe('QueryASTBuilder', () => {
});

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

describe('Complex Filters', () => {
it('should build query with nested AND/OR filters', () => {
const query: QuerySchema = {
const query: DriverQueryConfig = {
object: 'users',
filter: {
operator: 'and',
Expand Down
20 changes: 10 additions & 10 deletions packages/core/src/query/query-ast.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* ObjectUI - Query AST Builder
* Phase 3.3: QuerySchema AST implementation
* Phase 3.3: DriverQueryConfig AST implementation
* ObjectStack Spec v2.0.1: Window functions support
*/

import type {
QueryAST,
QuerySchema,
SqlQueryAST,
DriverQueryConfig,
SelectNode,
FromNode,
WhereNode,
Expand All @@ -32,11 +32,11 @@ import type {
} from '@object-ui/types';

/**
* Query AST Builder - Converts QuerySchema to AST
* Query AST Builder - Converts DriverQueryConfig to AST
*/
export class QueryASTBuilder {
build(query: QuerySchema): QueryAST {
const ast: QueryAST = {
build(query: DriverQueryConfig): SqlQueryAST {
const ast: SqlQueryAST = {
select: this.buildSelect(query),
from: this.buildFrom(query),
};
Expand Down Expand Up @@ -68,7 +68,7 @@ export class QueryASTBuilder {
return ast;
}

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

if (query.fields && query.fields.length > 0) {
Expand All @@ -94,7 +94,7 @@ export class QueryASTBuilder {
};
}

private buildFrom(query: QuerySchema): FromNode {
private buildFrom(query: DriverQueryConfig): FromNode {
return {
type: 'from',
table: query.object,
Expand Down Expand Up @@ -329,13 +329,13 @@ export class QueryASTBuilder {
return node;
}

optimize(ast: QueryAST): QueryAST {
optimize(ast: SqlQueryAST): SqlQueryAST {
return ast;
}
}

export const defaultQueryASTBuilder = new QueryASTBuilder();

export function buildQueryAST(query: QuerySchema): QueryAST {
export function buildQueryAST(query: DriverQueryConfig): SqlQueryAST {
return defaultQueryASTBuilder.build(query);
}
Loading
Loading