feat(cms-client): add dual-mode Payload client (local + HTTP)
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
17
packages/cms-client/src/client.ts
Normal file
17
packages/cms-client/src/client.ts
Normal file
@@ -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);
|
||||
}
|
||||
87
packages/cms-client/src/http-client.ts
Normal file
87
packages/cms-client/src/http-client.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type {
|
||||
FindOptions,
|
||||
PayloadClient,
|
||||
PayloadClientResult,
|
||||
} from "./types.js";
|
||||
|
||||
export class HTTPPayloadClient implements PayloadClient {
|
||||
constructor(private baseURL: string) {}
|
||||
|
||||
private async request<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
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<T>;
|
||||
}
|
||||
|
||||
async find<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
options?: FindOptions
|
||||
): Promise<PayloadClientResult<T>> {
|
||||
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<PayloadClientResult<T>>(
|
||||
`/api/${collection}${query ? `?${query}` : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
async findByID<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
options?: { depth?: number }
|
||||
): Promise<T> {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.depth) params.set("depth", String(options.depth));
|
||||
const query = params.toString();
|
||||
return this.request<T>(
|
||||
`/api/${collection}/${id}${query ? `?${query}` : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
async create<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
data: Record<string, unknown>,
|
||||
options?: { depth?: number }
|
||||
): Promise<T> {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.depth) params.set("depth", String(options.depth));
|
||||
const query = params.toString();
|
||||
return this.request<T>(
|
||||
`/api/${collection}${query ? `?${query}` : ""}`,
|
||||
{ method: "POST", body: JSON.stringify(data) }
|
||||
);
|
||||
}
|
||||
|
||||
async update<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Record<string, unknown>,
|
||||
options?: { depth?: number }
|
||||
): Promise<T> {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.depth) params.set("depth", String(options.depth));
|
||||
const query = params.toString();
|
||||
return this.request<T>(
|
||||
`/api/${collection}/${id}${query ? `?${query}` : ""}`,
|
||||
{ method: "PATCH", body: JSON.stringify(data) }
|
||||
);
|
||||
}
|
||||
|
||||
async delete<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string
|
||||
): Promise<T> {
|
||||
return this.request<T>(`/api/${collection}/${id}`, { method: "DELETE" });
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
78
packages/cms-client/src/local-client.ts
Normal file
78
packages/cms-client/src/local-client.ts
Normal file
@@ -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<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
options?: FindOptions
|
||||
): Promise<PayloadClientResult<T>> {
|
||||
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<T>;
|
||||
}
|
||||
|
||||
async findByID<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
options?: { depth?: number }
|
||||
): Promise<T> {
|
||||
const result = await this.payload.findByID({
|
||||
collection: collection as any,
|
||||
id,
|
||||
depth: options?.depth,
|
||||
});
|
||||
return result as unknown as T;
|
||||
}
|
||||
|
||||
async create<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
data: Record<string, unknown>,
|
||||
options?: { depth?: number }
|
||||
): Promise<T> {
|
||||
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<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Record<string, unknown>,
|
||||
options?: { depth?: number }
|
||||
): Promise<T> {
|
||||
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<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string
|
||||
): Promise<T> {
|
||||
const result = await this.payload.delete({
|
||||
collection: collection as any,
|
||||
id,
|
||||
});
|
||||
return result as unknown as T;
|
||||
}
|
||||
}
|
||||
52
packages/cms-client/src/types.ts
Normal file
52
packages/cms-client/src/types.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
export interface FindOptions {
|
||||
where?: Record<string, unknown>;
|
||||
sort?: string;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
depth?: number;
|
||||
locale?: string;
|
||||
}
|
||||
|
||||
export interface PayloadClientResult<T> {
|
||||
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<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
options?: FindOptions
|
||||
): Promise<PayloadClientResult<T>>;
|
||||
|
||||
findByID<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
options?: { depth?: number }
|
||||
): Promise<T>;
|
||||
|
||||
create<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
data: Record<string, unknown>,
|
||||
options?: { depth?: number }
|
||||
): Promise<T>;
|
||||
|
||||
update<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string,
|
||||
data: Record<string, unknown>,
|
||||
options?: { depth?: number }
|
||||
): Promise<T>;
|
||||
|
||||
delete<T = Record<string, unknown>>(
|
||||
collection: string,
|
||||
id: string
|
||||
): Promise<T>;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
|
||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user