Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

Commit deccef5

Browse files
committed
fix(types): the zod face of PageNodeSchema refuses the retired page kind jsx too (objectui#12464)
The zod mirror took `kind` from the spec's PageSchema by reference, so on the pinned 18.0.0-next.1 it still admitted 'jsx' and was wider than the narrowed TS face (zod-mirror-parity WiderLedger TS2322). It now picks its enum out of the spec's by reference with `.extract`, through the stripImportedDefaults boundary, identical to the spec's enum once the resolved spec carries the retirement. Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2eeebe3 commit deccef5

2 files changed

Lines changed: 23 additions & 0 deletions

File tree

‎packages/types/src/__tests__/page-kind-jsx-retired-12464.test.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@
1818
*
1919
* `Equal` (not `Extends`) on the whole union: re-adding `'jsx'` widens it, and
2020
* only an exact comparison fails on a widening.
21+
*
22+
* The zod face (`zod/layout.zod.ts` `PageNodeSchema`) refuses the same value
23+
* at `kind`: it picks its enum out of the spec's by reference, so it does not
24+
* wait for the resolved spec to carry the retirement.
2125
*/
2226
import { describe, expect, it } from 'vitest';
2327
import type { PageNodeSchema } from '../layout';
28+
import { PageNodeSchema as PageNodeZod } from '../zod/layout.zod';
2429

2530
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
2631
type Assert<T extends true> = T;
@@ -36,4 +41,11 @@ describe('PageNodeSchema.kind no longer declares the retired page kind jsx (obje
3641
const jsx: PageNodeSchema = { type: 'page', kind: 'jsx', source: '<Page />' };
3742
expect([html.kind, jsx.kind]).toEqual(['html', 'jsx']);
3843
});
44+
45+
it('the zod face refuses kind jsx at kind and accepts html', () => {
46+
const jsx = PageNodeZod.safeParse({ type: 'page', kind: 'jsx', source: '<Page />' });
47+
expect(jsx.success).toBe(false);
48+
expect(jsx.error?.issues.map((i) => i.path.join('.'))).toEqual(['kind']);
49+
expect(PageNodeZod.safeParse({ type: 'page', kind: 'html', source: '<Page />' }).success).toBe(true);
50+
});
3951
});

‎packages/types/src/zod/layout.zod.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,17 @@ export const PageNodeSchema = BaseSchema.extend(SpecPageFields.shape).extend({
12931293
icon: z.string().optional().describe('Page icon (Lucide icon name)'),
12941294
description: z.string().optional().describe('Page description'),
12951295
pageType: PageTypeSchema.optional().describe('Page type (record, home, app, utility)'),
1296+
// objectui#12464: the page kind `jsx` retired in @objectstack/spec 18
1297+
// (objectstack-ai/objectstack#23638), naming `html`. Picked out of the spec's
1298+
// own enum by reference (`.extract`), the way `PAGE_KIND_NODE_TYPE` is below:
1299+
// identical to the spec's enum once the resolved spec carries the
1300+
// retirement, it refuses `jsx` before that, and a kind the spec drops fails to
1301+
// compile here. Mirrors the TS face's `PageNodeSchema.kind`.
1302+
kind: stripImportedDefaults(SpecPageSchema)
1303+
.shape.kind.unwrap()
1304+
.extract(['full', 'slotted', 'html', 'react'])
1305+
.optional()
1306+
.describe('Page override mode (full | slotted | html | react)'),
12961307
object: z.string().optional().describe('Bound object name (for record pages)'),
12971308
template: z.string().optional().describe('Layout template name'),
12981309
variables: z.array(PageVariableSchema).optional().describe('Local page state variables'),

0 commit comments

Comments
 (0)