alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
146 lines (137 loc) • 3.78 kB
text/typescript
import {
type Static,
type TArray,
type TObject,
type TRecord,
TypeProvider,
z,
} from "../providers/TypeProvider.ts";
export const pageMetadataSchema = z.object({
number: z.integer().describe("Page number, starting from 0"),
size: z.integer().describe("Number of items per page (requested page size)"),
offset: z.integer().describe("Offset in the dataset (page × size)"),
numberOfElements: z
.integer()
.describe(
"Number of elements in THIS page (content.length). Different from totalElements which is the total across all pages.",
),
totalElements: z
.integer()
.describe(
"Total number of elements across all pages. Only available when counting is enabled.",
)
.optional(),
totalPages: z
.integer()
.describe(
"Total number of pages. Only available when `totalElements` is present.",
)
.optional(),
isEmpty: z
.boolean()
.describe(
"Indicates if the current page has no items (numberOfElements === 0)",
),
isFirst: z
.boolean()
.describe("Indicates if this is the first page (number === 0)"),
isLast: z
.boolean()
.describe("Indicates if this is the last page (no more pages after this)"),
sort: z
.object({
sorted: z.boolean().describe("Whether the results are sorted"),
fields: z.array(
z.object({
field: z.text({
description: "The field used for sorting",
}),
direction: z
.enum(["asc", "desc"])
.describe("The direction of the sort. Either 'asc' or 'desc'"),
}),
),
})
.optional(),
});
/**
* Create a pagination schema for the given object schema.
*
* Provides a standardized pagination response format compatible with Spring Data's Page interface.
* This schema can be used across any data source (databases, APIs, caches, etc.).
*
* @example
* ```ts
* const userSchema = z.object({ id: z.integer(), name: z.text() });
* const userPageSchema = pageSchema(userSchema);
* ```
*
* @example In an API endpoint
* ```ts
* $action({
* output: pageSchema(UserSchema),
* handler: async () => {
* return await userRepo.paginate();
* }
* })
* ```
*/
export const pageSchema = <T extends TObject | TRecord>(
objectSchema: T,
): TPage<T> =>
z.object({
content: z.array(objectSchema),
page: pageMetadataSchema,
}) as unknown as TPage<T>;
export type TPage<T extends TObject | TRecord> = TObject<{
content: TArray<T>;
page: typeof pageMetadataSchema;
}>;
/**
* Opinionated type definition for a paginated response.
*
* Inspired by Spring Data's Page interface with all essential pagination metadata.
* This generic type can be used with any data source.
*
* @example
* ```ts
* const page: Page<User> = {
* content: [user1, user2, ...],
* page: {
* number: 0,
* size: 10,
* offset: 0,
* numberOfElements: 10,
* totalElements: 1200,
* totalPages: 120,
* isEmpty: false,
* isFirst: true,
* isLast: false,
* sort: {
* sorted: true,
* fields: [
* { field: "name", direction: "asc" }
* ]
* }
* }
* }
* ```
*/
export type Page<T> = {
/**
* Array of items on the current page.
*/
content: T[];
page: Static<typeof pageMetadataSchema>;
};
export type PageMetadata = Static<typeof pageMetadataSchema>;
// ---------------------------------------------------------------------------------------------------------------------
declare module "alepha" {
interface TypeProvider {
/**
* Create a schema for a paginated response.
*/
page<T extends TObject | TRecord>(itemSchema: T): TPage<T>;
}
}
TypeProvider.prototype.page = (itemSchema) => pageSchema(itemSchema);