feat(cms-client): add dual-mode Payload client (local + HTTP)
This commit is contained in:
@@ -10,8 +10,12 @@
|
|||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"payload": "^3.14.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@repo/eslint-config": "workspace:*",
|
"@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 { createPayloadClient } from "./client.js";
|
||||||
export {};
|
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",
|
"extends": "@repo/typescript-config/base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
|
|||||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@@ -95,6 +95,10 @@ importers:
|
|||||||
version: link:../typescript-config
|
version: link:../typescript-config
|
||||||
|
|
||||||
packages/cms-client:
|
packages/cms-client:
|
||||||
|
dependencies:
|
||||||
|
payload:
|
||||||
|
specifier: ^3.14.0
|
||||||
|
version: 3.81.0(graphql@16.13.2)(typescript@5.9.3)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@repo/eslint-config':
|
'@repo/eslint-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
@@ -102,6 +106,9 @@ importers:
|
|||||||
'@repo/typescript-config':
|
'@repo/typescript-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../typescript-config
|
version: link:../typescript-config
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^22.0.0
|
||||||
|
version: 22.19.17
|
||||||
|
|
||||||
packages/cms-core:
|
packages/cms-core:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4310,7 +4317,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/busboy@1.5.4':
|
'@types/busboy@1.5.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.19.17
|
'@types/node': 25.5.2
|
||||||
|
|
||||||
'@types/chai@5.2.3':
|
'@types/chai@5.2.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4355,7 +4362,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/pg@8.10.2':
|
'@types/pg@8.10.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.19.17
|
'@types/node': 25.5.2
|
||||||
pg-protocol: 1.13.0
|
pg-protocol: 1.13.0
|
||||||
pg-types: 4.1.0
|
pg-types: 4.1.0
|
||||||
|
|
||||||
@@ -4380,7 +4387,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/ws@8.18.1':
|
'@types/ws@8.18.1':
|
||||||
dependencies:
|
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)':
|
'@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:
|
dependencies:
|
||||||
@@ -5024,7 +5031,7 @@ snapshots:
|
|||||||
|
|
||||||
happy-dom@20.8.9:
|
happy-dom@20.8.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.19.17
|
'@types/node': 25.5.2
|
||||||
'@types/whatwg-mimetype': 3.0.2
|
'@types/whatwg-mimetype': 3.0.2
|
||||||
'@types/ws': 8.18.1
|
'@types/ws': 8.18.1
|
||||||
entities: 7.0.1
|
entities: 7.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user