// The shapes this editor reads off the api-cms backend, kept LOCAL so the pages // are self-contained. They mirror the api's descriptor outputs // (`content.types`, `content.list`, `content.saveDraft`). import type { FieldSchema } from '@voltro/cms/web' // The caller's resolved identity, from `session.me`. The SSR gate reads it. export interface Identity { readonly type: string readonly id: string | null readonly tenantId: string | null } // One field of a content type, as `content.types` sends it (the wire subset of // @voltro/cms's FieldSchema — enough for to pick a widget). export interface FieldDescriptor { readonly name: string readonly kind: string readonly editorHint: string readonly isOptional: boolean readonly maxLength?: number readonly literals?: ReadonlyArray readonly referenceType?: string } // One registered content type. export interface ContentTypeDescriptor { readonly name: string readonly displayName: string readonly pluralName: string readonly fields: ReadonlyArray readonly columns: ReadonlyArray } // A row in the editor's list (the `content.list` projection). export interface ContentRow { readonly id: string readonly status: string readonly title?: string readonly slug?: string readonly updatedAt: string | Date } // `content.saveDraft` returns a typed RESULT union — success or per-field // violations the form annotates. NOT a thrown error. export type SaveResult = | { readonly ok: true; readonly id: string; readonly status: string } | { readonly ok: false; readonly violations: ReadonlyArray<{ field: string; rule: string; message: string }> } // Turn the wire field list into the `{ fields }` shape reads. The // wire carries `kind`/`editorHint` as strings; ContentForm only reads // `editorHint` + `maxLength`/`literals`, so the cast at this boundary is safe. export const toContentFormType = ( type: ContentTypeDescriptor, ): { fields: Record } => { const fields: Record = {} for (const f of type.fields) fields[f.name] = f as unknown as FieldSchema return { fields } }