feat: add Payload SDK package (#9463)

Adds Payload SDK package, which can be used to query Payload REST API in
a fully type safe way. Has support for all necessary operations,
including auth, type safe `select`, `populate`, `joins` properties and
simplified file uploading.

Its interface is _very_ similar to the Local API, can't even notice the
difference:
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
  },
})
```
This commit is contained in:
Sasha
2025-09-30 00:01:01 +03:00
committed by GitHub
parent 99043eeaeb
commit 92a5f075b6
50 changed files with 3253 additions and 3 deletions

43
test/helpers/getSDK.ts Normal file
View 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)
},
})
}

View File

@@ -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'
/**
@@ -17,7 +19,12 @@ export async function initPayloadInt<TInitializePayload extends boolean | undefi
): Promise<
TInitializePayload extends false
? { config: SanitizedConfig }
: { config: SanitizedConfig; payload: Payload; restClient: NextRESTClient }
: {
config: SanitizedConfig
payload: Payload
restClient: NextRESTClient
sdk: PayloadSDK<GeneratedTypes>
}
> {
const testSuiteName = testSuiteNameOverride ?? path.basename(dirname)
await runInit(testSuiteName, false, true, configFile)
@@ -34,5 +41,6 @@ export async function initPayloadInt<TInitializePayload extends boolean | undefi
console.log('initializing rest client')
const restClient = new NextRESTClient(payload.config)
console.log('initPayloadInt done')
return { config: payload.config, payload, restClient } as any
const sdk = getSDK(payload.config)
return { config: payload.config, sdk, payload, restClient } as any
}

View File

@@ -56,6 +56,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
View File

@@ -0,0 +1 @@
media

View 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,
}

View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

309
test/sdk/int.spec.ts Normal file
View File

@@ -0,0 +1,309 @@
import type { Payload } from 'payload'
import { randomUUID } from 'crypto'
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
View 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
View File

@@ -0,0 +1 @@
export const pagesSlug = 'pages'

View 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
View File

@@ -0,0 +1,3 @@
{
"extends": "../tsconfig.json"
}

View File

@@ -35,6 +35,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-*',