diff --git a/packages/cms-client/package.json b/packages/cms-client/package.json index f80cb23..46ca433 100644 --- a/packages/cms-client/package.json +++ b/packages/cms-client/package.json @@ -10,8 +10,12 @@ "lint": "eslint .", "typecheck": "tsc --noEmit" }, + "dependencies": { + "payload": "^3.14.0" + }, "devDependencies": { "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*" + "@repo/typescript-config": "workspace:*", + "@types/node": "^22.0.0" } } diff --git a/packages/cms-client/src/client.ts b/packages/cms-client/src/client.ts new file mode 100644 index 0000000..aceaa9c --- /dev/null +++ b/packages/cms-client/src/client.ts @@ -0,0 +1,17 @@ +import type { Payload } from "payload"; +import type { PayloadClient } from "./types.js"; +import { LocalPayloadClient } from "./local-client.js"; +import { HTTPPayloadClient } from "./http-client.js"; + +type PayloadClientOptions = + | { mode: "local"; payload: Payload } + | { mode: "http"; baseURL: string }; + +export function createPayloadClient( + options: PayloadClientOptions +): PayloadClient { + if (options.mode === "local") { + return new LocalPayloadClient(options.payload); + } + return new HTTPPayloadClient(options.baseURL); +} diff --git a/packages/cms-client/src/http-client.ts b/packages/cms-client/src/http-client.ts new file mode 100644 index 0000000..d00381d --- /dev/null +++ b/packages/cms-client/src/http-client.ts @@ -0,0 +1,87 @@ +import type { + FindOptions, + PayloadClient, + PayloadClientResult, +} from "./types.js"; + +export class HTTPPayloadClient implements PayloadClient { + constructor(private baseURL: string) {} + + private async request(path: string, options?: RequestInit): Promise { + const response = await fetch(`${this.baseURL}${path}`, { + headers: { "Content-Type": "application/json" }, + ...options, + }); + if (!response.ok) { + throw new Error( + `Payload API error: ${response.status} ${response.statusText}` + ); + } + return response.json() as Promise; + } + + async find>( + collection: string, + options?: FindOptions + ): Promise> { + const params = new URLSearchParams(); + if (options?.limit) params.set("limit", String(options.limit)); + if (options?.page) params.set("page", String(options.page)); + if (options?.sort) params.set("sort", options.sort); + if (options?.depth) params.set("depth", String(options.depth)); + if (options?.where) params.set("where", JSON.stringify(options.where)); + const query = params.toString(); + return this.request>( + `/api/${collection}${query ? `?${query}` : ""}` + ); + } + + async findByID>( + collection: string, + id: string, + options?: { depth?: number } + ): Promise { + const params = new URLSearchParams(); + if (options?.depth) params.set("depth", String(options.depth)); + const query = params.toString(); + return this.request( + `/api/${collection}/${id}${query ? `?${query}` : ""}` + ); + } + + async create>( + collection: string, + data: Record, + options?: { depth?: number } + ): Promise { + const params = new URLSearchParams(); + if (options?.depth) params.set("depth", String(options.depth)); + const query = params.toString(); + return this.request( + `/api/${collection}${query ? `?${query}` : ""}`, + { method: "POST", body: JSON.stringify(data) } + ); + } + + async update>( + collection: string, + id: string, + data: Record, + options?: { depth?: number } + ): Promise { + const params = new URLSearchParams(); + if (options?.depth) params.set("depth", String(options.depth)); + const query = params.toString(); + return this.request( + `/api/${collection}/${id}${query ? `?${query}` : ""}`, + { method: "PATCH", body: JSON.stringify(data) } + ); + } + + async delete>( + collection: string, + id: string + ): Promise { + return this.request(`/api/${collection}/${id}`, { method: "DELETE" }); + } +} diff --git a/packages/cms-client/src/index.ts b/packages/cms-client/src/index.ts index aff6975..f812f16 100644 --- a/packages/cms-client/src/index.ts +++ b/packages/cms-client/src/index.ts @@ -1,2 +1,8 @@ -// @repo/cms-client — Dual-mode Payload client (local + HTTP) -export {}; +export { createPayloadClient } from "./client.js"; +export { LocalPayloadClient } from "./local-client.js"; +export { HTTPPayloadClient } from "./http-client.js"; +export type { + PayloadClient, + PayloadClientResult, + FindOptions, +} from "./types.js"; diff --git a/packages/cms-client/src/local-client.ts b/packages/cms-client/src/local-client.ts new file mode 100644 index 0000000..f6e02c3 --- /dev/null +++ b/packages/cms-client/src/local-client.ts @@ -0,0 +1,78 @@ +import type { Payload } from "payload"; +import type { + FindOptions, + PayloadClient, + PayloadClientResult, +} from "./types.js"; + +export class LocalPayloadClient implements PayloadClient { + constructor(private payload: Payload) {} + + async find>( + collection: string, + options?: FindOptions + ): Promise> { + const result = await this.payload.find({ + collection: collection as any, + where: options?.where as any, + sort: options?.sort, + limit: options?.limit, + page: options?.page, + depth: options?.depth, + locale: options?.locale as any, + }); + return result as unknown as PayloadClientResult; + } + + async findByID>( + collection: string, + id: string, + options?: { depth?: number } + ): Promise { + const result = await this.payload.findByID({ + collection: collection as any, + id, + depth: options?.depth, + }); + return result as unknown as T; + } + + async create>( + collection: string, + data: Record, + options?: { depth?: number } + ): Promise { + const result = await this.payload.create({ + collection: collection as any, + data: data as any, + depth: options?.depth, + }); + return result as unknown as T; + } + + async update>( + collection: string, + id: string, + data: Record, + options?: { depth?: number } + ): Promise { + const result = await this.payload.update({ + collection: collection as any, + id, + data: data as any, + depth: options?.depth, + }); + return result as unknown as T; + } + + async delete>( + collection: string, + id: string + ): Promise { + const result = await this.payload.delete({ + collection: collection as any, + id, + }); + return result as unknown as T; + } +} diff --git a/packages/cms-client/src/types.ts b/packages/cms-client/src/types.ts new file mode 100644 index 0000000..b82b065 --- /dev/null +++ b/packages/cms-client/src/types.ts @@ -0,0 +1,52 @@ +export interface FindOptions { + where?: Record; + sort?: string; + limit?: number; + page?: number; + depth?: number; + locale?: string; +} + +export interface PayloadClientResult { + docs: T[]; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; +} + +export interface PayloadClient { + find>( + collection: string, + options?: FindOptions + ): Promise>; + + findByID>( + collection: string, + id: string, + options?: { depth?: number } + ): Promise; + + create>( + collection: string, + data: Record, + options?: { depth?: number } + ): Promise; + + update>( + collection: string, + id: string, + data: Record, + options?: { depth?: number } + ): Promise; + + delete>( + collection: string, + id: string + ): Promise; +} diff --git a/packages/cms-client/tsconfig.json b/packages/cms-client/tsconfig.json index 4f4ddbc..36de03f 100644 --- a/packages/cms-client/tsconfig.json +++ b/packages/cms-client/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "@repo/typescript-config/base.json", "compilerOptions": { + "lib": ["ES2022", "DOM", "DOM.Iterable"], "baseUrl": ".", "paths": { "@/*": ["./src/*"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97ccec2..7c46be9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,10 @@ importers: version: link:../typescript-config packages/cms-client: + dependencies: + payload: + specifier: ^3.14.0 + version: 3.81.0(graphql@16.13.2)(typescript@5.9.3) devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -102,6 +106,9 @@ importers: '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 packages/cms-core: dependencies: @@ -4310,7 +4317,7 @@ snapshots: '@types/busboy@1.5.4': dependencies: - '@types/node': 22.19.17 + '@types/node': 25.5.2 '@types/chai@5.2.3': dependencies: @@ -4355,7 +4362,7 @@ snapshots: '@types/pg@8.10.2': dependencies: - '@types/node': 22.19.17 + '@types/node': 25.5.2 pg-protocol: 1.13.0 pg-types: 4.1.0 @@ -4380,7 +4387,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 22.19.17 + '@types/node': 25.5.2 '@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: @@ -5024,7 +5031,7 @@ snapshots: happy-dom@20.8.9: dependencies: - '@types/node': 22.19.17 + '@types/node': 25.5.2 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 entities: 7.0.1