import { ZodSchema, ZodRawShape, ZodObject } from 'zod'; import * as React from 'react'; /** * Data Provider Types * * Core types for the data provider abstraction layer. * Based on React Admin's DataProvider pattern for familiarity. * * @module @dotdo/react/admin */ /** * Base interface for items with an ID. * All resource items must have a unique identifier. */ interface BaseRecord { /** Unique identifier - can be $id (dotdo) or id (standard) */ $id?: string; id?: string; [key: string]: unknown; } /** * Sort order for list queries */ type SortOrder = 'asc' | 'desc'; /** * Filter operators for queries */ type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'startsWith' | 'endsWith' | 'in' | 'nin'; /** * Query filter definition */ interface QueryFilter { field: string; operator: FilterOperator; value: unknown; } /** * Parameters for getList operation */ interface GetListParams { /** Resource name (e.g., 'User', 'Task') */ resource: string; /** Pagination settings */ pagination?: { page: number; perPage: number; }; /** Sort settings */ sort?: { field: string; order: SortOrder; }; /** Filters to apply */ filter?: Record | QueryFilter[]; /** Fields to select (projection) */ select?: string[]; } /** * Result from getList operation */ interface GetListResult { /** Array of records */ data: T[]; /** Total count for pagination */ total: number; /** Cursor for cursor-based pagination */ cursor?: string | null; /** Whether more pages exist */ hasMore?: boolean; } /** * Parameters for getOne operation */ interface GetOneParams { /** Resource name */ resource: string; /** Record ID */ id: string; /** Fields to select (projection) */ select?: string[]; } /** * Result from getOne operation */ interface GetOneResult { data: T; } /** * Parameters for getMany operation (batch get by IDs) */ interface GetManyParams { /** Resource name */ resource: string; /** Array of record IDs */ ids: string[]; /** Fields to select (projection) */ select?: string[]; } /** * Result from getMany operation */ interface GetManyResult { data: T[]; } /** * Parameters for create operation */ interface CreateParams { /** Resource name */ resource: string; /** Data to create (without ID) */ data: Omit; } /** * Result from create operation */ interface CreateResult { data: T; } /** * Parameters for update operation */ interface UpdateParams { /** Resource name */ resource: string; /** Record ID to update */ id: string; /** Partial data to update */ data: Partial>; /** Previous data for optimistic updates */ previousData?: T; } /** * Result from update operation */ interface UpdateResult { data: T; } /** * Parameters for delete operation */ interface DeleteParams { /** Resource name */ resource: string; /** Record ID to delete */ id: string; /** Previous data for rollback */ previousData?: T; } /** * Result from delete operation */ interface DeleteResult { data: T; } /** * Parameters for deleteMany operation */ interface DeleteManyParams { /** Resource name */ resource: string; /** Array of record IDs to delete */ ids: string[]; } /** * Result from deleteMany operation */ interface DeleteManyResult { /** Array of deleted record IDs */ data: string[]; } /** * Data Provider interface. * * Abstraction layer that allows swapping backends. * Implementations include DotdoDataProvider, MockDataProvider, etc. * * @example * ```ts * const provider: DataProvider = { * getList: async (params) => { ... }, * getOne: async (params) => { ... }, * create: async (params) => { ... }, * // ... * } * ``` */ interface DataProvider { /** * Get a list of records with pagination, sorting, and filtering */ getList(params: GetListParams): Promise>; /** * Get a single record by ID */ getOne(params: GetOneParams): Promise>; /** * Get multiple records by their IDs */ getMany(params: GetManyParams): Promise>; /** * Create a new record */ create(params: CreateParams): Promise>; /** * Update an existing record */ update(params: UpdateParams): Promise>; /** * Delete a record by ID */ delete(params: DeleteParams): Promise>; /** * Delete multiple records by their IDs */ deleteMany(params: DeleteManyParams): Promise; /** * Subscribe to real-time updates for a resource. * Returns an unsubscribe function. */ subscribe?(resource: string, callback: (event: SubscriptionEvent) => void): () => void; } /** * Real-time subscription event types */ type SubscriptionEventType = 'created' | 'updated' | 'deleted'; /** * Real-time subscription event */ interface SubscriptionEvent { type: SubscriptionEventType; resource: string; data?: T; id?: string; } /** * Configuration for creating a DataProvider */ interface DataProviderConfig { /** Base URL for API calls */ baseUrl?: string; /** Custom headers for requests */ headers?: Record; /** Timeout in milliseconds */ timeout?: number; /** Enable real-time subscriptions */ realtime?: boolean; } /** * Resource-specific configuration */ interface ResourceConfig { /** Resource name (e.g., 'User') */ name: string; /** Zod schema for validation */ schema?: ZodSchema; /** Default sort settings */ defaultSort?: { field: keyof T & string; order: SortOrder; }; /** Default filters */ defaultFilter?: Record; /** Fields to always include */ defaultSelect?: (keyof T & string)[]; /** Enable optimistic updates */ optimistic?: boolean; /** Cache TTL in milliseconds */ cacheTTL?: number; } /** * Admin Provider * * React context provider for the admin data layer. * Manages the data provider and resource configurations. * * @module @dotdo/react/admin */ /** * Value provided by AdminProvider context */ interface AdminContextValue { /** The data provider instance */ dataProvider: DataProvider; /** Registered resource configurations */ resources: Map; /** Register a resource configuration */ registerResource: (config: ResourceConfig) => void; /** Unregister a resource */ unregisterResource: (name: string) => void; /** Get resource configuration by name */ getResource: (name: string) => ResourceConfig | undefined; /** Invalidate cache for a resource */ invalidateResource: (name: string) => void; /** Global loading state across all resources */ isLoading: boolean; } /** * Props for AdminProvider component */ interface AdminProviderProps { /** The data provider to use for all operations */ dataProvider: DataProvider; /** Child components */ children: React.ReactNode; /** Initial resource configurations */ resources?: ResourceConfig[]; } /** * Admin context provider. * * Wraps your admin application to provide data access to all child components. * * @example * ```tsx * import { AdminProvider, DotdoDataProvider } from '@dotdo/react/admin' * * function App() { * const dataProvider = DotdoDataProvider({ * ns: 'https://api.example.com.ai/do/workspace' * }) * * return ( * * * * ) * } * ``` */ declare function AdminProvider({ dataProvider, children, resources: initialResources, }: AdminProviderProps): React.ReactElement; /** * Hook to access the admin context. * * Must be used within an AdminProvider. * * @returns The admin context value * @throws Error if used outside of AdminProvider */ declare function useAdminContext(): AdminContextValue; /** * Dotdo Data Provider * * DataProvider implementation for dotdo Durable Objects. * Provides CRUD operations over Cap'n Web RPC with real-time subscriptions. * * @module @dotdo/react/admin */ /** * Configuration for DotdoDataProvider */ interface DotdoDataProviderConfig { /** Namespace URL for the Durable Object (e.g., 'https://api.example.com.ai/do/workspace') */ ns: string; /** Custom headers for requests */ headers?: Record; /** Request timeout in milliseconds (default: 30000) */ timeout?: number; /** Enable real-time subscriptions (default: true) */ realtime?: boolean; /** Custom fetch function for testing */ fetch?: typeof fetch; } /** * Create a DotdoDataProvider. * * Connects to a dotdo Durable Object namespace and provides CRUD operations * via Cap'n Web RPC protocol. * * @example * ```ts * const dataProvider = DotdoDataProvider({ * ns: 'https://api.example.com.ai/do/workspace', * headers: { Authorization: 'Bearer token' }, * }) * ``` */ declare function DotdoDataProvider(config: DotdoDataProviderConfig): DataProvider; /** * Admin Error Handling * * Unified error types and utilities for the admin data layer. * * @module @dotdo/react/admin */ /** * Error codes for admin operations */ type AdminErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'NETWORK_ERROR' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'TIMEOUT' | 'UNKNOWN'; /** * Detailed error information */ interface AdminErrorDetails { /** Error code for programmatic handling */ code: AdminErrorCode; /** Human-readable message */ message: string; /** HTTP status code if applicable */ status?: number; /** Resource name where error occurred */ resource?: string; /** Record ID if applicable */ recordId?: string; /** Field-level validation errors */ fieldErrors?: Record; /** Original error for debugging */ originalError?: unknown; /** Whether the operation can be retried */ retryable?: boolean; /** Suggested retry delay in ms */ retryAfter?: number; } /** * Structured error class for admin operations. * * Provides consistent error handling across all data provider operations. * * @example * ```ts * try { * await dataProvider.create({ resource: 'User', data }) * } catch (err) { * if (isAdminError(err) && err.code === 'VALIDATION_ERROR') { * // Handle validation errors * console.log(err.fieldErrors) * } * } * ``` */ declare class AdminError extends Error { readonly code: AdminErrorCode; readonly status?: number; readonly resource?: string; readonly recordId?: string; readonly fieldErrors?: Record; readonly originalError?: unknown; readonly retryable: boolean; readonly retryAfter?: number; constructor(details: AdminErrorDetails); /** * Create an AdminError from an HTTP response */ static fromResponse(response: Response, resource?: string, body?: unknown): AdminError; /** * Create an AdminError from a network error */ static fromNetworkError(error: Error, resource?: string): AdminError; /** * Create an AdminError from a validation error */ static fromValidation(fieldErrors: Record, resource?: string): AdminError; /** * Create a not found error */ static notFound(resource: string, id: string): AdminError; /** * Get a user-friendly error message */ toUserMessage(): string; } /** * Type guard to check if an error is an AdminError */ declare function isAdminError(error: unknown): error is AdminError; /** * Format any error into an AdminError */ declare function formatAdminError(error: unknown, resource?: string): AdminError; /** * useResource Hook * * React hook for working with a resource collection through the DataProvider. * Provides data fetching, mutations, and real-time updates. * * @module @dotdo/react/admin */ /** * Configuration for useResource hook */ interface UseResourceConfig { /** Resource name (e.g., 'User', 'Task') */ resource: string; /** Initial sort settings */ sort?: { field: keyof T & string; order: SortOrder; }; /** Initial filters */ filter?: Record | QueryFilter[]; /** Items per page (default: 25) */ perPage?: number; /** Enable real-time updates (default: true) */ realtime?: boolean; /** Enable optimistic updates (default: true) */ optimistic?: boolean; } /** * Return type for useResource hook */ interface UseResourceResult { /** Current data array */ data: T[]; /** Total count of items (for pagination) */ total: number; /** Loading state for initial load */ isLoading: boolean; /** Loading state for mutations */ isMutating: boolean; /** Error from last operation */ error: AdminError | null; /** Current page (1-indexed) */ page: number; /** Items per page */ perPage: number; /** Whether more pages exist */ hasMore: boolean; /** Go to a specific page */ setPage: (page: number) => void; /** Change items per page */ setPerPage: (perPage: number) => void; /** Current sort settings */ sort: { field: string; order: SortOrder; } | null; /** Change sort settings */ setSort: (field: string, order?: SortOrder) => void; /** Current filters */ filter: Record; /** Update filters (merge with existing) */ setFilter: (filter: Record) => void; /** Clear all filters */ clearFilter: () => void; /** Create a new record */ create: (data: Omit) => Promise; /** Update an existing record */ update: (id: string, data: Partial>) => Promise; /** Delete a record */ remove: (id: string) => Promise; /** Delete multiple records */ removeMany: (ids: string[]) => Promise; /** Refetch data from server */ refetch: () => Promise; /** Find an item by ID from current data */ findById: (id: string) => T | undefined; } /** * React hook for working with a resource collection. * * Provides CRUD operations, pagination, sorting, filtering, and real-time updates. * * @example * ```tsx * function UserList() { * const { * data: users, * isLoading, * error, * create, * update, * remove, * setSort, * setFilter, * } = useResource({ resource: 'User' }) * * if (isLoading) return * if (error) return * * return ( *
* * setFilter({ search: e.target.value })} /> * {users.map(user => ( * remove(user.$id || user.id)} * /> * ))} *
* ) * } * ``` */ declare function useResource(config: UseResourceConfig): UseResourceResult; /** * useResourceRecord Hook * * React hook for working with a single record from a resource. * Provides loading, updating, deleting, and real-time updates for one item. * * @module @dotdo/react/admin */ /** * Configuration for useResourceRecord hook */ interface UseResourceRecordConfig { /** Resource name (e.g., 'User', 'Task') */ resource: string; /** Record ID to fetch */ id: string; /** Enable real-time updates (default: true) */ realtime?: boolean; /** Enable optimistic updates (default: true) */ optimistic?: boolean; } /** * Return type for useResourceRecord hook */ interface UseResourceRecordResult { /** The record data, or null if not found/loading */ data: T | null; /** Loading state */ isLoading: boolean; /** Saving state for mutations */ isSaving: boolean; /** Error from last operation */ error: AdminError | null; /** Whether the record was not found */ notFound: boolean; /** Update the record */ update: (data: Partial>) => Promise; /** Delete the record */ remove: () => Promise; /** Refetch the record from server */ refetch: () => Promise; } /** * React hook for working with a single resource record. * * Provides CRUD operations and real-time updates for one item. * * @example * ```tsx * function UserDetail({ userId }: { userId: string }) { * const { * data: user, * isLoading, * error, * notFound, * update, * remove, * } = useResourceRecord({ * resource: 'User', * id: userId, * }) * * if (isLoading) return * if (notFound) return * if (error) return * * return ( *
{ * e.preventDefault() * update({ name: formData.name }) * }}> * * * *
* ) * } * ``` */ declare function useResourceRecord(config: UseResourceRecordConfig): UseResourceRecordResult; /** * Schema Utilities * * Utilities for inferring resource configurations from Zod schemas. * Provides automatic field type detection for form generation. * * @module @dotdo/react/admin */ /** * Inferred field type from schema */ type InferredFieldType = 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'email' | 'url' | 'enum' | 'array' | 'object' | 'reference' | 'unknown'; /** * Inferred field definition */ interface InferredField { /** Field name */ name: string; /** Field type */ type: InferredFieldType; /** Whether the field is required */ required: boolean; /** Default value if any */ defaultValue?: unknown; /** For enum types, the allowed values */ enumValues?: string[]; /** For reference types, the referenced resource */ reference?: string; /** Human-readable label (derived from name) */ label: string; /** Whether this is the ID field */ isId: boolean; /** Whether this field should be hidden in forms */ hidden: boolean; /** Field description from schema */ description?: string; /** Minimum value/length */ min?: number; /** Maximum value/length */ max?: number; } /** * Resource definition derived from schema */ interface ResourceDefinition { /** Resource name */ name: string; /** ID field name */ idField: string; /** All fields */ fields: InferredField[]; /** Fields to show in list view */ listFields: InferredField[]; /** Fields to show in forms */ formFields: InferredField[]; /** Searchable fields */ searchFields: string[]; /** Sortable fields */ sortableFields: string[]; /** Filterable fields */ filterableFields: string[]; } /** * Infer fields from a Zod schema. * * Analyzes the schema structure to determine field types, validation rules, * and display properties. * * @example * ```ts * const UserSchema = z.object({ * $id: z.string(), * name: z.string().min(1), * email: z.string().email(), * role: z.enum(['admin', 'user']), * createdAt: z.string(), * }) * * const fields = inferFieldsFromSchema(UserSchema) * // [ * // { name: '$id', type: 'string', isId: true, hidden: true, ... }, * // { name: 'name', type: 'string', required: true, ... }, * // { name: 'email', type: 'email', required: true, ... }, * // { name: 'role', type: 'enum', enumValues: ['admin', 'user'], ... }, * // { name: 'createdAt', type: 'datetime', ... }, * // ] * ``` */ declare function inferFieldsFromSchema(schema: ZodObject): InferredField[]; /** * Create a resource definition from a Zod schema. * * Generates a complete resource definition including field categorization * for list views, forms, searching, and filtering. * * @example * ```ts * const userResource = createResourceFromSchema('User', UserSchema) * * // Use in admin components: * * * * ``` */ declare function createResourceFromSchema(name: string, schema: ZodObject): ResourceDefinition; /** * Cache Utilities * * Smart cache invalidation and optimistic update utilities * for the admin data layer. * * @module @dotdo/react/admin */ /** * Cache configuration */ interface CacheConfig { /** Time-to-live in milliseconds (default: 5 minutes) */ ttl?: number; /** Enable stale-while-revalidate pattern */ staleWhileRevalidate?: boolean; /** Maximum stale time in milliseconds */ maxStale?: number; /** Resources that invalidate this cache */ invalidatedBy?: string[]; } /** * Optimistic update configuration */ interface OptimisticConfig { /** Resource name */ resource: string; /** The operation type */ type: 'create' | 'update' | 'delete'; /** Record ID (for update/delete) */ id?: string; /** New/updated data */ data?: Partial; /** Original data for rollback */ previousData?: T; /** Callback when mutation succeeds */ onSuccess?: (data: T) => void; /** Callback when mutation fails */ onError?: (error: Error, previousData?: T) => void; /** Callback to rollback optimistic update */ onRollback?: (previousData?: T) => void; } /** * Create a cache key from parameters. * * Generates a consistent, serializable key for caching query results. * * @example * ```ts * const key = createCacheKey('User', { * pagination: { page: 1, perPage: 25 }, * sort: { field: 'name', order: 'asc' }, * filter: { role: 'admin' }, * }) * // 'User:{"filter":{"role":"admin"},"pagination":{"page":1,"perPage":25},"sort":{"field":"name","order":"asc"}}' * ``` */ declare function createCacheKey(resource: string, params?: Record): string; /** * Invalidate cache for a resource. * * Call this after mutations to ensure fresh data is fetched. * * @example * ```ts * await dataProvider.create({ resource: 'User', data }) * invalidateCache('User') * ``` */ declare function invalidateCache(resource: string): void; /** * Hook for managing optimistic updates with automatic rollback. * * @example * ```tsx * function TodoItem({ todo }) { * const { applyUpdate, isPending } = useOptimisticUpdate() * * const handleToggle = async () => { * await applyUpdate({ * resource: 'Todo', * type: 'update', * id: todo.id, * data: { completed: !todo.completed }, * previousData: todo, * onSuccess: () => console.log('Saved!'), * onError: (err) => console.error('Failed:', err), * }) * } * * return ( * * ) * } * ``` */ declare function useOptimisticUpdate(): { applyUpdate: (config: OptimisticConfig, mutationFn: () => Promise) => Promise; isPending: boolean; pendingUpdates: OptimisticConfig[]; pendingCount: number; }; export { type AdminContextValue, AdminError, type AdminErrorCode, type AdminErrorDetails, AdminProvider, type AdminProviderProps, type CacheConfig, type CreateParams, type CreateResult, type DataProvider, type DataProviderConfig, type DeleteManyParams, type DeleteManyResult, type DeleteParams, type DeleteResult, DotdoDataProvider, type DotdoDataProviderConfig, type FilterOperator, type GetListParams, type GetListResult, type GetManyParams, type GetManyResult, type GetOneParams, type GetOneResult, type InferredField, type OptimisticConfig, type QueryFilter, type ResourceConfig, type ResourceDefinition, type SortOrder, type UpdateParams, type UpdateResult, type UseResourceConfig, type UseResourceRecordConfig, type UseResourceRecordResult, type UseResourceResult, createCacheKey, createResourceFromSchema, formatAdminError, inferFieldsFromSchema, invalidateCache, isAdminError, useAdminContext, useOptimisticUpdate, useResource, useResourceRecord };