Compare commits

...

9 Commits

84 changed files with 1782 additions and 167 deletions

View File

@@ -125,11 +125,12 @@ export default buildConfig({
The following options are available:
| Option | Description |
| --------------- | --------------------- |
| **`autoGenerate`** | By default, Payload will auto-generate TypeScript interfaces for all collections and globals that your config defines. Opt out by setting `typescript.autoGenerate: false`. [More details](../typescript/overview). |
| **`declare`** | By default, Payload adds a `declare` block to your generated types, which makes sure that Payload uses your generated types for all Local API methods. Opt out by setting `typescript.declare: false`. |
| **`outputFile`** | Control the output path and filename of Payload's auto-generated types by defining the `typescript.outputFile` property to a full, absolute path. |
| Option | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`autoGenerate`** | By default, Payload will auto-generate TypeScript interfaces for all collections and globals that your config defines. Opt out by setting `typescript.autoGenerate: false`. [More details](../typescript/overview). |
| **`declare`** | By default, Payload adds a `declare` block to your generated types, which makes sure that Payload uses your generated types for all Local API methods. Opt out by setting `typescript.declare: false`. |
| **`outputFile`** | Control the output path and filename of Payload's auto-generated types by defining the `typescript.outputFile` property to a full, absolute path. |
| **`typeSafeDepth`** | Enable better result types for relationships depending on `depth`, disabled by default. [More Details](../queries/depth#type-safe-relationship-types-with-depth). |
## Config Location

View File

@@ -94,3 +94,19 @@ To set a max depth for a field, use the `maxDepth` property in your field config
]
}
```
## Type safe relationship types with depth
The `typescript.typeSafeDepth` config option allows you to have better result types with relationships for your operations.
For example:
```ts
const post = await payload.findByID({
collection: 'posts',
id,
depth: 1
})
const category = post.category
```
You may notice that `post.category` is typed as `Category | string` (or `Category | number` with number IDs) which isn't quite right since we passed `depth: 1`.
With `typescript.typeSafeDepth: true` this type will be resolved as just `Category` and in case of `depth: 0` - `string`.

View File

@@ -1,22 +1,24 @@
import type {
AllowedDepth,
AuthOperationsFromCollectionSlug,
CollectionSlug,
DataFromCollectionSlug,
DefaultDepth,
Payload,
RequestContext,
} from '../../../index.js'
import type { PayloadRequest } from '../../../types/index.js'
import type { ApplyDepthInternal, PayloadRequest } from '../../../types/index.js'
import type { Result } from '../login.js'
import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { loginOperation } from '../login.js'
export type Options<TSlug extends CollectionSlug> = {
export type Options<TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth> = {
collection: TSlug
context?: RequestContext
data: AuthOperationsFromCollectionSlug<TSlug>['login']
depth?: number
depth?: TDepth
fallbackLocale?: string
locale?: string
overrideAccess?: boolean
@@ -24,10 +26,13 @@ export type Options<TSlug extends CollectionSlug> = {
showHiddenFields?: boolean
}
export async function localLogin<TSlug extends CollectionSlug>(
export async function localLogin<
TSlug extends CollectionSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<{ user: DataFromCollectionSlug<TSlug> } & Result> {
options: Options<TSlug, TDepth>,
): Promise<{ user: ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth> } & Result> {
const {
collection: collectionSlug,
data,

View File

@@ -39,7 +39,9 @@ import type {
UploadField,
} from '../../fields/config/types.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
JsonObject,
RequestContext,
TypedAuthOperations,
@@ -48,6 +50,7 @@ import type {
TypedLocale,
} from '../../index.js'
import type {
ApplyDepthInternal,
PayloadRequest,
SelectType,
Sort,
@@ -567,8 +570,12 @@ export type Collection = {
}
}
export type BulkOperationResult<TSlug extends CollectionSlug, TSelect extends SelectType> = {
docs: TransformCollectionWithSelect<TSlug, TSelect>[]
export type BulkOperationResult<
TSlug extends CollectionSlug,
TSelect extends SelectType = SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
docs: ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>[], TDepth>
errors: {
id: DataFromCollectionSlug<TSlug>['id']
message: string

View File

@@ -1,7 +1,7 @@
import httpStatus from 'http-status'
import type { AccessResult } from '../../config/types.js'
import type { CollectionSlug } from '../../index.js'
import type { AllowedDepth, CollectionSlug, DefaultDepth } from '../../index.js'
import type { PayloadRequest, PopulateType, SelectType, Where } from '../../types/index.js'
import type {
BeforeOperationHook,
@@ -42,9 +42,10 @@ export type Arguments = {
export const deleteOperation = async <
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
incomingArgs: Arguments,
): Promise<BulkOperationResult<TSlug, TSelect>> => {
): Promise<BulkOperationResult<TSlug, TSelect, TDepth>> => {
let args = incomingArgs
try {
@@ -304,7 +305,7 @@ export const deleteOperation = async <
await commitTransaction(req)
}
return result
return result as BulkOperationResult<TSlug, TSelect, TDepth>
} catch (error: unknown) {
await killTransaction(args.req)
throw error

View File

@@ -1,5 +1,13 @@
import type { CollectionSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -18,14 +26,18 @@ import { getFileByPath } from '../../../uploads/getFileByPath.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { createOperation } from '../create.js'
export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> = {
export type Options<
TSlug extends CollectionSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
data: RequiredDataFromCollectionSlug<TSlug>
depth?: number
depth?: TDepth
disableTransaction?: boolean
disableVerificationEmail?: boolean
draft?: boolean
@@ -47,10 +59,11 @@ export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> =
export default async function createLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> {
options: Options<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>> {
const {
collection: collectionSlug,
data,

View File

@@ -1,5 +1,13 @@
import type { CollectionSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -14,13 +22,17 @@ import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { deleteOperation } from '../delete.js'
import { deleteByIDOperation } from '../deleteByID.js'
export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType> = {
export type BaseOptions<
TSlug extends CollectionSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
depth?: number
depth?: TDepth
disableTransaction?: boolean
fallbackLocale?: false | TypedLocale
locale?: TypedLocale
@@ -36,52 +48,65 @@ export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType
export type ByIDOptions<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
> = {
id: number | string
where?: never
} & BaseOptions<TSlug, TSelect>
} & BaseOptions<TSlug, TSelect, TDepth>
export type ManyOptions<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
> = {
id?: never
where: Where
} & BaseOptions<TSlug, TSelect>
} & BaseOptions<TSlug, TSelect, TDepth>
export type Options<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
> = ByIDOptions<TSlug, TSelect> | ManyOptions<TSlug, TSelect>
TDepth extends AllowedDepth = DefaultDepth,
> = ByIDOptions<TSlug, TSelect, TDepth> | ManyOptions<TSlug, TSelect, TDepth>
async function deleteLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: ByIDOptions<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
options: ByIDOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>>
async function deleteLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: ManyOptions<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect>>
options: ManyOptions<TSlug, TSelect, TDepth>,
): Promise<BulkOperationResult<TSlug, TSelect, TDepth>>
async function deleteLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>>
options: Options<TSlug, TSelect, TDepth>,
): Promise<
| ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>
| BulkOperationResult<TSlug, TSelect, TDepth>
>
async function deleteLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
options: Options<TSlug, TSelect, TDepth>,
): Promise<
| ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>
| BulkOperationResult<TSlug, TSelect, TDepth>
> {
const {
id,
collection: collectionSlug,
@@ -120,7 +145,7 @@ async function deleteLocal<
if (options.id) {
return deleteByIDOperation<TSlug, TSelect>(args)
}
return deleteOperation<TSlug, TSelect>(args)
return deleteOperation<TSlug, TSelect, TDepth>(args)
}
export default deleteLocal

View File

@@ -1,8 +1,15 @@
import type { DeepPartial } from 'ts-essentials'
import type { CollectionSlug, TypedLocale } from '../../..//index.js'
import type { Payload, RequestContext } from '../../../index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -18,14 +25,18 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { duplicateOperation } from '../duplicate.js'
export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> = {
export type Options<
TSlug extends CollectionSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
data?: DeepPartial<RequiredDataFromCollectionSlug<TSlug>>
depth?: number
depth?: TDepth
disableTransaction?: boolean
draft?: boolean
fallbackLocale?: false | TypedLocale
@@ -42,10 +53,11 @@ export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> =
export async function duplicate<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> {
options: Options<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>> {
const {
id,
collection: collectionSlug,

View File

@@ -1,12 +1,15 @@
import type { PaginatedDocs } from '../../../database/types.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
JoinQuery,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -21,14 +24,18 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { findOperation } from '../find.js'
export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> = {
export type Options<
TSlug extends CollectionSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
currentDepth?: number
depth?: number
depth?: TDepth
disableErrors?: boolean
draft?: boolean
fallbackLocale?: false | TypedLocale
@@ -51,10 +58,13 @@ export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> =
export async function findLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<PaginatedDocs<TransformCollectionWithSelect<TSlug, TSelect>>> {
options: Options<TSlug, TSelect, TDepth>,
): Promise<
PaginatedDocs<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>>
> {
const {
collection: collectionSlug,
currentDepth,

View File

@@ -1,6 +1,8 @@
/* eslint-disable no-restricted-exports */
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
JoinQuery,
Payload,
RequestContext,
@@ -8,6 +10,7 @@ import type {
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
ApplyDisableErrors,
Document,
PayloadRequest,
@@ -24,6 +27,7 @@ export type Options<
TSlug extends CollectionSlug,
TDisableErrors extends boolean,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
collection: TSlug
/**
@@ -31,7 +35,7 @@ export type Options<
*/
context?: RequestContext
currentDepth?: number
depth?: number
depth?: TDepth
disableErrors?: TDisableErrors
draft?: boolean
fallbackLocale?: false | TypedLocale
@@ -51,10 +55,16 @@ export default async function findByIDLocal<
TSlug extends CollectionSlug,
TDisableErrors extends boolean,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TDisableErrors, TSelect>,
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<TSlug, TSelect>, TDisableErrors>> {
): Promise<
ApplyDisableErrors<
ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>,
TDisableErrors
>
> {
const {
id,
collection: collectionSlug,

View File

@@ -1,5 +1,18 @@
import type { CollectionSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type { Document, PayloadRequest, PopulateType, SelectType } from '../../../types/index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
SelectType,
} from '../../../types/index.js'
import type { TypeWithVersion } from '../../../versions/types.js'
import type { DataFromCollectionSlug } from '../../config/types.js'
@@ -7,13 +20,13 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { findVersionByIDOperation } from '../findVersionByID.js'
export type Options<TSlug extends CollectionSlug> = {
export type Options<TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
depth?: number
depth?: TDepth
disableErrors?: boolean
draft?: boolean
fallbackLocale?: false | TypedLocale
@@ -27,10 +40,13 @@ export type Options<TSlug extends CollectionSlug> = {
user?: Document
}
export default async function findVersionByIDLocal<TSlug extends CollectionSlug>(
export default async function findVersionByIDLocal<
TSlug extends CollectionSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<TypeWithVersion<DataFromCollectionSlug<TSlug>>> {
): Promise<TypeWithVersion<ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth>>> {
const {
id,
collection: collectionSlug,

View File

@@ -1,6 +1,14 @@
import type { PaginatedDocs } from '../../../database/types.js'
import type { CollectionSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -15,13 +23,13 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { findVersionsOperation } from '../findVersions.js'
export type Options<TSlug extends CollectionSlug> = {
export type Options<TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
depth?: number
depth?: TDepth
draft?: boolean
fallbackLocale?: false | TypedLocale
limit?: number
@@ -37,10 +45,15 @@ export type Options<TSlug extends CollectionSlug> = {
where?: Where
}
export default async function findVersionsLocal<TSlug extends CollectionSlug>(
export default async function findVersionsLocal<
TSlug extends CollectionSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<PaginatedDocs<TypeWithVersion<DataFromCollectionSlug<TSlug>>>> {
): Promise<
PaginatedDocs<TypeWithVersion<ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth>>>
> {
const {
collection: collectionSlug,
depth,

View File

@@ -1,18 +1,31 @@
import type { CollectionSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type { Document, PayloadRequest, PopulateType, SelectType } from '../../../types/index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
SelectType,
} from '../../../types/index.js'
import type { DataFromCollectionSlug } from '../../config/types.js'
import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { restoreVersionOperation } from '../restoreVersion.js'
export type Options<TSlug extends CollectionSlug> = {
export type Options<TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth> = {
collection: TSlug
/**
* context, which will then be passed to req.context, which can be read by hooks
*/
context?: RequestContext
depth?: number
depth?: TDepth
draft?: boolean
fallbackLocale?: false | TypedLocale
id: string
@@ -25,10 +38,13 @@ export type Options<TSlug extends CollectionSlug> = {
user?: Document
}
export default async function restoreVersionLocal<TSlug extends CollectionSlug>(
export default async function restoreVersionLocal<
TSlug extends CollectionSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<DataFromCollectionSlug<TSlug>> {
): Promise<ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth>> {
const {
id,
collection: collectionSlug,

View File

@@ -1,7 +1,15 @@
import type { DeepPartial } from 'ts-essentials'
import type { CollectionSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
CollectionSlug,
DefaultDepth,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -22,7 +30,11 @@ import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { updateOperation } from '../update.js'
import { updateByIDOperation } from '../updateByID.js'
export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType> = {
export type BaseOptions<
TSlug extends CollectionSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
autosave?: boolean
collection: TSlug
/**
@@ -30,7 +42,7 @@ export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType
*/
context?: RequestContext
data: DeepPartial<RequiredDataFromCollectionSlug<TSlug>>
depth?: number
depth?: TDepth
disableTransaction?: boolean
draft?: boolean
fallbackLocale?: false | TypedLocale
@@ -51,54 +63,67 @@ export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType
export type ByIDOptions<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
> = {
id: number | string
limit?: never
where?: never
} & BaseOptions<TSlug, TSelect>
} & BaseOptions<TSlug, TSelect, TDepth>
export type ManyOptions<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
> = {
id?: never
limit?: number
where: Where
} & BaseOptions<TSlug, TSelect>
} & BaseOptions<TSlug, TSelect, TDepth>
export type Options<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
> = ByIDOptions<TSlug, TSelect> | ManyOptions<TSlug, TSelect>
TDepth extends AllowedDepth = DefaultDepth,
> = ByIDOptions<TSlug, TSelect, TDepth> | ManyOptions<TSlug, TSelect, TDepth>
async function updateLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: ByIDOptions<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>>
async function updateLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: ManyOptions<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect>>
): Promise<BulkOperationResult<TSlug, TSelect, TDepth>>
async function updateLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>>
): Promise<
| ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>
| BulkOperationResult<TSlug, TSelect, TDepth>
>
async function updateLocal<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
): Promise<
| ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>
| BulkOperationResult<TSlug, TSelect, TDepth>
> {
const {
id,
autosave,
@@ -155,7 +180,7 @@ async function updateLocal<
if (options.id) {
return updateByIDOperation<TSlug, TSelect>(args)
}
return updateOperation<TSlug, TSelect>(args)
return updateOperation<TSlug, TSelect, TDepth>(args)
}
export default updateLocal

View File

@@ -3,7 +3,7 @@ import type { DeepPartial } from 'ts-essentials'
import httpStatus from 'http-status'
import type { AccessResult } from '../../config/types.js'
import type { CollectionSlug } from '../../index.js'
import type { AllowedDepth, CollectionSlug, DefaultDepth } from '../../index.js'
import type { PayloadRequest, PopulateType, SelectType, Where } from '../../types/index.js'
import type {
BulkOperationResult,
@@ -49,9 +49,10 @@ export type Arguments<TSlug extends CollectionSlug> = {
export const updateOperation = async <
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
incomingArgs: Arguments<TSlug>,
): Promise<BulkOperationResult<TSlug, TSelect>> => {
): Promise<BulkOperationResult<TSlug, TSelect, TDepth>> => {
let args = incomingArgs
try {
@@ -246,7 +247,7 @@ export const updateOperation = async <
await commitTransaction(req)
}
return result
return result as BulkOperationResult<TSlug, TSelect, TDepth>
} catch (error: unknown) {
await killTransaction(args.req)
throw error

View File

@@ -66,6 +66,7 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
typescript: {
autoGenerate: true,
outputFile: `${typeof process?.cwd === 'function' ? process.cwd() : ''}/payload-types.ts`,
typeSafeDepth: false,
},
upload: {},
}

View File

@@ -1110,6 +1110,26 @@ export type Config = {
* to generate the TypeScript interfaces.
*/
schema?: Array<(args: { jsonSchema: JSONSchema4 }) => JSONSchema4>
/**
* This option allows you to have much better result types for relationship fields, depending on `depth`.
* @example:
* ```ts
* const post = await payload.findByID({
* collection: 'posts',
* depth: 1,
* id,
* })
* post.category
* ```
* Here, without this option `post.category` type will be `string | Category`
* which isn't exactly right, since we passed `depth: 1`. With this option - `Category`.
* The same if you'd pass `depth: 0`, the result type will be `string`.
*
* This option is opt in because it may break existing types. In next major version in may be enabled by default.
* @default false
*/
typeSafeDepth?: boolean
}
/**
* Customize the handling of incoming file uploads for collections that have uploads enabled.

View File

@@ -1,5 +1,13 @@
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
DefaultDepth,
GlobalSlug,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -12,9 +20,13 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { findOneOperation } from '../findOne.js'
export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
export type Options<
TSlug extends GlobalSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
context?: RequestContext
depth?: number
depth?: TDepth
draft?: boolean
fallbackLocale?: false | TypedLocale
includeLockStatus?: boolean
@@ -31,10 +43,11 @@ export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
export default async function findOneLocal<
TSlug extends GlobalSlug,
TSelect extends SelectFromGlobalSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> {
): Promise<ApplyDepthInternal<TransformGlobalWithSelect<TSlug, TSelect>, TDepth>> {
const {
slug: globalSlug,
depth,

View File

@@ -1,5 +1,18 @@
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type { Document, PayloadRequest, PopulateType, SelectType } from '../../../types/index.js'
import type {
AllowedDepth,
DefaultDepth,
GlobalSlug,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
SelectType,
} from '../../../types/index.js'
import type { TypeWithVersion } from '../../../versions/types.js'
import type { DataFromGlobalSlug } from '../../config/types.js'
@@ -7,9 +20,9 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { findVersionByIDOperation } from '../findVersionByID.js'
export type Options<TSlug extends GlobalSlug> = {
export type Options<TSlug extends GlobalSlug, TDepth extends AllowedDepth = DefaultDepth> = {
context?: RequestContext
depth?: number
depth?: TDepth
disableErrors?: boolean
fallbackLocale?: false | TypedLocale
id: string
@@ -24,10 +37,13 @@ export type Options<TSlug extends GlobalSlug> = {
}
// eslint-disable-next-line no-restricted-exports
export default async function findVersionByIDLocal<TSlug extends GlobalSlug>(
export default async function findVersionByIDLocal<
TSlug extends GlobalSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<TypeWithVersion<DataFromGlobalSlug<TSlug>>> {
): Promise<TypeWithVersion<ApplyDepthInternal<DataFromGlobalSlug<TSlug>, TDepth>>> {
const {
id,
slug: globalSlug,

View File

@@ -1,7 +1,15 @@
/* eslint-disable no-restricted-exports */
import type { PaginatedDocs } from '../../../database/types.js'
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
DefaultDepth,
GlobalSlug,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -16,9 +24,9 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { findVersionsOperation } from '../findVersions.js'
export type Options<TSlug extends GlobalSlug> = {
export type Options<TSlug extends GlobalSlug, TDepth extends AllowedDepth = DefaultDepth> = {
context?: RequestContext
depth?: number
depth?: TDepth
fallbackLocale?: false | TypedLocale
limit?: number
locale?: 'all' | TypedLocale
@@ -34,10 +42,13 @@ export type Options<TSlug extends GlobalSlug> = {
where?: Where
}
export default async function findVersionsLocal<TSlug extends GlobalSlug>(
export default async function findVersionsLocal<
TSlug extends GlobalSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<PaginatedDocs<TypeWithVersion<DataFromGlobalSlug<TSlug>>>> {
): Promise<PaginatedDocs<TypeWithVersion<ApplyDepthInternal<DataFromGlobalSlug<TSlug>, TDepth>>>> {
const {
slug: globalSlug,
depth,

View File

@@ -1,15 +1,27 @@
/* eslint-disable no-restricted-exports */
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type { Document, PayloadRequest, PopulateType } from '../../../types/index.js'
import type {
AllowedDepth,
DefaultDepth,
GlobalSlug,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
} from '../../../types/index.js'
import type { DataFromGlobalSlug } from '../../config/types.js'
import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { restoreVersionOperation } from '../restoreVersion.js'
export type Options<TSlug extends GlobalSlug> = {
export type Options<TSlug extends GlobalSlug, TDepth extends AllowedDepth = DefaultDepth> = {
context?: RequestContext
depth?: number
depth?: TDepth
fallbackLocale?: false | TypedLocale
id: string
locale?: TypedLocale
@@ -21,10 +33,13 @@ export type Options<TSlug extends GlobalSlug> = {
user?: Document
}
export default async function restoreVersionLocal<TSlug extends GlobalSlug>(
export default async function restoreVersionLocal<
TSlug extends GlobalSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug>,
): Promise<DataFromGlobalSlug<TSlug>> {
): Promise<ApplyDepthInternal<DataFromGlobalSlug<TSlug>, TDepth>> {
const { id, slug: globalSlug, depth, overrideAccess = true, populate, showHiddenFields } = options
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)

View File

@@ -1,7 +1,15 @@
import type { DeepPartial } from 'ts-essentials'
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
import type {
AllowedDepth,
DefaultDepth,
GlobalSlug,
Payload,
RequestContext,
TypedLocale,
} from '../../../index.js'
import type {
ApplyDepthInternal,
Document,
PayloadRequest,
PopulateType,
@@ -14,10 +22,14 @@ import { APIError } from '../../../errors/index.js'
import { createLocalReq } from '../../../utilities/createLocalReq.js'
import { updateOperation } from '../update.js'
export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
export type Options<
TSlug extends GlobalSlug,
TSelect extends SelectType,
TDepth extends AllowedDepth = DefaultDepth,
> = {
context?: RequestContext
data: DeepPartial<Omit<DataFromGlobalSlug<TSlug>, 'id'>>
depth?: number
depth?: TDepth
draft?: boolean
fallbackLocale?: false | TypedLocale
locale?: 'all' | TypedLocale
@@ -35,10 +47,11 @@ export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
export default async function updateLocal<
TSlug extends GlobalSlug,
TSelect extends SelectFromGlobalSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
payload: Payload,
options: Options<TSlug, TSelect>,
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> {
): Promise<ApplyDepthInternal<TransformGlobalWithSelect<TSlug, TSelect>, TDepth>> {
const {
slug: globalSlug,
data,

View File

@@ -55,6 +55,7 @@ import type { Options as FindGlobalVersionsOptions } from './globals/operations/
import type { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion.js'
import type { Options as UpdateGlobalOptions } from './globals/operations/local/update.js'
import type {
ApplyDepthInternal,
ApplyDisableErrors,
JsonObject,
SelectType,
@@ -120,10 +121,15 @@ export interface GeneratedTypes {
dbUntyped: {
defaultIDType: number | string
}
depthUntyped: {
allowed: number
decremented: number[]
default: number
}
globalsSelectUntyped: {
[slug: string]: SelectType
}
globalsUntyped: {
[slug: string]: JsonObject
}
@@ -141,6 +147,7 @@ export interface GeneratedTypes {
}
}
localeUntyped: null | string
userUntyped: User
}
@@ -214,6 +221,21 @@ type ResolveLocaleType<T> = 'locale' extends keyof T ? T['locale'] : T['localeUn
// @ts-expect-error
type ResolveUserType<T> = 'user' extends keyof T ? T['user'] : T['userUntyped']
type ResolveDepthType<T> = 'depth' extends keyof T
? T['depth']
: // @ts-expect-error
T['depthUntyped']
export type TypedDepthConfig = ResolveDepthType<GeneratedTypes>
export type AllowedDepth = TypedDepthConfig['allowed']
export type DefaultDepth = TypedDepthConfig['default']
export type DecrementedDepth = TypedDepthConfig['decremented']
export type DecrementDepth<Depth extends AllowedDepth> = DecrementedDepth[Depth]
export type TypedLocale = ResolveLocaleType<GeneratedTypes>
export type TypedUser = ResolveUserType<GeneratedTypes>
@@ -290,9 +312,13 @@ export class BasePayload {
* @param options
* @returns created document
*/
create = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: CreateOptions<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
create = async <
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: CreateOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>> => {
const { create } = localOperations
return create<TSlug, TSelect>(this, options)
}
@@ -300,9 +326,13 @@ export class BasePayload {
db: DatabaseAdapter
decrypt = decrypt
duplicate = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: DuplicateOptions<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
duplicate = async <
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: DuplicateOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>> => {
const { duplicate } = localOperations
return duplicate<TSlug, TSelect>(this, options)
}
@@ -325,11 +355,17 @@ export class BasePayload {
* @param options
* @returns documents satisfying query
*/
find = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: FindOptions<TSlug, TSelect>,
): Promise<PaginatedDocs<TransformCollectionWithSelect<TSlug, TSelect>>> => {
find = async <
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: FindOptions<TSlug, TSelect, TDepth>,
): Promise<
PaginatedDocs<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>>
> => {
const { find } = localOperations
return find<TSlug, TSelect>(this, options)
return find<TSlug, TSelect, TDepth>(this, options)
}
/**
@@ -341,16 +377,26 @@ export class BasePayload {
TSlug extends CollectionSlug,
TDisableErrors extends boolean,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: FindByIDOptions<TSlug, TDisableErrors, TSelect>,
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<TSlug, TSelect>, TDisableErrors>> => {
options: FindByIDOptions<TSlug, TDisableErrors, TSelect, TDepth>,
): Promise<
ApplyDisableErrors<
ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>,
TDisableErrors
>
> => {
const { findByID } = localOperations
return findByID<TSlug, TDisableErrors, TSelect>(this, options)
}
findGlobal = async <TSlug extends GlobalSlug, TSelect extends SelectFromGlobalSlug<TSlug>>(
options: FindGlobalOptions<TSlug, TSelect>,
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> => {
findGlobal = async <
TSlug extends GlobalSlug,
TSelect extends SelectFromGlobalSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: FindGlobalOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformGlobalWithSelect<TSlug, TSelect>, TDepth>> => {
const { findOne } = localGlobalOperations
return findOne<TSlug, TSelect>(this, options)
}
@@ -360,9 +406,12 @@ export class BasePayload {
* @param options
* @returns global version with specified ID
*/
findGlobalVersionByID = async <TSlug extends GlobalSlug>(
options: FindGlobalVersionByIDOptions<TSlug>,
): Promise<TypeWithVersion<DataFromGlobalSlug<TSlug>>> => {
findGlobalVersionByID = async <
TSlug extends GlobalSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: FindGlobalVersionByIDOptions<TSlug, TDepth>,
): Promise<TypeWithVersion<ApplyDepthInternal<DataFromGlobalSlug<TSlug>, TDepth>>> => {
const { findVersionByID } = localGlobalOperations
return findVersionByID<TSlug>(this, options)
}
@@ -372,9 +421,11 @@ export class BasePayload {
* @param options
* @returns versions satisfying query
*/
findGlobalVersions = async <TSlug extends GlobalSlug>(
options: FindGlobalVersionsOptions<TSlug>,
): Promise<PaginatedDocs<TypeWithVersion<DataFromGlobalSlug<TSlug>>>> => {
findGlobalVersions = async <TSlug extends GlobalSlug, TDepth extends AllowedDepth = DefaultDepth>(
options: FindGlobalVersionsOptions<TSlug, TDepth>,
): Promise<
PaginatedDocs<TypeWithVersion<ApplyDepthInternal<DataFromGlobalSlug<TSlug>, TDepth>>>
> => {
const { findVersions } = localGlobalOperations
return findVersions<TSlug>(this, options)
}
@@ -384,9 +435,12 @@ export class BasePayload {
* @param options
* @returns version with specified ID
*/
findVersionByID = async <TSlug extends CollectionSlug>(
options: FindVersionByIDOptions<TSlug>,
): Promise<TypeWithVersion<DataFromCollectionSlug<TSlug>>> => {
findVersionByID = async <
TSlug extends CollectionSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: FindVersionByIDOptions<TSlug, TDepth>,
): Promise<TypeWithVersion<ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth>>> => {
const { findVersionByID } = localOperations
return findVersionByID<TSlug>(this, options)
}
@@ -396,9 +450,11 @@ export class BasePayload {
* @param options
* @returns versions satisfying query
*/
findVersions = async <TSlug extends CollectionSlug>(
options: FindVersionsOptions<TSlug>,
): Promise<PaginatedDocs<TypeWithVersion<DataFromCollectionSlug<TSlug>>>> => {
findVersions = async <TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth>(
options: FindVersionsOptions<TSlug, TDepth>,
): Promise<
PaginatedDocs<TypeWithVersion<ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth>>>
> => {
const { findVersions } = localOperations
return findVersions<TSlug>(this, options)
}
@@ -422,9 +478,9 @@ export class BasePayload {
logger: Logger
login = async <TSlug extends CollectionSlug>(
options: LoginOptions<TSlug>,
): Promise<{ user: DataFromCollectionSlug<TSlug> } & LoginResult> => {
login = async <TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth>(
options: LoginOptions<TSlug, TDepth>,
): Promise<{ user: ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth> } & LoginResult> => {
const { login } = localOperations.auth
return login<TSlug>(this, options)
}
@@ -441,9 +497,12 @@ export class BasePayload {
* @param options
* @returns version with specified ID
*/
restoreGlobalVersion = async <TSlug extends GlobalSlug>(
options: RestoreGlobalVersionOptions<TSlug>,
): Promise<DataFromGlobalSlug<TSlug>> => {
restoreGlobalVersion = async <
TSlug extends GlobalSlug,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: RestoreGlobalVersionOptions<TSlug, TDepth>,
): Promise<ApplyDepthInternal<DataFromGlobalSlug<TSlug>, TDepth>> => {
const { restoreVersion } = localGlobalOperations
return restoreVersion<TSlug>(this, options)
}
@@ -453,9 +512,9 @@ export class BasePayload {
* @param options
* @returns version with specified ID
*/
restoreVersion = async <TSlug extends CollectionSlug>(
options: RestoreVersionOptions<TSlug>,
): Promise<DataFromCollectionSlug<TSlug>> => {
restoreVersion = async <TSlug extends CollectionSlug, TDepth extends AllowedDepth = DefaultDepth>(
options: RestoreVersionOptions<TSlug, TDepth>,
): Promise<ApplyDepthInternal<DataFromCollectionSlug<TSlug>, TDepth>> => {
const { restoreVersion } = localOperations
return restoreVersion<TSlug>(this, options)
}
@@ -483,9 +542,13 @@ export class BasePayload {
return unlock<TSlug>(this, options)
}
updateGlobal = async <TSlug extends GlobalSlug, TSelect extends SelectFromGlobalSlug<TSlug>>(
options: UpdateGlobalOptions<TSlug, TSelect>,
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> => {
updateGlobal = async <
TSlug extends GlobalSlug,
TSelect extends SelectFromGlobalSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: UpdateGlobalOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformGlobalWithSelect<TSlug, TSelect>, TDepth>> => {
const { update } = localGlobalOperations
return update<TSlug, TSelect>(this, options)
}
@@ -533,19 +596,34 @@ export class BasePayload {
* @param options
* @returns Updated document(s)
*/
delete<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: DeleteByIDOptions<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
delete<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: DeleteByIDOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>>
delete<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: DeleteManyOptions<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect>>
delete<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: DeleteManyOptions<TSlug, TSelect, TDepth>,
): Promise<BulkOperationResult<TSlug, TSelect, TDepth>>
delete<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: DeleteOptions<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
delete<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: DeleteOptions<TSlug, TSelect, TDepth>,
): Promise<
| ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>
| BulkOperationResult<TSlug, TSelect, TDepth>
> {
const { deleteLocal } = localOperations
return deleteLocal<TSlug, TSelect>(this, options)
return deleteLocal<TSlug, TSelect, TDepth>(this, options)
}
/**
@@ -744,24 +822,39 @@ export class BasePayload {
return this
}
update<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: UpdateManyOptions<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect>>
update<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: UpdateManyOptions<TSlug, TSelect, TDepth>,
): Promise<BulkOperationResult<TSlug, TSelect, TDepth>>
/**
* @description Update one or more documents
* @param options
* @returns Updated document(s)
*/
update<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: UpdateByIDOptions<TSlug, TSelect>,
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
update<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: UpdateByIDOptions<TSlug, TSelect, TDepth>,
): Promise<ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>>
update<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
options: UpdateOptions<TSlug, TSelect>,
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
update<
TSlug extends CollectionSlug,
TSelect extends SelectFromCollectionSlug<TSlug>,
TDepth extends AllowedDepth = DefaultDepth,
>(
options: UpdateOptions<TSlug, TSelect, TDepth>,
): Promise<
| ApplyDepthInternal<TransformCollectionWithSelect<TSlug, TSelect>, TDepth>
| BulkOperationResult<TSlug, TSelect, TDepth>
> {
const { update } = localOperations
return update<TSlug, TSelect>(this, options)
return update<TSlug, TSelect, TDepth>(this, options)
}
}

View File

@@ -1,5 +1,6 @@
import type { I18n, TFunction } from '@payloadcms/translations'
import type DataLoader from 'dataloader'
import type { Prettify } from 'ts-essentials'
import type { URL } from 'url'
import type {
@@ -9,8 +10,10 @@ import type {
} from '../collections/config/types.js'
import type payload from '../index.js'
import type {
AllowedDepth,
CollectionSlug,
DataFromGlobalSlug,
DecrementDepth,
GlobalSlug,
RequestContext,
TypedCollectionJoins,
@@ -239,3 +242,72 @@ export type TransformGlobalWithSelect<
: DataFromGlobalSlug<TSlug>
export type PopulateType = Partial<TypedCollectionSelect>
type ExcludeID<T> = Exclude<T, number | string>
type ExcludeObject<T> = Exclude<T, object>
type HasCollectionType<T> = keyof NonNullable<T> extends '__collection' ? true : false
type TypeIsPolymorphicRelationship<T> =
T extends Record<string, unknown>
? T['relationTo'] extends string
? '__collection' extends keyof ExcludeID<T['value']>
? true
: false
: false
: false
type ApplyDepthOnRelationship<T, Depth extends AllowedDepth> = 0 extends Depth
? ExcludeObject<T>
: // @ts-expect-error preserve T | null
ApplyDepth<ExcludeID<T>, DecrementDepth<Depth>>
type ApplyDepthOnPolyRelationship<T, Depth extends AllowedDepth> = Prettify<{
// @ts-expect-error index checked in TypeIsPolymorphicRelationship
relationTo: NonNullable<T>['relationTo']
value: 0 extends Depth
? // @ts-expect-error index checked in TypeIsPolymorphicRelationship
ExcludeObject<NonNullable<T>['value']>
: // @ts-expect-error index checked in TypeIsPolymorphicRelationship
ApplyDepth<ExcludeID<NonNullable<T>['value']>, DecrementDepth<Depth>>
}>
type ApplyDepthProcessKey<T, Depth extends AllowedDepth> =
// HAS ONE
HasCollectionType<T> extends true
? ApplyDepthOnRelationship<T, Depth>
: T extends (infer U)[]
? // HAS MANY
HasCollectionType<U> extends true
? ApplyDepthOnRelationship<U, Depth>[]
: // HAS MANY POLY
TypeIsPolymorphicRelationship<U> extends true
? (U extends Record<string, unknown> ? ApplyDepthOnPolyRelationship<U, Depth> : U)[]
: // JUST ARRAY / BLOCKS
(U extends Record<string, unknown> ? ApplyDepth<U, Depth> : U)[]
: // HAS ONE POLY
TypeIsPolymorphicRelationship<T> extends true
? T extends Record<string, unknown>
? ApplyDepthOnPolyRelationship<T, Depth>
: T
: // OBJECT (NAMED TAB OR GROUP)
T extends Record<string, unknown>
? ApplyDepth<T, Depth>
: T
export type ApplyDepth<T extends object, Depth extends AllowedDepth> = {
[K in keyof T as K]: ApplyDepthProcessKey<T[K], Depth>
}
/**
* Use this type to support both, `typescript.typeSafeDepth` enabled and disabled.
* This is not needed to use in an actual project, since you either have it enabled or disabled, use `ApplyDepth` directly.
* Having this wrapper is preferred over doing this check directly in `ApplyDepth` to:
* * Preserve hover type output of `payload.find()` to `PaginatedDocs<Post>` instead of `PaginatedDocs<ApplyDepth<Post>>`
* * With enabled, make hover type output of `payload.find({ depth: 0 })` to `PaginatedDocs<ApplyDepth<Post, 0>>` instead of `PaginatedDocs<{ id : number, ///}>`
*/
export type ApplyDepthInternal<
T extends object,
Depth extends AllowedDepth,
> = number extends AllowedDepth ? T : ApplyDepth<T, Depth>

View File

@@ -713,6 +713,13 @@ export function entityToJSONSchema(
jsonSchema.description = entityDescription
}
if (config.typescript?.typeSafeDepth) {
jsonSchema.properties.__collection = {
type: 'string',
enum: [entity.slug],
}
}
return jsonSchema
}
@@ -1086,11 +1093,13 @@ export function configToJSONSchema(
],
title: 'Config',
}
if (jobsSchemas.definitions?.size) {
for (const [key, value] of jobsSchemas.definitions) {
jsonSchema.definitions[key] = value
}
}
if (jobsSchemas.properties) {
jsonSchema.properties.jobs = {
type: 'object',
@@ -1100,6 +1109,59 @@ export function configToJSONSchema(
}
}
if (config.typescript?.typeSafeDepth) {
jsonSchema.properties.depth = {
type: 'object',
additionalProperties: false,
properties: {
allowed: {
type: 'number',
enum: Array.from({ length: config.maxDepth + 1 }, (_, i) => i),
},
decremented: {
type: 'array',
items: [
{ type: 'null' },
...Array.from({ length: config.maxDepth + 1 }, (_, i) => ({
type: 'number',
enum: [i],
})),
],
maxItems: config.maxDepth + 1,
minItems: config.maxDepth + 1,
},
default: {
type: 'number',
enum: [config.defaultDepth],
},
},
required: ['allowed', 'default', 'decremented'],
}
} else {
jsonSchema.properties.depth = {
type: 'object',
additionalProperties: false,
description: 'typescript.typeSafeDepth is not enabled',
properties: {
allowed: {
type: 'number',
},
decremented: {
type: 'array',
items: {
type: 'number',
},
},
default: {
type: 'number',
},
},
required: ['allowed', 'default', 'decremented'],
}
}
;(jsonSchema.required as string[]).push('depth')
if (config?.typescript?.schema?.length) {
for (const schema of config.typescript.schema) {
jsonSchema = schema({ jsonSchema })

View File

@@ -44,6 +44,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -93,6 +93,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -42,6 +42,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -86,6 +86,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -36,6 +36,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -57,6 +57,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -58,6 +58,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -52,6 +52,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -42,6 +42,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -36,6 +36,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -62,6 +62,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -48,6 +48,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -36,6 +36,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -36,6 +36,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -44,6 +44,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -46,6 +46,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -54,6 +54,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -64,6 +64,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -133,6 +133,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -48,6 +48,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -54,6 +54,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -60,6 +60,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface HooksUserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -112,6 +112,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -42,6 +42,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -56,6 +56,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -76,6 +76,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -48,6 +48,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -49,6 +49,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -36,6 +36,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -42,6 +42,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -46,6 +46,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -42,6 +42,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -45,6 +45,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -38,6 +38,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -70,6 +70,14 @@ export interface Config {
subTaskFails: WorkflowSubTaskFails;
};
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -539,5 +539,7 @@ export default buildConfigWithDefaults({
},
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
// Uncomment this when/if we enable it by default
// typeSafeDepth: false,
},
})

View File

@@ -72,6 +72,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -0,0 +1,22 @@
import type { BulkOperationResult, PaginatedDocs } from 'payload'
import payload from 'payload'
import { describe, expect, test } from 'tstyche'
import type { Post } from './payload-types.js'
describe('Relationships types', () => {
test("Depth types don't matter if typescript.typeSafeDepth is not enabled", () => {
expect(payload.find({ collection: 'posts', depth: 110 })).type.toBe<
Promise<PaginatedDocs<Post>>
>()
expect(payload.create({ collection: 'posts', depth: 20, data: {} as Post })).type.toBe<
Promise<Post>
>()
expect(payload.update({ collection: 'posts', where: {}, data: {} })).type.toBe<
Promise<BulkOperationResult<'posts'>>
>()
})
})

View File

@@ -56,6 +56,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -44,6 +44,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -40,6 +40,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -20,7 +20,148 @@ export default buildConfigWithDefaults({
},
],
},
{
slug: 'relationships',
fields: [
{
type: 'relationship',
relationTo: 'posts',
name: 'one',
required: true,
},
{
type: 'relationship',
relationTo: 'posts',
name: 'oneOptional',
},
{
type: 'relationship',
relationTo: 'posts',
required: true,
hasMany: true,
name: 'many',
},
{
type: 'relationship',
relationTo: 'posts',
hasMany: true,
name: 'manyOptional',
},
{
type: 'relationship',
relationTo: ['posts', 'users'],
name: 'onePoly',
required: true,
},
{
type: 'relationship',
relationTo: ['posts', 'users'],
name: 'onePolyOptional',
},
{
type: 'relationship',
relationTo: ['posts', 'users'],
name: 'manyPoly',
required: true,
hasMany: true,
},
{
type: 'relationship',
relationTo: ['posts', 'users'],
hasMany: true,
name: 'manyPolyOptional',
},
],
},
{
slug: 'relationships-to-joins',
fields: [
{
name: 'join',
type: 'relationship',
relationTo: 'joins',
required: true,
},
],
},
{
slug: 'joins',
fields: [
{
name: 'relatedRelations',
type: 'join',
on: 'join',
collection: 'relationships-to-joins',
},
],
},
{
slug: 'relationships-deep',
fields: [
{
type: 'relationship',
name: 'depthTwoOne',
relationTo: 'relationships',
required: true,
},
{
type: 'group',
name: 'group',
fields: [
{
name: 'blocks',
type: 'blocks',
blocks: [
{
slug: 'first',
fields: [
{
type: 'relationship',
relationTo: 'posts',
name: 'oneFirst',
required: true,
},
],
},
{
slug: 'second',
fields: [
{
type: 'relationship',
relationTo: 'posts',
name: 'oneSecond',
required: true,
},
],
},
],
},
{
name: 'array',
type: 'array',
fields: [
{
type: 'relationship',
relationTo: 'posts',
name: 'one',
required: true,
},
{
type: 'relationship',
relationTo: 'posts',
required: true,
hasMany: true,
name: 'many',
},
],
},
],
},
],
},
],
defaultDepth: 0,
maxDepth: 5,
admin: {
importMap: {
baseDir: path.resolve(dirname),
@@ -41,5 +182,6 @@ export default buildConfigWithDefaults({
],
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
typeSafeDepth: true,
},
})

View File

@@ -12,14 +12,26 @@ export interface Config {
};
collections: {
posts: Post;
relationships: Relationship;
'relationships-to-joins': RelationshipsToJoin;
joins: Join;
'relationships-deep': RelationshipsDeep;
users: User;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsJoins: {
joins: {
relatedRelations: 'relationships-to-joins';
};
};
collectionsSelect: {
posts: PostsSelect<false> | PostsSelect<true>;
relationships: RelationshipsSelect<false> | RelationshipsSelect<true>;
'relationships-to-joins': RelationshipsToJoinsSelect<false> | RelationshipsToJoinsSelect<true>;
joins: JoinsSelect<false> | JoinsSelect<true>;
'relationships-deep': RelationshipsDeepSelect<false> | RelationshipsDeepSelect<true>;
users: UsersSelect<false> | UsersSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
@@ -42,6 +54,15 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
depth: {
allowed: 0 | 1 | 2 | 3 | 4 | 5;
/**
* @minItems 6
* @maxItems 6
*/
decremented: [null, 0, 1, 2, 3, 4];
default: 0;
};
}
export interface UserAuthOperations {
forgotPassword: {
@@ -70,6 +91,61 @@ export interface Post {
text?: string | null;
updatedAt: string;
createdAt: string;
__collection?: 'posts';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships".
*/
export interface Relationship {
id: string;
one: string | Post;
oneOptional?: (string | null) | Post;
many: (string | Post)[];
manyOptional?: (string | Post)[] | null;
onePoly:
| {
relationTo: 'posts';
value: string | Post;
}
| {
relationTo: 'users';
value: string | User;
};
onePolyOptional?:
| ({
relationTo: 'posts';
value: string | Post;
} | null)
| ({
relationTo: 'users';
value: string | User;
} | null);
manyPoly: (
| {
relationTo: 'posts';
value: string | Post;
}
| {
relationTo: 'users';
value: string | User;
}
)[];
manyPolyOptional?:
| (
| {
relationTo: 'posts';
value: string | Post;
}
| {
relationTo: 'users';
value: string | User;
}
)[]
| null;
updatedAt: string;
createdAt: string;
__collection?: 'relationships';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -87,6 +163,68 @@ export interface User {
loginAttempts?: number | null;
lockUntil?: string | null;
password?: string | null;
__collection?: 'users';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships-to-joins".
*/
export interface RelationshipsToJoin {
id: string;
join: string | Join;
updatedAt: string;
createdAt: string;
__collection?: 'relationships-to-joins';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "joins".
*/
export interface Join {
id: string;
relatedRelations?: {
docs?: (string | RelationshipsToJoin)[] | null;
hasNextPage?: boolean | null;
} | null;
updatedAt: string;
createdAt: string;
__collection?: 'joins';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships-deep".
*/
export interface RelationshipsDeep {
id: string;
depthTwoOne: string | Relationship;
group?: {
blocks?:
| (
| {
oneFirst: string | Post;
id?: string | null;
blockName?: string | null;
blockType: 'first';
}
| {
oneSecond: string | Post;
id?: string | null;
blockName?: string | null;
blockType: 'second';
}
)[]
| null;
array?:
| {
one: string | Post;
many: (string | Post)[];
id?: string | null;
}[]
| null;
};
updatedAt: string;
createdAt: string;
__collection?: 'relationships-deep';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -99,6 +237,22 @@ export interface PayloadLockedDocument {
relationTo: 'posts';
value: string | Post;
} | null)
| ({
relationTo: 'relationships';
value: string | Relationship;
} | null)
| ({
relationTo: 'relationships-to-joins';
value: string | RelationshipsToJoin;
} | null)
| ({
relationTo: 'joins';
value: string | Join;
} | null)
| ({
relationTo: 'relationships-deep';
value: string | RelationshipsDeep;
} | null)
| ({
relationTo: 'users';
value: string | User;
@@ -110,6 +264,7 @@ export interface PayloadLockedDocument {
};
updatedAt: string;
createdAt: string;
__collection?: 'payload-locked-documents';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -133,6 +288,7 @@ export interface PayloadPreference {
| null;
updatedAt: string;
createdAt: string;
__collection?: 'payload-preferences';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -144,6 +300,7 @@ export interface PayloadMigration {
batch?: number | null;
updatedAt: string;
createdAt: string;
__collection?: 'payload-migrations';
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -154,6 +311,78 @@ export interface PostsSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships_select".
*/
export interface RelationshipsSelect<T extends boolean = true> {
one?: T;
oneOptional?: T;
many?: T;
manyOptional?: T;
onePoly?: T;
onePolyOptional?: T;
manyPoly?: T;
manyPolyOptional?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships-to-joins_select".
*/
export interface RelationshipsToJoinsSelect<T extends boolean = true> {
join?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "joins_select".
*/
export interface JoinsSelect<T extends boolean = true> {
relatedRelations?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships-deep_select".
*/
export interface RelationshipsDeepSelect<T extends boolean = true> {
depthTwoOne?: T;
group?:
| T
| {
blocks?:
| T
| {
first?:
| T
| {
oneFirst?: T;
id?: T;
blockName?: T;
};
second?:
| T
| {
oneSecond?: T;
id?: T;
blockName?: T;
};
};
array?:
| T
| {
one?: T;
many?: T;
id?: T;
};
};
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
@@ -210,6 +439,7 @@ export interface Menu {
text?: string | null;
updatedAt?: string | null;
createdAt?: string | null;
__collection?: 'menu';
}
/**
* This interface was referenced by `Config`'s JSON-Schema

View File

@@ -1,9 +1,33 @@
import type { BulkOperationResult, PaginatedDocs, SelectType, TypeWithVersion } from 'payload'
import type {
AllowedDepth,
ApplyDepth,
BulkOperationResult,
DecrementDepth,
DefaultDepth,
PaginatedDocs,
SelectType,
TypeWithVersion,
} from 'payload'
import payload from 'payload'
import { describe, expect, test } from 'tstyche'
import type { Menu, Post, User } from './payload-types.js'
import type {
Join,
Menu,
Post,
Relationship,
RelationshipsDeep,
RelationshipsToJoin,
User,
} from './payload-types.js'
/**
* The real value doesn't matter
*/
const getType = <T>(): T => {
return '' as T
}
describe('Types testing', () => {
test('payload.find', () => {
@@ -77,4 +101,280 @@ describe('Types testing', () => {
Promise<TypeWithVersion<Menu>>
>()
})
test('DefaultDepth is based on config.defaultDepth', () => {
expect(getType<DefaultDepth>()).type.toBe<0>()
})
test('AllowedDepth is based on config.maxDepth', () => {
expect(getType<AllowedDepth>()).type.toBe<0 | 1 | 2 | 3 | 4 | 5>()
})
test('Decrements depth with DecrementDepth', () => {
expect(getType<DecrementDepth<2>>()).type.toBe<1>()
})
interface RelationshipDepth0 extends Relationship {
many: string[]
manyOptional?: null | string[]
manyPoly: (
| {
relationTo: 'posts'
value: string
}
| {
relationTo: 'users'
value: string
}
)[]
manyPolyOptional?:
| (
| {
relationTo: 'posts'
value: string
}
| {
relationTo: 'users'
value: string
}
)[]
| null
one: string
oneOptional?: null | string
onePoly:
| {
relationTo: 'posts'
value: string
}
| {
relationTo: 'users'
value: string
}
onePolyOptional?:
| ({
relationTo: 'posts'
value: string
} | null)
| ({
relationTo: 'users'
value: string
} | null)
}
test('ApplyDepth with depth 0', () => {
expect(getType<ApplyDepth<Relationship, 0>>()).type.toBe<RelationshipDepth0>()
})
interface RelationshipDeepDepth0 extends RelationshipsDeep {
depthTwoOne: string
group?: {
array?:
| {
id?: null | string
many: string[]
one: string
}[]
| null
blocks?:
| (
| {
blockName?: null | string
blockType: 'first'
id?: null | string
oneFirst: string
}
| {
blockName?: null | string
blockType: 'second'
id?: null | string
oneSecond: string
}
)[]
| null
}
}
test('ApplyDepth deep fields with depth 0', () => {
expect(getType<ApplyDepth<RelationshipsDeep, 0>>()).type.toBe<RelationshipDeepDepth0>()
})
interface RelationshipDepth1 extends Relationship {
many: Post[]
manyOptional?: null | Post[]
manyPoly: (
| {
relationTo: 'posts'
value: Post
}
| {
relationTo: 'users'
value: User
}
)[]
manyPolyOptional?:
| (
| {
relationTo: 'posts'
value: Post
}
| {
relationTo: 'users'
value: User
}
)[]
| null
one: Post
oneOptional?: null | Post
onePoly:
| {
relationTo: 'posts'
value: Post
}
| {
relationTo: 'users'
value: User
}
onePolyOptional?:
| ({
relationTo: 'posts'
value: Post
} | null)
| ({
relationTo: 'users'
value: User
} | null)
}
test('ApplyDepth with depth 1', () => {
expect(getType<ApplyDepth<Relationship, 1>>()).type.toBe<RelationshipDepth1>()
})
interface RelationshipDeepDepth1 extends RelationshipsDeep {
depthTwoOne: ApplyDepth<Relationship, 0>
group?: {
array?:
| {
id?: null | string
many: Post[]
one: Post
}[]
| null
blocks?:
| (
| {
blockName?: null | string
blockType: 'first'
id?: null | string
oneFirst: Post
}
| {
blockName?: null | string
blockType: 'second'
id?: null | string
oneSecond: Post
}
)[]
| null
}
}
test('ApplyDepth deep fields with depth 1, decrements depth of other collection that has relationship', () => {
expect(getType<ApplyDepth<RelationshipsDeep, 1>>()).type.toBe<RelationshipDeepDepth1>()
})
interface RelationshipDeepDepth2 extends RelationshipsDeep {
depthTwoOne: ApplyDepth<Relationship, 1>
group?: {
array?:
| {
id?: null | string
many: Post[]
one: Post
}[]
| null
blocks?:
| (
| {
blockName?: null | string
blockType: 'first'
id?: null | string
oneFirst: Post
}
| {
blockName?: null | string
blockType: 'second'
id?: null | string
oneSecond: Post
}
)[]
| null
}
}
test('ApplyDepth deep fields with depth 2', () => {
expect(getType<ApplyDepth<RelationshipsDeep, 2>>()).type.toBe<RelationshipDeepDepth2>()
})
test('payload.find respects default depth', () => {
expect(payload.find({ collection: 'relationships' })).type.toBe<
Promise<PaginatedDocs<ApplyDepth<Relationship, 0>>>
>()
})
interface JoinDepth0 extends Join {
relatedRelations?: {
docs?: null | string[]
hasNextPage?: boolean | null
} | null
}
test('ApplyDepth joins with depth 0', () => {
expect(getType<ApplyDepth<Join, 0>>()).type.toBe<JoinDepth0>()
})
interface JoinDepth1 extends Join {
relatedRelations?: {
docs?: ApplyDepth<RelationshipsToJoin, 0>[] | null
hasNextPage?: boolean | null
} | null
}
test('ApplyDepth joins with depth 1', () => {
expect(getType<ApplyDepth<Join, 1>>()).type.toBe<JoinDepth1>()
})
interface JoinDepth2 extends Join {
relatedRelations?: {
docs?: ApplyDepth<RelationshipsToJoin, 1>[] | null
hasNextPage?: boolean | null
} | null
}
test('ApplyDepth joins with depth 2', () => {
expect(getType<ApplyDepth<Join, 2>>()).type.toBe<JoinDepth2>()
})
test('payload.find respects default depth', () => {
expect(payload.find({ collection: 'relationships' })).type.toBe<
Promise<PaginatedDocs<ApplyDepth<Relationship, 0>>>
>()
})
test('payload.find respects depth', () => {
expect(payload.find({ collection: 'relationships', depth: 2 })).type.toBe<
Promise<PaginatedDocs<ApplyDepth<Relationship, 2>>>
>()
})
test('payload.findByID respects depth', () => {
expect(payload.findByID({ id: '', collection: 'relationships', depth: 2 })).type.toBe<
Promise<ApplyDepth<Relationship, 2>>
>()
})
test('payload.create respects depth', () => {
expect(payload.create({ collection: 'relationships', data: {} as any, depth: 2 })).type.toBe<
Promise<ApplyDepth<Relationship, 2>>
>()
})
})

View File

@@ -102,6 +102,14 @@ export interface Config {
tasks: unknown;
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {

View File

@@ -72,6 +72,14 @@ export interface Config {
};
workflows: unknown;
};
/**
* typescript.typeSafeDepth is not enabled
*/
depth: {
allowed: number;
decremented: number[];
default: number;
};
}
export interface UserAuthOperations {
forgotPassword: {