UNPKG

@proofkit/fmodata

Version:

FileMaker OData API client

190 lines (189 loc) 8.61 kB
import { StandardSchemaV1 } from '@standard-schema/spec'; /** * Branded type for container field's database type. * This allows TypeScript to distinguish container fields from regular string fields * at the type level, enabling compile-time exclusion from select operations. */ export type ContainerDbType = string & { readonly __container: true; }; export interface ListFieldOptions<TItem = string, TAllowNull extends boolean = false> { itemValidator?: StandardSchemaV1<unknown, TItem>; allowNull?: TAllowNull; } /** * FieldBuilder provides a fluent API for defining table fields with type-safe metadata. * Supports chaining methods to configure primary keys, nullability, read-only status, entity IDs, and validators. * * @template TOutput - The output type after applying outputValidator (what you get when reading) * @template TInput - The input type after applying inputValidator (what you pass when writing) * @template TDbType - The database type (what FileMaker stores/expects) * @template TReadOnly - Whether this field is read-only (for type-level exclusion from insert/update) */ export declare class FieldBuilder<TOutput = any, TInput = TOutput, TDbType = TOutput, TReadOnly extends boolean = false> { private _primaryKey; private _notNull; private _readOnly; private _entityId?; private _outputValidator?; private _inputValidator?; private readonly _fieldType; private _comment?; constructor(fieldType: string); /** * Mark this field as the primary key for the table. * Primary keys are automatically read-only and non-nullable. */ primaryKey(): FieldBuilder<NonNullable<TOutput>, NonNullable<TInput>, NonNullable<TDbType>, true>; /** * Mark this field as non-nullable. * Updates the type to exclude null/undefined. */ notNull(): FieldBuilder<NonNullable<TOutput>, NonNullable<TInput>, NonNullable<TDbType>, TReadOnly>; /** * Mark this field as read-only. * Read-only fields are excluded from insert and update operations. */ readOnly(): FieldBuilder<TOutput, TInput, TDbType, true>; /** * Assign a FileMaker field ID (FMFID) to this field. * When useEntityIds is enabled, this ID will be used in API requests instead of the field name. */ entityId(id: `FMFID:${string}`): FieldBuilder<TOutput, TInput, TDbType, TReadOnly>; /** * Set a validator for the output (reading from database). * The output validator transforms/validates data coming FROM the database in list or get operations. * * @example * numberField().readValidator(z.coerce.boolean()) * // FileMaker returns 0/1, you get true/false */ readValidator<O, VInput = TDbType>(validator: StandardSchemaV1<VInput, O>): FieldBuilder<O, TInput, TDbType, TReadOnly>; /** * Set a validator for the input (writing to database). * The input validator transforms/validates data going TO the database in insert, update, and filter operations. * * @example * numberField().writeValidator(z.boolean().transform(v => v ? 1 : 0)) * // You pass true/false, FileMaker gets 1/0 */ writeValidator<I>(validator: StandardSchemaV1<I, TDbType>): FieldBuilder<TOutput, I, TDbType, TReadOnly>; /** * Add a comment to this field for metadata purposes. * This helps future developers understand the purpose of the field. * * @example * textField().comment("Account name of the user who last modified each record") */ comment(comment: string): FieldBuilder<TOutput, TInput, TDbType, TReadOnly>; /** * Get the metadata configuration for this field. * @internal Used by fmTableOccurrence to extract field configuration */ _getConfig(): { fieldType: string; primaryKey: boolean; notNull: boolean; readOnly: boolean; entityId: `FMFID:${string}` | undefined; outputValidator: StandardSchemaV1<any, TOutput> | undefined; inputValidator: StandardSchemaV1<TInput, any> | undefined; comment: string | undefined; }; /** * Clone this builder to allow immutable chaining. * @private */ private _clone; } /** * Create a text field (Edm.String in FileMaker OData). * By default, text fields are nullable. * * @example * textField() // string | null * textField().notNull() // string * textField().entityId("FMFID:1") // with entity ID */ export declare function textField(): FieldBuilder<string | null, string | null, string | null, false>; type ListOutput<TItem, TAllowNull extends boolean> = TAllowNull extends true ? TItem[] | null : TItem[]; type ListInput<TItem, TAllowNull extends boolean> = TAllowNull extends true ? TItem[] | null : TItem[]; /** * Create a text-backed FileMaker return-delimited list field. * By default, null/empty input is normalized to an empty array (`allowNull: false`). * * @example * listField() // output: string[], input: string[] * listField({ allowNull: true }) // output/input: string[] | null * listField({ itemValidator: z.coerce.number().int() }) // output/input: number[] */ export declare function listField(): FieldBuilder<string[], string[], string | null, false>; export declare function listField<TAllowNull extends boolean = false>(options: ListFieldOptions<string, TAllowNull>): FieldBuilder<ListOutput<string, TAllowNull>, ListInput<string, TAllowNull>, string | null, false>; export declare function listField<TItem, TAllowNull extends boolean = false>(options: { itemValidator: StandardSchemaV1<unknown, TItem>; allowNull?: TAllowNull; }): FieldBuilder<ListOutput<TItem, TAllowNull>, ListInput<TItem, TAllowNull>, string | null, false>; /** * Create a number field (Edm.Decimal in FileMaker OData). * By default, number fields are nullable. * * @example * numberField() // number | null * numberField().notNull() // number * numberField().outputValidator(z.coerce.boolean()) // transform to boolean on read */ export declare function numberField(): FieldBuilder<number | null, number | null, number | null, false>; /** * Create a date field (Edm.Date in FileMaker OData). * By default, date fields are nullable and represented as ISO date strings (YYYY-MM-DD), * while accepting either ISO strings or Date objects as input. * * @example * dateField() // output: string | null (ISO date format), input: string | Date | null * dateField().notNull() // string */ export declare function dateField(): FieldBuilder<string | null, string | Date | null, string | null, false>; /** * Create a time field (Edm.TimeOfDay in FileMaker OData). * By default, time fields are nullable and represented as ISO time strings (HH:mm:ss), * while accepting either ISO strings or Date objects as input. * * @example * timeField() // output: string | null (ISO time format), input: string | Date | null * timeField().notNull() // string */ export declare function timeField(): FieldBuilder<string | null, string | Date | null, string | null, false>; /** * Create a timestamp field (Edm.DateTimeOffset in FileMaker OData). * By default, timestamp fields are nullable and represented as ISO 8601 strings, * while accepting either ISO strings or Date objects as input. * * @example * timestampField() // output: string | null (ISO 8601 format), input: string | Date | null * timestampField().notNull() // string * timestampField().readOnly() // typical for CreationTimestamp */ export declare function timestampField(): FieldBuilder<string | null, string | Date | null, string | null, false>; /** * Create a container field (Edm.Stream in FileMaker OData). * Container fields store binary data and are represented as base64 strings in the API. * By default, container fields are nullable. * * Note: Container fields cannot be selected via .select() - they can only be accessed * via .getSingleField() due to FileMaker OData API limitations. * * @example * containerField() // string | null (base64 encoded) * containerField().notNull() // string */ export declare function containerField(): FieldBuilder<string | null, string | null, ContainerDbType | null, false>; /** * Create a calculated field (read-only field computed by FileMaker). * Calculated fields are automatically marked as read-only. * * @example * calcField() // string | null * calcField().notNull() // string */ export declare function calcField(): FieldBuilder<string | null, string | null, string | null, true>; export {};