diff --git a/.changeset/adr-0048-docpage-package-scoped.md b/.changeset/adr-0048-docpage-package-scoped.md new file mode 100644 index 000000000..d735e92cb --- /dev/null +++ b/.changeset/adr-0048-docpage-package-scoped.md @@ -0,0 +1,12 @@ +--- +"@object-ui/console": patch +--- + +ADR-0048: DocPage resolves docs package-scoped. The doc viewer at +`/apps/:appName/docs/:name` now passes the route's package segment as +`getItem('doc', name, { packageId })`, so the single-doc fetch is package-scoped +(prefer-local) on the server. Two installed packages may ship a doc with the +same bare name and each resolves within its own package — doc names no longer +need a globally-unique namespace prefix (the prefix becomes a convention, like +`page`/`dashboard`/`report`). The legacy top-level `/docs/:name` path (no +`appName`) keeps its context-free behavior. diff --git a/apps/console/src/pages/DocPage.tsx b/apps/console/src/pages/DocPage.tsx index a0a8250ee..580d947a3 100644 --- a/apps/console/src/pages/DocPage.tsx +++ b/apps/console/src/pages/DocPage.tsx @@ -34,7 +34,9 @@ interface DocItem { * "not found" notice — never an install-time or hard failure. */ export default function DocPage() { - const { name } = useParams<{ name: string }>(); + // `appName` is the parent route's package-id segment + // (/apps/:appName/docs/:name); undefined on the legacy top-level /docs/:name. + const { name, appName } = useParams<{ name: string; appName?: string }>(); const navigate = useNavigate(); const adapter = useAdapter(); const [doc, setDoc] = useState(null); @@ -47,7 +49,10 @@ export default function DocPage() { if (!name || !adapter) return; setState('loading'); try { - const raw: any = await adapter.getClient().meta.getItem('doc', name); + // ADR-0048 — pass the route's package so the single-doc fetch is + // package-scoped (prefer-local) on the server. With this, doc names + // need not be globally namespace-prefixed; the prefix becomes optional. + const raw: any = await adapter.getClient().meta.getItem('doc', name, appName ? { packageId: appName } : undefined); const item = raw?.item ?? raw?.data ?? raw; if (cancelled) return; if (item && typeof item.content === 'string') { @@ -73,7 +78,7 @@ export default function DocPage() { return () => { cancelled = true; }; - }, [name, adapter]); + }, [name, appName, adapter]); // SPA navigation for rewritten doc-to-doc links: anchors render as // plain ; intercept same-app clicks so following a