/** * `useSchemaController` — single canonical hook consumed by the Schema Builder * components. Replaces ~400 lines of near-identical `useEffect` + `useState` * wiring that previously lived in each of the four consumer apps. * * Pattern: TanStack Query native optimistic mutations (transition-safe; * `useOptimistic` is reserved for the Next.js / Conduit Server-Action path — * see §3.7 of the master plan). * * Phase 1b.2: now also manages `tenant_field_definitions` rows so EntityNode * can render column handles. Fields are fetched once per tenant (not per * entity) to avoid N+1 round-trips; the hook groups them by `entity_id` on * the client and exposes a `Record`. */ import type { LooseSupabaseClient, RenamePhysicalColumnResult } from '../service.js'; import type { AppScope, TenantEntity, TenantEntityRelation, TenantFieldDefinition } from '../types.js'; export interface UseSchemaControllerOptions { supabase: LooseSupabaseClient; tenantId: string | null; appScope?: AppScope; onError?: (message: string, err?: unknown) => void; onSuccess?: (message: string) => void; realtime?: boolean; } export interface SchemaController { entities: TenantEntity[]; relations: TenantEntityRelation[]; /** * Active tenant_field_definitions grouped by `entity_id`. Entities with no * fields are simply absent from the record (not present as `[]`). Phase 1b.2. */ fields: Record; /** * Resolved canvas position per entity id, from the `tenant_schema_layout` * overlay. Absent entities have never been placed and fall back to the * computed grid in SchemaCanvas. */ layout: Map; /** * True when the signed-in user holds platform_role = 'developer' — the only * role permitted to edit platform-owned schema. Drives whether the properties * panel unlocks label/description on a system entity; the database enforces * the same rule independently, so a tampered client gains nothing. */ isPlatformDeveloper: boolean; /** false only on a measured zero; undefined while loading or on read failure. */ physicalRenameAvailable: boolean | undefined; isLoading: boolean; loadError: Error | null; createEntity: (entity: Omit) => Promise; updateEntity: (id: string, updates: Partial) => Promise; deleteEntity: (id: string) => Promise; updateEntityPosition: (id: string, position: { x: number; y: number; }) => Promise; createRelation: (relation: Omit) => Promise; updateRelation: (id: string, updates: Partial) => Promise; deleteRelation: (id: string) => Promise; createField: (field: Omit) => Promise; updateField: (id: string, updates: Partial) => Promise; deleteField: (id: string) => Promise; /** * Phase 3A: atomically reorder the active fields of an entity. The id * array must cover exactly the entity's active fields (no missing/extra/ * duplicate ids) — the `reorder_entity_fields` RPC rejects partial arrays * with `invalid_parameter_value`. Optimistic cache update rolls back on * error via the usual toast sink. */ reorderFields: (entityId: string, orderedFieldIds: string[]) => Promise; /** * Phase 3B: rename a field. By default (`physical` omitted or false) this * is metadata-only — identical to `updateField(id, { field_name })` — and * returns the updated field row. When `physical: true` is passed, the * underlying Postgres column is renamed via the `rename_physical_column` * SECURITY DEFINER RPC in a two-phase flow: the UI is expected to call * with `dryRun: true` first, show the returned `would_execute` plus * affected views/policies to the user, and only call again with * `dryRun: false` on explicit confirmation. Every call writes an audit * row to `schema_mutations_audit`. */ renameField: (entityId: string, fieldId: string, newName: string, opts?: { physical?: boolean; dryRun?: boolean; }) => Promise; } export declare function useSchemaController({ supabase, tenantId, appScope, onError, onSuccess, realtime, }: UseSchemaControllerOptions): SchemaController; //# sourceMappingURL=useSchemaController.d.ts.map