Compare commits
3 Commits
main
...
poc-realti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f9a9e8b62 | ||
|
|
103dfe1ca0 | ||
|
|
86da15a175 |
1
.github/workflows/pr-title.yml
vendored
1
.github/workflows/pr-title.yml
vendored
@@ -59,6 +59,7 @@ jobs:
|
||||
richtext-\*
|
||||
richtext-lexical
|
||||
richtext-slate
|
||||
sdk
|
||||
storage-\*
|
||||
storage-azure
|
||||
storage-gcs
|
||||
|
||||
@@ -13,6 +13,8 @@ keywords: rest, api, documentation, Content Management System, cms, headless, ja
|
||||
The REST API is a fully functional HTTP client that allows you to interact with your Documents in a RESTful manner. It supports all CRUD operations and is equipped with automatic pagination, depth, and sorting.
|
||||
All Payload API routes are mounted and prefixed to your config's `routes.api` URL segment (default: `/api`).
|
||||
|
||||
To enhance DX, you can use [Payload SDK](#payload-rest-api-sdk) to query your REST API.
|
||||
|
||||
**REST query parameters:**
|
||||
|
||||
- [depth](../queries/depth) - automatically populates relationships and uploads
|
||||
@@ -752,3 +754,243 @@ const res = await fetch(`${api}/${collectionSlug}?depth=1&locale=en`, {
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Payload REST API SDK
|
||||
|
||||
The best, fully type-safe way to query Payload REST API is to use its SDK client, which can be installed with:
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/sdk
|
||||
```
|
||||
|
||||
Its usage is very similar to [the Local API](../local-api/overview).
|
||||
|
||||
Example:
|
||||
```ts
|
||||
import { PayloadSDK } from '@payloadcms/sdk'
|
||||
import type { Config } from './payload-types'
|
||||
|
||||
// Pass your config from generated types as generic
|
||||
const sdk = new PayloadSDK<Config>({
|
||||
baseURL: 'https://example.com/api',
|
||||
})
|
||||
|
||||
// Find operation
|
||||
const posts = await sdk.find({
|
||||
collection: 'posts',
|
||||
draft: true,
|
||||
limit: 10,
|
||||
locale: 'en',
|
||||
page: 1,
|
||||
where: { _status: { equals: 'published' } },
|
||||
})
|
||||
|
||||
// Find by ID operation
|
||||
const posts = await sdk.findByID({
|
||||
id,
|
||||
collection: 'posts',
|
||||
draft: true,
|
||||
locale: 'en',
|
||||
})
|
||||
|
||||
// Auth login operation
|
||||
const result = await sdk.login({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: '12345',
|
||||
},
|
||||
})
|
||||
|
||||
// Create operation
|
||||
const result = await sdk.create({
|
||||
collection: 'posts',
|
||||
data: { text: 'text' },
|
||||
})
|
||||
|
||||
// Create operation with a file
|
||||
// `file` can be either a Blob | File object or a string URL
|
||||
const result = await sdk.create({ collection: 'media', file, data: {} })
|
||||
|
||||
// Count operation
|
||||
const result = await sdk.count({ collection: 'posts', where: { id: { equals: post.id } } })
|
||||
|
||||
// Update (by ID) operation
|
||||
const result = await sdk.update({
|
||||
collection: 'posts',
|
||||
id: post.id,
|
||||
data: {
|
||||
text: 'updated-text',
|
||||
},
|
||||
})
|
||||
|
||||
// Update (bulk) operation
|
||||
const result = await sdk.update({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
id: {
|
||||
equals: post.id,
|
||||
},
|
||||
},
|
||||
data: { text: 'updated-text-bulk' },
|
||||
})
|
||||
|
||||
// Delete (by ID) operation
|
||||
const result = await sdk.delete({ id: post.id, collection: 'posts' })
|
||||
|
||||
// Delete (bulk) operation
|
||||
const result = await sdk.delete({ where: { id: { equals: post.id } }, collection: 'posts' })
|
||||
|
||||
// Find Global operation
|
||||
const result = await sdk.findGlobal({ slug: 'global' })
|
||||
|
||||
// Update Global operation
|
||||
const result = await sdk.updateGlobal({ slug: 'global', data: { text: 'some-updated-global' } })
|
||||
|
||||
// Auth Login operation
|
||||
const result = await sdk.login({
|
||||
collection: 'users',
|
||||
data: { email: 'dev@payloadcms.com', password: '123456' },
|
||||
})
|
||||
|
||||
// Auth Me operation
|
||||
const result = await sdk.me(
|
||||
{ collection: 'users' },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `JWT ${user.token}`,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Auth Refresh Token operation
|
||||
const result = await sdk.refreshToken(
|
||||
{ collection: 'users' },
|
||||
{ headers: { Authorization: `JWT ${user.token}` } },
|
||||
)
|
||||
|
||||
// Auth Forgot Password operation
|
||||
const result = await sdk.forgotPassword({
|
||||
collection: 'users',
|
||||
data: { email: user.email },
|
||||
})
|
||||
|
||||
// Auth Reset Password operation
|
||||
const result = await sdk.resetPassword({
|
||||
collection: 'users',
|
||||
data: { password: '1234567', token: resetPasswordToken },
|
||||
})
|
||||
|
||||
// Find Versions operation
|
||||
const result = await sdk.findVersions({
|
||||
collection: 'posts',
|
||||
where: { parent: { equals: post.id } },
|
||||
})
|
||||
|
||||
// Find Version by ID operation
|
||||
const result = await sdk.findVersionByID({ collection: 'posts', id: version.id })
|
||||
|
||||
// Restore Version operation
|
||||
const result = await sdk.restoreVersion({
|
||||
collection: 'posts',
|
||||
id,
|
||||
})
|
||||
|
||||
// Find Global Versions operation
|
||||
const result = await sdk.findGlobalVersions({
|
||||
slug: 'global',
|
||||
})
|
||||
|
||||
// Find Global Version by ID operation
|
||||
const result = await sdk.findGlobalVersionByID({ id: version.id, slug: 'global' })
|
||||
|
||||
// Restore Global Version operation
|
||||
const result = await sdk.restoreGlobalVersion({
|
||||
slug: 'global',
|
||||
id
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
Every operation has optional 3rd parameter which is used to add additional data to the RequestInit object (like headers):
|
||||
```ts
|
||||
await sdk.me({
|
||||
collection: "users"
|
||||
}, {
|
||||
// RequestInit object
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
To query custom endpoints, you can use the `request` method, which is used internally for all other methods:
|
||||
```ts
|
||||
await sdk.request({
|
||||
method: 'POST',
|
||||
path: '/send-data',
|
||||
json: {
|
||||
id: 1,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Custom `fetch` implementation and `baseInit` for shared `RequestInit` properties:
|
||||
```ts
|
||||
const sdk = new PayloadSDK<Config>({
|
||||
baseInit: { credentials: 'include' },
|
||||
baseURL: 'https://example.com/api',
|
||||
fetch: async (url, init) => {
|
||||
console.log('before req')
|
||||
const response = await fetch(url, init)
|
||||
console.log('after req')
|
||||
return response
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Example of a custom `fetch` implementation for testing the REST API without needing to spin up a next development server:
|
||||
```ts
|
||||
import type { GeneratedTypes, SanitizedConfig } from 'payload'
|
||||
|
||||
import config from '@payload-config'
|
||||
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes'
|
||||
import { PayloadSDK } from '@payloadcms/sdk'
|
||||
|
||||
export type TypedPayloadSDK = PayloadSDK<GeneratedTypes>
|
||||
|
||||
const api = {
|
||||
GET: REST_GET(config),
|
||||
POST: REST_POST(config),
|
||||
PATCH: REST_PATCH(config),
|
||||
DELETE: REST_DELETE(config),
|
||||
PUT: REST_PUT(config),
|
||||
}
|
||||
|
||||
const awaitedConfig = await config
|
||||
|
||||
export const sdk = new PayloadSDK<GeneratedTypes>({
|
||||
baseURL: ``,
|
||||
fetch: (path: string, init: RequestInit) => {
|
||||
const [slugs, search] = path.slice(1).split('?')
|
||||
const url = `${awaitedConfig.serverURL || 'http://localhost:3000'}${awaitedConfig.routes.api}/${slugs}${search ? `?${search}` : ''}`
|
||||
|
||||
if (init.body instanceof FormData) {
|
||||
const file = init.body.get('file') as Blob
|
||||
if (file && init.headers instanceof Headers) {
|
||||
init.headers.set('Content-Length', file.size.toString())
|
||||
}
|
||||
}
|
||||
const request = new Request(url, init)
|
||||
|
||||
const params = {
|
||||
params: Promise.resolve({
|
||||
slug: slugs.split('/'),
|
||||
}),
|
||||
}
|
||||
|
||||
return api[init.method.toUpperCase()](request, params)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"build:plugins": "turbo build --filter \"@payloadcms/plugin-*\"",
|
||||
"build:richtext-lexical": "turbo build --filter \"@payloadcms/richtext-lexical\"",
|
||||
"build:richtext-slate": "turbo build --filter \"@payloadcms/richtext-slate\"",
|
||||
"build:sdk": "turbo build --filter \"@payloadcms/sdk\"",
|
||||
"build:storage-azure": "turbo build --filter \"@payloadcms/storage-azure\"",
|
||||
"build:storage-gcs": "turbo build --filter \"@payloadcms/storage-gcs\"",
|
||||
"build:storage-s3": "turbo build --filter \"@payloadcms/storage-s3\"",
|
||||
|
||||
123
packages/payload/src/cache/database.ts
vendored
Normal file
123
packages/payload/src/cache/database.ts
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { CollectionConfig } from '../collections/config/types.js'
|
||||
import type { Payload, PayloadRequest } from '../types/index.js'
|
||||
import type { PayloadCache, PayloadCacheConstructor } from './index.js'
|
||||
|
||||
const req = {} as PayloadRequest
|
||||
|
||||
export const databaseCacheAdapter = ({
|
||||
collectionOverrides = {},
|
||||
}: {
|
||||
collectionOverrides?: Partial<CollectionConfig>
|
||||
}): PayloadCacheConstructor => {
|
||||
const constructor: PayloadCacheConstructor = class DatabaseCache<
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> implements PayloadCache<Data>
|
||||
{
|
||||
type = 'database'
|
||||
|
||||
constructor(private readonly payload: Payload) {}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
await this.payload.db.deleteMany({
|
||||
collection: 'payload-cache',
|
||||
req,
|
||||
where: {},
|
||||
})
|
||||
}
|
||||
|
||||
async delete(key: string): Promise<void> {
|
||||
await this.payload.db.deleteOne({
|
||||
collection: 'payload-cache',
|
||||
req,
|
||||
where: { key: { equals: key } },
|
||||
})
|
||||
}
|
||||
|
||||
async getAll(): Promise<{ data: Data; key: string }[]> {
|
||||
const { docs } = await this.payload.db.find<{
|
||||
data: Data
|
||||
key: string
|
||||
}>({
|
||||
collection: 'payload-cache',
|
||||
joins: false,
|
||||
limit: 0,
|
||||
pagination: false,
|
||||
req,
|
||||
select: {
|
||||
data: true,
|
||||
key: true,
|
||||
},
|
||||
})
|
||||
|
||||
return docs
|
||||
}
|
||||
|
||||
async getByKey(key: string): Promise<Data | null> {
|
||||
const doc = await this.payload.db.findOne<{
|
||||
data: Data
|
||||
id: number | string
|
||||
}>({
|
||||
collection: 'payload-cache',
|
||||
joins: false,
|
||||
req,
|
||||
select: {
|
||||
data: true,
|
||||
key: true,
|
||||
},
|
||||
where: { key: { equals: key } },
|
||||
})
|
||||
|
||||
return doc.data
|
||||
}
|
||||
|
||||
async getKeys(): Promise<string[]> {
|
||||
const { docs } = await this.payload.db.find<{
|
||||
key: string
|
||||
}>({
|
||||
collection: 'payload-cache',
|
||||
limit: 0,
|
||||
pagination: false,
|
||||
req,
|
||||
select: { key: true },
|
||||
})
|
||||
|
||||
return docs.map((doc) => doc.key)
|
||||
}
|
||||
|
||||
async set(key: string, data: Data): Promise<void> {
|
||||
await this.payload.db.updateOne({
|
||||
collection: 'payload-cache',
|
||||
data: {
|
||||
data,
|
||||
},
|
||||
joins: false,
|
||||
req,
|
||||
select: {},
|
||||
where: { key: { equals: key } },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
constructor.getPayloadCacheCollection = function () {
|
||||
return {
|
||||
slug: 'payload-cache',
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
type: 'text',
|
||||
index: true,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'data',
|
||||
type: 'json',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
...collectionOverrides,
|
||||
}
|
||||
}
|
||||
|
||||
return constructor
|
||||
}
|
||||
38
packages/payload/src/cache/in-memory.ts
vendored
Normal file
38
packages/payload/src/cache/in-memory.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { PayloadCache } from './index.js'
|
||||
|
||||
export class InMemoryCache<Data extends Record<string, unknown> = Record<string, unknown>>
|
||||
implements PayloadCache<Data>
|
||||
{
|
||||
store = new Map<string, Data>()
|
||||
type = 'local'
|
||||
|
||||
clear(): void {
|
||||
this.store.clear()
|
||||
}
|
||||
|
||||
delete(key: string): void {
|
||||
this.store.delete(key)
|
||||
}
|
||||
|
||||
getAll(): { data: Data; key: string }[] {
|
||||
const result: { data: Data; key: string }[] = []
|
||||
|
||||
this.store.forEach((data, key) => {
|
||||
result.push({ data, key })
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
getByKey(key: string): Data | null {
|
||||
return this.store.get(key) ?? null
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
return Array.from(this.store.keys())
|
||||
}
|
||||
|
||||
set(key: string, data: Data): void {
|
||||
this.store.set(key, data)
|
||||
}
|
||||
}
|
||||
25
packages/payload/src/cache/index.ts
vendored
Normal file
25
packages/payload/src/cache/index.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { CollectionConfig } from '../collections/config/types.js'
|
||||
import type { Payload } from '../types/index.js'
|
||||
|
||||
export interface PayloadCache<Data extends Record<string, unknown> = Record<string, unknown>> {
|
||||
clear(): Promise<void> | void
|
||||
|
||||
delete(key: string): Promise<void> | void
|
||||
|
||||
getAll(): { data: Data; key: string }[] | Promise<{ data: Data; key: string }[]>
|
||||
|
||||
getByKey(key: string): Data | null | Promise<Data | null>
|
||||
|
||||
getKeys(): Promise<string[]> | string[]
|
||||
|
||||
set(key: string, data: Data): Promise<void> | void
|
||||
|
||||
type: string
|
||||
}
|
||||
|
||||
export type PayloadCacheConstructor<
|
||||
Data extends Record<string, unknown> = Record<string, unknown>,
|
||||
> = {
|
||||
getPayloadCacheCollection?(): CollectionConfig
|
||||
new (payload: Payload): PayloadCache<Data>
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { type ClientGlobalConfig, createClientGlobalConfigs } from '../globals/c
|
||||
export type ServerOnlyRootProperties = keyof Pick<
|
||||
SanitizedConfig,
|
||||
| 'bin'
|
||||
| 'cache'
|
||||
| 'cors'
|
||||
| 'csrf'
|
||||
| 'custom'
|
||||
@@ -67,6 +68,7 @@ export const serverOnlyConfigProperties: readonly Partial<ServerOnlyRootProperti
|
||||
'graphQL',
|
||||
'jobs',
|
||||
'logger',
|
||||
'cache',
|
||||
// `admin`, `onInit`, `localization`, `collections`, and `globals` are all handled separately
|
||||
]
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { JobsConfig } from '../queues/config/types/index.js'
|
||||
import type { Config } from './types.js'
|
||||
|
||||
import defaultAccess from '../auth/defaultAccess.js'
|
||||
import { InMemoryCache } from '../cache/in-memory.js'
|
||||
|
||||
export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
||||
admin: {
|
||||
@@ -31,6 +32,7 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
||||
theme: 'all',
|
||||
},
|
||||
bin: [],
|
||||
cache: InMemoryCache,
|
||||
collections: [],
|
||||
cookiePrefix: 'payload',
|
||||
cors: [],
|
||||
|
||||
@@ -18,6 +18,7 @@ import { sanitizeGlobals } from '../globals/config/sanitize.js'
|
||||
import { getLockedDocumentsCollection } from '../lockedDocuments/lockedDocumentsCollection.js'
|
||||
import getPreferencesCollection from '../preferences/preferencesCollection.js'
|
||||
import { getDefaultJobsCollection } from '../queues/config/jobsCollection.js'
|
||||
import { emitEventEndpoint, subscribeEndpoint } from '../realtime/index.js'
|
||||
import checkDuplicateCollections from '../utilities/checkDuplicateCollections.js'
|
||||
import { defaults } from './defaults.js'
|
||||
|
||||
@@ -171,6 +172,13 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
|
||||
config.i18n = i18nConfig
|
||||
|
||||
if (typeof configWithDefaults.cache.getPayloadCacheCollection === 'function') {
|
||||
configWithDefaults.collections.push(configWithDefaults.cache.getPayloadCacheCollection())
|
||||
}
|
||||
|
||||
configWithDefaults.endpoints.push(subscribeEndpoint)
|
||||
configWithDefaults.endpoints.push(emitEventEndpoint)
|
||||
|
||||
// Need to add default jobs collection before locked documents collections
|
||||
if (Array.isArray(configWithDefaults.jobs?.tasks) && configWithDefaults.jobs.tasks.length > 0) {
|
||||
let defaultJobsCollection = getDefaultJobsCollection(config as unknown as Config)
|
||||
|
||||
@@ -28,6 +28,7 @@ import type {
|
||||
Imports,
|
||||
InternalImportMap,
|
||||
} from '../bin/generateImportMap/index.js'
|
||||
import type { PayloadCacheConstructor } from '../cache/index.js'
|
||||
import type {
|
||||
Collection,
|
||||
CollectionConfig,
|
||||
@@ -38,6 +39,7 @@ import type { EmailAdapter, SendEmailOptions } from '../email/types.js'
|
||||
import type { ErrorName } from '../errors/types.js'
|
||||
import type { GlobalConfig, Globals, SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||
import type { JobsConfig, Payload, RequestContext, TypedUser } from '../index.js'
|
||||
import type { RealtimeEvent, SanitizedRealtimeConfig } from '../realtime/index.js'
|
||||
import type { PayloadRequest, Where } from '../types/index.js'
|
||||
import type { PayloadLogger } from '../utilities/logger.js'
|
||||
|
||||
@@ -847,6 +849,8 @@ export type Config = {
|
||||
}
|
||||
/** Custom Payload bin scripts can be injected via the config. */
|
||||
bin?: BinScriptConfig[]
|
||||
/** Caching adapter, defaults to in-memory */
|
||||
cache?: PayloadCacheConstructor
|
||||
/**
|
||||
* Manage the datamodel of your application
|
||||
*
|
||||
@@ -1028,6 +1032,9 @@ export type Config = {
|
||||
* @see https://payloadcms.com/docs/plugins/overview
|
||||
*/
|
||||
plugins?: Plugin[]
|
||||
realtime?: {
|
||||
events?: RealtimeEvent[]
|
||||
}
|
||||
/** Control the routing structure that Payload binds itself to. */
|
||||
routes?: {
|
||||
/** The route for the admin panel.
|
||||
@@ -1109,6 +1116,7 @@ export type SanitizedConfig = {
|
||||
configDir: string
|
||||
rawConfig: string
|
||||
}
|
||||
realtime: false | SanitizedRealtimeConfig
|
||||
upload: {
|
||||
/**
|
||||
* Deduped list of adapters used in the project
|
||||
|
||||
@@ -19,6 +19,7 @@ import type { Options as VerifyEmailOptions } from './auth/operations/local/veri
|
||||
import type { Result as LoginResult } from './auth/operations/login.js'
|
||||
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword.js'
|
||||
import type { AuthStrategy, User } from './auth/types.js'
|
||||
import type { PayloadCache } from './cache/index.js'
|
||||
import type {
|
||||
BulkOperationResult,
|
||||
Collection,
|
||||
@@ -74,6 +75,7 @@ import { consoleEmailAdapter } from './email/consoleEmailAdapter.js'
|
||||
import { fieldAffectsData } from './fields/config/types.js'
|
||||
import localGlobalOperations from './globals/operations/local/index.js'
|
||||
import { getJobsLocalAPI } from './queues/localAPI.js'
|
||||
import { Realtime } from './realtime/index.js'
|
||||
import { getLogger } from './utilities/logger.js'
|
||||
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit.js'
|
||||
import { traverseFields } from './utilities/traverseFields.js'
|
||||
@@ -242,9 +244,11 @@ export class BasePayload {
|
||||
|
||||
authStrategies: AuthStrategy[]
|
||||
|
||||
collections: Record<CollectionSlug, Collection> = {}
|
||||
cache: PayloadCache
|
||||
|
||||
collections: Record<CollectionSlug, Collection> = {}
|
||||
config: SanitizedConfig
|
||||
|
||||
/**
|
||||
* @description Performs count operation
|
||||
* @param options
|
||||
@@ -292,8 +296,8 @@ export class BasePayload {
|
||||
const { create } = localOperations
|
||||
return create<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
db: DatabaseAdapter
|
||||
|
||||
decrypt = decrypt
|
||||
|
||||
duplicate = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
@@ -305,11 +309,11 @@ export class BasePayload {
|
||||
|
||||
email: InitializedEmailAdapter
|
||||
|
||||
encrypt = encrypt
|
||||
|
||||
// TODO: re-implement or remove?
|
||||
// errorHandler: ErrorHandler
|
||||
|
||||
encrypt = encrypt
|
||||
|
||||
extensions: (args: {
|
||||
args: OperationArgs<any>
|
||||
req: graphQLRequest<unknown, unknown>
|
||||
@@ -425,6 +429,8 @@ export class BasePayload {
|
||||
return login<TSlug>(this, options)
|
||||
}
|
||||
|
||||
realtime = new Realtime(this)
|
||||
|
||||
resetPassword = async <TSlug extends CollectionSlug>(
|
||||
options: ResetPasswordOptions<TSlug>,
|
||||
): Promise<ResetPasswordResult> => {
|
||||
@@ -688,6 +694,8 @@ export class BasePayload {
|
||||
})
|
||||
}
|
||||
|
||||
this.cache = new this.config.cache(this)
|
||||
|
||||
if (!options.disableOnInit) {
|
||||
if (typeof options.onInit === 'function') {
|
||||
await options.onInit(this)
|
||||
|
||||
97
packages/payload/src/realtime/index.ts
Normal file
97
packages/payload/src/realtime/index.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import type { Endpoint } from '../config/types.js'
|
||||
|
||||
export type RealtimeEvent = {
|
||||
data: Record<string, unknown>
|
||||
name: string
|
||||
}
|
||||
|
||||
export type SanitizedRealtimeConfig = {
|
||||
events: RealtimeEvent[]
|
||||
}
|
||||
|
||||
export type SubscribeCallback = (args: {
|
||||
data: Record<string, unknown>
|
||||
event: string
|
||||
}) => Promise<void> | void
|
||||
|
||||
export class Realtime {
|
||||
subscribers: SubscribeCallback[] = []
|
||||
|
||||
emit({ data, event }: { data: Record<string, unknown>; event: string }): void {
|
||||
this.subscribers.forEach((callback) => {
|
||||
void callback({ data, event })
|
||||
})
|
||||
}
|
||||
|
||||
subscribe(callback: SubscribeCallback): SubscribeCallback {
|
||||
this.subscribers.push(callback)
|
||||
|
||||
return callback
|
||||
}
|
||||
|
||||
unsubscribe(callback: SubscribeCallback): void {
|
||||
this.subscribers = this.subscribers.filter((each) => each !== callback)
|
||||
}
|
||||
}
|
||||
|
||||
export const subscribeEndpoint: Endpoint = {
|
||||
handler: (req) => {
|
||||
const headers = new Headers()
|
||||
headers.set('Content-Type', 'text/event-stream')
|
||||
headers.set('Cache-Control', 'no-cache')
|
||||
headers.set('Connection', 'keep-alive')
|
||||
headers.set('Transfer-Encoding', 'chunked')
|
||||
|
||||
const abortController = new AbortController()
|
||||
|
||||
const stream = new ReadableStream({
|
||||
cancel() {
|
||||
// Ensure abort is triggered if the stream is canceled
|
||||
abortController.abort()
|
||||
},
|
||||
start(controller) {
|
||||
let isClosed = false
|
||||
|
||||
const closeStream = () => {
|
||||
if (!isClosed) {
|
||||
isClosed = true
|
||||
req.payload.realtime.unsubscribe(subscription)
|
||||
try {
|
||||
controller.close()
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
const subscription = req.payload.realtime.subscribe((args) => {
|
||||
if (isClosed) {
|
||||
return
|
||||
} // Prevent enqueueing after close
|
||||
try {
|
||||
controller.enqueue(`data: ${JSON.stringify(args)}\n\n`)
|
||||
} catch {
|
||||
closeStream()
|
||||
}
|
||||
})
|
||||
|
||||
// Handle stream cancellation
|
||||
abortController.signal.addEventListener('abort', closeStream)
|
||||
},
|
||||
})
|
||||
|
||||
return new Response(stream, { headers })
|
||||
},
|
||||
method: 'get',
|
||||
path: '/realtime/subscribe',
|
||||
}
|
||||
|
||||
export const emitEventEndpoint: Endpoint = {
|
||||
handler: async (req) => {
|
||||
const json = await req.json()
|
||||
|
||||
req.payload.realtime.emit(json)
|
||||
|
||||
return new Response('Event emitted')
|
||||
},
|
||||
method: 'post',
|
||||
path: '/realtime/emit',
|
||||
}
|
||||
10
packages/sdk/.prettierignore
Normal file
10
packages/sdk/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
24
packages/sdk/.swcrc
Normal file
24
packages/sdk/.swcrc
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
}
|
||||
}
|
||||
18
packages/sdk/eslint.config.js
Normal file
18
packages/sdk/eslint.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { rootEslintConfig, rootParserOptions } from '../../eslint.config.js'
|
||||
|
||||
/** @typedef {import('eslint').Linter.Config} Config */
|
||||
|
||||
/** @type {Config[]} */
|
||||
export const index = [
|
||||
...rootEslintConfig,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
...rootParserOptions,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default index
|
||||
22
packages/sdk/license.md
Normal file
22
packages/sdk/license.md
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2024 Payload CMS, Inc. <info@payloadcms.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
63
packages/sdk/package.json
Normal file
63
packages/sdk/package.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "@payloadcms/sdk",
|
||||
"version": "3.1.0",
|
||||
"description": "The official Payload REST API SDK",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git",
|
||||
"directory": "packages/sdk"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Payload",
|
||||
"email": "info@payloadcms.com",
|
||||
"url": "https://payloadcms.com"
|
||||
}
|
||||
],
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build"
|
||||
},
|
||||
"dependencies": {
|
||||
"payload": "workspace:*",
|
||||
"qs-esm": "7.0.2",
|
||||
"ts-essentials": "10.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
31
packages/sdk/src/auth/forgotPassword.ts
Normal file
31
packages/sdk/src/auth/forgotPassword.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { AuthCollectionSlug, PayloadGeneratedTypes, TypedAuth } from '../types.js'
|
||||
|
||||
export type ForgotPasswordOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
> = {
|
||||
collection: TSlug
|
||||
data: {
|
||||
disableEmail?: boolean
|
||||
expiration?: number
|
||||
} & Omit<TypedAuth<T>[TSlug]['forgotPassword'], 'password'>
|
||||
}
|
||||
|
||||
export async function forgotPassword<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: ForgotPasswordOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<{ message: string }> {
|
||||
const response = await sdk.request({
|
||||
init,
|
||||
json: options.data,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}/forgot-password`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
35
packages/sdk/src/auth/login.ts
Normal file
35
packages/sdk/src/auth/login.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
AuthCollectionSlug,
|
||||
DataFromCollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
TypedAuth,
|
||||
} from '../types.js'
|
||||
|
||||
export type LoginOptions<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>> = {
|
||||
collection: TSlug
|
||||
data: TypedAuth<T>[TSlug]['login']
|
||||
}
|
||||
|
||||
export type LoginResult<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>> = {
|
||||
exp?: number
|
||||
message: string
|
||||
token?: string
|
||||
// @ts-expect-error auth collection and user collection
|
||||
user: DataFromCollectionSlug<T, TSlug>
|
||||
}
|
||||
|
||||
export async function login<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: LoginOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<LoginResult<T, TSlug>> {
|
||||
const response = await sdk.request({
|
||||
init,
|
||||
json: options.data,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}/login`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
30
packages/sdk/src/auth/me.ts
Normal file
30
packages/sdk/src/auth/me.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { AuthCollectionSlug, DataFromCollectionSlug, PayloadGeneratedTypes } from '../types.js'
|
||||
|
||||
export type MeOptions<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>> = {
|
||||
collection: TSlug
|
||||
}
|
||||
|
||||
export type MeResult<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>> = {
|
||||
collection?: TSlug
|
||||
exp?: number
|
||||
message: string
|
||||
strategy?: string
|
||||
token?: string
|
||||
// @ts-expect-error auth collection and user collection
|
||||
user: DataFromCollectionSlug<T, TSlug>
|
||||
}
|
||||
|
||||
export async function me<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: MeOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<MeResult<T, TSlug>> {
|
||||
const response = await sdk.request({
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/${options.collection}/me`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
32
packages/sdk/src/auth/refreshToken.ts
Normal file
32
packages/sdk/src/auth/refreshToken.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { AuthCollectionSlug, DataFromCollectionSlug, PayloadGeneratedTypes } from '../types.js'
|
||||
|
||||
export type RefreshOptions<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>> = {
|
||||
collection: TSlug
|
||||
}
|
||||
|
||||
export type RefreshResult<T extends PayloadGeneratedTypes, TSlug extends AuthCollectionSlug<T>> = {
|
||||
exp: number
|
||||
refreshedToken: string
|
||||
setCookie?: boolean
|
||||
strategy?: string
|
||||
// @ts-expect-error auth collection and user collection
|
||||
user: DataFromCollectionSlug<T, TSlug>
|
||||
}
|
||||
|
||||
export async function refreshToken<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: RefreshOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<RefreshResult<T, TSlug>> {
|
||||
const response = await sdk.request({
|
||||
init,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}/refresh-token`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
40
packages/sdk/src/auth/resetPassword.ts
Normal file
40
packages/sdk/src/auth/resetPassword.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { AuthCollectionSlug, DataFromCollectionSlug, PayloadGeneratedTypes } from '../types.js'
|
||||
|
||||
export type ResetPasswordOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
> = {
|
||||
collection: TSlug
|
||||
data: {
|
||||
password: string
|
||||
token: string
|
||||
}
|
||||
}
|
||||
|
||||
export type ResetPasswordResult<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
> = {
|
||||
token?: string
|
||||
// @ts-expect-error auth collection and user collection
|
||||
user: DataFromCollectionSlug<T, TSlug>
|
||||
}
|
||||
|
||||
export async function resetPassword<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: ResetPasswordOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<ResetPasswordResult<T, TSlug>> {
|
||||
const response = await sdk.request({
|
||||
init,
|
||||
json: options.data,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}/reset-password`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
27
packages/sdk/src/auth/verifyEmail.ts
Normal file
27
packages/sdk/src/auth/verifyEmail.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { AuthCollectionSlug, PayloadGeneratedTypes } from '../types.js'
|
||||
|
||||
export type VerifyEmailOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
> = {
|
||||
collection: TSlug
|
||||
token: string
|
||||
}
|
||||
|
||||
export async function verifyEmail<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends AuthCollectionSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: VerifyEmailOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<{ message: string }> {
|
||||
const response = await sdk.request({
|
||||
init,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}/verify/${options.token}`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
25
packages/sdk/src/collections/count.ts
Normal file
25
packages/sdk/src/collections/count.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { Where } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { CollectionSlug, PayloadGeneratedTypes, TypedLocale } from '../types.js'
|
||||
|
||||
export type CountOptions<T extends PayloadGeneratedTypes, TSlug extends CollectionSlug<T>> = {
|
||||
collection: TSlug
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export async function count<T extends PayloadGeneratedTypes, TSlug extends CollectionSlug<T>>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: CountOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<{ totalDocs: number }> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/${options.collection}/count`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
60
packages/sdk/src/collections/create.ts
Normal file
60
packages/sdk/src/collections/create.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { SelectType } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
RequiredDataFromCollectionSlug,
|
||||
TransformCollectionWithSelect,
|
||||
TypedLocale,
|
||||
UploadCollectionSlug,
|
||||
} from '../types.js'
|
||||
|
||||
import { resolveFileFromOptions } from '../utilities/resolveFileFromOptions.js'
|
||||
|
||||
export type CreateOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
collection: TSlug
|
||||
data: RequiredDataFromCollectionSlug<T, TSlug>
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
/** File Blob object or URL to the file. Only for upload collections */
|
||||
file?: TSlug extends UploadCollectionSlug<T> ? Blob | string : never
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
select?: TSelect
|
||||
}
|
||||
|
||||
export async function create<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: CreateOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformCollectionWithSelect<T, TSlug, TSelect>> {
|
||||
let file: Blob | undefined = undefined
|
||||
|
||||
if (options.file) {
|
||||
file = await resolveFileFromOptions(options.file)
|
||||
}
|
||||
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
file,
|
||||
init,
|
||||
json: options.data,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}`,
|
||||
})
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
return json.doc
|
||||
}
|
||||
77
packages/sdk/src/collections/delete.ts
Normal file
77
packages/sdk/src/collections/delete.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { SelectType, Where } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
BulkOperationResult,
|
||||
CollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
SelectFromCollectionSlug,
|
||||
TransformCollectionWithSelect,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type DeleteBaseOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
collection: TSlug
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
locale?: TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
select?: TSelect
|
||||
}
|
||||
|
||||
export type DeleteByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
> = {
|
||||
id: number | string
|
||||
where?: never
|
||||
} & DeleteBaseOptions<T, TSlug, TSelect>
|
||||
|
||||
export type DeleteManyOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
> = {
|
||||
id?: never
|
||||
where: Where
|
||||
} & DeleteBaseOptions<T, TSlug, TSelect>
|
||||
|
||||
export type DeleteOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
> = DeleteByIDOptions<T, TSlug, TSelect> | DeleteManyOptions<T, TSlug, TSelect>
|
||||
|
||||
export async function deleteOperation<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: DeleteOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<
|
||||
BulkOperationResult<T, TSlug, TSelect> | TransformCollectionWithSelect<T, TSlug, TSelect>
|
||||
> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'DELETE',
|
||||
path: `/${options.collection}${options.id ? `/${options.id}` : ''}`,
|
||||
})
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
if (options.id) {
|
||||
return json.doc
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
49
packages/sdk/src/collections/find.ts
Normal file
49
packages/sdk/src/collections/find.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { PaginatedDocs, SelectType, Sort, Where } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
JoinQuery,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TransformCollectionWithSelect,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
collection: TSlug
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
joins?: JoinQuery<T, TSlug>
|
||||
limit?: number
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
page?: number
|
||||
populate?: PopulateType<T>
|
||||
select?: TSelect
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export async function find<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<PaginatedDocs<TransformCollectionWithSelect<T, TSlug, TSelect>>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/${options.collection}`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
62
packages/sdk/src/collections/findByID.ts
Normal file
62
packages/sdk/src/collections/findByID.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { ApplyDisableErrors, SelectType } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
JoinQuery,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TransformCollectionWithSelect,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
collection: TSlug
|
||||
depth?: number
|
||||
disableErrors?: TDisableErrors
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
id: number | string
|
||||
joins?: JoinQuery<T, TSlug>
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
select?: TSelect
|
||||
}
|
||||
|
||||
export async function findByID<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectType,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindByIDOptions<T, TSlug, TDisableErrors, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<T, TSlug, TSelect>, TDisableErrors>> {
|
||||
try {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/${options.collection}/${options.id}`,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
} else {
|
||||
throw new Error()
|
||||
}
|
||||
} catch {
|
||||
if (options.disableErrors) {
|
||||
// @ts-expect-error generic nullable
|
||||
return null
|
||||
}
|
||||
|
||||
throw new Error(`Error retrieving the document ${options.collection}/${options.id}`)
|
||||
}
|
||||
}
|
||||
58
packages/sdk/src/collections/findVersionByID.ts
Normal file
58
packages/sdk/src/collections/findVersionByID.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { ApplyDisableErrors, SelectType, TypeWithVersion } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
DataFromCollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindVersionByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
> = {
|
||||
collection: TSlug
|
||||
depth?: number
|
||||
disableErrors?: TDisableErrors
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
id: number | string
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
select?: SelectType
|
||||
}
|
||||
|
||||
export async function findVersionByID<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindVersionByIDOptions<T, TSlug, TDisableErrors>,
|
||||
init?: RequestInit,
|
||||
): Promise<ApplyDisableErrors<TypeWithVersion<DataFromCollectionSlug<T, TSlug>>, TDisableErrors>> {
|
||||
try {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/${options.collection}/versions/${options.id}`,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
} else {
|
||||
throw new Error()
|
||||
}
|
||||
} catch {
|
||||
if (options.disableErrors) {
|
||||
// @ts-expect-error generic nullable
|
||||
return null
|
||||
}
|
||||
|
||||
throw new Error(`Error retrieving the version document ${options.collection}/${options.id}`)
|
||||
}
|
||||
}
|
||||
45
packages/sdk/src/collections/findVersions.ts
Normal file
45
packages/sdk/src/collections/findVersions.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { PaginatedDocs, SelectType, Sort, TypeWithVersion, Where } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
DataFromCollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindVersionsOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
> = {
|
||||
collection: TSlug
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
limit?: number
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
page?: number
|
||||
populate?: PopulateType<T>
|
||||
select?: SelectType
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export async function findVersions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindVersionsOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<PaginatedDocs<TypeWithVersion<DataFromCollectionSlug<T, TSlug>>>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/${options.collection}/versions`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
39
packages/sdk/src/collections/restoreVersion.ts
Normal file
39
packages/sdk/src/collections/restoreVersion.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
DataFromCollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type RestoreVersionByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
> = {
|
||||
collection: TSlug
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
id: number | string
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
}
|
||||
|
||||
export async function restoreVersion<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: RestoreVersionByIDOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<DataFromCollectionSlug<T, TSlug>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'POST',
|
||||
path: `/${options.collection}/versions/${options.id}`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
96
packages/sdk/src/collections/update.ts
Normal file
96
packages/sdk/src/collections/update.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { SelectType, Where } from 'payload'
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
BulkOperationResult,
|
||||
CollectionSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
RequiredDataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
TransformCollectionWithSelect,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
import { resolveFileFromOptions } from '../utilities/resolveFileFromOptions.js'
|
||||
|
||||
export type UpdateBaseOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
autosave?: boolean
|
||||
collection: TSlug
|
||||
data: DeepPartial<RequiredDataFromCollectionSlug<T, TSlug>>
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
file?: File | string
|
||||
filePath?: string
|
||||
locale?: TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
publishSpecificLocale?: string
|
||||
select?: TSelect
|
||||
}
|
||||
|
||||
export type UpdateByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
> = {
|
||||
id: number | string
|
||||
limit?: never
|
||||
where?: never
|
||||
} & UpdateBaseOptions<T, TSlug, TSelect>
|
||||
|
||||
export type UpdateManyOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
> = {
|
||||
id?: never
|
||||
limit?: number
|
||||
where: Where
|
||||
} & UpdateBaseOptions<T, TSlug, TSelect>
|
||||
|
||||
export type UpdateOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
> = UpdateByIDOptions<T, TSlug, TSelect> | UpdateManyOptions<T, TSlug, TSelect>
|
||||
|
||||
export async function update<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectFromCollectionSlug<T, TSlug>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: UpdateOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<
|
||||
BulkOperationResult<T, TSlug, TSelect> | TransformCollectionWithSelect<T, TSlug, TSelect>
|
||||
> {
|
||||
let file: Blob | undefined = undefined
|
||||
|
||||
if (options.file) {
|
||||
file = await resolveFileFromOptions(options.file)
|
||||
}
|
||||
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
file,
|
||||
init,
|
||||
json: options.data,
|
||||
method: 'PATCH',
|
||||
path: `/${options.collection}${options.id ? `/${options.id}` : ''}`,
|
||||
})
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
if (options.id) {
|
||||
return json.doc
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
44
packages/sdk/src/globals/findOne.ts
Normal file
44
packages/sdk/src/globals/findOne.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { SelectType } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
GlobalSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
SelectFromGlobalSlug,
|
||||
TransformGlobalWithSelect,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindGlobalOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
select?: TSelect
|
||||
slug: TSlug
|
||||
}
|
||||
|
||||
export async function findGlobal<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TSelect extends SelectFromGlobalSlug<T, TSlug>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindGlobalOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformGlobalWithSelect<T, TSlug, TSelect>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/globals/${options.slug}`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
58
packages/sdk/src/globals/findVersionByID.ts
Normal file
58
packages/sdk/src/globals/findVersionByID.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { ApplyDisableErrors, SelectType, TypeWithVersion } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindGlobalVersionByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
> = {
|
||||
depth?: number
|
||||
disableErrors?: TDisableErrors
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
id: number | string
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
select?: SelectType
|
||||
slug: TSlug
|
||||
}
|
||||
|
||||
export async function findGlobalVersionByID<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindGlobalVersionByIDOptions<T, TSlug, TDisableErrors>,
|
||||
init?: RequestInit,
|
||||
): Promise<ApplyDisableErrors<TypeWithVersion<DataFromGlobalSlug<T, TSlug>>, TDisableErrors>> {
|
||||
try {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/globals/${options.slug}/versions/${options.id}`,
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
} else {
|
||||
throw new Error()
|
||||
}
|
||||
} catch {
|
||||
if (options.disableErrors) {
|
||||
// @ts-expect-error generic nullable
|
||||
return null
|
||||
}
|
||||
|
||||
throw new Error(`Error retrieving the version document ${options.slug}/${options.id}`)
|
||||
}
|
||||
}
|
||||
45
packages/sdk/src/globals/findVersions.ts
Normal file
45
packages/sdk/src/globals/findVersions.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { PaginatedDocs, SelectType, Sort, TypeWithVersion, Where } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type FindGlobalVersionsOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
> = {
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
limit?: number
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
page?: number
|
||||
populate?: PopulateType<T>
|
||||
select?: SelectType
|
||||
slug: TSlug
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export async function findGlobalVersions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: FindGlobalVersionsOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<PaginatedDocs<TypeWithVersion<DataFromGlobalSlug<T, TSlug>>>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'GET',
|
||||
path: `/globals/${options.slug}/versions`,
|
||||
})
|
||||
|
||||
return response.json()
|
||||
}
|
||||
43
packages/sdk/src/globals/restoreVersion.ts
Normal file
43
packages/sdk/src/globals/restoreVersion.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { TypeWithVersion } from 'payload'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type RestoreGlobalVersionByIDOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
> = {
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
id: number | string
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
slug: TSlug
|
||||
}
|
||||
|
||||
export async function restoreGlobalVersion<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: RestoreGlobalVersionByIDOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<TypeWithVersion<DataFromGlobalSlug<T, TSlug>>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
method: 'POST',
|
||||
path: `/globals/${options.slug}/versions/${options.id}`,
|
||||
})
|
||||
|
||||
const { doc } = await response.json()
|
||||
|
||||
return doc
|
||||
}
|
||||
51
packages/sdk/src/globals/update.ts
Normal file
51
packages/sdk/src/globals/update.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { SelectType } from 'payload'
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type {
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
PayloadGeneratedTypes,
|
||||
PopulateType,
|
||||
SelectFromGlobalSlug,
|
||||
TransformGlobalWithSelect,
|
||||
TypedLocale,
|
||||
} from '../types.js'
|
||||
|
||||
export type UpdateGlobalOptions<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
data: DeepPartial<Omit<DataFromGlobalSlug<T, TSlug>, 'id'>>
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | TypedLocale<T>
|
||||
locale?: 'all' | TypedLocale<T>
|
||||
populate?: PopulateType<T>
|
||||
publishSpecificLocale?: TypedLocale<T>
|
||||
select?: TSelect
|
||||
slug: TSlug
|
||||
}
|
||||
|
||||
export async function updateGlobal<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TSelect extends SelectFromGlobalSlug<T, TSlug>,
|
||||
>(
|
||||
sdk: PayloadSDK<T>,
|
||||
options: UpdateGlobalOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformGlobalWithSelect<T, TSlug, TSelect>> {
|
||||
const response = await sdk.request({
|
||||
args: options,
|
||||
init,
|
||||
json: options.data,
|
||||
method: 'POST',
|
||||
path: `/globals/${options.slug}`,
|
||||
})
|
||||
|
||||
const { result } = await response.json()
|
||||
|
||||
return result
|
||||
}
|
||||
385
packages/sdk/src/index.ts
Normal file
385
packages/sdk/src/index.ts
Normal file
@@ -0,0 +1,385 @@
|
||||
import type { ApplyDisableErrors, PaginatedDocs, SelectType, TypeWithVersion } from 'payload'
|
||||
|
||||
import type { ForgotPasswordOptions } from './auth/forgotPassword.js'
|
||||
import type { LoginOptions, LoginResult } from './auth/login.js'
|
||||
import type { MeOptions, MeResult } from './auth/me.js'
|
||||
import type { ResetPasswordOptions, ResetPasswordResult } from './auth/resetPassword.js'
|
||||
import type { CountOptions } from './collections/count.js'
|
||||
import type { CreateOptions } from './collections/create.js'
|
||||
import type { DeleteByIDOptions, DeleteManyOptions, DeleteOptions } from './collections/delete.js'
|
||||
import type { FindOptions } from './collections/find.js'
|
||||
import type { FindByIDOptions } from './collections/findByID.js'
|
||||
import type { FindVersionByIDOptions } from './collections/findVersionByID.js'
|
||||
import type { FindVersionsOptions } from './collections/findVersions.js'
|
||||
import type { RestoreVersionByIDOptions } from './collections/restoreVersion.js'
|
||||
import type { FindGlobalVersionByIDOptions } from './globals/findVersionByID.js'
|
||||
import type { FindGlobalVersionsOptions } from './globals/findVersions.js'
|
||||
import type { RestoreGlobalVersionByIDOptions } from './globals/restoreVersion.js'
|
||||
import type { UpdateGlobalOptions } from './globals/update.js'
|
||||
import type {
|
||||
AuthCollectionSlug,
|
||||
BulkOperationResult,
|
||||
CollectionSlug,
|
||||
DataFromCollectionSlug,
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
PayloadGeneratedTypes,
|
||||
SelectFromCollectionSlug,
|
||||
SelectFromGlobalSlug,
|
||||
TransformCollectionWithSelect,
|
||||
TransformGlobalWithSelect,
|
||||
} from './types.js'
|
||||
import type { OperationArgs } from './utilities/buildSearchParams.js'
|
||||
|
||||
import { forgotPassword } from './auth/forgotPassword.js'
|
||||
import { login } from './auth/login.js'
|
||||
import { me } from './auth/me.js'
|
||||
import { type RefreshOptions, type RefreshResult, refreshToken } from './auth/refreshToken.js'
|
||||
import { resetPassword } from './auth/resetPassword.js'
|
||||
import { verifyEmail, type VerifyEmailOptions } from './auth/verifyEmail.js'
|
||||
import { count } from './collections/count.js'
|
||||
import { create } from './collections/create.js'
|
||||
import { deleteOperation } from './collections/delete.js'
|
||||
import { find } from './collections/find.js'
|
||||
import { findByID } from './collections/findByID.js'
|
||||
import { findVersionByID } from './collections/findVersionByID.js'
|
||||
import { findVersions } from './collections/findVersions.js'
|
||||
import { restoreVersion } from './collections/restoreVersion.js'
|
||||
import {
|
||||
update,
|
||||
type UpdateByIDOptions,
|
||||
type UpdateManyOptions,
|
||||
type UpdateOptions,
|
||||
} from './collections/update.js'
|
||||
import { findGlobal, type FindGlobalOptions } from './globals/findOne.js'
|
||||
import { findGlobalVersionByID } from './globals/findVersionByID.js'
|
||||
import { findGlobalVersions } from './globals/findVersions.js'
|
||||
import { restoreGlobalVersion } from './globals/restoreVersion.js'
|
||||
import { updateGlobal } from './globals/update.js'
|
||||
import { emitEvent } from './realtime/emitEvent.js'
|
||||
import { subscribe } from './realtime/subscribe.js'
|
||||
import { buildSearchParams } from './utilities/buildSearchParams.js'
|
||||
|
||||
type Args = {
|
||||
/** Base passed `RequestInit` to `fetch`. For base headers / credentials include etc. */
|
||||
baseInit?: RequestInit
|
||||
|
||||
/**
|
||||
* Base API URL for requests.
|
||||
* @example 'https://example.com/api'
|
||||
*/
|
||||
baseURL: string
|
||||
|
||||
/**
|
||||
* This option allows you to pass a custom `fetch` implementation.
|
||||
* The function always receives `path` as the first parameter and `RequestInit` as the second.
|
||||
* @example For testing without needing an HTTP server:
|
||||
* ```typescript
|
||||
* import type { GeneratedTypes, SanitizedConfig } from 'payload';
|
||||
* import config from '@payload-config';
|
||||
* import { REST_DELETE, REST_GET, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes';
|
||||
* import { PayloadSDK } from '@payloadcms/sdk';
|
||||
*
|
||||
* export type TypedPayloadSDK = PayloadSDK<GeneratedTypes>;
|
||||
*
|
||||
* const api = {
|
||||
* GET: REST_GET(config),
|
||||
* POST: REST_POST(config),
|
||||
* PATCH: REST_PATCH(config),
|
||||
* DELETE: REST_DELETE(config),
|
||||
* PUT: REST_PUT(config),
|
||||
* };
|
||||
*
|
||||
* const awaitedConfig = await config;
|
||||
*
|
||||
* export const sdk = new PayloadSDK<GeneratedTypes>({
|
||||
* baseURL: '',
|
||||
* fetch: (path: string, init: RequestInit) => {
|
||||
* const [slugs, search] = path.slice(1).split('?');
|
||||
* const url = `${awaitedConfig.serverURL || 'http://localhost:3000'}${awaitedConfig.routes.api}/${slugs}${search ? `?${search}` : ''}`;
|
||||
*
|
||||
* if (init.body instanceof FormData) {
|
||||
* const file = init.body.get('file') as Blob;
|
||||
* if (file && init.headers instanceof Headers) {
|
||||
* init.headers.set('Content-Length', file.size.toString());
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const request = new Request(url, init);
|
||||
*
|
||||
* const params = {
|
||||
* params: Promise.resolve({
|
||||
* slug: slugs.split('/'),
|
||||
* }),
|
||||
* };
|
||||
*
|
||||
* return api[init.method.toUpperCase()](request, params);
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
fetch?: typeof fetch
|
||||
}
|
||||
|
||||
export class PayloadSDK<T extends PayloadGeneratedTypes = PayloadGeneratedTypes> {
|
||||
baseInit: RequestInit
|
||||
|
||||
baseURL: string
|
||||
|
||||
fetch: typeof fetch
|
||||
|
||||
realtime = {
|
||||
emitEvent: (args: { data: Record<string, any>; event: string }): Promise<Response> => {
|
||||
return emitEvent(this, args)
|
||||
},
|
||||
subscribe: (
|
||||
onEvent: (args: { data: Record<string, any>; event: string }) => void,
|
||||
): Promise<{ response: Response; unsubscribe: () => Promise<void> }> => {
|
||||
return subscribe(this, onEvent)
|
||||
},
|
||||
}
|
||||
|
||||
constructor(args: Args) {
|
||||
this.baseURL = args.baseURL
|
||||
this.fetch = args.fetch ?? ((...args) => globalThis.fetch(...args))
|
||||
this.baseInit = args.baseInit ?? {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Performs count operation
|
||||
* @param options
|
||||
* @returns count of documents satisfying query
|
||||
*/
|
||||
count<TSlug extends CollectionSlug<T>>(
|
||||
options: CountOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<{ totalDocs: number }> {
|
||||
return count(this, options, init)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Performs create operation
|
||||
* @param options
|
||||
* @returns created document
|
||||
*/
|
||||
create<TSlug extends CollectionSlug<T>, TSelect extends SelectType>(
|
||||
options: CreateOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformCollectionWithSelect<T, TSlug, TSelect>> {
|
||||
return create(this, options, init)
|
||||
}
|
||||
delete<TSlug extends CollectionSlug<T>, TSelect extends SelectFromCollectionSlug<T, TSlug>>(
|
||||
options: DeleteManyOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<BulkOperationResult<T, TSlug, TSelect>>
|
||||
|
||||
delete<TSlug extends CollectionSlug<T>, TSelect extends SelectFromCollectionSlug<T, TSlug>>(
|
||||
options: DeleteByIDOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformCollectionWithSelect<T, TSlug, TSelect>>
|
||||
|
||||
/**
|
||||
* @description Update one or more documents
|
||||
* @param options
|
||||
* @returns Updated document(s)
|
||||
*/
|
||||
delete<TSlug extends CollectionSlug<T>, TSelect extends SelectFromCollectionSlug<T, TSlug>>(
|
||||
options: DeleteOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<
|
||||
BulkOperationResult<T, TSlug, TSelect> | TransformCollectionWithSelect<T, TSlug, TSelect>
|
||||
> {
|
||||
return deleteOperation(this, options, init)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Find documents with criteria
|
||||
* @param options
|
||||
* @returns documents satisfying query
|
||||
*/
|
||||
find<TSlug extends CollectionSlug<T>, TSelect extends SelectType>(
|
||||
options: FindOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<PaginatedDocs<TransformCollectionWithSelect<T, TSlug, TSelect>>> {
|
||||
return find(this, options, init)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Find document by ID
|
||||
* @param options
|
||||
* @returns document with specified ID
|
||||
*/
|
||||
findByID<
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectType,
|
||||
>(
|
||||
options: FindByIDOptions<T, TSlug, TDisableErrors, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<T, TSlug, TSelect>, TDisableErrors>> {
|
||||
return findByID(this, options, init)
|
||||
}
|
||||
|
||||
findGlobal<TSlug extends GlobalSlug<T>, TSelect extends SelectFromGlobalSlug<T, TSlug>>(
|
||||
options: FindGlobalOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformGlobalWithSelect<T, TSlug, TSelect>> {
|
||||
return findGlobal(this, options, init)
|
||||
}
|
||||
|
||||
findGlobalVersionByID<TSlug extends GlobalSlug<T>, TDisableErrors extends boolean>(
|
||||
options: FindGlobalVersionByIDOptions<T, TSlug, TDisableErrors>,
|
||||
init?: RequestInit,
|
||||
): Promise<ApplyDisableErrors<TypeWithVersion<DataFromGlobalSlug<T, TSlug>>, TDisableErrors>> {
|
||||
return findGlobalVersionByID(this, options, init)
|
||||
}
|
||||
findGlobalVersions<TSlug extends GlobalSlug<T>>(
|
||||
options: FindGlobalVersionsOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<PaginatedDocs<TypeWithVersion<DataFromGlobalSlug<T, TSlug>>>> {
|
||||
return findGlobalVersions(this, options, init)
|
||||
}
|
||||
|
||||
findVersionByID<TSlug extends CollectionSlug<T>, TDisableErrors extends boolean>(
|
||||
options: FindVersionByIDOptions<T, TSlug, TDisableErrors>,
|
||||
init?: RequestInit,
|
||||
): Promise<
|
||||
ApplyDisableErrors<TypeWithVersion<DataFromCollectionSlug<T, TSlug>>, TDisableErrors>
|
||||
> {
|
||||
return findVersionByID(this, options, init)
|
||||
}
|
||||
findVersions<TSlug extends CollectionSlug<T>>(
|
||||
options: FindVersionsOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<PaginatedDocs<TypeWithVersion<DataFromCollectionSlug<T, TSlug>>>> {
|
||||
return findVersions(this, options, init)
|
||||
}
|
||||
|
||||
forgotPassword<TSlug extends AuthCollectionSlug<T>>(
|
||||
options: ForgotPasswordOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<{ message: string }> {
|
||||
return forgotPassword(this, options, init)
|
||||
}
|
||||
|
||||
login<TSlug extends AuthCollectionSlug<T>>(
|
||||
options: LoginOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<LoginResult<T, TSlug>> {
|
||||
return login(this, options, init)
|
||||
}
|
||||
|
||||
me<TSlug extends AuthCollectionSlug<T>>(
|
||||
options: MeOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<MeResult<T, TSlug>> {
|
||||
return me(this, options, init)
|
||||
}
|
||||
|
||||
refreshToken<TSlug extends AuthCollectionSlug<T>>(
|
||||
options: RefreshOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<RefreshResult<T, TSlug>> {
|
||||
return refreshToken(this, options, init)
|
||||
}
|
||||
|
||||
async request({
|
||||
args = {},
|
||||
file,
|
||||
init: incomingInit,
|
||||
json,
|
||||
method,
|
||||
path,
|
||||
}: {
|
||||
args?: OperationArgs
|
||||
file?: Blob
|
||||
init?: RequestInit
|
||||
json?: unknown
|
||||
method: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT'
|
||||
path: string
|
||||
}): Promise<Response> {
|
||||
const headers = new Headers({ ...this.baseInit.headers, ...incomingInit?.headers })
|
||||
|
||||
const init: RequestInit = {
|
||||
method,
|
||||
...this.baseInit,
|
||||
...incomingInit,
|
||||
headers,
|
||||
}
|
||||
|
||||
if (json) {
|
||||
if (file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
formData.append('_payload', JSON.stringify(json))
|
||||
init.body = formData
|
||||
} else {
|
||||
headers.set('Content-Type', 'application/json')
|
||||
init.body = JSON.stringify(json)
|
||||
}
|
||||
}
|
||||
|
||||
const response = await this.fetch(`${this.baseURL}${path}${buildSearchParams(args)}`, init)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
resetPassword<TSlug extends AuthCollectionSlug<T>>(
|
||||
options: ResetPasswordOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<ResetPasswordResult<T, TSlug>> {
|
||||
return resetPassword(this, options, init)
|
||||
}
|
||||
|
||||
restoreGlobalVersion<TSlug extends GlobalSlug<T>>(
|
||||
options: RestoreGlobalVersionByIDOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<TypeWithVersion<DataFromGlobalSlug<T, TSlug>>> {
|
||||
return restoreGlobalVersion(this, options, init)
|
||||
}
|
||||
|
||||
restoreVersion<TSlug extends CollectionSlug<T>>(
|
||||
options: RestoreVersionByIDOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<DataFromCollectionSlug<T, TSlug>> {
|
||||
return restoreVersion(this, options, init)
|
||||
}
|
||||
|
||||
update<TSlug extends CollectionSlug<T>, TSelect extends SelectFromCollectionSlug<T, TSlug>>(
|
||||
options: UpdateManyOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<BulkOperationResult<T, TSlug, TSelect>>
|
||||
|
||||
update<TSlug extends CollectionSlug<T>, TSelect extends SelectFromCollectionSlug<T, TSlug>>(
|
||||
options: UpdateByIDOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformCollectionWithSelect<T, TSlug, TSelect>>
|
||||
|
||||
/**
|
||||
* @description Update one or more documents
|
||||
* @param options
|
||||
* @returns Updated document(s)
|
||||
*/
|
||||
update<TSlug extends CollectionSlug<T>, TSelect extends SelectFromCollectionSlug<T, TSlug>>(
|
||||
options: UpdateOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<
|
||||
BulkOperationResult<T, TSlug, TSelect> | TransformCollectionWithSelect<T, TSlug, TSelect>
|
||||
> {
|
||||
return update(this, options, init)
|
||||
}
|
||||
|
||||
updateGlobal<TSlug extends GlobalSlug<T>, TSelect extends SelectFromGlobalSlug<T, TSlug>>(
|
||||
options: UpdateGlobalOptions<T, TSlug, TSelect>,
|
||||
init?: RequestInit,
|
||||
): Promise<TransformGlobalWithSelect<T, TSlug, TSelect>> {
|
||||
return updateGlobal(this, options, init)
|
||||
}
|
||||
|
||||
verifyEmail<TSlug extends AuthCollectionSlug<T>>(
|
||||
options: VerifyEmailOptions<T, TSlug>,
|
||||
init?: RequestInit,
|
||||
): Promise<{ message: string }> {
|
||||
return verifyEmail(this, options, init)
|
||||
}
|
||||
}
|
||||
18
packages/sdk/src/realtime/emitEvent.ts
Normal file
18
packages/sdk/src/realtime/emitEvent.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { PayloadGeneratedTypes } from '../types.js'
|
||||
|
||||
export const emitEvent = async <T extends PayloadGeneratedTypes>(
|
||||
sdk: PayloadSDK<T>,
|
||||
args: {
|
||||
data: Record<string, any>
|
||||
event: string
|
||||
},
|
||||
): Promise<Response> => {
|
||||
const response = await sdk.request({
|
||||
json: args,
|
||||
method: 'POST',
|
||||
path: '/realtime/emit',
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
65
packages/sdk/src/realtime/subscribe.ts
Normal file
65
packages/sdk/src/realtime/subscribe.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import type { PayloadSDK } from '../index.js'
|
||||
import type { PayloadGeneratedTypes } from '../types.js'
|
||||
|
||||
export const subscribe = async <T extends PayloadGeneratedTypes>(
|
||||
sdk: PayloadSDK<T>,
|
||||
onEvent: (args: { data: Record<string, any>; event: string }) => void,
|
||||
): Promise<{ response: Response; unsubscribe: () => Promise<void> }> => {
|
||||
const response = await sdk.request({
|
||||
method: 'GET',
|
||||
path: '/realtime/subscribe',
|
||||
})
|
||||
|
||||
if (
|
||||
!response.ok ||
|
||||
!response.headers.get('Content-Type')?.includes('text/event-stream') ||
|
||||
!response.body
|
||||
) {
|
||||
throw new Error('Expected SSE response, but got: ' + response.statusText)
|
||||
}
|
||||
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let done = false
|
||||
let buffer = ''
|
||||
|
||||
const unsubscribe = async () => {
|
||||
// Cancel the stream
|
||||
done = true
|
||||
await reader.cancel()
|
||||
}
|
||||
|
||||
const processStream = async () => {
|
||||
try {
|
||||
while (!done) {
|
||||
const { done: doneReading, value } = await reader.read()
|
||||
if (doneReading) {
|
||||
break
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value, { stream: true })
|
||||
buffer += chunk
|
||||
|
||||
let boundary = buffer.indexOf('\n\n')
|
||||
while (boundary !== -1) {
|
||||
const eventRaw = buffer.slice(0, boundary).trim()
|
||||
buffer = buffer.slice(boundary + 2)
|
||||
const data = JSON.parse(eventRaw.slice(5, boundary))
|
||||
onEvent(data)
|
||||
|
||||
boundary = buffer.indexOf('\n\n')
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (!done) {
|
||||
console.error('Error processing the stream:', err)
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
}
|
||||
|
||||
void processStream()
|
||||
|
||||
return { response, unsubscribe }
|
||||
}
|
||||
169
packages/sdk/src/types.ts
Normal file
169
packages/sdk/src/types.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import type {
|
||||
JsonObject,
|
||||
SelectType,
|
||||
StringKeyOf,
|
||||
TransformDataWithSelect,
|
||||
TypeWithID,
|
||||
Where,
|
||||
} from 'payload'
|
||||
import type { MarkOptional, NonNever } from 'ts-essentials'
|
||||
|
||||
export interface PayloadGeneratedTypes {
|
||||
auth: {
|
||||
[slug: string]: {
|
||||
forgotPassword: {
|
||||
email: string
|
||||
}
|
||||
login: {
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
registerFirstUser: {
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
unlock: {
|
||||
email: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collections: {
|
||||
[slug: string]: JsonObject & TypeWithID
|
||||
}
|
||||
collectionsJoins: {
|
||||
[slug: string]: {
|
||||
[schemaPath: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
collectionsSelect: {
|
||||
[slug: string]: any
|
||||
}
|
||||
db: {
|
||||
defaultIDType: number | string
|
||||
}
|
||||
globals: {
|
||||
[slug: string]: JsonObject
|
||||
}
|
||||
|
||||
globalsSelect: {
|
||||
[slug: string]: any
|
||||
}
|
||||
|
||||
locale: null | string
|
||||
}
|
||||
|
||||
export type TypedCollection<T extends PayloadGeneratedTypes> = T['collections']
|
||||
|
||||
export type TypedGlobal<T extends PayloadGeneratedTypes> = T['globals']
|
||||
|
||||
export type TypedCollectionSelect<T extends PayloadGeneratedTypes> = T['collectionsSelect']
|
||||
|
||||
export type TypedCollectionJoins<T extends PayloadGeneratedTypes> = T['collectionsJoins']
|
||||
|
||||
export type TypedGlobalSelect<T extends PayloadGeneratedTypes> = T['globalsSelect']
|
||||
|
||||
export type TypedAuth<T extends PayloadGeneratedTypes> = T['auth']
|
||||
|
||||
export type CollectionSlug<T extends PayloadGeneratedTypes> = StringKeyOf<TypedCollection<T>>
|
||||
|
||||
export type GlobalSlug<T extends PayloadGeneratedTypes> = StringKeyOf<TypedGlobal<T>>
|
||||
|
||||
export type AuthCollectionSlug<T extends PayloadGeneratedTypes> = StringKeyOf<TypedAuth<T>>
|
||||
|
||||
export type TypedUploadCollection<T extends PayloadGeneratedTypes> = NonNever<{
|
||||
[K in keyof TypedCollection<T>]:
|
||||
| 'filename'
|
||||
| 'filesize'
|
||||
| 'mimeType'
|
||||
| 'url' extends keyof TypedCollection<T>[K]
|
||||
? TypedCollection<T>[K]
|
||||
: never
|
||||
}>
|
||||
|
||||
export type UploadCollectionSlug<T extends PayloadGeneratedTypes> = StringKeyOf<
|
||||
TypedUploadCollection<T>
|
||||
>
|
||||
|
||||
export type DataFromCollectionSlug<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
> = TypedCollection<T>[TSlug]
|
||||
|
||||
export type DataFromGlobalSlug<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
> = TypedGlobal<T>[TSlug]
|
||||
|
||||
export type SelectFromCollectionSlug<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
> = TypedCollectionSelect<T>[TSlug]
|
||||
|
||||
export type SelectFromGlobalSlug<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
> = TypedGlobalSelect<T>[TSlug]
|
||||
|
||||
export type TransformCollectionWithSelect<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = TSelect extends SelectType
|
||||
? TransformDataWithSelect<DataFromCollectionSlug<T, TSlug>, TSelect>
|
||||
: DataFromCollectionSlug<T, TSlug>
|
||||
|
||||
export type TransformGlobalWithSelect<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends GlobalSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = TSelect extends SelectType
|
||||
? TransformDataWithSelect<DataFromGlobalSlug<T, TSlug>, TSelect>
|
||||
: DataFromGlobalSlug<T, TSlug>
|
||||
|
||||
export type RequiredDataFromCollection<TData extends JsonObject> = MarkOptional<
|
||||
TData,
|
||||
'createdAt' | 'id' | 'sizes' | 'updatedAt'
|
||||
>
|
||||
|
||||
export type RequiredDataFromCollectionSlug<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
> = RequiredDataFromCollection<DataFromCollectionSlug<T, TSlug>>
|
||||
|
||||
export type TypedLocale<T extends PayloadGeneratedTypes> = NonNullable<T['locale']>
|
||||
|
||||
export type JoinQuery<T extends PayloadGeneratedTypes, TSlug extends CollectionSlug<T>> =
|
||||
TypedCollectionJoins<T>[TSlug] extends Record<string, string>
|
||||
?
|
||||
| false
|
||||
| Partial<{
|
||||
[K in keyof TypedCollectionJoins<T>[TSlug]]:
|
||||
| {
|
||||
limit?: number
|
||||
sort?: string
|
||||
where?: Where
|
||||
}
|
||||
| false
|
||||
}>
|
||||
: never
|
||||
|
||||
export type PopulateType<T extends PayloadGeneratedTypes> = Partial<TypedCollectionSelect<T>>
|
||||
|
||||
export type IDType<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
> = DataFromCollectionSlug<T, TSlug>['id']
|
||||
|
||||
export type BulkOperationResult<
|
||||
T extends PayloadGeneratedTypes,
|
||||
TSlug extends CollectionSlug<T>,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
docs: TransformCollectionWithSelect<T, TSlug, TSelect>[]
|
||||
errors: {
|
||||
id: DataFromCollectionSlug<T, TSlug>['id']
|
||||
message: string
|
||||
}[]
|
||||
}
|
||||
72
packages/sdk/src/utilities/buildSearchParams.ts
Normal file
72
packages/sdk/src/utilities/buildSearchParams.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { SelectType, Sort, Where } from 'payload'
|
||||
|
||||
import { stringify } from 'qs-esm'
|
||||
|
||||
export type OperationArgs = {
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
fallbackLocale?: false | string
|
||||
joins?: false | Record<string, unknown>
|
||||
limit?: number
|
||||
locale?: string
|
||||
page?: number
|
||||
populate?: Record<string, unknown>
|
||||
select?: SelectType
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export const buildSearchParams = (args: OperationArgs): string => {
|
||||
const search: Record<string, unknown> = {}
|
||||
|
||||
if (typeof args.depth === 'number') {
|
||||
search.depth = String(args.depth)
|
||||
}
|
||||
|
||||
if (typeof args.page === 'number') {
|
||||
search.page = String(args.page)
|
||||
}
|
||||
|
||||
if (typeof args.limit === 'number') {
|
||||
search.limit = String(args.limit)
|
||||
}
|
||||
|
||||
if (typeof args.draft === 'boolean') {
|
||||
search.draft = String(args.draft)
|
||||
}
|
||||
|
||||
if (args.fallbackLocale) {
|
||||
search['fallback-locale'] = String(args.fallbackLocale)
|
||||
}
|
||||
|
||||
if (args.locale) {
|
||||
search.locale = args.locale
|
||||
}
|
||||
|
||||
if (args.sort) {
|
||||
const sanitizedSort = Array.isArray(args.sort) ? args.sort.join(',') : args.sort
|
||||
search.sort = sanitizedSort
|
||||
}
|
||||
|
||||
if (args.select) {
|
||||
search.select = args.select
|
||||
}
|
||||
|
||||
if (args.where) {
|
||||
search.where = args.where
|
||||
}
|
||||
|
||||
if (args.populate) {
|
||||
search.populate = args.populate
|
||||
}
|
||||
|
||||
if (args.joins) {
|
||||
search.joins = args.joins
|
||||
}
|
||||
|
||||
if (Object.keys(search).length > 0) {
|
||||
return stringify(search, { addQueryPrefix: true })
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
11
packages/sdk/src/utilities/resolveFileFromOptions.ts
Normal file
11
packages/sdk/src/utilities/resolveFileFromOptions.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const resolveFileFromOptions = async (file: Blob | string) => {
|
||||
if (typeof file === 'string') {
|
||||
const response = await fetch(file)
|
||||
const fileName = file.split('/').pop() ?? ''
|
||||
const blob = await response.blob()
|
||||
|
||||
return new File([blob], fileName, { type: blob.type })
|
||||
} else {
|
||||
return file
|
||||
}
|
||||
}
|
||||
25
packages/sdk/tsconfig.json
Normal file
25
packages/sdk/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false /* Do not emit outputs. */,
|
||||
"emitDeclarationOnly": true,
|
||||
"strict": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
"eslint.config.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }]
|
||||
}
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -1372,6 +1372,22 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../payload
|
||||
|
||||
packages/sdk:
|
||||
dependencies:
|
||||
payload:
|
||||
specifier: workspace:*
|
||||
version: link:../payload
|
||||
qs-esm:
|
||||
specifier: 7.0.2
|
||||
version: 7.0.2
|
||||
ts-essentials:
|
||||
specifier: 10.0.3
|
||||
version: 10.0.3(typescript@5.7.2)
|
||||
devDependencies:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../eslint-config
|
||||
|
||||
packages/storage-azure:
|
||||
dependencies:
|
||||
'@azure/abort-controller':
|
||||
@@ -1681,6 +1697,9 @@ importers:
|
||||
'@payloadcms/richtext-slate':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/richtext-slate
|
||||
'@payloadcms/sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/sdk
|
||||
'@payloadcms/storage-azure':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/storage-azure
|
||||
|
||||
@@ -28,6 +28,9 @@ export const packagePublishList = [
|
||||
'email-nodemailer',
|
||||
'email-resend',
|
||||
|
||||
// SDK,
|
||||
'sdk',
|
||||
|
||||
// Storage
|
||||
'storage-s3',
|
||||
'storage-azure',
|
||||
|
||||
105
test/_community/RealtimeUsersProvider.tsx
Normal file
105
test/_community/RealtimeUsersProvider.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
'use client'
|
||||
|
||||
import { PayloadSDK } from '@payloadcms/sdk'
|
||||
import { useAuth } from '@payloadcms/ui'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
const sdk = new PayloadSDK({
|
||||
baseInit: { credentials: 'include' },
|
||||
baseURL: 'http://localhost:3000/api',
|
||||
})
|
||||
|
||||
export const RealtimeUsersProvider = ({ children }) => {
|
||||
const { user } = useAuth()
|
||||
const [userPositions, setUserPositions] = useState<{ email: string; x: number; y: number }[]>([])
|
||||
const debounceTimeout = useRef<null | number>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let unsubscribe: (() => Promise<void>) | undefined = undefined
|
||||
|
||||
// Emit user connection event
|
||||
void sdk.realtime.emitEvent({
|
||||
data: user,
|
||||
event: 'userConnected',
|
||||
})
|
||||
|
||||
const subscribe = async () => {
|
||||
if (!user) {
|
||||
return
|
||||
}
|
||||
|
||||
const result = await sdk.realtime.subscribe(({ data, event }) => {
|
||||
if (event === 'userMouseMove' && data.email !== user.email) {
|
||||
setUserPositions((prev) => {
|
||||
const updated = [...prev.filter((pos) => pos.email !== data.email), data]
|
||||
return updated
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
unsubscribe = result.unsubscribe
|
||||
}
|
||||
|
||||
void subscribe()
|
||||
|
||||
return () => {
|
||||
if (unsubscribe) {
|
||||
void unsubscribe()
|
||||
}
|
||||
}
|
||||
}, [user])
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!user) {
|
||||
return
|
||||
}
|
||||
|
||||
if (debounceTimeout.current) {
|
||||
clearTimeout(debounceTimeout.current)
|
||||
}
|
||||
|
||||
debounceTimeout.current = window.setTimeout(() => {
|
||||
sdk.realtime.emitEvent({
|
||||
data: { email: user.email, x: e.clientX, y: e.clientY },
|
||||
event: 'userMouseMove',
|
||||
})
|
||||
}, 10) // Adjust debounce delay as needed
|
||||
}
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove)
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMouseMove)
|
||||
if (debounceTimeout.current) {
|
||||
clearTimeout(debounceTimeout.current)
|
||||
}
|
||||
}
|
||||
}, [user])
|
||||
|
||||
return (
|
||||
<>
|
||||
{userPositions.map(({ email, x, y }) => (
|
||||
<div
|
||||
key={email}
|
||||
style={{
|
||||
backgroundColor: 'rgba(0, 150, 255, 0.8)',
|
||||
borderRadius: '8px',
|
||||
color: '#fff',
|
||||
left: x,
|
||||
padding: '5px 10px',
|
||||
pointerEvents: 'none', // Prevent interfering with user interactions
|
||||
position: 'fixed', // Changed to fixed
|
||||
top: y,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
whiteSpace: 'nowrap',
|
||||
zIndex: 1000, // Ensure it's above other elements
|
||||
}}
|
||||
>
|
||||
{email}
|
||||
</div>
|
||||
))}
|
||||
<pre>{JSON.stringify(userPositions, null, 2)}</pre> {/* Debug rendered positions */}
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -11,36 +11,43 @@ import { MenuGlobal } from './globals/Menu/index.js'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
// ...extend config here
|
||||
collections: [PostsCollection, MediaCollection],
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
export default buildConfigWithDefaults(
|
||||
{
|
||||
// ...extend config here
|
||||
collections: [PostsCollection, MediaCollection],
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
components: {
|
||||
providers: [`/RealtimeUsersProvider.tsx#RealtimeUsersProvider`],
|
||||
},
|
||||
},
|
||||
editor: lexicalEditor({}),
|
||||
globals: [
|
||||
// ...add more globals here
|
||||
MenuGlobal,
|
||||
],
|
||||
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
},
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
title: 'example post',
|
||||
},
|
||||
})
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
},
|
||||
editor: lexicalEditor({}),
|
||||
globals: [
|
||||
// ...add more globals here
|
||||
MenuGlobal,
|
||||
],
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
},
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
title: 'example post',
|
||||
},
|
||||
})
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
{ disableAutoLogin: true },
|
||||
)
|
||||
|
||||
43
test/helpers/getSDK.ts
Normal file
43
test/helpers/getSDK.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { GeneratedTypes, SanitizedConfig } from 'payload'
|
||||
|
||||
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes'
|
||||
import { PayloadSDK } from '@payloadcms/sdk'
|
||||
|
||||
export type TypedPayloadSDK = PayloadSDK<GeneratedTypes>
|
||||
|
||||
/**
|
||||
* SDK with a custom fetch to run the routes directly without an HTTP server.
|
||||
*/
|
||||
export const getSDK = (config: SanitizedConfig) => {
|
||||
const api = {
|
||||
GET: REST_GET(config),
|
||||
POST: REST_POST(config),
|
||||
PATCH: REST_PATCH(config),
|
||||
DELETE: REST_DELETE(config),
|
||||
PUT: REST_PUT(config),
|
||||
}
|
||||
|
||||
return new PayloadSDK<GeneratedTypes>({
|
||||
baseURL: ``,
|
||||
fetch: (path: string, init: RequestInit) => {
|
||||
const [slugs, search] = path.slice(1).split('?')
|
||||
const url = `${config.serverURL || 'http://localhost:3000'}${config.routes.api}/${slugs}${search ? `?${search}` : ''}`
|
||||
|
||||
if (init.body instanceof FormData) {
|
||||
const file = init.body.get('file') as Blob
|
||||
if (file && init.headers instanceof Headers) {
|
||||
init.headers.set('Content-Length', file.size.toString())
|
||||
}
|
||||
}
|
||||
const request = new Request(url, init)
|
||||
|
||||
const params = {
|
||||
params: Promise.resolve({
|
||||
slug: slugs.split('/'),
|
||||
}),
|
||||
}
|
||||
|
||||
return api[init.method.toUpperCase()](request, params)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { Payload, SanitizedConfig } from 'payload'
|
||||
import type { PayloadSDK } from '@payloadcms/sdk'
|
||||
import type { GeneratedTypes, Payload, SanitizedConfig } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { getPayload } from 'payload'
|
||||
|
||||
import { runInit } from '../runInit.js'
|
||||
import { getSDK } from './getSDK.js'
|
||||
import { NextRESTClient } from './NextRESTClient.js'
|
||||
|
||||
/**
|
||||
@@ -13,7 +15,12 @@ export async function initPayloadInt(
|
||||
dirname: string,
|
||||
testSuiteNameOverride?: string,
|
||||
initializePayload = true,
|
||||
): Promise<{ config: SanitizedConfig; payload?: Payload; restClient?: NextRESTClient }> {
|
||||
): Promise<{
|
||||
config: SanitizedConfig
|
||||
payload?: Payload
|
||||
restClient?: NextRESTClient
|
||||
sdk?: PayloadSDK<GeneratedTypes>
|
||||
}> {
|
||||
const testSuiteName = testSuiteNameOverride ?? path.basename(dirname)
|
||||
await runInit(testSuiteName, false, true)
|
||||
console.log('importing config', path.resolve(dirname, 'config.ts'))
|
||||
@@ -29,5 +36,8 @@ export async function initPayloadInt(
|
||||
console.log('initializing rest client')
|
||||
const restClient = new NextRESTClient(payload.config)
|
||||
console.log('initPayloadInt done')
|
||||
return { config: payload.config, payload, restClient }
|
||||
|
||||
const sdk = getSDK(payload.config)
|
||||
|
||||
return { config: payload.config, payload, restClient, sdk }
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"@payloadcms/plugin-stripe": "workspace:*",
|
||||
"@payloadcms/richtext-lexical": "workspace:*",
|
||||
"@payloadcms/richtext-slate": "workspace:*",
|
||||
"@payloadcms/sdk": "workspace:*",
|
||||
"@payloadcms/storage-azure": "workspace:*",
|
||||
"@payloadcms/storage-gcs": "workspace:*",
|
||||
"@payloadcms/storage-s3": "workspace:*",
|
||||
|
||||
1
test/sdk/.gitignore
vendored
Normal file
1
test/sdk/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
media
|
||||
40
test/sdk/collections/Posts.ts
Normal file
40
test/sdk/collections/Posts.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const postsSlug = 'posts'
|
||||
|
||||
export const PostsCollection: CollectionConfig = {
|
||||
slug: postsSlug,
|
||||
admin: {
|
||||
useAsTitle: 'text',
|
||||
},
|
||||
access: { create: () => true, update: () => true, delete: () => true, read: () => true },
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'number2',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
}
|
||||
14
test/sdk/collections/Users.ts
Normal file
14
test/sdk/collections/Users.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {},
|
||||
fields: [
|
||||
// Email added by default
|
||||
// Add more fields as needed
|
||||
],
|
||||
}
|
||||
51
test/sdk/config.ts
Normal file
51
test/sdk/config.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
import { PostsCollection } from './collections/Posts.js'
|
||||
import { Users } from './collections/Users.js'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
Users,
|
||||
PostsCollection,
|
||||
{
|
||||
access: { create: () => true, read: () => true, update: () => true },
|
||||
slug: 'media',
|
||||
upload: { staticDir: path.resolve(dirname, './media') },
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: 'global',
|
||||
fields: [{ type: 'text', name: 'text' }],
|
||||
versions: true,
|
||||
},
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
locales: ['en', 'es', 'de'],
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
},
|
||||
})
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
19
test/sdk/eslint.config.js
Normal file
19
test/sdk/eslint.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { rootParserOptions } from '../../eslint.config.js'
|
||||
import testEslintConfig from '../eslint.config.js'
|
||||
|
||||
/** @typedef {import('eslint').Linter.Config} Config */
|
||||
|
||||
/** @type {Config[]} */
|
||||
export const index = [
|
||||
...testEslintConfig,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
...rootParserOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default index
|
||||
BIN
test/sdk/image.jpg
Normal file
BIN
test/sdk/image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
310
test/sdk/int.spec.ts
Normal file
310
test/sdk/int.spec.ts
Normal file
@@ -0,0 +1,310 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import { randomUUID } from 'crypto'
|
||||
import { RESTClient } from 'helpers/rest.js'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { TypedPayloadSDK } from '../helpers/getSDK.js'
|
||||
import type { Post } from './payload-types.js'
|
||||
|
||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||
import { createStreamableFile } from '../uploads/createStreamableFile.js'
|
||||
|
||||
let payload: Payload
|
||||
let post: Post
|
||||
let sdk: TypedPayloadSDK
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const testUserCredentials = {
|
||||
email: 'test@payloadcms.com',
|
||||
password: '123456',
|
||||
}
|
||||
|
||||
describe('@payloadcms/sdk', () => {
|
||||
beforeAll(async () => {
|
||||
;({ payload, sdk } = await initPayloadInt(dirname))
|
||||
|
||||
post = await payload.create({ collection: 'posts', data: { number: 1, number2: 3 } })
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: { ...testUserCredentials },
|
||||
})
|
||||
await payload.updateGlobal({ slug: 'global', data: { text: 'some-global' } })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
if (typeof payload.db.destroy === 'function') {
|
||||
await payload.db.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should execute find', async () => {
|
||||
const result = await sdk.find({ collection: 'posts', where: { id: { equals: post.id } } })
|
||||
|
||||
expect(result.docs[0].id).toBe(post.id)
|
||||
})
|
||||
|
||||
it('should execute findVersions', async () => {
|
||||
const result = await sdk.findVersions({
|
||||
collection: 'posts',
|
||||
where: { parent: { equals: post.id } },
|
||||
})
|
||||
|
||||
expect(result.docs[0].parent).toBe(post.id)
|
||||
})
|
||||
|
||||
it('should execute findByID', async () => {
|
||||
const result = await sdk.findByID({ collection: 'posts', id: post.id })
|
||||
|
||||
expect(result.id).toBe(post.id)
|
||||
})
|
||||
|
||||
it('should execute findByID with disableErrors: true', async () => {
|
||||
const result = await sdk.findByID({
|
||||
disableErrors: true,
|
||||
collection: 'posts',
|
||||
// eslint-disable-next-line jest/no-conditional-in-test
|
||||
id: typeof post.id === 'string' ? randomUUID() : 999,
|
||||
})
|
||||
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it('should execute findVersionByID', async () => {
|
||||
const {
|
||||
docs: [version],
|
||||
} = await payload.findVersions({ collection: 'posts', where: { parent: { equals: post.id } } })
|
||||
|
||||
const result = await sdk.findVersionByID({ collection: 'posts', id: version.id })
|
||||
|
||||
expect(result.id).toBe(version.id)
|
||||
})
|
||||
|
||||
it('should execute create', async () => {
|
||||
const result = await sdk.create({ collection: 'posts', data: { text: 'text' } })
|
||||
|
||||
expect(result.text).toBe('text')
|
||||
})
|
||||
|
||||
it('should execute create with file', async () => {
|
||||
const filePath = path.join(dirname, './image.jpg')
|
||||
const { file, handle } = await createStreamableFile(filePath)
|
||||
const res = await sdk.create({ collection: 'media', file, data: {} })
|
||||
expect(res.id).toBeTruthy()
|
||||
await handle.close()
|
||||
})
|
||||
|
||||
it('should execute count', async () => {
|
||||
const result = await sdk.count({ collection: 'posts', where: { id: { equals: post.id } } })
|
||||
|
||||
expect(result.totalDocs).toBe(1)
|
||||
})
|
||||
|
||||
it('should execute update (by ID)', async () => {
|
||||
const result = await sdk.update({
|
||||
collection: 'posts',
|
||||
id: post.id,
|
||||
data: { text: 'updated-text' },
|
||||
})
|
||||
|
||||
expect(result.text).toBe('updated-text')
|
||||
})
|
||||
|
||||
it('should execute update (bulk)', async () => {
|
||||
const result = await sdk.update({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
id: {
|
||||
equals: post.id,
|
||||
},
|
||||
},
|
||||
data: { text: 'updated-text-bulk' },
|
||||
})
|
||||
|
||||
expect(result.docs[0].text).toBe('updated-text-bulk')
|
||||
})
|
||||
|
||||
it('should execute delete (by ID)', async () => {
|
||||
const post = await payload.create({ collection: 'posts', data: {} })
|
||||
|
||||
const result = await sdk.delete({ id: post.id, collection: 'posts' })
|
||||
|
||||
expect(result.id).toBe(post.id)
|
||||
|
||||
const resultLocal = await payload.findByID({
|
||||
collection: 'posts',
|
||||
id: post.id,
|
||||
disableErrors: true,
|
||||
})
|
||||
|
||||
expect(resultLocal).toBeNull()
|
||||
})
|
||||
|
||||
it('should execute delete (bulk)', async () => {
|
||||
const post = await payload.create({ collection: 'posts', data: {} })
|
||||
|
||||
const result = await sdk.delete({ where: { id: { equals: post.id } }, collection: 'posts' })
|
||||
|
||||
expect(result.docs[0].id).toBe(post.id)
|
||||
|
||||
const resultLocal = await payload.findByID({
|
||||
collection: 'posts',
|
||||
id: post.id,
|
||||
disableErrors: true,
|
||||
})
|
||||
|
||||
expect(resultLocal).toBeNull()
|
||||
})
|
||||
|
||||
it('should execute restoreVersion', async () => {
|
||||
const post = await payload.create({ collection: 'posts', data: { text: 'old' } })
|
||||
|
||||
const {
|
||||
docs: [currentVersion],
|
||||
} = await payload.findVersions({ collection: 'posts', where: { parent: { equals: post.id } } })
|
||||
|
||||
await payload.update({ collection: 'posts', id: post.id, data: { text: 'new' } })
|
||||
|
||||
const result = await sdk.restoreVersion({
|
||||
collection: 'posts',
|
||||
id: currentVersion.id,
|
||||
})
|
||||
|
||||
expect(result.text).toBe('old')
|
||||
|
||||
const resultDB = await payload.findByID({ collection: 'posts', id: post.id })
|
||||
|
||||
expect(resultDB.text).toBe('old')
|
||||
})
|
||||
|
||||
it('should execute findGlobal', async () => {
|
||||
const result = await sdk.findGlobal({ slug: 'global' })
|
||||
expect(result.text).toBe('some-global')
|
||||
})
|
||||
|
||||
it('should execute findGlobalVersions', async () => {
|
||||
const result = await sdk.findGlobalVersions({
|
||||
slug: 'global',
|
||||
})
|
||||
|
||||
expect(result.docs[0].version).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should execute findGlobalVersionByID', async () => {
|
||||
const {
|
||||
docs: [version],
|
||||
} = await payload.findGlobalVersions({
|
||||
slug: 'global',
|
||||
})
|
||||
|
||||
const result = await sdk.findGlobalVersionByID({ id: version.id, slug: 'global' })
|
||||
|
||||
expect(result.id).toBe(version.id)
|
||||
})
|
||||
|
||||
it('should execute updateGlobal', async () => {
|
||||
const result = await sdk.updateGlobal({ slug: 'global', data: { text: 'some-updated-global' } })
|
||||
expect(result.text).toBe('some-updated-global')
|
||||
})
|
||||
|
||||
it('should execute restoreGlobalVersion', async () => {
|
||||
await payload.updateGlobal({ slug: 'global', data: { text: 'old' } })
|
||||
|
||||
const {
|
||||
docs: [currentVersion],
|
||||
} = await payload.findGlobalVersions({
|
||||
slug: 'global',
|
||||
})
|
||||
|
||||
await payload.updateGlobal({ slug: 'global', data: { text: 'new' } })
|
||||
|
||||
const { version: result } = await sdk.restoreGlobalVersion({
|
||||
slug: 'global',
|
||||
id: currentVersion.id,
|
||||
})
|
||||
|
||||
expect(result.text).toBe('old')
|
||||
|
||||
const resultDB = await payload.findGlobal({ slug: 'global' })
|
||||
|
||||
expect(resultDB.text).toBe('old')
|
||||
})
|
||||
|
||||
it('should execute login', async () => {
|
||||
const res = await sdk.login({
|
||||
collection: 'users',
|
||||
data: { email: testUserCredentials.email, password: testUserCredentials.password },
|
||||
})
|
||||
|
||||
expect(res.user.email).toBe(testUserCredentials.email)
|
||||
})
|
||||
|
||||
it('should execute me', async () => {
|
||||
const { token } = await sdk.login({
|
||||
collection: 'users',
|
||||
data: { email: testUserCredentials.email, password: testUserCredentials.password },
|
||||
})
|
||||
|
||||
const res = await sdk.me(
|
||||
{ collection: 'users' },
|
||||
{ headers: { Authorization: `JWT ${token}` } },
|
||||
)
|
||||
|
||||
expect(res.user.email).toBe(testUserCredentials.email)
|
||||
})
|
||||
|
||||
it('should execute refreshToken', async () => {
|
||||
const { token } = await sdk.login({
|
||||
collection: 'users',
|
||||
data: { email: testUserCredentials.email, password: testUserCredentials.password },
|
||||
})
|
||||
|
||||
const res = await sdk.refreshToken(
|
||||
{ collection: 'users' },
|
||||
{ headers: { Authorization: `JWT ${token}` } },
|
||||
)
|
||||
|
||||
expect(res.user.email).toBe(testUserCredentials.email)
|
||||
})
|
||||
|
||||
it('should execute forgotPassword and resetPassword', async () => {
|
||||
const user = await payload.create({
|
||||
collection: 'users',
|
||||
data: { email: 'new@payloadcms.com', password: 'HOW TO rEmeMber this password' },
|
||||
})
|
||||
|
||||
const resForgotPassword = await sdk.forgotPassword({
|
||||
collection: 'users',
|
||||
data: { email: user.email },
|
||||
})
|
||||
|
||||
expect(resForgotPassword.message).toBeTruthy()
|
||||
|
||||
const afterForgotPassword = await payload.findByID({
|
||||
showHiddenFields: true,
|
||||
collection: 'users',
|
||||
id: user.id,
|
||||
})
|
||||
|
||||
expect(afterForgotPassword.resetPasswordToken).toBeTruthy()
|
||||
|
||||
const verifyEmailResult = await sdk.resetPassword({
|
||||
collection: 'users',
|
||||
data: { password: '1234567', token: afterForgotPassword.resetPasswordToken },
|
||||
})
|
||||
|
||||
expect(verifyEmailResult.user.email).toBe(user.email)
|
||||
|
||||
const {
|
||||
user: { email },
|
||||
} = await sdk.login({
|
||||
collection: 'users',
|
||||
data: { email: user.email, password: '1234567' },
|
||||
})
|
||||
|
||||
expect(email).toBe(user.email)
|
||||
})
|
||||
})
|
||||
291
test/sdk/payload-types.ts
Normal file
291
test/sdk/payload-types.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
collections: {
|
||||
users: User;
|
||||
posts: Post;
|
||||
media: Media;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
collectionsJoins: {};
|
||||
collectionsSelect: {
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
media: MediaSelect<false> | MediaSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
global: Global;
|
||||
};
|
||||
globalsSelect: {
|
||||
global: GlobalSelect<false> | GlobalSelect<true>;
|
||||
};
|
||||
locale: 'en' | 'es' | 'de';
|
||||
user: User & {
|
||||
collection: 'users';
|
||||
};
|
||||
jobs: {
|
||||
tasks: unknown;
|
||||
workflows: unknown;
|
||||
};
|
||||
}
|
||||
export interface UserAuthOperations {
|
||||
forgotPassword: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
login: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: string;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
number2?: number | null;
|
||||
group?: {
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "media".
|
||||
*/
|
||||
export interface Media {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
url?: string | null;
|
||||
thumbnailURL?: string | null;
|
||||
filename?: string | null;
|
||||
mimeType?: string | null;
|
||||
filesize?: number | null;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
focalX?: number | null;
|
||||
focalY?: number | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'media';
|
||||
value: string | Media;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: string;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users_select".
|
||||
*/
|
||||
export interface UsersSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
email?: T;
|
||||
resetPasswordToken?: T;
|
||||
resetPasswordExpiration?: T;
|
||||
salt?: T;
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
number?: T;
|
||||
number2?: T;
|
||||
group?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
number?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "media_select".
|
||||
*/
|
||||
export interface MediaSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
url?: T;
|
||||
thumbnailURL?: T;
|
||||
filename?: T;
|
||||
mimeType?: T;
|
||||
filesize?: T;
|
||||
width?: T;
|
||||
height?: T;
|
||||
focalX?: T;
|
||||
focalY?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents_select".
|
||||
*/
|
||||
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
|
||||
document?: T;
|
||||
globalSlug?: T;
|
||||
user?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences_select".
|
||||
*/
|
||||
export interface PayloadPreferencesSelect<T extends boolean = true> {
|
||||
user?: T;
|
||||
key?: T;
|
||||
value?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations_select".
|
||||
*/
|
||||
export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
batch?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global".
|
||||
*/
|
||||
export interface Global {
|
||||
id: string;
|
||||
text?: string | null;
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global_select".
|
||||
*/
|
||||
export interface GlobalSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "auth".
|
||||
*/
|
||||
export interface Auth {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
declare module 'payload' {
|
||||
// @ts-ignore
|
||||
export interface GeneratedTypes extends Config {}
|
||||
}
|
||||
1
test/sdk/shared.ts
Normal file
1
test/sdk/shared.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const pagesSlug = 'pages'
|
||||
13
test/sdk/tsconfig.eslint.json
Normal file
13
test/sdk/tsconfig.eslint.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// extend your base config to share compilerOptions, etc
|
||||
//"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// ensure that nobody can accidentally use this config for a build
|
||||
"noEmit": true
|
||||
},
|
||||
"include": [
|
||||
// whatever paths you intend to lint
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx"
|
||||
]
|
||||
}
|
||||
3
test/sdk/tsconfig.json
Normal file
3
test/sdk/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../tsconfig.json"
|
||||
}
|
||||
@@ -31,6 +31,7 @@ export const tgzToPkgNameMap = {
|
||||
'@payloadcms/plugin-stripe': 'payloadcms-plugin-stripe-*',
|
||||
'@payloadcms/richtext-lexical': 'payloadcms-richtext-lexical-*',
|
||||
'@payloadcms/richtext-slate': 'payloadcms-richtext-slate-*',
|
||||
'@payloadcms/sdk': 'payloadcms-sdk-*',
|
||||
'@payloadcms/storage-azure': 'payloadcms-storage-azure-*',
|
||||
'@payloadcms/storage-gcs': 'payloadcms-storage-gcs-*',
|
||||
'@payloadcms/storage-s3': 'payloadcms-storage-s3-*',
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
],
|
||||
"paths": {
|
||||
"@payload-config": [
|
||||
"./test/live-preview/config.ts"
|
||||
"./test/_community/config.ts"
|
||||
],
|
||||
"@payloadcms/live-preview": [
|
||||
"./packages/live-preview/src"
|
||||
|
||||
Reference in New Issue
Block a user