@elytracms/studio 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/cms.ts +8 -31
- package/convex/content.ts +3 -44
- package/convex/delivery.ts +8 -42
- package/convex/functions.ts +17 -2
- package/convex/members.ts +2 -2
- package/convex/referenceMaintenance.ts +4 -73
- package/convex/references.ts +4 -2
- package/convex/schema.ts +17 -37
- package/convex/sync.ts +10 -42
- package/convex/triggers.ts +57 -0
- package/convex/webhooks.ts +3 -2
- package/dist/components/app-views/default-modules.d.ts +41 -0
- package/dist/components/app-views/default-modules.js +201 -0
- package/dist/components/app-views/default-modules.js.map +1 -0
- package/dist/components/app-views/module.d.ts +93 -0
- package/dist/components/app-views/module.js +75 -0
- package/dist/components/app-views/module.js.map +1 -0
- package/dist/components/app-views/sidebar.d.ts +95 -0
- package/dist/components/app-views/sidebar.js +153 -0
- package/dist/components/app-views/sidebar.js.map +1 -0
- package/dist/components/builder/builder-store.d.ts +92 -40
- package/dist/components/builder/builder-store.js +30 -126
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-breakpoints.d.ts +53 -0
- package/dist/components/builder/canvas-breakpoints.js +55 -0
- package/dist/components/builder/canvas-breakpoints.js.map +1 -0
- package/dist/components/builder/canvas.js +35 -61
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.js +3 -3
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.js +31 -40
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/insert-context.js +1 -1
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/page-tree.js +3 -4
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/property-inspector.js +4 -64
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +23 -5
- package/dist/components/cms/bulk-actions.js +7 -3
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +22 -6
- package/dist/components/cms/bulk-bar.js +46 -9
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell.d.ts +0 -4
- package/dist/components/cms/cell.js +1 -5
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.d.ts +4 -2
- package/dist/components/cms/cms-index.js +6 -4
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +7 -6
- package/dist/components/cms/cms-store.js +55 -18
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +8 -4
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/collections-sidebar.d.ts +10 -1
- package/dist/components/cms/collections-sidebar.js +13 -2
- package/dist/components/cms/collections-sidebar.js.map +1 -1
- package/dist/components/cms/detail-editor.js +2 -2
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/doc-state.d.ts +0 -5
- package/dist/components/cms/doc-state.js +1 -7
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/editable-cell.js +1 -1
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-control.d.ts +11 -2
- package/dist/components/cms/field-control.js +22 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +71 -0
- package/dist/components/cms/field-renderer.js +67 -0
- package/dist/components/cms/field-renderer.js.map +1 -0
- package/dist/components/cms/filter-builder.js +6 -28
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +35 -0
- package/dist/components/cms/scalar-value-editor.js +43 -0
- package/dist/components/cms/scalar-value-editor.js.map +1 -0
- package/dist/components/collab/collab-context.d.ts +6 -14
- package/dist/components/collab/collab-context.js +3 -12
- package/dist/components/collab/collab-context.js.map +1 -1
- package/dist/components/collab/collab-seed.d.ts +7 -19
- package/dist/components/collab/collab-seed.js +9 -31
- package/dist/components/collab/collab-seed.js.map +1 -1
- package/dist/components/components/registry-browser.js +6 -12
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/guards.d.ts +0 -9
- package/dist/components/guards.js +0 -12
- package/dist/components/guards.js.map +1 -1
- package/dist/components/project-session.js +8 -3
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/section-nav.d.ts +10 -10
- package/dist/components/section-nav.js +48 -25
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.d.ts +0 -2
- package/dist/components/section-state.js +0 -9
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.d.ts +3 -7
- package/dist/components/section-surface.js +6 -14
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/sections.d.ts +0 -1
- package/dist/components/sections.js +0 -10
- package/dist/components/sections.js.map +1 -1
- package/dist/components/studio-project-screen.d.ts +3 -4
- package/dist/components/studio-project-screen.js +41 -160
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.d.ts +0 -4
- package/dist/components/studio-shell.js +14 -14
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +19 -1
- package/dist/lib/data/collection-content.js +16 -0
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/operations/graph-commands.d.ts +2 -6
- package/dist/lib/operations/graph-commands.js +1 -2
- package/dist/lib/operations/graph-commands.js.map +1 -1
- package/dist/lib/operations/index.d.ts +0 -2
- package/dist/lib/operations/index.js +0 -2
- package/dist/lib/operations/index.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +14 -24
- package/dist/lib/operations/session-hydration.js +15 -34
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +1 -3
- package/dist/lib/persistence/convex-adapter.js +1 -154
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.js +0 -2
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/references/reference-model.d.ts +0 -16
- package/dist/lib/references/reference-model.js +2 -54
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +3 -4
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/registry/index.d.ts +0 -16
- package/dist/lib/registry/index.js +0 -15
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/webhooks/tags.js +2 -16
- package/dist/lib/webhooks/tags.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +27 -1
- package/dist/lib/workspace/index.js +34 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/router/match.d.ts +22 -34
- package/dist/router/match.js +22 -40
- package/dist/router/match.js.map +1 -1
- package/package.json +3 -2
- package/convex/graphs.ts +0 -55
- package/convex/publishing.ts +0 -76
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio-project-screen.js","sourceRoot":"","sources":["../../src/components/studio-project-screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport { isSingleton, resolveSingletonDocument } from '@elytracms/core/cms-core'\nimport { validSampleGraph } from '@elytracms/core/project-graph'\nimport type { Project, SectionState } from '@elytracms/core/studio-core'\nimport {\n Link,\n Navigate,\n STUDIO_PROJECT_ROUTE,\n studioPath,\n useParams,\n useSearch,\n} from '@elytracms/studio/router'\nimport { matchStudioRoute } from '@elytracms/studio/router/match'\nimport type { StudioRouteMatch } from '@elytracms/studio/router/match'\nimport { studioSearch } from '@elytracms/studio/router/search'\nimport type { StudioSearch } from '@elytracms/studio/router/search'\nimport { Button, Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { DataProvider } from '@elytracms/studio/lib/data'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport { useStudio } from './studio-context'\nimport { StudioShell } from './studio-shell'\nimport { firstAvailableSection } from './sections'\nimport { SectionSurface } from './section-surface'\nimport { SectionStateView } from './section-state'\nimport { SettingsForm } from './settings-form'\nimport { AssetLibrary } from './assets/asset-library'\nimport { AssetsProvider } from './assets/assets-store'\nimport { BuilderProvider } from './builder/builder-store'\nimport { CollabProvider } from './collab/collab-context'\nimport { RoutesShell } from './routes/routes-shell'\nimport { ExtensionsView } from './extensions/extensions-view'\nimport { RegistryBrowser } from './components/registry-browser'\nimport { ComponentsIndex } from './components/components-index'\nimport { ComponentDetail } from './components/component-detail'\nimport {\n CmsSessionGate,\n ProjectSessionProvider,\n useProjectSession,\n} from './project-session'\nimport { CmsProvider, useCms } from './cms/cms-store'\nimport { CollectionsSidebar } from './cms/collections-sidebar'\nimport { CmsIndex } from './cms/cms-index'\nimport { CollectionTable } from './cms/collection-table'\nimport { DetailEditor } from './cms/detail-editor'\nimport { OperationErrorBanner } from './operation-error'\n\n/**\n * EC-223 Stage 6 (catch-all routing): the studio's per-project route component.\n *\n * The consumer mounts ONE splat route (`/$projectId/$`) whose component is this.\n * Everything that was a per-project file route — the provider stack, the section\n * rail shell, and every section screen — now lives inside the package and is\n * resolved here from the URL. The consumer's route tree shrinks to a single\n * ~10-line mount, which is the whole point of Stage 6: the engine owns its\n * navigation, so an external repo consumes the studio without rebuilding a route\n * file per screen.\n *\n * `projectId` stays a real route param (the `$projectId` segment); the trailing\n * pathname (`_splat`) is matched against the engine route table\n * (`matchStudioRoute`) and the deeper ids (collection / document / component)\n * flow down as props. Every screen already takes its ids as props, so this\n * collapse is mechanical — the package never read deep params through the router;\n * the disappearing route-file wrappers did.\n */\nexport function StudioProjectScreen() {\n const params = useParams({ strict: false }) as { projectId?: string; _splat?: string }\n // The route's VALIDATED search (the splat's `validateSearch: studioSearch`).\n // Read it through `useSearch`, not `useLocation().search`: the validated search\n // is resolved at route-match time and stable from the component's first render,\n // whereas `location.search` can lag a tick during hydration — long enough for a\n // child `<Navigate>` (the builder redirect) to fire on a momentarily-empty\n // search and drop its `?page=` deep-link target.\n const rawSearch = useSearch({ strict: false }) as Record<string, unknown>\n const studio = useStudio()\n const projectId = params.projectId ?? ''\n const project = studio.getProject(projectId)\n\n // Record the active project once it resolves (preserves recents across sections).\n useEffect(() => {\n if (project) studio.activate(project.id)\n }, [project, studio])\n\n if (!project) return <ProjectNotFound />\n\n const match = matchStudioRoute(params._splat ?? '')\n const search = studioSearch(rawSearch)\n\n return (\n <StudioShell project={project}>\n {/* EC-177 gap 1: in Convex mode the session HYDRATES from persistence\n before any provider mounts — the builder boots from the latest\n persisted revision, publishing reflects the persisted state, and the\n preview content engine serves live documents. Playground renders\n synchronously from fixtures (session: null), exactly as before. */}\n <ProjectSessionProvider key={project.id} projectId={project.id}>\n <ProjectProviders project={project}>\n <StudioSectionResolver project={project} match={match} search={search} />\n </ProjectProviders>\n </ProjectSessionProvider>\n </StudioShell>\n )\n}\n\n/** The per-project provider stack, mounted once the session source is known. */\nfunction ProjectProviders({ project, children }: { project: Project; children: ReactNode }) {\n const { session } = useProjectSession()\n // A project created from a starter kit (EC-119) opens on the kit's graph —\n // but only until something is persisted: the hydrated revision wins.\n const kit = getInstantiatedKit(project.id)\n const graph = session?.graph ?? kit?.graph ?? validSampleGraph\n\n return (\n // Key by project id so the session data sources + graph reset when switching projects.\n // One canonical graph per project session: the Builder canvas and its Content tab\n // (bindings, repeating sections, template pages) read the same live graph. The data\n // store hydrates/persists this project's `cms-data-sources` records.\n <DataProvider key={project.id} projectId={project.id}>\n <BuilderProvider key={project.id} graph={graph} projectId={project.id}>\n {/* EC-195: project-level so the canvas resolves `asset`-typed Image props\n and the inspector picks from the live library — not just the Assets\n section. Asset METADATA only (small); blobs stay lazy. */}\n <AssetsProvider key={project.id} projectId={project.id}>\n <CollabProvider\n key={project.id}\n projectId={project.id}\n graph={graph}\n publish={session?.publish}\n >\n {children}\n </CollabProvider>\n </AssetsProvider>\n </BuilderProvider>\n </DataProvider>\n )\n}\n\n/**\n * The URL→screen switch (EC-223 Stage 6) — what the per-section `<Outlet />`s\n * used to resolve. Renders inside the shell + provider stack, so every screen\n * sees the same live graph, CMS store, and breadcrumb context the file routes\n * gave it. `index` and `builder` are redirect-only screens, kept here (rather\n * than short-circuited before the shell) because the retired routes nested under\n * the project layout too — they mounted the stack and then navigated, exact\n * parity.\n */\nfunction StudioSectionResolver({\n project,\n match,\n search,\n}: {\n project: Project\n match: StudioRouteMatch\n search: StudioSearch\n}) {\n const projectId = project.id\n\n switch (match.section) {\n case 'index':\n // Bare `/$projectId` → the project's first available section.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: firstAvailableSection(project) }}\n replace\n />\n )\n\n case 'settings':\n return (\n <SectionSurface project={project} section=\"settings\" forcedState={search.state}>\n <SettingsForm project={project} />\n </SectionSurface>\n )\n\n case 'assets':\n return (\n <SectionSurface project={project} section=\"assets\" forcedState={search.state}>\n {/* The AssetsProvider is mounted at the project level (EC-195). */}\n <AssetLibrary projectId={projectId} focusAssetId={search.asset} />\n </SectionSurface>\n )\n\n case 'routes':\n // Deterministic forced states (?state=) for the surface (EC-036).\n return search.state && search.state !== 'working' ? (\n <SectionStateView state={search.state} title=\"Routes\" />\n ) : (\n <RoutesShell project={project} />\n )\n\n case 'extensions':\n return search.state && search.state !== 'working' ? (\n <SectionStateView state={search.state} title=\"Extensions\" />\n ) : (\n <ExtensionsView />\n )\n\n case 'builder':\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. A `?page=<id>` deep link lands on that document's editor;\n // otherwise the page-collection list. The page-graph-only params have no\n // home here and are dropped.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId,\n _splat: search.page\n ? studioPath.cmsDocument('page', search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n )\n\n case 'components':\n return (\n <ComponentsScreen\n projectId={projectId}\n componentId={match.componentId}\n state={search.state}\n />\n )\n\n case 'cms':\n return (\n <CmsScreen\n project={project}\n collectionId={match.collectionId}\n documentId={match.documentId}\n state={search.state}\n field={search.field}\n />\n )\n\n case 'not-found':\n default:\n return <StudioRouteNotFound />\n }\n}\n\n/**\n * Components section (EC-050): a registry-browser sidebar beside the active\n * screen — the index (no selection) or a component's detail. Forced states\n * (?state=) replace the whole surface.\n */\nfunction ComponentsScreen({\n projectId,\n componentId,\n state,\n}: {\n projectId: string\n componentId: string | null\n state?: SectionState\n}) {\n if (state && state !== 'working') {\n return <SectionStateView state={state} title=\"Components\" />\n }\n return (\n <div className=\"flex h-full min-h-0\">\n <aside className=\"hidden h-full min-h-0 w-[300px] shrink-0 border-r border-border md:block\">\n <RegistryBrowser projectId={projectId} activeComponentId={componentId ?? undefined} />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {componentId === null ? (\n <ComponentsIndex projectId={projectId} />\n ) : (\n <ComponentDetail projectId={projectId} componentId={componentId} />\n )}\n </div>\n </div>\n )\n}\n\n/**\n * CMS section: hydrates the persisted (or kit-seeded) collections + documents,\n * then renders the collections sidebar beside the active screen (index, a\n * collection table, or a document editor). Forced states (?state=) replace the\n * whole surface before the provider mounts.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n}) {\n const projectId = project.id\n if (state && state !== 'working') {\n return <SectionStateView state={state} title=\"CMS\" />\n }\n\n // A project created from a starter kit (EC-119) opens the CMS on the kit's schemas + content.\n const kit = getInstantiatedKit(projectId)\n\n return (\n // EC-177 gap 1: in Convex mode every CMS mount loads the CURRENT persisted\n // schema + documents (fixtures only when nothing is persisted — first run).\n // Playground renders synchronously with `null` → fixture seeds, as before.\n <CmsSessionGate>\n {(cms) => (\n <CmsProvider\n key={projectId}\n projectId={projectId}\n initialCollections={cms?.collections ?? kit?.collections}\n initialDocuments={cms?.documents ?? kit?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialLocaleConfig={\n cms\n ? // Hydrated sessions follow the project's persisted locale settings.\n { default: project.settings.defaultLocale, locales: [...project.settings.locales] }\n : kit?.localeConfig\n }\n >\n <div className=\"flex h-full min-h-0 flex-col\">\n <CmsPersistErrorBanner />\n <div className=\"flex min-h-0 flex-1\">\n <aside className=\"hidden h-full min-h-0 w-[240px] shrink-0 border-r border-border md:block\">\n <CollectionsSidebar\n projectId={projectId}\n activeCollectionId={collectionId ?? undefined}\n />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-hidden\">\n <CmsContent\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n field={field}\n />\n </div>\n </div>\n </div>\n </CmsProvider>\n )}\n </CmsSessionGate>\n )\n}\n\n/** Resolves the active CMS screen inside the provider (needs the live store). */\nfunction CmsContent({\n projectId,\n collectionId,\n documentId,\n field,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} />\n\n if (documentId === null) {\n const collection = cms.getCollection(collectionId)\n // EC-274: a singleton is a UI lens — at the collection URL it opens its one\n // document directly *while in its expected shape*, but degrades to the list\n // (with a \"for review\" lane) on drift. SingletonGate decides per the data.\n if (collection && isSingleton(collection)) {\n return <SingletonGate projectId={projectId} collectionId={collectionId} />\n }\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n\n return (\n <DetailEditor\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n focusPath={field}\n />\n )\n}\n\n/**\n * EC-274 — the singleton lens gate. At a singleton collection's URL, resolve its\n * lone document and open it directly; on drift (> 1 document) fall back to the\n * list with the \"for review\" lane so an editor can resolve the conflict. The one\n * document keeps a normal id — nothing here names a literal document id.\n */\nfunction SingletonGate({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n const cms = useCms()\n const collection = cms.getCollection(collectionId)\n const docs = cms.documentsOf(collectionId)\n const resolution = collection\n ? resolveSingletonDocument(collection, docs)\n : ({ kind: 'empty' } as const)\n\n // Materialize the lone document on first visit (an empty draft, persisted like\n // any create). Idempotent in the store; only fires while genuinely empty.\n useEffect(() => {\n if (resolution.kind === 'empty') cms.ensureSingletonDocument(collectionId)\n }, [resolution.kind, cms, collectionId])\n\n if (resolution.kind === 'ready') {\n return (\n <DetailEditor projectId={projectId} collectionId={collectionId} documentId={resolution.doc.id} />\n )\n }\n if (resolution.kind === 'drift') {\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n Opening {collection?.form?.label ?? collectionId}…\n </Text>\n </div>\n )\n}\n\n/** Failed/denied persisted CMS mutations are always visible (EC-141). */\nfunction CmsPersistErrorBanner() {\n const cms = useCms()\n if (!cms.persistError) return null\n return (\n <div className=\"shrink-0 border-b border-border p-2\">\n <OperationErrorBanner error={cms.persistError} onDismiss={cms.clearPersistError} />\n </div>\n )\n}\n\nfunction ProjectNotFound() {\n return (\n <div className=\"grid min-h-dvh place-items-center p-8\">\n <Stack gap=\"3\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading size=\"display\">Project not found</Heading>\n <Text tone=\"muted\">\n This project doesn’t exist or was deleted. It may only have existed in this session.\n </Text>\n <Button asChild>\n <Link to=\"/\">Back to projects</Link>\n </Button>\n </Stack>\n </div>\n )\n}\n\nfunction StudioRouteNotFound() {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"2\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading level={2} size=\"h2\">\n Page not found\n </Heading>\n <Text tone=\"muted\">This studio URL doesn’t resolve to a section.</Text>\n </Stack>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"studio-project-screen.js","sourceRoot":"","sources":["../../src/components/studio-project-screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Project } from '@elytracms/core/studio-core'\nimport { Link, Navigate, STUDIO_PROJECT_ROUTE, useParams, useSearch } from '@elytracms/studio/router'\nimport { matchStudioRoute } from '@elytracms/studio/router/match'\nimport type { StudioRouteMatch } from '@elytracms/studio/router/match'\nimport { studioSearch } from '@elytracms/studio/router/search'\nimport type { StudioSearch } from '@elytracms/studio/router/search'\nimport { Button, Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { DataProvider } from '@elytracms/studio/lib/data'\nimport { useStudio } from './studio-context'\nimport { StudioShell } from './studio-shell'\nimport { AssetsProvider } from './assets/assets-store'\nimport { CollabProvider } from './collab/collab-context'\nimport { ProjectSessionProvider } from './project-session'\nimport { firstAvailableModule, flatPath } from './app-views/module'\nimport { useAppViews } from './app-views/default-modules'\n\n/**\n * EC-223 Stage 6 (catch-all routing): the studio's per-project route component.\n *\n * The consumer mounts ONE splat route (`/$projectId/$`) whose component is this.\n * Everything that was a per-project file route — the provider stack, the section\n * rail shell, and every section screen — now lives inside the package and is\n * resolved here from the URL. The consumer's route tree shrinks to a single\n * ~10-line mount, which is the whole point of Stage 6: the engine owns its\n * navigation, so an external repo consumes the studio without rebuilding a route\n * file per screen.\n *\n * `projectId` stays a real route param (the `$projectId` segment); the trailing\n * pathname (`_splat`) is matched against the engine route table\n * (`matchStudioRoute`) into a module id + segments, and the active module (EC-239)\n * resolves the screen. Every surface already takes its ids as props, so the\n * resolver just hands the module its parsed deep-link.\n */\nexport function StudioProjectScreen() {\n const params = useParams({ strict: false }) as { projectId?: string; _splat?: string }\n // The route's VALIDATED search (the splat's `validateSearch: studioSearch`).\n // Read it through `useSearch`, not `useLocation().search`: the validated search\n // is resolved at route-match time and stable from the component's first render,\n // whereas `location.search` can lag a tick during hydration — long enough for a\n // child `<Navigate>` (the builder redirect) to fire on a momentarily-empty\n // search and drop its `?page=` deep-link target.\n const rawSearch = useSearch({ strict: false }) as Record<string, unknown>\n const studio = useStudio()\n const projectId = params.projectId ?? ''\n const project = studio.getProject(projectId)\n\n // Record the active project once it resolves (preserves recents across sections).\n useEffect(() => {\n if (project) studio.activate(project.id)\n }, [project, studio])\n\n if (!project) return <ProjectNotFound />\n\n const match = matchStudioRoute(params._splat ?? '')\n const search = studioSearch(rawSearch)\n\n return (\n <StudioShell project={project}>\n {/* EC-177 gap 1: in Convex mode the session HYDRATES from persistence\n before any provider mounts — the builder boots from the latest\n persisted revision, publishing reflects the persisted state, and the\n preview content engine serves live documents. Playground renders\n synchronously from fixtures (session: null), exactly as before. */}\n <ProjectSessionProvider key={project.id} projectId={project.id}>\n <ProjectProviders project={project}>\n <StudioSectionResolver project={project} match={match} search={search} />\n </ProjectProviders>\n </ProjectSessionProvider>\n </StudioShell>\n )\n}\n\n/**\n * The per-project provider stack, mounted once the session source is known.\n *\n * EC-287: there is no project-level builder graph anymore — layouts are code and\n * the DB holds content only. The block-field canvas mounts its OWN scoped\n * `BuilderProvider` per document (`composition-field-editor`), so no graph is\n * threaded here. `CollabProvider` stays as the canvas's session-scoped\n * presence/perspective store, seeded graph-lessly.\n */\nfunction ProjectProviders({ project, children }: { project: Project; children: ReactNode }) {\n return (\n // Key by project id so the session data sources reset when switching projects.\n // The data store hydrates/persists this project's `cms-data-sources` records.\n <DataProvider key={project.id} projectId={project.id}>\n {/* EC-195: project-level so the canvas resolves `asset`-typed Image props\n and the inspector picks from the live library — not just the Assets\n section. Asset METADATA only (small); blobs stay lazy. */}\n <AssetsProvider key={project.id} projectId={project.id}>\n <CollabProvider key={project.id} projectId={project.id}>\n {children}\n </CollabProvider>\n </AssetsProvider>\n </DataProvider>\n )\n}\n\n/**\n * The URL→screen switch (EC-223 Stage 6), now driven by the app-view module\n * registry (EC-239). The bare project URL redirects to the first available\n * module; otherwise the matched module id is resolved from the registry (defaults\n * + `config.appViews`) and its `parsePath` interprets the trailing segments into a\n * deep-link the module renders. An unknown module id or an unparseable path is a\n * not-found; module CONFIG problems (duplicate/invalid `appViews`) surface as\n * visible issues in the shell, never silently. Renders inside the shell + provider\n * stack, so every surface sees the same live CMS store and breadcrumb context.\n */\nfunction StudioSectionResolver({\n project,\n match,\n search,\n}: {\n project: Project\n match: StudioRouteMatch\n search: StudioSearch\n}) {\n const projectId = project.id\n const appViews = useAppViews()\n\n if (match.section === 'index') {\n // Bare `/$projectId` → the project's first available module.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: firstAvailableModule(appViews.modules, project) }}\n replace\n />\n )\n }\n\n const module = appViews.get(match.moduleId)\n if (!module) return <StudioRouteNotFound />\n\n // A flat module (no `parsePath`) accepts only the bare path; a nested one\n // (CMS/components) interprets its segments. `null` → an over-deep path → not-found.\n const deepLink = (module.parsePath ?? flatPath)(match.segments)\n if (deepLink === null) return <StudioRouteNotFound />\n\n return <>{module.render({ project, projectId, deepLink, search })}</>\n}\n\nfunction ProjectNotFound() {\n return (\n <div className=\"grid min-h-dvh place-items-center p-8\">\n <Stack gap=\"3\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading size=\"display\">Project not found</Heading>\n <Text tone=\"muted\">\n This project doesn’t exist or was deleted. It may only have existed in this session.\n </Text>\n <Button asChild>\n <Link to=\"/\">Back to projects</Link>\n </Button>\n </Stack>\n </div>\n )\n}\n\nfunction StudioRouteNotFound() {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"2\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading level={2} size=\"h2\">\n Page not found\n </Heading>\n <Text tone=\"muted\">This studio URL doesn’t resolve to a section.</Text>\n </Stack>\n </div>\n )\n}\n"]}
|
|
@@ -12,7 +12,3 @@ export interface StudioShellProps {
|
|
|
12
12
|
* overflow-safe.
|
|
13
13
|
*/
|
|
14
14
|
export declare function StudioShell({ project, children }: StudioShellProps): import("react").JSX.Element;
|
|
15
|
-
/** Small inline helper used by section surfaces that want a consistent empty hint. */
|
|
16
|
-
export declare function SurfaceHint({ children }: {
|
|
17
|
-
children: ReactNode;
|
|
18
|
-
}): import("react").JSX.Element;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router';
|
|
3
3
|
import { ChevronRight } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useState } from 'react';
|
|
5
|
-
import { Row, Spacer,
|
|
5
|
+
import { Row, Spacer, Toaster } from '@elytracms/studio/ui';
|
|
6
6
|
import { readRailCollapsed, storeRailCollapsed } from '@elytracms/studio/lib/rail';
|
|
7
7
|
import { CurrentUserMenu } from './current-user-menu';
|
|
8
8
|
import { DeliveryHealthBanner } from './delivery-health-banner';
|
|
@@ -10,7 +10,8 @@ import { PlaygroundBadge } from './playground-badge';
|
|
|
10
10
|
import { DevLoginBadge } from './dev-login-badge';
|
|
11
11
|
import { ProjectSwitcher } from './project-switcher';
|
|
12
12
|
import { SectionNav } from './section-nav';
|
|
13
|
-
import {
|
|
13
|
+
import { useAppViews } from './app-views/default-modules';
|
|
14
|
+
import { activeModuleFromPath } from './app-views/sidebar';
|
|
14
15
|
import { BreadcrumbProvider, useBreadcrumbTrail } from './breadcrumb-context';
|
|
15
16
|
/**
|
|
16
17
|
* The Studio shell (brief §4.3, §8.1): a persistent top bar + section rail that
|
|
@@ -21,7 +22,7 @@ import { BreadcrumbProvider, useBreadcrumbTrail } from './breadcrumb-context';
|
|
|
21
22
|
*/
|
|
22
23
|
export function StudioShell({ project, children }) {
|
|
23
24
|
const { pathname } = useLocation();
|
|
24
|
-
const
|
|
25
|
+
const activeModule = activeModuleFromPath(pathname);
|
|
25
26
|
// User-toggled, persisted rail collapse (EC-214). Defaults to expanded; the
|
|
26
27
|
// stored preference is read after mount (no flash script needed for a rail).
|
|
27
28
|
const [railCollapsed, setRailCollapsed] = useState(false);
|
|
@@ -35,22 +36,25 @@ export function StudioShell({ project, children }) {
|
|
|
35
36
|
return next;
|
|
36
37
|
});
|
|
37
38
|
}, []);
|
|
38
|
-
return (_jsxs(BreadcrumbProvider, { children: [_jsxs("div", { className: "flex h-dvh flex-col bg-background", children: [_jsxs("header", { className: "flex h-12 shrink-0 items-center gap-2 border-b border-border px-3", children: [_jsx(Link, { to: "/", className: "grid size-6 shrink-0 place-items-center", "aria-label": "All projects", children: _jsx("span", { className: "size-5 rounded-[5px] bg-primary shadow-sm" }) }), _jsx(ProjectSwitcher, { activeProject: project }), _jsx(HeaderBreadcrumb, { projectId: project.id,
|
|
39
|
+
return (_jsxs(BreadcrumbProvider, { children: [_jsxs("div", { className: "flex h-dvh flex-col bg-background", children: [_jsxs("header", { className: "flex h-12 shrink-0 items-center gap-2 border-b border-border px-3", children: [_jsx(Link, { to: "/", className: "grid size-6 shrink-0 place-items-center", "aria-label": "All projects", children: _jsx("span", { className: "size-5 rounded-[5px] bg-primary shadow-sm" }) }), _jsx(ProjectSwitcher, { activeProject: project }), _jsx(HeaderBreadcrumb, { projectId: project.id, activeModule: activeModule }), _jsx(Spacer, {}), _jsx(PlaygroundBadge, {}), _jsx(DevLoginBadge, {}), _jsx(CurrentUserMenu, { projectId: project.id })] }), _jsx(DeliveryHealthBanner, {}), _jsxs("div", { className: "flex min-h-0 flex-1", children: [_jsx(SectionNav, { project: project, collapsed: railCollapsed, onToggleCollapsed: toggleRail }), _jsx("main", { className: "flex min-w-0 flex-1 flex-col overflow-hidden", children: _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: children }) })] })] }), _jsx(Toaster, {})] }));
|
|
39
40
|
}
|
|
40
41
|
const CRUMB_LINK = 'max-w-[12rem] truncate text-muted-foreground transition-colors hover:text-foreground';
|
|
41
42
|
const CRUMB_CURRENT = 'max-w-[16rem] truncate font-medium text-foreground';
|
|
42
43
|
/**
|
|
43
44
|
* The single global breadcrumb (EC-214): project (the switcher) › section › …the
|
|
44
|
-
* active record. The section crumb is
|
|
45
|
-
*
|
|
46
|
-
* current page (plain text);
|
|
45
|
+
* active record. The section crumb is the active app-view module's label (EC-239,
|
|
46
|
+
* resolved from the registry); deeper crumbs are contributed by the active surface
|
|
47
|
+
* via {@link useBreadcrumbTrail}. The last crumb is the current page (plain text);
|
|
48
|
+
* earlier crumbs link back.
|
|
47
49
|
*/
|
|
48
|
-
function HeaderBreadcrumb({ projectId,
|
|
50
|
+
function HeaderBreadcrumb({ projectId, activeModule, }) {
|
|
51
|
+
const views = useAppViews();
|
|
49
52
|
const extra = useBreadcrumbTrail();
|
|
50
|
-
|
|
53
|
+
const module = activeModule ? views.get(activeModule) : undefined;
|
|
54
|
+
if (!module)
|
|
51
55
|
return null;
|
|
52
56
|
const sectionIsLast = extra.length === 0;
|
|
53
|
-
return (_jsxs(Row, { gap: "1", className: "min-w-0 text-sm", "aria-label": "Breadcrumb", children: [_jsx(ChevronRight, { className: "size-3.5 shrink-0 text-muted-foreground/50", "aria-hidden": true }), sectionIsLast ? (_jsx("span", { className: CRUMB_CURRENT, children:
|
|
57
|
+
return (_jsxs(Row, { gap: "1", className: "min-w-0 text-sm", "aria-label": "Breadcrumb", children: [_jsx(ChevronRight, { className: "size-3.5 shrink-0 text-muted-foreground/50", "aria-hidden": true }), sectionIsLast ? (_jsx("span", { className: CRUMB_CURRENT, children: module.label })) : (_jsx(Link, { to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: module.id }, className: CRUMB_LINK, children: module.label })), extra.map((item, index) => {
|
|
54
58
|
const isLast = index === extra.length - 1;
|
|
55
59
|
return (_jsxs(Row, { gap: "1", className: "min-w-0", children: [_jsx(ChevronRight, { className: "size-3.5 shrink-0 text-muted-foreground/50", "aria-hidden": true }), !isLast && item.to ? (
|
|
56
60
|
// `to` and `params` are always contributed as a unit (the type
|
|
@@ -58,8 +62,4 @@ function HeaderBreadcrumb({ projectId, activeSection, }) {
|
|
|
58
62
|
_jsx(Link, { to: item.to, params: item.params, className: CRUMB_LINK, children: item.label })) : (_jsx("span", { className: isLast ? CRUMB_CURRENT : CRUMB_LINK, children: item.label }))] }, `${item.label}-${index}`));
|
|
59
63
|
})] }));
|
|
60
64
|
}
|
|
61
|
-
/** Small inline helper used by section surfaces that want a consistent empty hint. */
|
|
62
|
-
export function SurfaceHint({ children }) {
|
|
63
|
-
return (_jsx(Text, { size: "sm", tone: "muted", className: "px-1", children: children }));
|
|
64
|
-
}
|
|
65
65
|
//# sourceMappingURL=studio-shell.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio-shell.js","sourceRoot":"","sources":["../../src/components/studio-shell.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGxD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"studio-shell.js","sourceRoot":"","sources":["../../src/components/studio-shell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { ChevronRight } from 'lucide-react'\nimport { useCallback, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Project, ProjectId } from '@elytracms/core/studio-core'\nimport { Row, Spacer, Toaster } from '@elytracms/studio/ui'\nimport { readRailCollapsed, storeRailCollapsed } from '@elytracms/studio/lib/rail'\nimport { CurrentUserMenu } from './current-user-menu'\nimport { DeliveryHealthBanner } from './delivery-health-banner'\nimport { PlaygroundBadge } from './playground-badge'\nimport { DevLoginBadge } from './dev-login-badge'\nimport { ProjectSwitcher } from './project-switcher'\nimport { SectionNav } from './section-nav'\nimport { useAppViews } from './app-views/default-modules'\nimport { activeModuleFromPath } from './app-views/sidebar'\nimport { BreadcrumbProvider, useBreadcrumbTrail } from './breadcrumb-context'\n\nexport interface StudioShellProps {\n project: Project\n children: ReactNode\n}\n\n/**\n * The Studio shell (brief §4.3, §8.1): a persistent top bar + section rail that\n * preserve the active project while the user moves between surfaces. The top bar\n * carries the single global breadcrumb reaching the active record (EC-214) — no\n * secondary band, no per-view title repeating the section. Responsive and\n * overflow-safe.\n */\nexport function StudioShell({ project, children }: StudioShellProps) {\n const { pathname } = useLocation()\n const activeModule = activeModuleFromPath(pathname)\n\n // User-toggled, persisted rail collapse (EC-214). Defaults to expanded; the\n // stored preference is read after mount (no flash script needed for a rail).\n const [railCollapsed, setRailCollapsed] = useState(false)\n useEffect(() => {\n setRailCollapsed(readRailCollapsed())\n }, [])\n const toggleRail = useCallback(() => {\n setRailCollapsed((prev) => {\n const next = !prev\n storeRailCollapsed(next)\n return next\n })\n }, [])\n\n return (\n <BreadcrumbProvider>\n <div className=\"flex h-dvh flex-col bg-background\">\n {/* Top bar — global controls + the one breadcrumb to the active record. */}\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border px-3\">\n <Link to=\"/\" className=\"grid size-6 shrink-0 place-items-center\" aria-label=\"All projects\">\n <span className=\"size-5 rounded-[5px] bg-primary shadow-sm\" />\n </Link>\n <ProjectSwitcher activeProject={project} />\n <HeaderBreadcrumb projectId={project.id} activeModule={activeModule} />\n <Spacer />\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {/* Dev-only unattended-auth marker (renders nothing in prod / when unset). */}\n <DevLoginBadge />\n {/* EC-150: signed-in user + their role on this project + sign-out. */}\n <CurrentUserMenu projectId={project.id} />\n </header>\n\n {/* EC-268: warns when instant-publish env is incomplete (connected mode only). */}\n <DeliveryHealthBanner />\n\n <div className=\"flex min-h-0 flex-1\">\n <SectionNav\n project={project}\n collapsed={railCollapsed}\n onToggleCollapsed={toggleRail}\n />\n\n <main className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto\">{children}</div>\n </main>\n </div>\n </div>\n {/* EC-268: transient action feedback (e.g. a publish that fails at transport). */}\n <Toaster />\n </BreadcrumbProvider>\n )\n}\n\nconst CRUMB_LINK =\n 'max-w-[12rem] truncate text-muted-foreground transition-colors hover:text-foreground'\nconst CRUMB_CURRENT = 'max-w-[16rem] truncate font-medium text-foreground'\n\n/**\n * The single global breadcrumb (EC-214): project (the switcher) › section › …the\n * active record. The section crumb is the active app-view module's label (EC-239,\n * resolved from the registry); deeper crumbs are contributed by the active surface\n * via {@link useBreadcrumbTrail}. The last crumb is the current page (plain text);\n * earlier crumbs link back.\n */\nfunction HeaderBreadcrumb({\n projectId,\n activeModule,\n}: {\n projectId: ProjectId\n activeModule: string | null\n}) {\n const views = useAppViews()\n const extra = useBreadcrumbTrail()\n const module = activeModule ? views.get(activeModule) : undefined\n if (!module) return null\n const sectionIsLast = extra.length === 0\n\n return (\n <Row gap=\"1\" className=\"min-w-0 text-sm\" aria-label=\"Breadcrumb\">\n <ChevronRight className=\"size-3.5 shrink-0 text-muted-foreground/50\" aria-hidden />\n {sectionIsLast ? (\n <span className={CRUMB_CURRENT}>{module.label}</span>\n ) : (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: module.id }}\n className={CRUMB_LINK}\n >\n {module.label}\n </Link>\n )}\n {extra.map((item, index) => {\n const isLast = index === extra.length - 1\n return (\n <Row gap=\"1\" key={`${item.label}-${index}`} className=\"min-w-0\">\n <ChevronRight className=\"size-3.5 shrink-0 text-muted-foreground/50\" aria-hidden />\n {!isLast && item.to ? (\n // `to` and `params` are always contributed as a unit (the type\n // pairs them); the assertion satisfies the route's required params.\n <Link to={item.to} params={item.params!} className={CRUMB_LINK}>\n {item.label}\n </Link>\n ) : (\n <span className={isLast ? CRUMB_CURRENT : CRUMB_LINK}>{item.label}</span>\n )}\n </Row>\n )\n })}\n </Row>\n )\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { GetDocumentResult, ListDocumentsQuery, ListDocumentsResult, ResolvedDocument } from '@elytracms/core/content';
|
|
2
|
-
import type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core';
|
|
3
3
|
import type { AssetRecord } from '@elytracms/core/persistence';
|
|
4
4
|
import type { ListingQuery } from '@elytracms/core/runtime-renderer';
|
|
5
5
|
/**
|
|
@@ -34,6 +34,14 @@ export interface ContentSessionState {
|
|
|
34
34
|
publishedKeys?: readonly string[];
|
|
35
35
|
/** Locale config of the session content; defaults to the sample config. */
|
|
36
36
|
localeConfig?: LocaleConfig;
|
|
37
|
+
/**
|
|
38
|
+
* The project's config-sourced routes (EC-207). Wired into the content client so
|
|
39
|
+
* the canvas preview's `resolveLink` (EC-291) derives a rich-text link's URL from
|
|
40
|
+
* the SAME routes delivery serves — the studio-live half of the host link bake.
|
|
41
|
+
* Absent (playground/tests) → no routes, so `resolveLink` returns null and a
|
|
42
|
+
* reference link degrades to the visible `unresolved-ref` issue.
|
|
43
|
+
*/
|
|
44
|
+
routes?: readonly RouteRecord[];
|
|
37
45
|
/**
|
|
38
46
|
* The project's asset records (assets table). Wired into the lookup so the
|
|
39
47
|
* content client resolves `asset`-typed FIELDS on listing/relation results
|
|
@@ -96,6 +104,16 @@ export declare function resolveCanvasRelation(ref: DocumentRef, locale?: Locale)
|
|
|
96
104
|
* state. Reuses the same `listDocuments` path as `runCollectionListQuery`.
|
|
97
105
|
*/
|
|
98
106
|
export declare function resolveCanvasListing(query: ListingQuery, locale?: Locale): ResolvedDocument[];
|
|
107
|
+
/**
|
|
108
|
+
* Resolve a rich-text internal link's target document to its URL path for the canvas
|
|
109
|
+
* (EC-291) — the studio analog of the host's baked links map. Derives the URL through
|
|
110
|
+
* the active session's client (published perspective, active locale, config-sourced
|
|
111
|
+
* routes), so the canvas previews a `{type:'reference'}` link's `href` exactly as
|
|
112
|
+
* delivery serves it. `null` when the target is unpublished, missing, or addressed by
|
|
113
|
+
* no route → the rich-text renderer shows the EC-290 `unresolved-ref` issue. Reuses
|
|
114
|
+
* the same `resolveLink` path as the host bake, so both agree on the derived URL.
|
|
115
|
+
*/
|
|
116
|
+
export declare function resolveCanvasLink(ref: DocumentRef, locale?: Locale): string | null;
|
|
99
117
|
/** A pickable entry of a collection (the template-page preview switcher). */
|
|
100
118
|
export interface CollectionEntrySummary {
|
|
101
119
|
id: string;
|
|
@@ -137,6 +137,10 @@ function clientFor(locale) {
|
|
|
137
137
|
perspective: 'published',
|
|
138
138
|
locales: activeLocaleConfig(),
|
|
139
139
|
}),
|
|
140
|
+
// EC-291: the routes a rich-text link resolves against (config-sourced). The
|
|
141
|
+
// canvas previews PUBLISHED content, so a link derives the live URL — matching
|
|
142
|
+
// the host's baked link map.
|
|
143
|
+
routes: contentSession?.routes ?? [],
|
|
140
144
|
});
|
|
141
145
|
clients.set(locale, client);
|
|
142
146
|
return client;
|
|
@@ -182,6 +186,18 @@ export function resolveCanvasListing(query, locale = getStudioLocale()) {
|
|
|
182
186
|
});
|
|
183
187
|
return result.ok ? result.documents : [];
|
|
184
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Resolve a rich-text internal link's target document to its URL path for the canvas
|
|
191
|
+
* (EC-291) — the studio analog of the host's baked links map. Derives the URL through
|
|
192
|
+
* the active session's client (published perspective, active locale, config-sourced
|
|
193
|
+
* routes), so the canvas previews a `{type:'reference'}` link's `href` exactly as
|
|
194
|
+
* delivery serves it. `null` when the target is unpublished, missing, or addressed by
|
|
195
|
+
* no route → the rich-text renderer shows the EC-290 `unresolved-ref` issue. Reuses
|
|
196
|
+
* the same `resolveLink` path as the host bake, so both agree on the derived URL.
|
|
197
|
+
*/
|
|
198
|
+
export function resolveCanvasLink(ref, locale = getStudioLocale()) {
|
|
199
|
+
return clientFor(locale).resolveLink(ref);
|
|
200
|
+
}
|
|
185
201
|
/** Published entries of a collection, labeled by the schema's title field. */
|
|
186
202
|
export function listCollectionEntries(collectionId) {
|
|
187
203
|
const collection = playgroundCollection(collectionId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection-content.js","sourceRoot":"","sources":["../../../src/lib/data/collection-content.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createContentClient,\n createContentContext,\n} from '@elytracms/core/content'\nimport type {\n ContentClient,\n ContentDocumentSource,\n ContentLookup,\n GetDocumentResult,\n ListDocumentsQuery,\n ListDocumentsResult,\n ResolvedDocument,\n} from '@elytracms/core/content'\nimport { documentKey, sampleLocaleConfig } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { ListingQuery } from '@elytracms/core/runtime-renderer'\nimport { getStudioLocale } from '../locale/locale-store'\n\n/**\n * The studio's content delivery engine for canvas previews (EC-166).\n *\n * Repeating sections and template pages preview *delivery-shaped* documents —\n * the exact shape `@elytracms/core/content` serves in production — so the canvas\n * shows what publishing will show. This module wires the canonical content\n * client (closed AD-3 query vocabulary, published perspective, EC-018 locale\n * fallback, depth-1 population, resolved assets) over a **content session**:\n *\n * - Playground mode (and tests): the deterministic EC-145 sample fixtures —\n * byte-identical to the pre-hydration behavior, no backend.\n * - Convex mode: the project-session gate (`components/studio/project-session`)\n * configures the session with the project's HYDRATED schema + documents, so\n * \"Preview with\" lists and section previews show the live content — never\n * fixture titles (EC-177 gap 1).\n *\n * Nothing here invents query semantics; both shapes go through the same\n * canonical client.\n */\n\n/** The schema + documents the preview engine serves (one project session). */\nexport interface ContentSessionState {\n collections: readonly CollectionDef[]\n documents: readonly CmsDocument[]\n /**\n * Published document keys via the version POINTER (EC-224). When present, the\n * preview's published perspective resolves by the pin — never the flat\n * `state` enum — so it matches what delivery serves. Absent (playground\n * fixtures / tests) → the rows' own `state` gates the perspective, unchanged.\n */\n publishedKeys?: readonly string[]\n /** Locale config of the session content; defaults to the sample config. */\n localeConfig?: LocaleConfig\n /**\n * The project's asset records (assets table). Wired into the lookup so the\n * content client resolves `asset`-typed FIELDS on listing/relation results\n * (e.g. a ProductGrid product's `gallery`) to delivery shape — the same assets\n * the renderer's `resolveAsset` reads for static props (EC-262). Absent\n * (playground/tests) → assets come only from `asset`-collection fixture docs.\n */\n assets?: readonly AssetRecord[]\n}\n\n/**\n * Default (no-session) collection schema: EMPTY. The studio ships no demo content\n * (Oli, 2026-06-19) — with no backend/content session configured, there is nothing\n * to preview, and the studio says so rather than serving fixtures. A real project's\n * collections + documents arrive via {@link configureContentSession} (Convex mode).\n */\nexport const playgroundCollections: readonly CollectionDef[] = []\n\nlet contentSession: ContentSessionState | null = null\nlet lookup: ContentLookup = createLookup(playgroundCollections, [], [])\nconst clients = new Map<Locale, ContentClient>()\n\n/**\n * Point the preview engine at a project's hydrated content (Convex mode), or\n * back at the deterministic playground fixtures (`null` — the default, and\n * the only state playground mode ever sees).\n */\nexport function configureContentSession(next: ContentSessionState | null): void {\n contentSession = next\n lookup = next\n ? createLookup(next.collections, next.documents, next.publishedKeys, next.assets)\n : createLookup(playgroundCollections, [], [])\n clients.clear()\n}\n\n/** Whether a hydrated content session is configured (Convex mode). */\nexport function hasContentSession(): boolean {\n return contentSession !== null\n}\n\n/** The collections the preview engine currently serves (session or fixtures). */\nfunction activeCollections(): readonly CollectionDef[] {\n return contentSession?.collections ?? playgroundCollections\n}\n\nfunction activeLocaleConfig(): LocaleConfig {\n return contentSession?.localeConfig ?? sampleLocaleConfig\n}\n\n/** Resolve a collection in the active content session (fixtures in playground). */\nexport function playgroundCollection(id: string): CollectionDef | undefined {\n return activeCollections().find((collection) => collection.id === id)\n}\n\n/**\n * Overlay the active session's canonical `filterable` declarations onto a\n * schema that doesn't carry any of its own. The inspector hands the Content\n * tab fixture collection defs; the filter/sort pickers must offer exactly the\n * EC-143 declaration set the delivery engine enforces, so both ends agree. A\n * collection that declares its own filterable fields is left untouched.\n */\nexport function withPlaygroundFilterable(\n collections: readonly CollectionDef[],\n): readonly CollectionDef[] {\n return collections.map((collection) => {\n if (collection.fields.some((field) => field.filterable === true)) return collection\n const counterpart = playgroundCollection(collection.id)\n if (counterpart === undefined) return collection\n const flagged = new Set(\n counterpart.fields.filter((field) => field.filterable === true).map((field) => field.name),\n )\n if (flagged.size === 0) return collection\n return {\n ...collection,\n fields: collection.fields.map((field) =>\n flagged.has(field.name) ? { ...field, filterable: true } : field,\n ),\n }\n })\n}\n\n/** Synthesize the persistence-shaped asset record the asset resolver reads. */\nfunction assetRecordOf(doc: CmsDocument): AssetRecord {\n const values = doc.values\n const alt = doc.localized?.en?.alt\n return {\n id: doc.id,\n projectId: 'playground',\n filename: typeof values.filename === 'string' ? values.filename : doc.id,\n contentType: 'image/png',\n byteSize: 0,\n alt: typeof alt === 'string' ? alt : undefined,\n storageKey: typeof values.url === 'string' ? values.url : doc.id,\n createdAt: '2026-01-01T00:00:00.000Z',\n updatedAt: '2026-01-01T00:00:00.000Z',\n }\n}\n\nfunction createLookup(\n collectionDefs: readonly CollectionDef[],\n documents: readonly CmsDocument[],\n publishedKeys?: readonly string[],\n assetRecords?: readonly AssetRecord[],\n): ContentLookup {\n const collections = new Map(collectionDefs.map((c) => [c.id, c]))\n // EC-224: published-ness is the version POINTER, not the flat `state` enum.\n // A hydrated session hands `publishedKeys` (the pinned set) → wrap each row as\n // a `DocumentHistory` so the published perspective resolves by the pin (the\n // live row is always a draft now) with ZERO dependence on `state`. Without a\n // pointer set (playground fixtures / tests) we keep bare rows, whose own\n // `state` gates the published perspective — byte-identical to before.\n const published = publishedKeys ? new Set(publishedKeys) : null\n const toSource = (doc: CmsDocument): ContentDocumentSource => {\n if (!published) return doc\n return published.has(documentKey({ collection: doc.collection, id: doc.id }))\n ? { draft: doc, published: doc, versions: [] }\n : { draft: doc, published: undefined, versions: [] }\n }\n const byKey = new Map(\n documents.map((doc) => [`${doc.collection}:${doc.id}`, toSource(doc)] as const),\n )\n // Asset resolution source for `asset`-typed FIELDS on resolved documents\n // (listing/relation results, e.g. a product's `gallery`). Two sources, real\n // records last so they win: (1) `asset`-collection fixture documents — how\n // playground/tests model assets; (2) the project's real asset-table records\n // (EC-262) — a Convex session has its assets here, NOT as `asset` documents,\n // so without these the content client resolves no nested image.\n const assets = new Map<string, AssetRecord>()\n for (const doc of documents) {\n if (doc.collection === 'asset') assets.set(doc.id, assetRecordOf(doc))\n }\n for (const record of assetRecords ?? []) assets.set(record.id, record)\n return {\n collection: (id) => collections.get(id),\n document: (ref) => byKey.get(`${ref.collection}:${ref.id}`),\n documents: (collectionId) =>\n documents.filter((doc) => doc.collection === collectionId).map(toSource),\n asset: (id) => assets.get(id),\n // Prefer the record's resolved servable `url` — the one the renderer's\n // resolveAsset reads (EC-262). Real asset-table records carry a Convex blob\n // locator in `storageKey` and the servable URL in `url`; fixture/`asset`-document\n // records (assetRecordOf) leave `url` unset and store the servable url in\n // `storageKey`. Falling back keeps both correct.\n assetUrl: (asset) => asset.url ?? asset.storageKey,\n }\n}\n\n/**\n * One deterministic client per locale: published perspective, EC-018 locale\n * fallback. The studio's ambient active locale (EC-176, `lib/locale`) picks\n * the client at query time, so the canvas previews follow the locale switcher\n * exactly like delivery would. The cache resets whenever the content session changes.\n */\nfunction clientFor(locale: Locale): ContentClient {\n const existing = clients.get(locale)\n if (existing) return existing\n const client = createContentClient({\n lookup,\n context: createContentContext({\n locale,\n perspective: 'published',\n locales: activeLocaleConfig(),\n }),\n })\n clients.set(locale, client)\n return client\n}\n\n/**\n * Run a closed-vocabulary list query (AD-3: eq/in/lt/lte/gt/gte, AND-only,\n * filterable fields only) and return the delivery-shaped result. Invalid\n * queries come back as structured errors — never thrown, never silently\n * widened. Resolves in the studio's active locale unless one is passed.\n */\nexport function runCollectionListQuery(\n collectionId: string,\n query: ListDocumentsQuery = {},\n locale: Locale = getStudioLocale(),\n): ListDocumentsResult {\n return clientFor(locale).listDocuments(collectionId, query)\n}\n\n/** Resolve one document (by id or slug) into delivery shape. */\nexport function getCollectionDocument(\n collectionId: string,\n idOrSlug: string,\n locale: Locale = getStudioLocale(),\n): GetDocumentResult {\n return clientFor(locale).getDocument(collectionId, idOrSlug)\n}\n\n/**\n * Populate a composition-node `relation` prop to delivery shape for the canvas\n * (EC-254) — the studio analog of the host's baked relations map. Resolves the\n * ref through the active session's client (published perspective, active locale),\n * so the canvas previews a block's picked documents (a ProductGrid's products, a\n * Reusable's element) exactly as delivery renders them. `null` when the target is\n * missing or not visible → the block degrades to its own empty state.\n */\nexport function resolveCanvasRelation(\n ref: DocumentRef,\n locale: Locale = getStudioLocale(),\n): ResolvedDocument | null {\n return clientFor(locale).resolveRelation(ref)\n}\n\n/**\n * Resolve a listing block's query to its documents for the canvas (EC-255) — the\n * studio analog of the host's baked listings map. Runs the query (collection +\n * AND-only filter, optional sort/limit) through the active session's client\n * (published perspective, active locale), so the canvas previews a dynamic grid (a\n * ProductGrid keyed to a category) exactly as delivery renders it. An invalid query\n * (structured, never thrown) or a non-list lookup yields `[]` → the block's empty\n * state. Reuses the same `listDocuments` path as `runCollectionListQuery`.\n */\nexport function resolveCanvasListing(\n query: ListingQuery,\n locale: Locale = getStudioLocale(),\n): ResolvedDocument[] {\n const result = clientFor(locale).listDocuments(query.collection, {\n where: query.where,\n ...(query.sort ? { sort: query.sort } : {}),\n ...(query.limit !== undefined ? { limit: query.limit } : {}),\n })\n return result.ok ? result.documents : []\n}\n\n/** A pickable entry of a collection (the template-page preview switcher). */\nexport interface CollectionEntrySummary {\n id: string\n label: string\n}\n\n/** Published entries of a collection, labeled by the schema's title field. */\nexport function listCollectionEntries(collectionId: string): CollectionEntrySummary[] {\n const collection = playgroundCollection(collectionId)\n const result = runCollectionListQuery(collectionId)\n if (!result.ok) return []\n return result.documents.map((doc) => {\n const title = collection?.titleField ? doc[collection.titleField] : undefined\n return { id: doc.id, label: typeof title === 'string' && title.length > 0 ? title : doc.id }\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"collection-content.js","sourceRoot":"","sources":["../../../src/lib/data/collection-content.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createContentClient,\n createContentContext,\n} from '@elytracms/core/content'\nimport type {\n ContentClient,\n ContentDocumentSource,\n ContentLookup,\n GetDocumentResult,\n ListDocumentsQuery,\n ListDocumentsResult,\n ResolvedDocument,\n} from '@elytracms/core/content'\nimport { documentKey, sampleLocaleConfig } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n Locale,\n LocaleConfig,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { ListingQuery } from '@elytracms/core/runtime-renderer'\nimport { getStudioLocale } from '../locale/locale-store'\n\n/**\n * The studio's content delivery engine for canvas previews (EC-166).\n *\n * Repeating sections and template pages preview *delivery-shaped* documents —\n * the exact shape `@elytracms/core/content` serves in production — so the canvas\n * shows what publishing will show. This module wires the canonical content\n * client (closed AD-3 query vocabulary, published perspective, EC-018 locale\n * fallback, depth-1 population, resolved assets) over a **content session**:\n *\n * - Playground mode (and tests): the deterministic EC-145 sample fixtures —\n * byte-identical to the pre-hydration behavior, no backend.\n * - Convex mode: the project-session gate (`components/studio/project-session`)\n * configures the session with the project's HYDRATED schema + documents, so\n * \"Preview with\" lists and section previews show the live content — never\n * fixture titles (EC-177 gap 1).\n *\n * Nothing here invents query semantics; both shapes go through the same\n * canonical client.\n */\n\n/** The schema + documents the preview engine serves (one project session). */\nexport interface ContentSessionState {\n collections: readonly CollectionDef[]\n documents: readonly CmsDocument[]\n /**\n * Published document keys via the version POINTER (EC-224). When present, the\n * preview's published perspective resolves by the pin — never the flat\n * `state` enum — so it matches what delivery serves. Absent (playground\n * fixtures / tests) → the rows' own `state` gates the perspective, unchanged.\n */\n publishedKeys?: readonly string[]\n /** Locale config of the session content; defaults to the sample config. */\n localeConfig?: LocaleConfig\n /**\n * The project's config-sourced routes (EC-207). Wired into the content client so\n * the canvas preview's `resolveLink` (EC-291) derives a rich-text link's URL from\n * the SAME routes delivery serves — the studio-live half of the host link bake.\n * Absent (playground/tests) → no routes, so `resolveLink` returns null and a\n * reference link degrades to the visible `unresolved-ref` issue.\n */\n routes?: readonly RouteRecord[]\n /**\n * The project's asset records (assets table). Wired into the lookup so the\n * content client resolves `asset`-typed FIELDS on listing/relation results\n * (e.g. a ProductGrid product's `gallery`) to delivery shape — the same assets\n * the renderer's `resolveAsset` reads for static props (EC-262). Absent\n * (playground/tests) → assets come only from `asset`-collection fixture docs.\n */\n assets?: readonly AssetRecord[]\n}\n\n/**\n * Default (no-session) collection schema: EMPTY. The studio ships no demo content\n * (Oli, 2026-06-19) — with no backend/content session configured, there is nothing\n * to preview, and the studio says so rather than serving fixtures. A real project's\n * collections + documents arrive via {@link configureContentSession} (Convex mode).\n */\nexport const playgroundCollections: readonly CollectionDef[] = []\n\nlet contentSession: ContentSessionState | null = null\nlet lookup: ContentLookup = createLookup(playgroundCollections, [], [])\nconst clients = new Map<Locale, ContentClient>()\n\n/**\n * Point the preview engine at a project's hydrated content (Convex mode), or\n * back at the deterministic playground fixtures (`null` — the default, and\n * the only state playground mode ever sees).\n */\nexport function configureContentSession(next: ContentSessionState | null): void {\n contentSession = next\n lookup = next\n ? createLookup(next.collections, next.documents, next.publishedKeys, next.assets)\n : createLookup(playgroundCollections, [], [])\n clients.clear()\n}\n\n/** Whether a hydrated content session is configured (Convex mode). */\nexport function hasContentSession(): boolean {\n return contentSession !== null\n}\n\n/** The collections the preview engine currently serves (session or fixtures). */\nfunction activeCollections(): readonly CollectionDef[] {\n return contentSession?.collections ?? playgroundCollections\n}\n\nfunction activeLocaleConfig(): LocaleConfig {\n return contentSession?.localeConfig ?? sampleLocaleConfig\n}\n\n/** Resolve a collection in the active content session (fixtures in playground). */\nexport function playgroundCollection(id: string): CollectionDef | undefined {\n return activeCollections().find((collection) => collection.id === id)\n}\n\n/**\n * Overlay the active session's canonical `filterable` declarations onto a\n * schema that doesn't carry any of its own. The inspector hands the Content\n * tab fixture collection defs; the filter/sort pickers must offer exactly the\n * EC-143 declaration set the delivery engine enforces, so both ends agree. A\n * collection that declares its own filterable fields is left untouched.\n */\nexport function withPlaygroundFilterable(\n collections: readonly CollectionDef[],\n): readonly CollectionDef[] {\n return collections.map((collection) => {\n if (collection.fields.some((field) => field.filterable === true)) return collection\n const counterpart = playgroundCollection(collection.id)\n if (counterpart === undefined) return collection\n const flagged = new Set(\n counterpart.fields.filter((field) => field.filterable === true).map((field) => field.name),\n )\n if (flagged.size === 0) return collection\n return {\n ...collection,\n fields: collection.fields.map((field) =>\n flagged.has(field.name) ? { ...field, filterable: true } : field,\n ),\n }\n })\n}\n\n/** Synthesize the persistence-shaped asset record the asset resolver reads. */\nfunction assetRecordOf(doc: CmsDocument): AssetRecord {\n const values = doc.values\n const alt = doc.localized?.en?.alt\n return {\n id: doc.id,\n projectId: 'playground',\n filename: typeof values.filename === 'string' ? values.filename : doc.id,\n contentType: 'image/png',\n byteSize: 0,\n alt: typeof alt === 'string' ? alt : undefined,\n storageKey: typeof values.url === 'string' ? values.url : doc.id,\n createdAt: '2026-01-01T00:00:00.000Z',\n updatedAt: '2026-01-01T00:00:00.000Z',\n }\n}\n\nfunction createLookup(\n collectionDefs: readonly CollectionDef[],\n documents: readonly CmsDocument[],\n publishedKeys?: readonly string[],\n assetRecords?: readonly AssetRecord[],\n): ContentLookup {\n const collections = new Map(collectionDefs.map((c) => [c.id, c]))\n // EC-224: published-ness is the version POINTER, not the flat `state` enum.\n // A hydrated session hands `publishedKeys` (the pinned set) → wrap each row as\n // a `DocumentHistory` so the published perspective resolves by the pin (the\n // live row is always a draft now) with ZERO dependence on `state`. Without a\n // pointer set (playground fixtures / tests) we keep bare rows, whose own\n // `state` gates the published perspective — byte-identical to before.\n const published = publishedKeys ? new Set(publishedKeys) : null\n const toSource = (doc: CmsDocument): ContentDocumentSource => {\n if (!published) return doc\n return published.has(documentKey({ collection: doc.collection, id: doc.id }))\n ? { draft: doc, published: doc, versions: [] }\n : { draft: doc, published: undefined, versions: [] }\n }\n const byKey = new Map(\n documents.map((doc) => [`${doc.collection}:${doc.id}`, toSource(doc)] as const),\n )\n // Asset resolution source for `asset`-typed FIELDS on resolved documents\n // (listing/relation results, e.g. a product's `gallery`). Two sources, real\n // records last so they win: (1) `asset`-collection fixture documents — how\n // playground/tests model assets; (2) the project's real asset-table records\n // (EC-262) — a Convex session has its assets here, NOT as `asset` documents,\n // so without these the content client resolves no nested image.\n const assets = new Map<string, AssetRecord>()\n for (const doc of documents) {\n if (doc.collection === 'asset') assets.set(doc.id, assetRecordOf(doc))\n }\n for (const record of assetRecords ?? []) assets.set(record.id, record)\n return {\n collection: (id) => collections.get(id),\n document: (ref) => byKey.get(`${ref.collection}:${ref.id}`),\n documents: (collectionId) =>\n documents.filter((doc) => doc.collection === collectionId).map(toSource),\n asset: (id) => assets.get(id),\n // Prefer the record's resolved servable `url` — the one the renderer's\n // resolveAsset reads (EC-262). Real asset-table records carry a Convex blob\n // locator in `storageKey` and the servable URL in `url`; fixture/`asset`-document\n // records (assetRecordOf) leave `url` unset and store the servable url in\n // `storageKey`. Falling back keeps both correct.\n assetUrl: (asset) => asset.url ?? asset.storageKey,\n }\n}\n\n/**\n * One deterministic client per locale: published perspective, EC-018 locale\n * fallback. The studio's ambient active locale (EC-176, `lib/locale`) picks\n * the client at query time, so the canvas previews follow the locale switcher\n * exactly like delivery would. The cache resets whenever the content session changes.\n */\nfunction clientFor(locale: Locale): ContentClient {\n const existing = clients.get(locale)\n if (existing) return existing\n const client = createContentClient({\n lookup,\n context: createContentContext({\n locale,\n perspective: 'published',\n locales: activeLocaleConfig(),\n }),\n // EC-291: the routes a rich-text link resolves against (config-sourced). The\n // canvas previews PUBLISHED content, so a link derives the live URL — matching\n // the host's baked link map.\n routes: contentSession?.routes ?? [],\n })\n clients.set(locale, client)\n return client\n}\n\n/**\n * Run a closed-vocabulary list query (AD-3: eq/in/lt/lte/gt/gte, AND-only,\n * filterable fields only) and return the delivery-shaped result. Invalid\n * queries come back as structured errors — never thrown, never silently\n * widened. Resolves in the studio's active locale unless one is passed.\n */\nexport function runCollectionListQuery(\n collectionId: string,\n query: ListDocumentsQuery = {},\n locale: Locale = getStudioLocale(),\n): ListDocumentsResult {\n return clientFor(locale).listDocuments(collectionId, query)\n}\n\n/** Resolve one document (by id or slug) into delivery shape. */\nexport function getCollectionDocument(\n collectionId: string,\n idOrSlug: string,\n locale: Locale = getStudioLocale(),\n): GetDocumentResult {\n return clientFor(locale).getDocument(collectionId, idOrSlug)\n}\n\n/**\n * Populate a composition-node `relation` prop to delivery shape for the canvas\n * (EC-254) — the studio analog of the host's baked relations map. Resolves the\n * ref through the active session's client (published perspective, active locale),\n * so the canvas previews a block's picked documents (a ProductGrid's products, a\n * Reusable's element) exactly as delivery renders them. `null` when the target is\n * missing or not visible → the block degrades to its own empty state.\n */\nexport function resolveCanvasRelation(\n ref: DocumentRef,\n locale: Locale = getStudioLocale(),\n): ResolvedDocument | null {\n return clientFor(locale).resolveRelation(ref)\n}\n\n/**\n * Resolve a listing block's query to its documents for the canvas (EC-255) — the\n * studio analog of the host's baked listings map. Runs the query (collection +\n * AND-only filter, optional sort/limit) through the active session's client\n * (published perspective, active locale), so the canvas previews a dynamic grid (a\n * ProductGrid keyed to a category) exactly as delivery renders it. An invalid query\n * (structured, never thrown) or a non-list lookup yields `[]` → the block's empty\n * state. Reuses the same `listDocuments` path as `runCollectionListQuery`.\n */\nexport function resolveCanvasListing(\n query: ListingQuery,\n locale: Locale = getStudioLocale(),\n): ResolvedDocument[] {\n const result = clientFor(locale).listDocuments(query.collection, {\n where: query.where,\n ...(query.sort ? { sort: query.sort } : {}),\n ...(query.limit !== undefined ? { limit: query.limit } : {}),\n })\n return result.ok ? result.documents : []\n}\n\n/**\n * Resolve a rich-text internal link's target document to its URL path for the canvas\n * (EC-291) — the studio analog of the host's baked links map. Derives the URL through\n * the active session's client (published perspective, active locale, config-sourced\n * routes), so the canvas previews a `{type:'reference'}` link's `href` exactly as\n * delivery serves it. `null` when the target is unpublished, missing, or addressed by\n * no route → the rich-text renderer shows the EC-290 `unresolved-ref` issue. Reuses\n * the same `resolveLink` path as the host bake, so both agree on the derived URL.\n */\nexport function resolveCanvasLink(\n ref: DocumentRef,\n locale: Locale = getStudioLocale(),\n): string | null {\n return clientFor(locale).resolveLink(ref)\n}\n\n/** A pickable entry of a collection (the template-page preview switcher). */\nexport interface CollectionEntrySummary {\n id: string\n label: string\n}\n\n/** Published entries of a collection, labeled by the schema's title field. */\nexport function listCollectionEntries(collectionId: string): CollectionEntrySummary[] {\n const collection = playgroundCollection(collectionId)\n const result = runCollectionListQuery(collectionId)\n if (!result.ok) return []\n return result.documents.map((doc) => {\n const title = collection?.titleField ? doc[collection.titleField] : undefined\n return { id: doc.id, label: typeof title === 'string' && title.length > 0 ? title : doc.id }\n })\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComponentNode,
|
|
1
|
+
import type { ComponentNode, InsertTarget, ProjectGraph, PropValue } from '@elytracms/core/project-graph';
|
|
2
2
|
import type { WorkingGraph, WorkingPageSeo } from '../working-graph';
|
|
3
3
|
import type { OperationResult, OperationsClient } from './operations-package';
|
|
4
4
|
/**
|
|
@@ -79,10 +79,6 @@ export type BuilderAction =
|
|
|
79
79
|
type: 'removeProp';
|
|
80
80
|
id: string;
|
|
81
81
|
name: string;
|
|
82
|
-
} | {
|
|
83
|
-
type: 'setCondition';
|
|
84
|
-
id: string;
|
|
85
|
-
condition: Condition | undefined;
|
|
86
82
|
}
|
|
87
83
|
/**
|
|
88
84
|
* EC-171 legacy page SEO (in-memory only): set a working page's `seo` block.
|
|
@@ -114,7 +110,7 @@ export interface GraphInvocation {
|
|
|
114
110
|
* After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/
|
|
115
111
|
* `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every
|
|
116
112
|
* page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/
|
|
117
|
-
* removeProp
|
|
113
|
+
* removeProp) returns `null` — page-node graph commands are gone;
|
|
118
114
|
* those edits stay in-memory until the page is saved as a CMS document.
|
|
119
115
|
*/
|
|
120
116
|
export declare function graphInvocationFor(projectId: string, action: BuilderAction, prev: BuilderSnapshot, next: {
|
|
@@ -5,7 +5,7 @@ import { toProjectGraph } from '../working-graph';
|
|
|
5
5
|
* After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/
|
|
6
6
|
* `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every
|
|
7
7
|
* page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/
|
|
8
|
-
* removeProp
|
|
8
|
+
* removeProp) returns `null` — page-node graph commands are gone;
|
|
9
9
|
* those edits stay in-memory until the page is saved as a CMS document.
|
|
10
10
|
*/
|
|
11
11
|
export function graphInvocationFor(projectId, action, prev, next) {
|
|
@@ -43,7 +43,6 @@ export function graphInvocationFor(projectId, action, prev, next) {
|
|
|
43
43
|
case 'wrap':
|
|
44
44
|
case 'setProp':
|
|
45
45
|
case 'removeProp':
|
|
46
|
-
case 'setCondition':
|
|
47
46
|
case 'setPageSeo':
|
|
48
47
|
return null;
|
|
49
48
|
default:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-commands.js","sourceRoot":"","sources":["../../../src/lib/operations/graph-commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph-commands.js","sourceRoot":"","sources":["../../../src/lib/operations/graph-commands.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAmEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,MAAqB,EACrB,IAAqB,EACrB,IAA+B;IAE/B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAA;YAC9C,OAAO;gBACL,SAAS,EAAE,YAAY;gBACvB,KAAK,EAAE;oBACL,SAAS;oBACT,wDAAwD;oBACxD,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;oBACnC,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;iBAClD;aACF,CAAA;QACH,CAAC;QAED,KAAK,WAAW;YACd,OAAO;gBACL,SAAS,EAAE,YAAY;gBACvB,uEAAuE;gBACvE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE;aACjE,CAAA;QAEH,uEAAuE;QACvE,oEAAoE;QACpE,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,WAAW,CAAC;QACjB,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,IAAI,CAAA;QAEb;YACE,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAA4B,EAC5B,SAAiB,EACjB,KAAkC;IAElC,mEAAmE;IACnE,kEAAkE;IAClE,MAAM,SAAS,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9D,IAAI,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;QAAE,OAAO,MAAM,CAAA;IAChG,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAA;AACpG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAA4B,EAC5B,SAAiB,EACjB,YAAyC;IAEzC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAA;IAC/E,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAA;IACjC,OAAO,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACrD,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,UAA4B,EAC5B,SAAiB;IAEjB,OAAO,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACvD,CAAC","sourcesContent":["import type {\n ComponentNode,\n InsertTarget,\n ProjectGraph,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { toProjectGraph } from '../working-graph'\nimport type { WorkingGraph, WorkingPageSeo } from '../working-graph'\nimport type { OperationResult, OperationsClient } from './operations-package'\n\n/**\n * Builder action → graph operation mapping (EC-141, reshaped by EC-187).\n *\n * The canonical `ProjectGraph` is now LAYOUTS-ONLY, and the page-node graph\n * commands (`graph.insertNode`/`removeNode`/`duplicateNode`/`moveNode`/\n * `reorderNode`/`setNodeProp`/`removeNodeProp`/`setNodeCondition`) were DELETED\n * from the operations layer — a page is a CMS document now, edited through the\n * composition-field path, not mirrored as node ops. So every page-tree edit\n * here returns `null`: the legacy `/builder` page editor is in-memory-only for\n * page edits (it is retired in a later slice).\n *\n * What still persists: `loadGraph` (restoring a saved revision), `undo`, and\n * `redo` each save the whole graph via `graph.save` — but a `graph.save`\n * persists a CANONICAL layouts-only `ProjectGraph`, so the WorkingGraph\n * `present` is converted via `toProjectGraph` before it is sent.\n */\n\n/** The builder store's action union (owned here so the mapping stays React-free). */\nexport type BuilderAction =\n /** `pageId` additionally switches the active page (reference deep links, EC-172). */\n | { type: 'select'; id: string | null; pageId?: string }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: 'desktop' | 'tablet' | 'mobile' }\n | { type: 'undo' }\n | { type: 'redo' }\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n /**\n * EC-171 legacy page SEO (in-memory only): set a working page's `seo` block.\n * Page SEO persists via the `page` CMS document now, never as a node op — so\n * this action does not mirror to a `graph.*` command.\n */\n | { type: 'setPageSeo'; pageId: string; seo: WorkingPageSeo | undefined }\n | { type: 'loadGraph'; graph: ProjectGraph }\n\n/** The slice of builder state the mapping needs (pre-dispatch snapshot). */\nexport interface BuilderSnapshot {\n present: WorkingGraph\n pageId: string\n nodeSeq: number\n}\n\n/** A graph operation invocation, dispatchable via `operations.execute`. */\nexport interface GraphInvocation {\n operation: string\n input: unknown\n}\n\n/**\n * Map a dispatched builder action onto the `graph.*` command that persists it.\n *\n * After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/\n * `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every\n * page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/\n * removeProp) returns `null` — page-node graph commands are gone;\n * those edits stay in-memory until the page is saved as a CMS document.\n */\nexport function graphInvocationFor(\n projectId: string,\n action: BuilderAction,\n prev: BuilderSnapshot,\n next: { present: WorkingGraph },\n): GraphInvocation | null {\n switch (action.type) {\n case 'undo':\n case 'redo': {\n if (prev.present === next.present) return null\n return {\n operation: 'graph.save',\n input: {\n projectId,\n // graph.save persists the canonical layouts-only graph.\n graph: toProjectGraph(next.present),\n message: action.type === 'undo' ? 'Undo' : 'Redo',\n },\n }\n }\n\n case 'loadGraph':\n return {\n operation: 'graph.save',\n // `loadGraph` carries a canonical (layouts-only) ProjectGraph restore.\n input: { projectId, graph: action.graph, message: 'Load graph' },\n }\n\n // Page-tree edits no longer mirror to node-level graph commands (those\n // operations were deleted) — the page editor is in-memory-only now.\n case 'select':\n case 'hover':\n case 'viewport':\n case 'insert':\n case 'remove':\n case 'duplicate':\n case 'reorder':\n case 'move':\n case 'wrap':\n case 'setProp':\n case 'removeProp':\n case 'setPageSeo':\n return null\n\n default:\n return null\n }\n}\n\n/**\n * Make the persisted latest revision equal `graph` (the session's working\n * graph) so node-level `graph.*` commands apply onto identical state. Saves a\n * baseline revision when the project has no revision yet or the persisted\n * latest diverged from the session (the builder session is the source of\n * truth for the draft timeline — see the builder-store design note).\n */\nexport async function ensureGraphBaseline(\n operations: OperationsClient,\n projectId: string,\n graph: ProjectGraph | WorkingGraph,\n): Promise<OperationResult<unknown>> {\n // A working graph (the live session) is persisted as the canonical\n // layouts-only graph; a canonical graph passes through unchanged.\n const canonical = 'pages' in graph ? toProjectGraph(graph) : graph\n const latest = await operations.graph.getLatest({ projectId })\n if (latest.ok && JSON.stringify(latest.value.graph) === JSON.stringify(canonical)) return latest\n return operations.graph.save({ projectId, graph: canonical, message: 'Builder session baseline' })\n}\n\n/**\n * Persist \"publish the session's latest state\" (EC-141): ensure the working\n * graph is the persisted latest revision (as the canonical layouts-only\n * graph), then pin it live via `publishing.publish`. Single-environment\n * (EC-179): a deployment IS one environment, so there is no target environment\n * and no promote.\n */\nexport async function persistPublish(\n operations: OperationsClient,\n projectId: string,\n workingGraph: ProjectGraph | WorkingGraph,\n): Promise<OperationResult<unknown>> {\n const baseline = await ensureGraphBaseline(operations, projectId, workingGraph)\n if (!baseline.ok) return baseline\n return operations.publishing.publish({ projectId })\n}\n\n/** Persist an unpublish: revert the deployment to draft. */\nexport async function persistUnpublish(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<unknown>> {\n return operations.publishing.unpublish({ projectId })\n}\n"]}
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
export * from './operations-package';
|
|
7
7
|
export * from './studio-operations';
|
|
8
8
|
export * from './persist-queue';
|
|
9
|
-
export * from './graph-commands';
|
|
10
|
-
export * from './graph-persistence';
|
|
11
9
|
export * from './cms-persistence';
|
|
12
10
|
export * from './asset-repository';
|
|
13
11
|
export * from './session-hydration';
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
export * from './operations-package';
|
|
7
7
|
export * from './studio-operations';
|
|
8
8
|
export * from './persist-queue';
|
|
9
|
-
export * from './graph-commands';
|
|
10
|
-
export * from './graph-persistence';
|
|
11
9
|
export * from './cms-persistence';
|
|
12
10
|
export * from './asset-repository';
|
|
13
11
|
export * from './session-hydration';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/operations/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/operations/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA","sourcesContent":["/**\n * Studio operations layer (EC-141). The single seam every studio mutation\n * goes through — see `studio-operations.ts` for the bootstrap and\n * `operations-package.ts` for why the package is bridged by path.\n */\nexport * from './operations-package'\nexport * from './studio-operations'\nexport * from './persist-queue'\nexport * from './cms-persistence'\nexport * from './asset-repository'\nexport * from './session-hydration'\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ProjectGraph } from '@elytracms/core/project-graph';
|
|
2
1
|
import type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core';
|
|
3
2
|
import type { AssetRecord } from '@elytracms/core/persistence';
|
|
4
3
|
import type { OperationResult, OperationsClient } from './operations-package';
|
|
@@ -6,12 +5,16 @@ import type { OperationResult, OperationsClient } from './operations-package';
|
|
|
6
5
|
* Persisted-session hydration (Milestone B sign-off fix, EC-177 gap 1).
|
|
7
6
|
*
|
|
8
7
|
* Against a persistent backend (Convex mode) a studio session must BOOT from
|
|
9
|
-
* what is persisted — the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
8
|
+
* what is persisted — the saved CMS schema + documents (+ asset records) — and
|
|
9
|
+
* fall back to the deterministic fixtures only when the project has nothing
|
|
10
|
+
* persisted yet (first run). Before this, every session seeded from fixtures,
|
|
11
|
+
* so reloads hid the editor's work.
|
|
12
|
+
*
|
|
13
|
+
* EC-287: the DB-resident project graph and its publish path are retired —
|
|
14
|
+
* layouts/config/schema/components are CODE, the DB holds content only. A
|
|
15
|
+
* session no longer hydrates a graph revision or graph-publishing state; CMS
|
|
16
|
+
* content (with per-document `publishedVersion`, EC-224) is the only persisted
|
|
17
|
+
* surface a session boots from.
|
|
15
18
|
*
|
|
16
19
|
* This module is the pure load half: read-only `@elytracms/core/operations` queries
|
|
17
20
|
* composed into one hydration result. `not-found` is a *normal* answer (the
|
|
@@ -48,25 +51,10 @@ export interface HydratedCms {
|
|
|
48
51
|
*/
|
|
49
52
|
assets: AssetRecord[];
|
|
50
53
|
}
|
|
51
|
-
/** The deployment's persisted publish, with the pinned revision's graph snapshot. */
|
|
52
|
-
export interface HydratedPublish {
|
|
53
|
-
/** The pinned graph revision number. */
|
|
54
|
-
revision: number;
|
|
55
|
-
/** Serialized graph of the pinned revision (collab version snapshot shape). */
|
|
56
|
-
serializedGraph: string;
|
|
57
|
-
publishedAt?: string;
|
|
58
|
-
}
|
|
59
54
|
/** Everything a Convex-mode session boots from. `null` fields mean "nothing persisted" (first run). */
|
|
60
55
|
export interface HydratedProjectSession {
|
|
61
|
-
/** The latest persisted graph revision's graph, or `null` when no revision exists. */
|
|
62
|
-
graph: ProjectGraph | null;
|
|
63
56
|
/** Persisted CMS schema + documents, or `null` when no content schema was ever saved. */
|
|
64
57
|
cms: HydratedCms | null;
|
|
65
|
-
/**
|
|
66
|
-
* The currently-published revision snapshot, or `null` when nothing is
|
|
67
|
-
* published. Single-environment (EC-179): a deployment IS one environment.
|
|
68
|
-
*/
|
|
69
|
-
publish: HydratedPublish | null;
|
|
70
58
|
}
|
|
71
59
|
/**
|
|
72
60
|
* Load the persisted CMS content state. `null` when the project has no
|
|
@@ -78,7 +66,9 @@ export interface HydratedProjectSession {
|
|
|
78
66
|
*/
|
|
79
67
|
export declare function loadHydratedCms(operations: OperationsClient, projectId: string): Promise<OperationResult<HydratedCms | null>>;
|
|
80
68
|
/**
|
|
81
|
-
* Load everything a project session hydrates from
|
|
82
|
-
*
|
|
69
|
+
* Load everything a project session hydrates from. Post-EC-287 that is just the
|
|
70
|
+
* persisted CMS content (schema + documents + assets): the project graph and its
|
|
71
|
+
* publish path are gone, so there is no graph revision or graph-publishing state
|
|
72
|
+
* to load. Per-document `publishedVersion` (EC-224) is the only published surface.
|
|
83
73
|
*/
|
|
84
74
|
export declare function loadProjectSession(operations: OperationsClient, projectId: string): Promise<OperationResult<HydratedProjectSession>>;
|