@fluentity/core
Version:
Fluentity is a fluent, model-oriented, typed HTTP client for TypeScript and framework agnostic.
328 lines (327 loc) • 11.1 kB
TypeScript
import { Attributes, Model } from './Model';
import { QueryBuilder } from './QueryBuilder';
import { HasManyRelationBuilder } from './HasManyRelationBuilder';
import { HasOneRelationBuilder } from './HasOneRelationBuilder';
import { AdapterOptions, Fluentity } from './index';
/**
* Type that determines the appropriate relation builder or model instance based on the model type.
* Maps model types to their corresponding relation builder types or model instances:
* - Single model -> HasOneRelationBuilder<T> | T
* - Array of models -> HasManyRelationBuilder<T> | T[]
*
* This type is used internally to ensure type safety when working with relationships.
*
* @template T - The model type to determine the relation type for
* @example
* ```typescript
* // Has-one relationship
* type UserProfile = Relation<Profile>; // HasOneRelationBuilder<Profile> | Profile
*
* // Has-many relationship
* type UserPosts = Relation<Post[]>; // HasManyRelationBuilder<Post> | Post[]
*
* // Usage in model definitions
* class User extends Model {
* @HasOne(() => Profile)
* profile: Relation<Profile>;
*
* @HasMany(() => Post)
* posts: Relation<Post[]>;
* }
* ```
*/
export type Relation<T extends Model<Attributes> | Array<Model<Attributes>>> = T extends Array<infer U extends Model<Attributes>> ? HasManyRelationBuilder<U> : T extends Model<Attributes> ? HasOneRelationBuilder<T> : never;
/**
* Type that determines the appropriate data type based on the relation builder type.
* Returns T if the builder is a HasOneRelationBuilder, otherwise returns T[].
*
* @template T - The model type
* @template B - The relation builder type (defaults to RelationBuilder<T>)
* @example
* ```typescript
* // For has-one relationships
* type ProfileData = RelationData<Profile>; // Profile
*
* // For has-many relationships
* type PostsData = RelationData<Post>; // Post[]
* ```
*/
export type RelationData<T extends Model<Attributes>, B extends RelationBuilder<T> = RelationBuilder<T>> = B extends HasOneRelationBuilder<T> ? T : T[];
/**
* Base class for building and managing relationships between models.
* Provides methods for querying related models and building API requests.
* This class is extended by HasOneRelationBuilder and HasManyRelationBuilder
* to implement specific relationship behaviors.
*
* Features:
* - Query building and filtering
* - Sorting and pagination
* - Relationship traversal
* - Custom query scopes
*
* @template T - The type of model this relation builder works with
* @example
* ```typescript
* // Basic usage with a has-one relationship
* class UserProfile extends RelationBuilder<Profile> {
* // Custom relationship logic
* }
*
* // Usage with query building
* const posts = await user.posts
* .where({ status: 'published' })
* .orderBy('created_at', 'desc')
* .limit(10)
* .all();
* ```
*/
export declare class RelationBuilder<T extends Model<Attributes>, A extends AdapterOptions = AdapterOptions> {
#private;
/**
* Creates a new relation builder instance.
* Sets up the query builder and configures the resource path.
* Handles inheritance of parent query parameters and custom scopes.
*
* @param model - The model instance to build relations for
* @param parentQuery - Query builder instance for constructing API requests
* @param resource - Optional custom resource name for the relation
* @throws {Error} If the model or query builder is invalid
* @example
* ```typescript
* // Create a basic relation builder
* const builder = new RelationBuilder(User, new QueryBuilder());
*
* // Create with custom resource
* const builder = new RelationBuilder(User, new QueryBuilder(), 'custom-users');
*
* // Create with parent query
* const parentQuery = new QueryBuilder().where({ active: true });
* const builder = new RelationBuilder(User, parentQuery);
* ```
*/
constructor(model: T, parentQuery?: QueryBuilder<A>);
/**
* Type definition for dynamic scope methods that can be added at runtime.
* Allows for custom query scopes to be added to the builder.
*/
[key: string]: any;
/**
* Gets the data associated with this relation builder.
* Returns T[] if T is an array type, otherwise returns T.
* @public
*/
get data(): RelationData<T, this>;
/**
* Sets the data associated with this relation builder.
* @param value - The data to set
*/
set data(value: RelationData<T, this>);
/**
* Gets the Fluentity instance for making API requests.
* Provides access to the singleton instance that manages API communication.
*
* @protected
* @returns The singleton Fluentity instance
* @throws {Error} If Fluentity has not been initialized
*/
protected get fluentity(): Fluentity;
/**
* Gets the query builder instance for constructing API URLs and managing query parameters.
* @protected
* @returns The query builder instance
*/
protected get queryBuilder(): QueryBuilder<A>;
/**
* Gets the related model instance that this builder operates on.
* @protected
* @returns The related model instance
*/
protected get relatedModel(): T;
/**
* Gets a model instance by ID without making an API request.
* Creates a new model instance with the given ID for local operations.
* Useful for setting up relationships or references.
*
* @param id - The ID of the model to get
* @returns A new model instance with the given ID
* @example
* ```typescript
* // Get a reference to a post
* const post = user.posts.id(123);
*
* // Use in relationship setup
* const comment = await Comment.create({
* content: 'Great post!',
* post: user.posts.id(123)
* });
* ```
*/
id(id: string | number): T;
/**
* Fetches a model instance by ID from the API.
* Makes a GET request to retrieve the model data.
* Throws an error if the model is not found.
*
* @param id - The ID of the model to fetch
* @returns A promise that resolves to the fetched model instance
* @throws {Error} If the model is not found
* @example
* ```typescript
* // Fetch a post by ID
* const post = await user.posts.find(123);
*
* // Fetch with error handling
* try {
* const post = await user.posts.find(123);
* console.log(`Found post: ${post.title}`);
* } catch (error) {
* if (error.status === 404) {
* console.log('Post not found');
* } else {
* console.error('Error fetching post:', error);
* }
* }
* ```
*/
find(id: string | number): Promise<T>;
/**
* Adds a where clause to the query.
* Filters results based on exact field-value matches.
* Multiple calls to where() will merge the conditions.
*
* @param where - Object containing field-value pairs to filter by
* @returns The relation builder instance for method chaining
* @example
* ```typescript
* // Simple equality conditions
* const posts = await user.posts
* .where({ status: 'published' })
* .all();
*
* // Multiple conditions
* const posts = await user.posts
* .where({
* status: 'published',
* type: 'article',
* featured: true
* })
* .all();
*
* // Multiple where calls
* const posts = await user.posts
* .where({ status: 'published' })
* .where({ type: 'article' })
* .all();
* ```
*/
where(where: Record<string, any>): RelationBuilder<T>;
/**
* Adds filter conditions to the query.
* Supports more complex filtering operations than where().
* Can handle comparison operators, arrays, and nested conditions.
*
* @param filters - Object containing filter conditions
* @returns The relation builder instance for method chaining
* @example
* ```typescript
* // Comparison operators
* const posts = await user.posts
* .filter({
* views: { gt: 1000 },
* rating: { gte: 4.5 }
* })
* .all();
*
* // Array conditions
* const posts = await user.posts
* .filter({
* tags: { in: ['featured', 'popular'] },
* status: ['published', 'draft']
* })
* .all();
*
* // Nested conditions
* const posts = await user.posts
* .filter({
* author: {
* role: 'admin',
* status: 'active'
* }
* })
* .all();
* ```
*/
filter(filters: Record<string, any>): RelationBuilder<T>;
/**
* Adds an order by clause to the query.
* Sorts the results by the specified field and direction.
* Multiple calls to orderBy() will use the last one.
*
* @param sort - The field to order by (default: 'id')
* @param direction - The direction to order in ('asc' or 'desc', default: 'asc')
* @returns The relation builder instance for method chaining
* @example
* ```typescript
* // Sort by single field
* const posts = await user.posts
* .orderBy('created_at', 'desc')
* .all();
*
* // Sort by multiple fields
* const posts = await user.posts
* .orderBy('status', 'asc')
* .orderBy('created_at', 'desc')
* .all();
*
* // Default sorting
* const posts = await user.posts
* .orderBy() // Sorts by 'id' in ascending order
* .all();
* ```
*/
orderBy(sort?: string, direction?: string): RelationBuilder<T>;
/**
* Limits the number of results returned.
* Restricts the query to return at most n results.
* Useful for pagination or limiting large result sets.
*
* @param n - The maximum number of results to return
* @returns The relation builder instance for method chaining
* @example
* ```typescript
* // Get only 10 posts
* const posts = await user.posts
* .limit(10)
* .all();
*
* // Use with pagination
* const posts = await user.posts
* .limit(20)
* .offset(40) // Get posts 41-60
* .all();
* ```
*/
limit(n: number): RelationBuilder<T>;
/**
* Sets the offset for pagination in the query results.
* Skips n records before starting to return results.
* Often used with limit() for pagination.
*
* @param n - The number of records to skip
* @returns The relation builder instance for method chaining
* @example
* ```typescript
* // Skip first 20 posts
* const posts = await user.posts
* .offset(20)
* .all();
*
* // Use with limit for pagination
* const posts = await user.posts
* .limit(10)
* .offset(30) // Get posts 31-40
* .all();
* ```
*/
offset(n: number): RelationBuilder<T>;
}