UNPKG

@proofkit/fmodata

Version:

FileMaker OData API client

251 lines (250 loc) 9.9 kB
import { FFetchOptions } from '@fetchkit/ffetch'; import { StandardSchemaV1 } from '@standard-schema/spec'; export interface BasicAuth { username: string; password: string; } export interface ApiKeyAuth { apiKey: string; } export interface ClarisIdAuth { clarisId: { username: string; password: string; }; } export type Auth = BasicAuth | ApiKeyAuth | ClarisIdAuth; export interface CountedListResult<T> { records: T[]; count: number; } export interface ExecutableBuilder<T> { execute(options?: ExecuteOptions): Promise<Result<T>>; getRequestConfig(): { method: string; url: string; body?: any; }; /** * Convert this builder to a native Request object for batch processing. * @param baseUrl - The base URL for the OData service * @param options - Optional execution options (e.g., includeODataAnnotations) * @returns A native Request object */ toRequest(baseUrl: string, options?: ExecuteOptions): Request; /** * Process a raw Response object into a typed Result. * This allows builders to apply their own validation and transformation logic. * @param response - The native Response object from the batch operation * @param options - Optional execution options (e.g., skipValidation, includeODataAnnotations) * @returns A typed Result with the builder's expected return type */ processResponse(response: Response, options?: ExecuteOptions): Promise<Result<T>>; } export interface ExecutionContext { /** * @internal * Returns the Effect Layer for this context, enabling service-based Effect pipelines. * All HTTP requests are made through the Layer's HttpClient service. */ _getLayer?(): import('./services.js').FMODataLayer; } export type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = { [K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output> ? Output : never; }; export type WithSpecialColumns<T> = T extends Record<string, any> ? T & { ROWID: number; ROWMODID: number; } : never; export type ExcludeSystemFields<T extends keyof any> = Exclude<T, "ROWID" | "ROWMODID">; export interface ODataRecordMetadata { "@id": string; "@editLink": string; } export interface ODataListResponse<T> { "@context": string; value: (T & ODataRecordMetadata)[]; } export type ODataSingleResponse<T> = T & ODataRecordMetadata & { "@context": string; }; export interface ODataFieldResponse<T> { "@context": string; value: T; } export type Result<T, E = import('./errors.js').FMODataErrorType> = { data: T; error: undefined; } | { data: undefined; error: E; }; export interface BatchItemResult<T> { data: T | undefined; error: import('./errors.js').FMODataErrorType | undefined; status: number; } export interface BatchResult<T extends readonly any[]> { results: { [K in keyof T]: BatchItemResult<T[K]>; }; successCount: number; errorCount: number; truncated: boolean; firstErrorIndex: number | null; } export type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> & Required<Pick<T, Keys>>; export type AutoRequiredKeys<Schema extends Record<string, StandardSchemaV1>> = { [K in keyof Schema]: Extract<StandardSchemaV1.InferOutput<Schema[K]>, null | undefined> extends never ? K : never; }[keyof Schema]; export type ExcludedFields<IdField extends keyof any | undefined, ReadOnly extends readonly any[]> = IdField extends keyof any ? IdField | ReadOnly[number] : ReadOnly[number]; /** * Configuration for automatic retry of transient errors. * Uses exponential backoff with optional jitter. */ export interface RetryPolicy { /** * Maximum number of retry attempts (default: 3) */ maxRetries?: number; /** * Base delay in milliseconds for exponential backoff (default: 500) */ baseDelay?: number; /** * Whether to add random jitter to delay to prevent thundering herd (default: true) */ jitter?: boolean; } export interface ExecuteOptions { includeODataAnnotations?: boolean; skipValidation?: boolean; /** * Overrides the default behavior of the database to normalize the database name in THIS REQUEST ONLY. * Defaults to true at the database level. */ normalizeDatabaseName?: boolean; /** * Overrides the default behavior of the database to use entity IDs (rather than field names) in THIS REQUEST ONLY */ useEntityIds?: boolean; /** * Overrides the default behavior of the database to include special columns (ROWID and ROWMODID) in THIS REQUEST ONLY. * Note: Special columns are only included when there is no $select query. */ includeSpecialColumns?: boolean; /** * Optional retry policy for transient errors (SchemaLockedError, NetworkError, TimeoutError, HTTP 5xx). * When set, failed requests matching transient error conditions will be retried * with exponential backoff. */ retryPolicy?: RetryPolicy; } /** * Type for the fetchHandler callback function. * This is a convenience type export that matches the fetchHandler signature in FFetchOptions. * * @example * ```typescript * import type { FetchHandler } from '@proofkit/fmodata'; * * const myFetchHandler: FetchHandler = (input, init) => { * console.log('Custom fetch:', input); * return fetch(input, init); * }; * * await query.execute({ * fetchHandler: myFetchHandler * }); * ``` */ export type FetchHandler = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>; /** * Combined type for execute() method options. * * Uses FFetchOptions from @fetchkit/ffetch to ensure proper type inference. * FFetchOptions is re-exported in the package to ensure type availability in consuming packages. */ export type ExecuteMethodOptions<EO extends ExecuteOptions = ExecuteOptions> = RequestInit & FFetchOptions & ExecuteOptions & EO; /** * Get the Accept header value based on includeODataAnnotations option * @param includeODataAnnotations - Whether to include OData annotations * @returns Accept header value */ export declare function getAcceptHeader(includeODataAnnotations?: boolean): string; type WithODataAnnotations<T> = T extends CountedListResult<infer U> ? CountedListResult<WithODataAnnotations<U>> : T extends readonly (infer U)[] ? WithODataAnnotations<U>[] : T extends Record<string, any> ? T & ODataRecordMetadata : T; export type ConditionallyWithODataAnnotations<T, IncludeODataAnnotations extends boolean> = IncludeODataAnnotations extends true ? WithODataAnnotations<T> : T; /** * Normalizes includeSpecialColumns with a database-level default. * Uses distributive conditional types to handle unions correctly. * @template IncludeSpecialColumns - The includeSpecialColumns value from execute options * @template DatabaseDefault - The database-level includeSpecialColumns setting (defaults to false) */ export type NormalizeIncludeSpecialColumns<IncludeSpecialColumns extends boolean | undefined, DatabaseDefault extends boolean = false> = [IncludeSpecialColumns] extends [true] ? true : [IncludeSpecialColumns] extends [false] ? false : DatabaseDefault; type WithSpecialColumnsDeep<T> = T extends CountedListResult<infer U> ? CountedListResult<WithSpecialColumnsDeep<U>> : T extends readonly (infer U)[] ? WithSpecialColumnsDeep<U>[] : T extends Record<string, any> ? T & { ROWID: number; ROWMODID: number; } : T; /** * Conditionally adds ROWID and ROWMODID special columns to a type. * Special columns are only included when: * - includeSpecialColumns is true AND * - hasSelect is false (no $select query was applied) AND * - T is an object type (not a primitive like string or number) * * Handles both single objects and arrays of objects. */ export type ConditionallyWithSpecialColumns<T, IncludeSpecialColumns extends boolean, HasSelect extends boolean> = IncludeSpecialColumns extends true ? (HasSelect extends false ? WithSpecialColumnsDeep<T> : T) : T; export type ExtractSchemaFromOccurrence<Occ> = Occ extends { baseTable: { schema: infer S; }; } ? S extends Record<string, StandardSchemaV1> ? S : Record<string, StandardSchemaV1> : Record<string, StandardSchemaV1>; export interface GenericFieldMetadata { $Nullable?: boolean; "@Index"?: boolean; "@Calculation"?: boolean; "@Summary"?: boolean; "@Global"?: boolean; "@Org.OData.Core.V1.Permissions"?: "Org.OData.Core.V1.Permission@Read"; } export type StringFieldMetadata = GenericFieldMetadata & { $Type: "Edm.String"; $DefaultValue?: "USER" | "USERNAME" | "CURRENT_USER"; $MaxLength?: number; }; export type DecimalFieldMetadata = GenericFieldMetadata & { $Type: "Edm.Decimal"; "@AutoGenerated"?: boolean; }; export type DateFieldMetadata = GenericFieldMetadata & { $Type: "Edm.Date"; $DefaultValue?: "CURDATE" | "CURRENT_DATE"; }; export type TimeOfDayFieldMetadata = GenericFieldMetadata & { $Type: "Edm.TimeOfDay"; $DefaultValue?: "CURTIME" | "CURRENT_TIME"; }; export type DateTimeOffsetFieldMetadata = GenericFieldMetadata & { $Type: "Edm.Date"; $DefaultValue?: "CURTIMESTAMP" | "CURRENT_TIMESTAMP"; "@VersionId"?: boolean; }; export interface StreamFieldMetadata { $Type: "Edm.Stream"; $Nullable?: boolean; "@EnclosedPath": string; "@ExternalOpenPath": string; "@ExternalSecurePath"?: string; } export type FieldMetadata = StringFieldMetadata | DecimalFieldMetadata | DateFieldMetadata | TimeOfDayFieldMetadata | DateTimeOffsetFieldMetadata | StreamFieldMetadata; export type EntityType = { $Kind: "EntityType"; $Key: string[]; } & Record<string, FieldMetadata>; export interface EntitySet { $Kind: "EntitySet"; $Type: string; } export type Metadata = Record<string, EntityType | EntitySet>; export {};