From 76a09baefedffcf5269cc354b6aa14726d615eba Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 16:37:20 +0000 Subject: [PATCH] fix(scripts): doc-snippet gate collects .md under content/docs, and states its scan surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `check-doc-snippet-types.mjs` says a document is covered unless it is named in `UNGATED_DOCS` with a reason, and carries a fragment rule so that no snippet is ever silently skipped. The collector then picked documents by extension, so 143 `.mdx` under `content/docs` were admitted and 40 `.md` guides were excluded — none of them in the ledger, so they were neither covered nor declared ungated. The collector now reads both extensions, and the scan surface is stated in the docblock beside the coverage rule rather than left to be read off `listDocuments`. Eight of the newly collected guides are fixed outright; the remaining twenty get a ledger entry carrying the same measured diagnostic mix the existing entries use, several naming the missing export by hand. Part of #5174 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE --- content/docs/guide/console-architecture.md | 3 + content/docs/guide/data-source.md | 6 +- content/docs/guide/fields.md | 6 +- content/docs/guide/metadata-diagnostics.md | 4 +- content/docs/guide/quick-start.md | 1 + content/docs/guide/react-pages.md | 1 + content/docs/guide/schema-playground.md | 9 +- content/docs/guide/slotted-pages.md | 1 + scripts/check-doc-snippet-types.mjs | 149 ++++++++++++++++++++- 9 files changed, 165 insertions(+), 15 deletions(-) diff --git a/content/docs/guide/console-architecture.md b/content/docs/guide/console-architecture.md index a36ddfd9f3..26f46000e5 100644 --- a/content/docs/guide/console-architecture.md +++ b/content/docs/guide/console-architecture.md @@ -129,6 +129,8 @@ Navigation items can be conditionally hidden using expressions: Actions are typed with `ActionDef` from `@object-ui/core`: ```ts +import { useActionRunner } from '@object-ui/react'; + const { execute } = useActionRunner({ context: { objectName: 'contacts' }, }); @@ -182,6 +184,7 @@ items exist only in `AppSidebar`, which the console no longer mounts (`ConsoleLa Per-app branding is applied via `AppShell`'s `branding` prop: + ```tsx { find(resource: string, params?: QueryParams): Promise>; @@ -82,11 +82,14 @@ import '@object-ui/components'; import '@object-ui/fields'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { createObjectStackAdapter } from '@object-ui/data-objectstack'; +import type { BaseSchema } from '@object-ui/types'; const dataSource = createObjectStackAdapter({ baseUrl: 'https://api.example.com' }); +const mySchema: BaseSchema = { type: 'table', objectName: 'users' }; + function App() { return ( @@ -159,6 +162,7 @@ class UserDataSource implements DataSource { ObjectUI uses OData-style query keys for broad compatibility: + ```typescript await dataSource.find('users', { $select: ['id', 'name', 'email'], diff --git a/content/docs/guide/fields.md b/content/docs/guide/fields.md index b20fa7c097..21a9560f5d 100644 --- a/content/docs/guide/fields.md +++ b/content/docs/guide/fields.md @@ -33,7 +33,7 @@ const MyRatingField = ({ value, onChange }: CellRendererProps) => { {[1, 2, 3, 4, 5].map(star => ( onChange(star)} + onClick={() => onChange?.(star)} style={{ color: star <= value ? 'gold' : 'grey' }} > ★ @@ -91,7 +91,7 @@ If you are building your own custom component (like a Kanban board card), you ca ```tsx import { getCellRenderer } from '@object-ui/fields'; -export const KanbanCard = ({ task }) => { +export const KanbanCard = ({ task }: { task: { name: string; assignee: string } }) => { // Get the standard renderer for a 'user' type field const UserRenderer = getCellRenderer('user'); @@ -101,7 +101,7 @@ export const KanbanCard = ({ task }) => {
diff --git a/content/docs/guide/metadata-diagnostics.md b/content/docs/guide/metadata-diagnostics.md index 3407477ecc..1661b0b0d6 100644 --- a/content/docs/guide/metadata-diagnostics.md +++ b/content/docs/guide/metadata-diagnostics.md @@ -62,6 +62,8 @@ GET /api/v1/meta/diagnostics?severity=error Response: ```ts +import type { MetadataDiagnostics } from '@object-ui/data-objectstack'; + interface MetadataDiagnosticsSummary { entries: Array<{ type: string; @@ -196,7 +198,7 @@ A few high-leverage patterns: ```ts import { MetadataClient } from '@object-ui/data-objectstack'; -const client = new MetadataClient(/* config */); +const client = new MetadataClient({ baseUrl: 'https://api.example.com' }); const summary = await client.diagnostics({ severity: 'error' }); console.log(summary.total, 'invalid item(s)'); ``` diff --git a/content/docs/guide/quick-start.md b/content/docs/guide/quick-start.md index 0ccdbd22f0..3d4cf4714a 100644 --- a/content/docs/guide/quick-start.md +++ b/content/docs/guide/quick-start.md @@ -40,6 +40,7 @@ pnpm add -D tailwindcss @tailwindcss/vite Add Tailwind to your `vite.config.ts`: + ```ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; diff --git a/content/docs/guide/react-pages.md b/content/docs/guide/react-pages.md index f2784866cb..bbd29bb755 100644 --- a/content/docs/guide/react-pages.md +++ b/content/docs/guide/react-pages.md @@ -177,6 +177,7 @@ only way the new adapter reaches the blocks inside the page. So a host that constructs a new adapter on every render resets every react page on every render. Provide it from state or a module constant: + ```tsx // ❌ new adapter object every render — every react page below loses its state diff --git a/content/docs/guide/schema-playground.md b/content/docs/guide/schema-playground.md index 225b9cd417..946c9ca9ce 100644 --- a/content/docs/guide/schema-playground.md +++ b/content/docs/guide/schema-playground.md @@ -26,12 +26,11 @@ Add the playground to any React project with ObjectUI installed: ```tsx import { useState } from 'react'; import { SchemaRenderer } from '@object-ui/react'; -import { registerDefaultRenderers } from '@object-ui/components'; -import { registerAllFields } from '@object-ui/fields'; -import { Registry } from '@object-ui/core'; +import { initializeComponents } from '@object-ui/components'; +// Side-effect import: loading the package runs its own field registration. +import '@object-ui/fields'; -registerDefaultRenderers(); -registerAllFields(Registry); +initializeComponents(); function SchemaPlayground() { const [schema, setSchema] = useState('{\n "type": "button",\n "label": "Click me"\n}'); diff --git a/content/docs/guide/slotted-pages.md b/content/docs/guide/slotted-pages.md index 3b1cc8a544..f179735081 100644 --- a/content/docs/guide/slotted-pages.md +++ b/content/docs/guide/slotted-pages.md @@ -147,6 +147,7 @@ same rules as `action:bar`: regardless of how few actions exist (the synthesized Share / Delete use this). + ```ts slots: { header: { diff --git a/scripts/check-doc-snippet-types.mjs b/scripts/check-doc-snippet-types.mjs index 59f3792ed4..14f9e44560 100644 --- a/scripts/check-doc-snippet-types.mjs +++ b/scripts/check-doc-snippet-types.mjs @@ -155,7 +155,7 @@ * types, unbuilt tree) turns every document red at once and reads * as "the docs are full of defects". * - * ## Coverage is declared, never assumed + * ## Coverage is declared, never assumed — and the scan surface is stated here * * A document is covered unless it is named in `UNGATED_DOCS` with a reason. The * default is therefore COVERED: a new page is compiled from the day it lands, @@ -163,6 +163,24 @@ * run — an entry naming a file that does not exist, or that holds no `ts` / `tsx` * block at all, fails as a stale entry, so the list can only shrink. * + * That rule is only true of documents the walk actually reaches, so the SCAN + * SURFACE is stated in the same breath as the coverage rule rather than left to + * be read off the collector: + * + * every `.mdx` and `.md` page under `content/docs`, plus every + * `packages//README.md`. + * + * Stating it here is objectui#5174's finding, and the finding was not the missing + * extension — it was that a reader had to open `listDocuments` to learn that + * "covered by default" meant "covered if the filename ends in `.mdx`". The + * collector admitted 143 `.mdx` under `content/docs` and silently excluded 40 + * `.md` guides — the getting-started pages a reader copies from most. None of + * them was in the ledger, so they were neither covered NOR declared ungated: + * they were invisible to this gate's own accounting, and the summary line below + * could not mention them. That is precisely the silent skip the fragment rule + * exists to prevent, arriving one level up, at the document instead of the block. + * Anything added to the scan surface later belongs in that list, on the same day. + * * ⚠️ The honest limit, stated because a reader of a green run needs it: an * ungated document is NOT compiled and NOT counted. Its snippets are unverified, * exactly as they were before this gate existed. The ledger is a debt list with @@ -187,9 +205,19 @@ const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** Documentation surfaces read by this gate. Kept identical in spirit to * `check-doc-links.mjs`: the pages a reader lands on, plus every published * package README (which ships to npm inside the package's `files`). */ -const MDX_ROOT = 'content/docs'; +const DOCS_ROOT = 'content/docs'; const PACKAGES_DIR = 'packages'; +/** Page extensions collected under `DOCS_ROOT`. BOTH are collected, and that is + * the whole content of the scan surface: `content/docs` is authored in a mix of + * `.mdx` and `.md` — the same guide tree, the same renderer, the same reader — + * and an extension is not a coverage decision. Collecting only `.mdx` is how + * objectui#5174 happened: 40 `.md` guides sat outside the ledger, so they were + * neither covered nor declared ungated, which is precisely the silent skip the + * fragment rule below exists to prevent. Anything else under the tree (`.json` + * sidecars) holds no prose and is not a page. */ +const DOC_EXTENSIONS = ['.mdx', '.md']; + /** Fence languages treated as compilable TypeScript. `js` / `jsx` are NOT in the * set: they are not type-annotated, so a strict program judges them on rules * their authors never opted into. */ @@ -199,6 +227,14 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * Documents whose snippets are NOT compiled, each with the reason. The default * is covered; this list is the debt, by name, and it can only shrink. * + * ⚠️ 20 of these entries are `.md` pages under `content/docs` that objectui#5174 + * made visible: the collector now reads `.md`, and an entry with a measured reason + * is what a page that cannot pass yet is owed. They are DISCLOSED debt, not new + * debt — every one of them was equally unverified before, just unnamed. Their + * reasons carry the same measured diagnostic mix as the rest, and several name + * the missing export by hand, because a documented symbol a package does not + * export is the reader-visible half (objectui#5160). + * * The reasons are deliberately concrete about WHAT would have to change, because * "does not compile" is three different jobs: a page whose snippets reference * ambient names it never defines needs the blocks made self-contained (or @@ -210,6 +246,53 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * @type {Record} */ const UNGATED_DOCS = { + 'content/docs/api/schema-reference.md': + '1 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; plus TS2305x1 TS2724x1 — candidate real defects, un-triaged. The ' + + 'missing-export diagnostics name `DashboardSchema` (@object-ui/types), `PageSchema` ' + + '(@object-ui/types) — the reader-visible half of this entry', + 'content/docs/guide/architecture-overview.md': + '1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 5 ' + + 'undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s); plus TS2686x1 — candidate real ' + + 'defects, un-triaged', + 'content/docs/guide/architecture.md': + '8 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '13 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 3 unresolved-module diagnostic(s); plus TS2322x1 — candidate real ' + + 'defects, un-triaged', + 'content/docs/guide/building-crud-app.md': + '1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '20 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 4 unresolved-module diagnostic(s); plus TS2305x2 TS2339x1 TS2345x1 ' + + 'TS2554x1 TS2724x1 TS2882x1 — candidate real defects, un-triaged. The missing-export ' + + 'diagnostics name `Field` (@object-ui/types), `ObjectSchema` (@object-ui/types), ' + + '`registerAllComponents` (@object-ui/components) — the reader-visible half of this entry', + 'content/docs/guide/component-registry.md': + '3 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '51 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 6 unresolved-module diagnostic(s); plus TS2305x13 TS2339x1 — candidate ' + + 'real defects, un-triaged. The missing-export diagnostics name `getComponentRegistry` ' + + '(@object-ui/react) x5, `registerDefaultRenderers` (@object-ui/components) x4, `BaseSchema` ' + + '(@object-ui/core) x3, `InputRenderer` (@object-ui/components) — the reader-visible half of ' + + 'this entry', + 'content/docs/guide/deployment.md': + '6 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s); plus TS2322x1 — candidate real ' + + 'defects, un-triaged', + 'content/docs/guide/expressions.md': + '8 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 6 ' + + 'undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; plus TS2724x1 TS7006x3 — candidate real defects, un-triaged. The ' + + 'missing-export diagnostics name `getExpressionEvaluator` (@object-ui/core) — the ' + + 'reader-visible half of this entry', + 'content/docs/guide/layout.md': + '21 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '20 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 4 unresolved-module diagnostic(s)', + 'content/docs/guide/notifications.md': + '9 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s)', 'content/docs/guide/objectos-integration.mdx': '36 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + '10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + @@ -221,6 +304,52 @@ const UNGATED_DOCS = { 'this page still needs the 8 parse-failing blocks re-fenced or declared, the undefined-name ' + 'blocks made self-contained, and the `@objectstack/*` runtime imports resolvable — none of ' + 'which this repo can do from here.', + 'content/docs/guide/plugin-development.md': + '10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 6 unresolved-module diagnostic(s); plus TS2339x5 TS2882x1 TS7006x3 — ' + + 'candidate real defects, un-triaged', + 'content/docs/guide/plugins.md': + '10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 6 unresolved-module diagnostic(s); plus TS2882x1 TS7006x3 — candidate ' + + 'real defects, un-triaged', + 'content/docs/guide/public-forms.md': + '1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 8 ' + + 'undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s)', + 'content/docs/guide/schema-overview.md': + '9 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 2 ' + + 'undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; plus TS2305x2 TS2724x6 — candidate real defects, un-triaged. The ' + + 'missing-export diagnostics name `AppSchema` (@object-ui/types) x2, `ThemeSchema` ' + + '(@object-ui/types) x2, `AppSchema` (@object-ui/types/zod), `ReportSchema` ' + + '(@object-ui/types), `ReportSchema` (@object-ui/types/zod), `ThemeSchema` ' + + '(@object-ui/types/zod) — the reader-visible half of this entry', + 'content/docs/guide/schema-rendering.md': + '8 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s); plus TS2305x4 TS2322x1 TS2451x2 ' + + 'TS7006x5 — candidate real defects, un-triaged. The missing-export diagnostics name ' + + '`FormSchema` (@object-ui/core), `getComponentRegistry` (@object-ui/react), `PageSchema` ' + + '(@object-ui/core), `registerDefaultRenderers` (@object-ui/components) — the reader-visible ' + + 'half of this entry', + 'content/docs/guide/theming.md': + '3 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '23 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s); plus TS2339x1 TS2353x1 — candidate ' + + 'real defects, un-triaged', + 'content/docs/guide/troubleshooting.md': + '8 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s); plus TS2322x1 TS2339x2 TS2559x1 ' + + 'TS2724x1 — candidate real defects, un-triaged. The missing-export diagnostics name ' + + '`componentSchema` (@object-ui/types/zod) — the reader-visible half of this entry', + 'content/docs/guide/user-state-persistence.md': + '10 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '1 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; plus TS2353x2 TS7031x1 — candidate real defects, un-triaged', + 'content/docs/plugins/index.md': + '1 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; 1 unresolved-module diagnostic(s); plus TS7006x1 — candidate real ' + + 'defects, un-triaged', 'content/docs/plugins/plugin-calendar-view.mdx': '2 unresolved-module diagnostic(s) — and NOT a defect: the page is a migration guide whose ' + '"Before" blocks quote the retired `@object-ui/plugin-calendar-view` import on purpose. Covering ' + @@ -240,10 +369,19 @@ const UNGATED_DOCS = { '43 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 5 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines', 'content/docs/plugins/plugin-timeline.mdx': '1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies', + 'content/docs/rfcs/0001-clipboard-paste.md': + '7 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + + '11 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; plus TS18004x1 TS2391x3 TS7006x1 — candidate real defects, un-triaged', 'content/docs/utilities/create-plugin.mdx': '1 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 1 unresolved-module diagnostic(s)', 'content/docs/utilities/data-objectstack.mdx': '16 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2391x1 — candidate real defects, un-triaged', + 'content/docs/utilities/index.md': + '2 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + + 'page never defines; plus TS2724x1 — candidate real defects, un-triaged. The missing-export ' + + 'diagnostics name `ObjectStackProvider` (@object-ui/data-objectstack) — the reader-visible ' + + 'half of this entry', 'content/docs/utilities/runner.mdx': '5 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 3 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 3 unresolved-module diagnostic(s)', 'packages/app-shell/README.md': @@ -390,11 +528,12 @@ export function listDocuments(root = repoRoot) { for (const entry of readdirSync(dir).sort()) { const p = join(dir, entry); if (statSync(p).isDirectory()) walk(p); - else if (entry.endsWith('.mdx')) out.push(relative(root, p).split(sep).join('/')); + else if (DOC_EXTENSIONS.some((ext) => entry.endsWith(ext))) + out.push(relative(root, p).split(sep).join('/')); } }; - const mdxRoot = join(root, MDX_ROOT); - if (existsSync(mdxRoot)) walk(mdxRoot); + const docsRoot = join(root, DOCS_ROOT); + if (existsSync(docsRoot)) walk(docsRoot); const pkgDir = join(root, PACKAGES_DIR); if (existsSync(pkgDir)) { for (const entry of readdirSync(pkgDir).sort()) {