@proofkit/fmodata
Version:
FileMaker OData API client
251 lines (250 loc) • 9.9 kB
TypeScript
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 {};