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
38 changes: 38 additions & 0 deletions .changeset/10582-retire-column-width-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
'@object-ui/types': minor
'@object-ui/plugin-kanban': minor
---

**BREAKING (scored `minor` per this repo's version-alignment convention)** —
`ColumnWidthConfig` and its Zod mirror `ColumnWidthConfigSchema` are deleted
(objectui#10582, ADR-0049 enforce-or-remove). No schema key references the
type and nothing reads it, so a `ColumnWidthConfig` an author built was never
read by anything.

**Why nothing was behind it.** The one key that carried the type, the
`'kanban'` arm's `columnWidths`, retired with that arm in this release
(objectui#8802). The one reader, the `useColumnWidths` hook in
`@object-ui/plugin-kanban`, was removed in this release too (objectui#8522).

**What each package's published entry loses**, read off the 17.6.0 tarballs on
npm:

- `@object-ui/plugin-kanban`: 17.6.0 exports `ColumnWidthConfig` from its
entry. There it is the package's own declaration, and `KanbanSchema` types
its `columnWidths` member with it. This release exports it no more.
- `@object-ui/types`: 17.6.0 exports neither `ColumnWidthConfig` nor
`ColumnWidthConfigSchema`. Both entered this package in this release, when
objectui#7664 moved the kanban dialect's declarations here, and both leave
before any version publishes them. For these two names, the objectui#7664
entries in this release are superseded by this one.

## Migration

- `import type { ColumnWidthConfig } from '@object-ui/plugin-kanban'` no
longer resolves: TypeScript reports that the module has no exported member of
that name. Delete the import and the annotation that used it. Nothing
replaces the type, and removing it changes nothing at runtime, because a type
import is erased at compile time.
- The objectui#8522 entry in this same release says `ColumnWidthConfig` stays
exported from `@object-ui/types` and from `@object-ui/plugin-kanban`. That
was true of that change. This entry removes it from both.
3 changes: 2 additions & 1 deletion packages/plugin-kanban/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ export function bucketCardsIntoColumns(

// Export types for external use
// ⛔ `KanbanSchema` RETIRED with the bare `kanban` node type key (objectui#8802).
export type { KanbanCard, KanbanColumn, CardTemplate, ColumnWidthConfig, InlineFieldDefinition } from './types';
// ⛔ `ColumnWidthConfig` DELETED — no key referenced it, nothing read it (objectui#10582).
export type { KanbanCard, KanbanColumn, CardTemplate, InlineFieldDefinition } from './types';
export { ObjectKanban };
export type { ObjectKanbanComponentProps } from './ObjectKanban';

Expand Down
4 changes: 3 additions & 1 deletion packages/plugin-kanban/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@
// `kanban` node type key (objectui#8802, maintainer ruling 2026-09-09).
// `ObjectKanbanSchema` (also from `@object-ui/types`) is the surviving face,
// and `ObjectKanban` takes it directly.
// ⛔ `ColumnWidthConfig` is NOT re-exported any more either: it was DELETED from
// `@object-ui/types` (objectui#10582) — no schema key referenced it and, since
// objectui#8522, nothing in this package read it.
export type {
KanbanCard,
KanbanColumn,
CardTemplate,
ColumnWidthConfig,
} from '@object-ui/types';

/**
Expand Down
139 changes: 139 additions & 0 deletions packages/types/src/__tests__/column-width-config-retired-10582.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* ObjectUI
* Copyright (c) 2024-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/**
* Retirement pin — `ColumnWidthConfig` / `ColumnWidthConfigSchema` are DELETED
* (objectui#10582, ADR-0049 enforce-or-remove: a declaration nothing pulls on
* retires immediately).
*
* ## What stood behind the pair before the deletion
*
* Nothing. The one schema key that carried the type, the `'kanban'` arm's
* `columnWidths`, retired with that arm (objectui#8802), and the arm's named
* refusal now answers the whole `type: "kanban"` literal. The one reader,
* `plugin-kanban`'s `useColumnWidths` hook, was removed by objectui#8522. What
* was left was a published authoring type an author (or an AI) could build and
* nothing would ever read.
*
* ## Why a deletion and not a tombstone
*
* A tombstone (`?: never`, `retirementTombstone()`) exists for a MEMBER KEY,
* because `BaseSchema` is `.passthrough()` and a deleted member would be kept
* unjudged. A standalone type has no such escape hatch: an importer of a
* deleted name fails to compile with "has no exported member", which is the
* loud refusal. That is the route objectui#7068 took for `ActionCallback` /
* `ActionCallbackSchema` and objectui#7664 for the `DeclarativeKanban*` trio,
* and the shape of this pin follows `action-callback-retired-7068.test.ts`.
*
* ## Tree-scoped, not file-scoped
*
* The last block walks every TypeScript source under `packages/`, `apps/` and
* `examples/` for a DECLARATION or an EXPORT of either name — a re-declaration
* in some other package would type-check cleanly and put the dead shape back,
* and a file-scoped pin sees only the files its author thought of. Prose that
* tells the story (docblocks, this file) is not a declaration and is not read
* as one.
*/

import { readFileSync, readdirSync, statSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, it, expect } from 'vitest';
import ts from 'typescript';
import * as complexZod from '../zod/complex.zod.js';
import * as zodBarrel from '../zod/index.zod.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(HERE, '../../../..');
const SELF = fileURLToPath(import.meta.url);

const RETIRED = ['ColumnWidthConfig', 'ColumnWidthConfigSchema'] as const;
const RETIRED_TOKEN = /\bColumnWidthConfig(?:Schema)?\b/;

/** Every top-level name a source file DECLARES or EXPORTS, read off its AST. */
function declaredAndExportedNames(file: string): string[] {
const kind = file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
const sf = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.ESNext, false, kind);
const names: string[] = [];
for (const s of sf.statements) {
if (
(ts.isInterfaceDeclaration(s) || ts.isTypeAliasDeclaration(s) || ts.isClassDeclaration(s)
|| ts.isFunctionDeclaration(s) || ts.isEnumDeclaration(s)) && s.name
) {
names.push(s.name.text);
} else if (ts.isVariableStatement(s)) {
for (const d of s.declarationList.declarations) if (ts.isIdentifier(d.name)) names.push(d.name.text);
} else if (ts.isExportDeclaration(s) && s.exportClause && ts.isNamedExports(s.exportClause)) {
for (const e of s.exportClause.elements) names.push(e.name.text);
}
}
return names;
}

/* ── the two entry points: neither exports either name ─────────────────── */

describe('`ColumnWidthConfig` / `ColumnWidthConfigSchema` are DELETED from both `@object-ui/types` entry points (objectui#10582)', () => {
it('the Zod mirror is exported from neither `complex.zod.ts` nor the `@object-ui/types/zod` barrel', () => {
expect('ColumnWidthConfigSchema' in complexZod).toBe(false);
expect('ColumnWidthConfigSchema' in zodBarrel).toBe(false);
// Lit control: the sibling mirror declared beside it still exports through both.
expect('CardTemplateSchema' in complexZod).toBe(true);
expect('CardTemplateSchema' in zodBarrel).toBe(true);
});

it('`complex.ts` declares no `ColumnWidthConfig` — read off the AST, so the docblock that tells the story does not count', () => {
const names = declaredAndExportedNames(resolve(HERE, '../complex.ts'));
expect(names).not.toContain('ColumnWidthConfig');
expect(names).toContain('CardTemplate'); // lit control
});

it.each([
['the root entry `index.ts`', '../index.ts', 'ColumnWidthConfig', 'CardTemplate'],
['the zod entry `zod/index.zod.ts`', '../zod/index.zod.ts', 'ColumnWidthConfigSchema', 'CardTemplateSchema'],
])('%s exports no retired name, and still exports its sibling', (_label, file, retired, sibling) => {
const names = declaredAndExportedNames(resolve(HERE, file));
expect(names).not.toContain(retired);
expect(names).toContain(sibling); // lit control
});
});

/* ── tree-scoped: no source anywhere declares or exports either name ───── */

describe('no source under packages/, apps/ or examples/ declares or exports a retired name (objectui#10582)', () => {
const SKIP = new Set(['node_modules', 'dist', 'build', 'coverage', '.turbo', '.next', 'out']);
const walk = (dir: string): string[] =>
readdirSync(dir).flatMap((name) => {
if (SKIP.has(name)) return [];
const p = join(dir, name);
if (statSync(p).isDirectory()) return walk(p);
return /\.(?:ts|tsx|mts|cts)$/.test(name) && !name.endsWith('.d.ts') ? [p] : [];
});
const sources = ['packages', 'apps', 'examples'].flatMap((d) => walk(resolve(ROOT, d)));

it('no declaration and no export specifier of either name, in any file', () => {
const offenders = sources
.filter((f) => f !== SELF && RETIRED_TOKEN.test(readFileSync(f, 'utf8')))
.flatMap((f) =>
declaredAndExportedNames(f)
.filter((n) => (RETIRED as readonly string[]).includes(n))
.map((n) => `${relative(ROOT, f)}: ${n}`),
);
expect(offenders).toEqual([]);
});

it('the walk can see the corpus and the reader can find a live export (non-vacuity)', () => {
const rel = new Set(sources.map((f) => relative(ROOT, f)));
expect(rel.has('packages/plugin-kanban/src/types.ts')).toBe(true);
expect(rel.has('packages/types/src/zod/complex.zod.ts')).toBe(true);
// The same reader, on the files the retired names used to leave through,
// still sees the sibling that did not retire.
expect(declaredAndExportedNames(resolve(ROOT, 'packages/plugin-kanban/src/types.ts'))).toContain('CardTemplate');
expect(declaredAndExportedNames(resolve(ROOT, 'packages/plugin-kanban/src/index.tsx'))).toContain('CardTemplate');
expect(declaredAndExportedNames(resolve(ROOT, 'packages/types/src/zod/complex.zod.ts'))).toContain('CardTemplateSchema');
});
});
8 changes: 3 additions & 5 deletions packages/types/src/__tests__/zod-mirror-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ import type { z } from 'zod';

import { AppActionSchema, AppComponentSchema, MenuItemSchema as AppMenuItemSchema, NavigationAreaSchema, NavigationItemSchema } from '../zod/app.zod.js';
import { BaseSchema, ComponentConfigSchema, ComponentInputSchema, ComponentMetaSchema, KeyedI18nLabelSchema, SchemaNodeSchema } from '../zod/base.zod.js';
import { CalendarEventSchema, CalendarViewSchema, CarouselItemSchema, CarouselSchema, ChatbotSchema, ChatbotEnhancedSchema, ChatbotFloatingSchema, ChatMessageSchema, ChatMessageSourceSchema, ChatToolInvocationSchema, DashboardComponentSchema, DashboardConfigSchema, DashboardWidgetConfigSchema, DashboardWidgetLayoutSchema, DashboardWidgetSchema, FilterBuilderSchema, FilterFieldSchema, KanbanCardSchema, KanbanColumnSchema, CardTemplateSchema, ColumnWidthConfigSchema, FilterBuilderConditionSchema, FilterGroupSchema } from '../zod/complex.zod.js';
import { CalendarEventSchema, CalendarViewSchema, CarouselItemSchema, CarouselSchema, ChatbotSchema, ChatbotEnhancedSchema, ChatbotFloatingSchema, ChatMessageSchema, ChatMessageSourceSchema, ChatToolInvocationSchema, DashboardComponentSchema, DashboardConfigSchema, DashboardWidgetConfigSchema, DashboardWidgetLayoutSchema, DashboardWidgetSchema, FilterBuilderSchema, FilterFieldSchema, KanbanCardSchema, KanbanColumnSchema, CardTemplateSchema, FilterBuilderConditionSchema, FilterGroupSchema } from '../zod/complex.zod.js';
import { ActionSchema, CRUDDialogSchema, DetailSchema } from '../zod/crud.zod.js';
import { AlertSchema, AvatarSchema, BadgeSchema, BarChartSchema, ChartDataSeriesSchema, ChartSchema, DataTableSchema, DrillDownConfigSchema, HtmlSchema, KbdSchema, ListItemSchema, ListSchema, MarkdownSchema, StaticTableColumnSchema, StatisticSchema, TableColumnSchema, TableSchema, TimelineEventSchema, TimelineSchema, TreeNodeSchema, TreeViewSchema } from '../zod/data-display.zod.js';
import { AccordionItemSchema, AccordionSchema, CollapsibleSchema, ToggleGroupItemSchema, ToggleGroupSchema } from '../zod/disclosure.zod.js';
Expand All @@ -692,7 +692,7 @@ import { DetailViewFieldSchema, DetailViewSchema, DetailViewSectionSchema, Detai

import type { AppAction as Ts_AppAction, AppComponentSchema as Ts_AppComponentSchema, NavigationArea as Ts_NavigationArea } from '../app';
import type { BaseSchema as Ts_BaseSchema, ComponentConfig as Ts_ComponentConfig, ComponentInput as Ts_ComponentInput, ComponentMeta as Ts_ComponentMeta, KeyedI18nLabel as Ts_KeyedI18nLabel } from '../base';
import type { CalendarEvent as Ts_CalendarEvent, CalendarViewSchema as Ts_CalendarViewSchema, CarouselItem as Ts_CarouselItem, CarouselSchema as Ts_CarouselSchema, ChatbotSchema as Ts_ChatbotSchema, ChatbotEnhancedSchema as Ts_ChatbotEnhancedSchema, ChatbotFloatingSchema as Ts_ChatbotFloatingSchema, ChatMessage as Ts_ChatMessage, ChatMessageSource as Ts_ChatMessageSource, ChatToolInvocation as Ts_ChatToolInvocation, DashboardComponentSchema as Ts_DashboardComponentSchema, DashboardWidgetLayout as Ts_DashboardWidgetLayout, DashboardWidgetSchema as Ts_DashboardWidgetSchema, FilterBuilderSchema as Ts_FilterBuilderSchema, FilterField as Ts_FilterField, KanbanCard as Ts_KanbanCard, KanbanColumn as Ts_KanbanColumn, CardTemplate as Ts_CardTemplate, ColumnWidthConfig as Ts_ColumnWidthConfig } from '../complex';
import type { CalendarEvent as Ts_CalendarEvent, CalendarViewSchema as Ts_CalendarViewSchema, CarouselItem as Ts_CarouselItem, CarouselSchema as Ts_CarouselSchema, ChatbotSchema as Ts_ChatbotSchema, ChatbotEnhancedSchema as Ts_ChatbotEnhancedSchema, ChatbotFloatingSchema as Ts_ChatbotFloatingSchema, ChatMessage as Ts_ChatMessage, ChatMessageSource as Ts_ChatMessageSource, ChatToolInvocation as Ts_ChatToolInvocation, DashboardComponentSchema as Ts_DashboardComponentSchema, DashboardWidgetLayout as Ts_DashboardWidgetLayout, DashboardWidgetSchema as Ts_DashboardWidgetSchema, FilterBuilderSchema as Ts_FilterBuilderSchema, FilterField as Ts_FilterField, KanbanCard as Ts_KanbanCard, KanbanColumn as Ts_KanbanColumn, CardTemplate as Ts_CardTemplate } from '../complex';
import type { DashboardConfig as Ts_DashboardConfig, DashboardWidgetConfig as Ts_DashboardWidgetConfig } from '../designer';
import type { CRUDDialogSchema as Ts_CRUDDialogSchema, DetailSchema as Ts_DetailSchema } from '../crud';
import type { AlertSchema as Ts_AlertSchema, AvatarSchema as Ts_AvatarSchema, BadgeSchema as Ts_BadgeSchema, BarChartSchema as Ts_BarChartSchema, ChartDataSeries as Ts_ChartDataSeries, ChartSchema as Ts_ChartSchema, DataTableSchema as Ts_DataTableSchema, DrillDownConfig as Ts_DrillDownConfig, HtmlSchema as Ts_HtmlSchema, KbdSchema as Ts_KbdSchema, ListItem as Ts_ListItem, ListSchema as Ts_ListSchema, MarkdownSchema as Ts_MarkdownSchema, StaticTableColumn as Ts_StaticTableColumn, StatisticSchema as Ts_StatisticSchema, TableColumn as Ts_TableColumn, TableSchema as Ts_TableSchema, TimelineEvent as Ts_TimelineEvent, TimelineSchema as Ts_TimelineSchema, TreeViewSchema as Ts_TreeViewSchema, BreadcrumbItem as Ts_BreadcrumbItem, BreadcrumbSchema as Ts_BreadcrumbSchema } from '../data-display';
Expand Down Expand Up @@ -1535,7 +1535,6 @@ const MIRRORS = {
'complex.zod.ts#KanbanCardSchema': KanbanCardSchema,
'complex.zod.ts#KanbanColumnSchema': KanbanColumnSchema,
'complex.zod.ts#CardTemplateSchema': CardTemplateSchema,
'complex.zod.ts#ColumnWidthConfigSchema': ColumnWidthConfigSchema,
'crud.zod.ts#CRUDDialogSchema': CRUDDialogSchema,
'crud.zod.ts#DetailSchema': DetailSchema,
'data-display.zod.ts#AlertSchema': AlertSchema,
Expand Down Expand Up @@ -1700,7 +1699,6 @@ interface Declared {
'complex.zod.ts#KanbanCardSchema': Ts_KanbanCard;
'complex.zod.ts#KanbanColumnSchema': Ts_KanbanColumn;
'complex.zod.ts#CardTemplateSchema': Ts_CardTemplate;
'complex.zod.ts#ColumnWidthConfigSchema': Ts_ColumnWidthConfig;
'crud.zod.ts#CRUDDialogSchema': Ts_CRUDDialogSchema;
'crud.zod.ts#DetailSchema': Ts_DetailSchema;
'data-display.zod.ts#AlertSchema': Ts_AlertSchema;
Expand Down Expand Up @@ -4224,7 +4222,7 @@ const ZOD_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'zod');
* MINUEND under it had moved. Nothing failed on any of those days, because nothing
* compared the registry to a number. objectui#7433 is that absence, not the digits.
*/
const EXPECTED_MIRROR_PAIRS = 161;
const EXPECTED_MIRROR_PAIRS = 160;

/**
* A ledger this file can size from its own AST. `WiderThanDeclared` joined at
Expand Down
25 changes: 6 additions & 19 deletions packages/types/src/complex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,12 @@ export interface KanbanColumn {
* renders through the surviving spelling and this retirement moves zero stored
* documents.
*
* {@link KanbanCard}, {@link KanbanColumn}, {@link CardTemplate} and
* {@link ColumnWidthConfig} are NOT retired — the renderer and the
* `CardTemplates` component still consume the first three. The one
* `plugin-kanban` reader of {@link ColumnWidthConfig} was removed by
* objectui#8522, whose ruling did not reach the type itself.
* {@link KanbanCard}, {@link KanbanColumn} and {@link CardTemplate} are NOT
* retired — the renderer and the `CardTemplates` component still consume
* them. `ColumnWidthConfig`, which this arm also carried, lost its one
* `plugin-kanban` reader to objectui#8522 and was then deleted, with its Zod
* mirror, by objectui#10582 (pinned in
* `./__tests__/column-width-config-retired-10582.test.ts`).
*
* Pinned in `./__tests__/bare-kanban-node-key-retired-8802.test.ts`.
*/
Expand All @@ -270,20 +271,6 @@ export interface CardTemplate {
values: Record<string, any>;
}

/**
* Configuration for custom column widths.
*/
export interface ColumnWidthConfig {
/** Default column width in pixels */
defaultWidth?: number;
/** Minimum column width in pixels */
minWidth?: number;
/** Maximum column width in pixels */
maxWidth?: number;
/** Per-column width overrides keyed by column ID */
overrides?: Record<string, number>;
}

/**
* Calendar view mode — the registered `calendar-view` renderer's rendered set.
*
Expand Down
1 change: 0 additions & 1 deletion packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,6 @@ export type {
// (objectui#8802, maintainer ruling 2026-09-09) — `ObjectKanbanSchema` below
// is the surviving kanban face.
CardTemplate,
ColumnWidthConfig,
CalendarViewMode,
CalendarEvent,
CalendarViewSchema,
Expand Down
10 changes: 0 additions & 10 deletions packages/types/src/zod/complex.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,6 @@ export const CardTemplateSchema = z.object({
values: z.record(z.string(), z.any()).describe('Pre-filled field values'),
});

/**
* Column Width Config Schema — mirrors {@link ColumnWidthConfig} in `../complex.ts`.
*/
export const ColumnWidthConfigSchema = z.object({
defaultWidth: z.number().optional().describe('Default column width in pixels'),
minWidth: z.number().optional().describe('Minimum column width in pixels'),
maxWidth: z.number().optional().describe('Maximum column width in pixels'),
overrides: z.record(z.string(), z.number()).optional().describe('Per-column width overrides keyed by column ID'),
});

/**
* ⛔ The `'kanban'` arm is RETIRED (objectui#8802, maintainer ruling 2026-09-09)
* — this is its NAMED REFUSAL, the half a deletion would not have given.
Expand Down
1 change: 0 additions & 1 deletion packages/types/src/zod/index.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ export {
KanbanCardSchema,
KanbanColumnSchema,
CardTemplateSchema,
ColumnWidthConfigSchema,
// ⛔ `KanbanSchema` RETIRED with the bare `kanban` node type key
// (objectui#8802, maintainer ruling 2026-09-09). `RetiredKanbanNodeSchema`
// takes its place inside `ComplexSchema` so an authored `type: "kanban"` is
Expand Down
Loading