diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8e82aa95f..78379cc24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: build +name: ci on: pull_request: @@ -528,7 +528,9 @@ jobs: # Build listed templates with packed local packages and then runs their int and e2e tests build-and-test-templates: runs-on: ubuntu-24.04 - needs: build + needs: [changes, build] + if: ${{ needs.changes.outputs.needs_build == 'true' }} + name: build-template-${{ matrix.template }}-${{ matrix.database }} strategy: fail-fast: false matrix: @@ -558,8 +560,6 @@ jobs: # - template: with-vercel-website # database: postgres - name: ${{ matrix.template }}-${{ matrix.database }} - env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres diff --git a/packages/next/src/utilities/initPage/handleAuthRedirect.ts b/packages/next/src/utilities/initPage/handleAuthRedirect.ts index ea5463ff0..20818ecc1 100644 --- a/packages/next/src/utilities/initPage/handleAuthRedirect.ts +++ b/packages/next/src/utilities/initPage/handleAuthRedirect.ts @@ -1,4 +1,4 @@ -import type { User } from 'payload' +import type { TypedUser } from 'payload' import { formatAdminURL } from 'payload/shared' import * as qs from 'qs-esm' @@ -7,7 +7,7 @@ type Args = { config route: string searchParams: { [key: string]: string | string[] } - user?: User + user?: TypedUser } export const handleAuthRedirect = ({ config, route, searchParams, user }: Args): string => { diff --git a/packages/next/src/utilities/initReq.ts b/packages/next/src/utilities/initReq.ts index d81015876..62c6cd98d 100644 --- a/packages/next/src/utilities/initReq.ts +++ b/packages/next/src/utilities/initReq.ts @@ -6,7 +6,7 @@ import type { PayloadRequest, SanitizedConfig, SanitizedPermissions, - User, + TypedUser, } from 'payload' import { initI18n } from '@payloadcms/translations' @@ -37,7 +37,7 @@ type PartialResult = { languageCode: AcceptedLanguages payload: Payload responseHeaders: Headers - user: null | User + user: null | TypedUser } // Create cache instances for different parts of our application diff --git a/packages/next/src/views/Account/ResetPreferences/index.tsx b/packages/next/src/views/Account/ResetPreferences/index.tsx index 0c2fe8d4f..7c0cff7b4 100644 --- a/packages/next/src/views/Account/ResetPreferences/index.tsx +++ b/packages/next/src/views/Account/ResetPreferences/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { User } from 'payload' +import type { TypedUser } from 'payload' import { Button, ConfirmationModal, toast, useModal, useTranslation } from '@payloadcms/ui' import * as qs from 'qs-esm' @@ -9,7 +9,7 @@ const confirmResetModalSlug = 'confirm-reset-modal' export const ResetPreferences: React.FC<{ readonly apiRoute: string - readonly user?: User + readonly user?: TypedUser }> = ({ apiRoute, user }) => { const { openModal } = useModal() const { t } = useTranslation() diff --git a/packages/next/src/views/Account/Settings/index.tsx b/packages/next/src/views/Account/Settings/index.tsx index e078e4db5..b0396f23c 100644 --- a/packages/next/src/views/Account/Settings/index.tsx +++ b/packages/next/src/views/Account/Settings/index.tsx @@ -1,5 +1,5 @@ import type { I18n } from '@payloadcms/translations' -import type { BasePayload, Config, LanguageOptions, User } from 'payload' +import type { BasePayload, Config, LanguageOptions, TypedUser } from 'payload' import { FieldLabel } from '@payloadcms/ui' import React from 'react' @@ -17,7 +17,7 @@ export const Settings: React.FC<{ readonly languageOptions: LanguageOptions readonly payload: BasePayload readonly theme: Config['admin']['theme'] - readonly user?: User + readonly user?: TypedUser }> = (props) => { const { className, i18n, languageOptions, payload, theme, user } = props diff --git a/packages/next/src/views/Version/fetchVersions.ts b/packages/next/src/views/Version/fetchVersions.ts index 85188c1cb..cf6236c66 100644 --- a/packages/next/src/views/Version/fetchVersions.ts +++ b/packages/next/src/views/Version/fetchVersions.ts @@ -4,8 +4,8 @@ import { type PayloadRequest, type SelectType, type Sort, + type TypedUser, type TypeWithVersion, - type User, type Where, } from 'payload' @@ -28,7 +28,7 @@ export const fetchVersion = async ({ overrideAccess?: boolean req: PayloadRequest select?: SelectType - user?: User + user?: TypedUser }): Promise> => { try { if (collectionSlug) { @@ -88,7 +88,7 @@ export const fetchVersions = async ({ req: PayloadRequest select?: SelectType sort?: Sort - user?: User + user?: TypedUser where?: Where }): Promise>> => { const where: Where = { and: [...(whereFromArgs ? [whereFromArgs] : [])] } @@ -160,7 +160,7 @@ export const fetchLatestVersion = async ({ req: PayloadRequest select?: SelectType status: 'draft' | 'published' - user?: User + user?: TypedUser where?: Where }): Promise> => { const and: Where[] = [ diff --git a/packages/next/src/views/Versions/types.ts b/packages/next/src/views/Versions/types.ts index 6a51ec119..e0293c519 100644 --- a/packages/next/src/views/Versions/types.ts +++ b/packages/next/src/views/Versions/types.ts @@ -4,7 +4,7 @@ import type { SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig, - User, + TypedUser, } from 'payload' export type DefaultVersionsViewProps = { @@ -18,6 +18,6 @@ export type DefaultVersionsViewProps = { i18n: I18n id: number | string limit: number - user: User + user: TypedUser versionsData: PaginatedDocs } diff --git a/packages/payload/src/admin/forms/Field.ts b/packages/payload/src/admin/forms/Field.ts index 13debab2b..0f6f69023 100644 --- a/packages/payload/src/admin/forms/Field.ts +++ b/packages/payload/src/admin/forms/Field.ts @@ -1,8 +1,9 @@ import type { I18nClient } from '@payloadcms/translations' import type { MarkOptional } from 'ts-essentials' -import type { SanitizedFieldPermissions, User } from '../../auth/types.js' +import type { SanitizedFieldPermissions } from '../../auth/types.js' import type { ClientBlock, ClientField, Field } from '../../fields/config/types.js' +import type { TypedUser } from '../../index.js' import type { DocumentPreferences } from '../../preferences/types.js' import type { Operation, Payload, PayloadRequest } from '../../types/index.js' import type { @@ -90,7 +91,7 @@ export type ServerComponentProps = { preferences: DocumentPreferences req: PayloadRequest siblingData: Data - user: User + user: TypedUser value?: unknown } diff --git a/packages/payload/src/auth/operations/login.ts b/packages/payload/src/auth/operations/login.ts index 0de2961e1..1e2d35fba 100644 --- a/packages/payload/src/auth/operations/login.ts +++ b/packages/payload/src/auth/operations/login.ts @@ -5,9 +5,8 @@ import type { Collection, DataFromCollectionSlug, } from '../../collections/config/types.js' -import type { CollectionSlug } from '../../index.js' +import type { CollectionSlug, TypedUser } from '../../index.js' import type { PayloadRequest, Where } from '../../types/index.js' -import type { User } from '../types.js' import { buildAfterOperation } from '../../collections/operations/utils.js' import { @@ -32,7 +31,7 @@ import { resetLoginAttempts } from '../strategies/local/resetLoginAttempts.js' export type Result = { exp?: number token?: string - user?: User + user?: TypedUser } export type Arguments = { diff --git a/packages/payload/src/auth/operations/me.ts b/packages/payload/src/auth/operations/me.ts index f2c5dc98c..a5977dd1f 100644 --- a/packages/payload/src/auth/operations/me.ts +++ b/packages/payload/src/auth/operations/me.ts @@ -1,8 +1,9 @@ import { decodeJwt } from 'jose' import type { Collection } from '../../collections/config/types.js' +import type { TypedUser } from '../../index.js' import type { PayloadRequest } from '../../types/index.js' -import type { ClientUser, User } from '../types.js' +import type { ClientUser } from '../types.js' export type MeOperationResult = { collection?: string @@ -42,7 +43,7 @@ export const meOperation = async (args: Arguments): Promise = overrideAccess: false, req, showHiddenFields: false, - })) as User + })) as TypedUser if (user) { user.collection = collection.config.slug diff --git a/packages/payload/src/auth/sendVerificationEmail.ts b/packages/payload/src/auth/sendVerificationEmail.ts index e728d5bd9..3734f8794 100644 --- a/packages/payload/src/auth/sendVerificationEmail.ts +++ b/packages/payload/src/auth/sendVerificationEmail.ts @@ -3,8 +3,9 @@ import { URL } from 'url' import type { Collection } from '../collections/config/types.js' import type { SanitizedConfig } from '../config/types.js' import type { InitializedEmailAdapter } from '../email/types.js' +import type { TypedUser } from '../index.js' import type { PayloadRequest } from '../types/index.js' -import type { User, VerifyConfig } from './types.js' +import type { VerifyConfig } from './types.js' type Args = { collection: Collection @@ -13,7 +14,7 @@ type Args = { email: InitializedEmailAdapter req: PayloadRequest token: string - user: User + user: TypedUser } export async function sendVerificationEmail(args: Args): Promise { diff --git a/packages/payload/src/auth/strategies/apiKey.ts b/packages/payload/src/auth/strategies/apiKey.ts index b7842531c..86c3123e3 100644 --- a/packages/payload/src/auth/strategies/apiKey.ts +++ b/packages/payload/src/auth/strategies/apiKey.ts @@ -1,8 +1,9 @@ import crypto from 'crypto' import type { SanitizedCollectionConfig } from '../../collections/config/types.js' +import type { TypedUser } from '../../index.js' import type { Where } from '../../types/index.js' -import type { AuthStrategyFunction, User } from '../index.js' +import type { AuthStrategyFunction } from '../index.js' export const APIKeyAuthentication = (collectionConfig: SanitizedCollectionConfig): AuthStrategyFunction => @@ -49,7 +50,7 @@ export const APIKeyAuthentication = user!._strategy = 'api-key' return { - user: user as User, + user: user as TypedUser, } } } catch (ignore) { diff --git a/packages/payload/src/auth/types.ts b/packages/payload/src/auth/types.ts index 0a2a4a5f0..82af27142 100644 --- a/packages/payload/src/auth/types.ts +++ b/packages/payload/src/auth/types.ts @@ -1,6 +1,6 @@ import type { DeepRequired } from 'ts-essentials' -import type { CollectionSlug, GlobalSlug, Payload } from '../index.js' +import type { CollectionSlug, GlobalSlug, Payload, TypedUser } from '../index.js' import type { PayloadRequest, Where } from '../types/index.js' /** @@ -122,7 +122,10 @@ type BaseUser = { username?: string } -export type User = { +/** + * @deprecated Use `TypedUser` instead. This will be removed in 4.0. + */ +export type UntypedUser = { [key: string]: any } & BaseUser @@ -179,7 +182,7 @@ export type AuthStrategyResult = { | ({ _strategy?: string collection?: string - } & User) + } & TypedUser) | null } diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index 921ae3200..9d293a423 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -15,7 +15,7 @@ import type { AuthArgs } from './auth/operations/auth.js' import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword.js' import type { Result as LoginResult } from './auth/operations/login.js' import type { Result as ResetPasswordResult } from './auth/operations/resetPassword.js' -import type { AuthStrategy, User } from './auth/types.js' +import type { AuthStrategy, UntypedUser } from './auth/types.js' import type { BulkOperationResult, Collection, @@ -216,7 +216,7 @@ export interface GeneratedTypes { } } localeUntyped: null | string - userUntyped: User + userUntyped: UntypedUser } // Helper type to resolve the correct type using conditional types @@ -299,6 +299,10 @@ type ResolveLocaleType = 'locale' extends keyof T ? T['locale'] : T['localeUn type ResolveUserType = 'user' extends keyof T ? T['user'] : T['userUntyped'] export type TypedLocale = ResolveLocaleType + +/** + * @todo rename to `User` in 4.0 + */ export type TypedUser = ResolveUserType // @ts-expect-error @@ -1099,7 +1103,7 @@ export type { SanitizedFieldPermissions, SanitizedGlobalPermission, SanitizedPermissions, - User, + UntypedUser as User, VerifyConfig, } from './auth/types.js' export { generateImportMap } from './bin/generateImportMap/index.js' diff --git a/packages/payload/src/utilities/createLocalReq.ts b/packages/payload/src/utilities/createLocalReq.ts index 09cce6afb..46ec57543 100644 --- a/packages/payload/src/utilities/createLocalReq.ts +++ b/packages/payload/src/utilities/createLocalReq.ts @@ -1,5 +1,4 @@ -import type { User } from '../auth/types.js' -import type { Payload, RequestContext, TypedLocale } from '../index.js' +import type { Payload, RequestContext, TypedLocale, TypedUser } from '../index.js' import type { PayloadRequest } from '../types/index.js' import { getDataLoader } from '../collections/dataloader.js' @@ -91,7 +90,7 @@ export type CreateLocalReqOptions = { locale?: string req?: Partial urlSuffix?: string - user?: User + user?: TypedUser } type CreateLocalReq = (options: CreateLocalReqOptions, payload: Payload) => Promise diff --git a/packages/payload/src/versions/schedule/job.ts b/packages/payload/src/versions/schedule/job.ts index 7d66396f0..56115178b 100644 --- a/packages/payload/src/versions/schedule/job.ts +++ b/packages/payload/src/versions/schedule/job.ts @@ -1,5 +1,5 @@ -import type { User } from '../../auth/types.js' import type { Field } from '../../fields/config/types.js' +import type { TypedUser } from '../../index.js' import type { TaskConfig } from '../../queues/config/types/taskTypes.js' import type { SchedulePublishTaskInput } from './types.js' @@ -21,14 +21,14 @@ export const getSchedulePublishTask = ({ const userID = input.user - let user: null | User = null + let user: null | TypedUser = null if (userID) { user = (await req.payload.findByID({ id: userID, collection: adminUserSlug, depth: 0, - })) as User + })) as TypedUser user.collection = adminUserSlug } diff --git a/packages/plugin-import-export/src/export/createExport.ts b/packages/plugin-import-export/src/export/createExport.ts index 55b1c98f1..c23ae8ce8 100644 --- a/packages/plugin-import-export/src/export/createExport.ts +++ b/packages/plugin-import-export/src/export/createExport.ts @@ -1,5 +1,5 @@ /* eslint-disable perfectionist/sort-objects */ -import type { PayloadRequest, Sort, User, Where } from 'payload' +import type { PayloadRequest, Sort, TypedUser, Where } from 'payload' import { stringify } from 'csv-stringify/sync' import { APIError } from 'payload' @@ -38,7 +38,7 @@ export type CreateExportArgs = { download?: boolean input: Export req: PayloadRequest - user?: User + user?: TypedUser } export const createExport = async (args: CreateExportArgs) => { diff --git a/packages/plugin-import-export/src/export/getCreateExportCollectionTask.ts b/packages/plugin-import-export/src/export/getCreateExportCollectionTask.ts index 6335c6ea2..21fadf345 100644 --- a/packages/plugin-import-export/src/export/getCreateExportCollectionTask.ts +++ b/packages/plugin-import-export/src/export/getCreateExportCollectionTask.ts @@ -1,4 +1,4 @@ -import type { Config, TaskConfig, User } from 'payload' +import type { Config, TaskConfig, TypedUser } from 'payload' import type { CreateExportArgs, Export } from './createExport.js' @@ -29,13 +29,13 @@ export const getCreateCollectionExportTask = ( return { slug: 'createCollectionExport', handler: async ({ input, req }: CreateExportArgs) => { - let user: undefined | User + let user: TypedUser | undefined if (input.userCollection && input.user) { user = (await req.payload.findByID({ id: input.user, collection: input.userCollection, - })) as User + })) as TypedUser } if (!user) { diff --git a/packages/plugin-multi-tenant/src/providers/TenantSelectionProvider/index.tsx b/packages/plugin-multi-tenant/src/providers/TenantSelectionProvider/index.tsx index e1c248990..844c70ca3 100644 --- a/packages/plugin-multi-tenant/src/providers/TenantSelectionProvider/index.tsx +++ b/packages/plugin-multi-tenant/src/providers/TenantSelectionProvider/index.tsx @@ -1,4 +1,4 @@ -import type { OptionObject, Payload, User } from 'payload' +import type { OptionObject, Payload, TypedUser } from 'payload' import { cookies as getCookies } from 'next/headers.js' @@ -10,7 +10,7 @@ type Args = { payload: Payload tenantsCollectionSlug: string useAsTitle: string - user: User + user: TypedUser } export const TenantSelectionProvider = async ({ diff --git a/packages/plugin-multi-tenant/src/queries/findTenantOptions.ts b/packages/plugin-multi-tenant/src/queries/findTenantOptions.ts index ae98cbe6a..20f8f79bf 100644 --- a/packages/plugin-multi-tenant/src/queries/findTenantOptions.ts +++ b/packages/plugin-multi-tenant/src/queries/findTenantOptions.ts @@ -1,11 +1,11 @@ -import type { PaginatedDocs, Payload, User } from 'payload' +import type { PaginatedDocs, Payload, TypedUser } from 'payload' type Args = { limit: number payload: Payload tenantsCollectionSlug: string useAsTitle: string - user?: User + user?: TypedUser } export const findTenantOptions = async ({ limit, diff --git a/packages/plugin-multi-tenant/src/types.ts b/packages/plugin-multi-tenant/src/types.ts index 62f3a0f8e..6ccc06642 100644 --- a/packages/plugin-multi-tenant/src/types.ts +++ b/packages/plugin-multi-tenant/src/types.ts @@ -1,5 +1,5 @@ import type { AcceptedLanguages } from '@payloadcms/translations' -import type { ArrayField, CollectionSlug, Field, RelationshipField, User } from 'payload' +import type { ArrayField, CollectionSlug, Field, RelationshipField, TypedUser } from 'payload' export type MultiTenantPluginConfig = { /** @@ -138,7 +138,7 @@ export type MultiTenantPluginConfig = { * Useful for super-admin type users */ userHasAccessToAllTenants?: ( - user: ConfigTypes extends { user: unknown } ? ConfigTypes['user'] : User, + user: ConfigTypes extends { user: unknown } ? ConfigTypes['user'] : TypedUser, ) => boolean /** * Opt out of adding access constraints to the tenants collection @@ -165,4 +165,4 @@ export type UserWithTenantsField = { tenant: number | string | Tenant }[] | null -} & User +} & TypedUser diff --git a/packages/plugin-multi-tenant/src/utilities/getGlobalViewRedirect.ts b/packages/plugin-multi-tenant/src/utilities/getGlobalViewRedirect.ts index ee2630fed..64b0acce9 100644 --- a/packages/plugin-multi-tenant/src/utilities/getGlobalViewRedirect.ts +++ b/packages/plugin-multi-tenant/src/utilities/getGlobalViewRedirect.ts @@ -1,4 +1,4 @@ -import type { Payload, User, ViewTypes } from 'payload' +import type { Payload, TypedUser, ViewTypes } from 'payload' import { formatAdminURL } from 'payload/shared' @@ -15,7 +15,7 @@ type Args = { tenantFieldName: string tenantsCollectionSlug: string useAsTitle: string - user?: User + user?: TypedUser view: ViewTypes } export async function getGlobalViewRedirect({ diff --git a/packages/plugin-multi-tenant/src/utilities/withTenantAccess.ts b/packages/plugin-multi-tenant/src/utilities/withTenantAccess.ts index 0d4e1a5bb..3d716a91e 100644 --- a/packages/plugin-multi-tenant/src/utilities/withTenantAccess.ts +++ b/packages/plugin-multi-tenant/src/utilities/withTenantAccess.ts @@ -4,7 +4,7 @@ import type { AccessResult, AllOperations, CollectionConfig, - User, + TypedUser, Where, } from 'payload' @@ -53,7 +53,7 @@ export const withTenantAccess = args.req.user && args.req.user.collection === adminUsersSlug && !userHasAccessToAllTenants( - args.req.user as ConfigType extends { user: unknown } ? ConfigType['user'] : User, + args.req.user as ConfigType extends { user: unknown } ? ConfigType['user'] : TypedUser, ) ) { const tenantConstraint = getTenantAccess({ diff --git a/packages/richtext-lexical/src/features/link/server/baseFields.ts b/packages/richtext-lexical/src/features/link/server/baseFields.ts index 6b60400a1..93461025b 100644 --- a/packages/richtext-lexical/src/features/link/server/baseFields.ts +++ b/packages/richtext-lexical/src/features/link/server/baseFields.ts @@ -5,7 +5,7 @@ import type { SanitizedConfig, TextField, TextFieldSingleValidation, - User, + TypedUser, } from 'payload' import type { LinkFields } from '../nodes/types.js' @@ -120,7 +120,7 @@ export const getBaseFields = ( ? ({ relationTo, user }) => { const hidden = config.collections.find(({ slug }) => slug === relationTo)?.admin .hidden - if (typeof hidden === 'function' && hidden({ user } as { user: User })) { + if (typeof hidden === 'function' && hidden({ user } as { user: TypedUser })) { return false } return true diff --git a/packages/richtext-slate/src/field/elements/link/LinkDrawer/baseFields.ts b/packages/richtext-slate/src/field/elements/link/LinkDrawer/baseFields.ts index 0afbee6ff..b4ccb5bfe 100644 --- a/packages/richtext-slate/src/field/elements/link/LinkDrawer/baseFields.ts +++ b/packages/richtext-slate/src/field/elements/link/LinkDrawer/baseFields.ts @@ -1,4 +1,4 @@ -import type { Field, SanitizedConfig, User } from 'payload' +import type { Field, SanitizedConfig, TypedUser } from 'payload' export const getBaseFields = (config: SanitizedConfig): Field[] => [ { @@ -47,7 +47,7 @@ export const getBaseFields = (config: SanitizedConfig): Field[] => [ type: 'relationship', filterOptions: ({ relationTo, user }) => { const hidden = config.collections.find(({ slug }) => slug === relationTo).admin.hidden - if (typeof hidden === 'function' && hidden({ user } as { user: User })) { + if (typeof hidden === 'function' && hidden({ user } as { user: TypedUser })) { return false } }, diff --git a/packages/ui/src/forms/Form/types.ts b/packages/ui/src/forms/Form/types.ts index 27757efad..9cfd1ef64 100644 --- a/packages/ui/src/forms/Form/types.ts +++ b/packages/ui/src/forms/Form/types.ts @@ -4,7 +4,7 @@ import type { FormField, FormState, Row, - User, + TypedUser, ValidationFieldError, } from 'payload' import type React from 'react' @@ -123,7 +123,7 @@ export type MODIFY_CONDITION = { path: string result: boolean type: 'MODIFY_CONDITION' - user: User + user: TypedUser } export type UPDATE = { diff --git a/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/index.ts b/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/index.ts index 95cf90f25..c53ae679e 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/index.ts +++ b/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/index.ts @@ -4,7 +4,7 @@ import type { PayloadRequest, SelectMode, SelectType, - User, + TypedUser, } from 'payload' import { iterateFields } from './iterateFields.js' @@ -18,7 +18,7 @@ type Args = { select?: SelectType selectMode?: SelectMode siblingData: Data - user: User + user: TypedUser } export const calculateDefaultValues = async ({ diff --git a/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/iterateFields.ts b/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/iterateFields.ts index ad9426c86..2986d4296 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/iterateFields.ts +++ b/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/iterateFields.ts @@ -1,4 +1,12 @@ -import type { Data, Field, PayloadRequest, SelectMode, SelectType, TabAsField, User } from 'payload' +import type { + Data, + Field, + PayloadRequest, + SelectMode, + SelectType, + TabAsField, + TypedUser, +} from 'payload' import { defaultValuePromise } from './promise.js' @@ -11,7 +19,7 @@ type Args = { select?: SelectType selectMode?: SelectMode siblingData: Data - user: User + user: TypedUser } export const iterateFields = async ({ diff --git a/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/promise.ts b/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/promise.ts index 70a8dff24..98ebe7e37 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/promise.ts +++ b/packages/ui/src/forms/fieldSchemasToFormState/calculateDefaultValues/promise.ts @@ -6,7 +6,7 @@ import type { SelectMode, SelectType, TabAsField, - User, + TypedUser, } from 'payload' import { getBlockSelect, getDefaultValue, stripUnselectedFields } from 'payload' @@ -23,7 +23,7 @@ type Args = { select?: SelectType selectMode?: SelectMode siblingData: Data - user: User + user: TypedUser } // TODO: Make this works for rich text subfields diff --git a/packages/ui/src/providers/Auth/index.tsx b/packages/ui/src/providers/Auth/index.tsx index 8a9f6b99d..8398aaa6d 100644 --- a/packages/ui/src/providers/Auth/index.tsx +++ b/packages/ui/src/providers/Auth/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { ClientUser, SanitizedPermissions, User } from 'payload' +import type { ClientUser, SanitizedPermissions, TypedUser } from 'payload' import { useModal } from '@faceless-ui/modal' import { usePathname, useRouter } from 'next/navigation.js' @@ -23,7 +23,7 @@ export type UserWithToken = { } export type AuthContext = { - fetchFullUser: () => Promise + fetchFullUser: () => Promise logOut: () => Promise permissions?: SanitizedPermissions refreshCookie: (forceRefresh?: boolean) => void diff --git a/packages/ui/src/providers/Root/index.tsx b/packages/ui/src/providers/Root/index.tsx index a5a5220b8..336bdecba 100644 --- a/packages/ui/src/providers/Root/index.tsx +++ b/packages/ui/src/providers/Root/index.tsx @@ -6,7 +6,7 @@ import type { Locale, SanitizedPermissions, ServerFunctionClient, - User, + TypedUser, } from 'payload' import { DndContext, pointerWithin } from '@dnd-kit/core' @@ -52,7 +52,7 @@ type Props = { readonly switchLanguageServerAction?: (lang: string) => Promise readonly theme: Theme readonly translations: I18nClient['translations'] - readonly user: null | User + readonly user: null | TypedUser } export const RootProvider: React.FC = ({ diff --git a/templates/blank/src/payload-types.ts b/templates/blank/src/payload-types.ts index 3864ed590..a9606af9a 100644 --- a/templates/blank/src/payload-types.ts +++ b/templates/blank/src/payload-types.ts @@ -128,6 +128,13 @@ export interface User { hash?: string | null; loginAttempts?: number | null; lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; password?: string | null; } /** @@ -220,6 +227,13 @@ export interface UsersSelect { hash?: T; loginAttempts?: T; lockUntil?: T; + sessions?: + | T + | { + id?: T; + createdAt?: T; + expiresAt?: T; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/templates/plugin/package.json b/templates/plugin/package.json index 560cc979a..86cd24e1e 100644 --- a/templates/plugin/package.json +++ b/templates/plugin/package.json @@ -36,6 +36,8 @@ "dev:generate-importmap": "pnpm dev:payload generate:importmap", "dev:generate-types": "pnpm dev:payload generate:types", "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload", + "generate:types": "pnpm dev:generate-types", + "generate:importmap": "pnpm dev:generate-importmap", "lint": "eslint", "lint:fix": "eslint ./src --fix", "prepublishOnly": "pnpm clean && pnpm build", diff --git a/templates/website/src/payload-types.ts b/templates/website/src/payload-types.ts index 5978fa225..8f61fdc6c 100644 --- a/templates/website/src/payload-types.ts +++ b/templates/website/src/payload-types.ts @@ -383,6 +383,13 @@ export interface User { hash?: string | null; loginAttempts?: number | null; lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; password?: string | null; } /** @@ -1276,6 +1283,13 @@ export interface UsersSelect { hash?: T; loginAttempts?: T; lockUntil?: T; + sessions?: + | T + | { + id?: T; + createdAt?: T; + expiresAt?: T; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/templates/with-postgres/package.json b/templates/with-postgres/package.json index 4a7661545..21c66711b 100644 --- a/templates/with-postgres/package.json +++ b/templates/with-postgres/package.json @@ -6,7 +6,6 @@ "type": "module", "scripts": { "build": "cross-env NODE_OPTIONS=--no-deprecation next build", - "ci": "payload migrate && pnpm build", "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev", "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev", "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap", @@ -16,21 +15,22 @@ "start": "cross-env NODE_OPTIONS=--no-deprecation next start", "test": "pnpm run test:int && pnpm run test:e2e", "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test", - "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" + "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts", + "ci": "payload migrate && pnpm build" }, "dependencies": { - "@payloadcms/db-postgres": "3.43.0", - "@payloadcms/next": "3.43.0", - "@payloadcms/payload-cloud": "3.43.0", - "@payloadcms/richtext-lexical": "3.43.0", - "@payloadcms/ui": "3.43.0", + "@payloadcms/next": "3.44.0", + "@payloadcms/payload-cloud": "3.44.0", + "@payloadcms/richtext-lexical": "3.44.0", + "@payloadcms/ui": "3.44.0", "cross-env": "^7.0.3", "graphql": "^16.8.1", "next": "15.3.0", - "payload": "3.43.0", + "payload": "3.44.0", "react": "19.1.0", "react-dom": "19.1.0", - "sharp": "0.32.6" + "sharp": "0.32.6", + "@payloadcms/db-postgres": "3.44.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/templates/with-postgres/src/migrations/20250616_201724_initial.json b/templates/with-postgres/src/migrations/20250629_202651_initial.json similarity index 90% rename from templates/with-postgres/src/migrations/20250616_201724_initial.json rename to templates/with-postgres/src/migrations/20250629_202651_initial.json index 25824e57b..b350cc63a 100644 --- a/templates/with-postgres/src/migrations/20250616_201724_initial.json +++ b/templates/with-postgres/src/migrations/20250629_202651_initial.json @@ -1,9 +1,93 @@ { - "id": "fd232901-8d4d-4ce0-93b1-988f26975158", + "id": "77ca1be4-16d8-4f65-bad8-0b86d2692050", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", "tables": { + "public.users_sessions": { + "name": "users_sessions", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "users_sessions_order_idx": { + "name": "users_sessions_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_sessions_parent_id_idx": { + "name": "users_sessions_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_sessions_parent_id_fk": { + "name": "users_sessions_parent_id_fk", + "tableFrom": "users_sessions", + "tableTo": "users", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.users": { "name": "users", "schema": "", diff --git a/templates/with-postgres/src/migrations/20250616_201724_initial.ts b/templates/with-postgres/src/migrations/20250629_202651_initial.ts similarity index 89% rename from templates/with-postgres/src/migrations/20250616_201724_initial.ts rename to templates/with-postgres/src/migrations/20250629_202651_initial.ts index 2ae627c7d..ebe2acf7d 100644 --- a/templates/with-postgres/src/migrations/20250616_201724_initial.ts +++ b/templates/with-postgres/src/migrations/20250629_202651_initial.ts @@ -2,7 +2,15 @@ import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres' export async function up({ db, payload, req }: MigrateUpArgs): Promise { await db.execute(sql` - CREATE TABLE "users" ( + CREATE TABLE "users_sessions" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "created_at" timestamp(3) with time zone, + "expires_at" timestamp(3) with time zone NOT NULL + ); + + CREATE TABLE "users" ( "id" serial PRIMARY KEY NOT NULL, "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, @@ -71,11 +79,14 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL ); + ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order"); + CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id"); CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at"); CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at"); CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email"); @@ -103,7 +114,8 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { export async function down({ db, payload, req }: MigrateDownArgs): Promise { await db.execute(sql` - DROP TABLE "users" CASCADE; + DROP TABLE "users_sessions" CASCADE; + DROP TABLE "users" CASCADE; DROP TABLE "media" CASCADE; DROP TABLE "payload_locked_documents" CASCADE; DROP TABLE "payload_locked_documents_rels" CASCADE; diff --git a/templates/with-postgres/src/migrations/index.ts b/templates/with-postgres/src/migrations/index.ts index f4ce9a04b..df35ff0f6 100644 --- a/templates/with-postgres/src/migrations/index.ts +++ b/templates/with-postgres/src/migrations/index.ts @@ -1,9 +1,9 @@ -import * as migration_20250616_201724_initial from './20250616_201724_initial' +import * as migration_20250629_202651_initial from './20250629_202651_initial' export const migrations = [ { - up: migration_20250616_201724_initial.up, - down: migration_20250616_201724_initial.down, - name: '20250616_201724_initial', + up: migration_20250629_202651_initial.up, + down: migration_20250629_202651_initial.down, + name: '20250629_202651_initial', }, ] diff --git a/templates/with-postgres/src/payload-types.ts b/templates/with-postgres/src/payload-types.ts index d3cacc91c..72b7ce4b6 100644 --- a/templates/with-postgres/src/payload-types.ts +++ b/templates/with-postgres/src/payload-types.ts @@ -128,6 +128,13 @@ export interface User { hash?: string | null; loginAttempts?: number | null; lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; password?: string | null; } /** @@ -220,6 +227,13 @@ export interface UsersSelect { hash?: T; loginAttempts?: T; lockUntil?: T; + sessions?: + | T + | { + id?: T; + createdAt?: T; + expiresAt?: T; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/templates/with-vercel-mongodb/package.json b/templates/with-vercel-mongodb/package.json index fb23c355b..7b7b14436 100644 --- a/templates/with-vercel-mongodb/package.json +++ b/templates/with-vercel-mongodb/package.json @@ -18,18 +18,18 @@ "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" }, "dependencies": { - "@payloadcms/db-mongodb": "3.43.0", - "@payloadcms/next": "3.43.0", - "@payloadcms/payload-cloud": "3.43.0", - "@payloadcms/richtext-lexical": "3.43.0", - "@payloadcms/storage-vercel-blob": "3.43.0", - "@payloadcms/ui": "3.43.0", + "@payloadcms/db-mongodb": "3.44.0", + "@payloadcms/next": "3.44.0", + "@payloadcms/payload-cloud": "3.44.0", + "@payloadcms/richtext-lexical": "3.44.0", + "@payloadcms/ui": "3.44.0", "cross-env": "^7.0.3", "graphql": "^16.8.1", "next": "15.3.0", - "payload": "3.43.0", + "payload": "3.44.0", "react": "19.1.0", - "react-dom": "19.1.0" + "react-dom": "19.1.0", + "@payloadcms/storage-vercel-blob": "3.44.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -49,7 +49,6 @@ "vite-tsconfig-paths": "5.1.4", "vitest": "3.2.3" }, - "packageManager": "pnpm@10.12.3", "engines": { "node": "^18.20.2 || >=20.9.0" }, @@ -57,5 +56,6 @@ "onlyBuiltDependencies": [ "sharp" ] - } + }, + "packageManager": "pnpm@10.12.4" } diff --git a/templates/with-vercel-mongodb/src/payload-types.ts b/templates/with-vercel-mongodb/src/payload-types.ts index d98da0d04..a9606af9a 100644 --- a/templates/with-vercel-mongodb/src/payload-types.ts +++ b/templates/with-vercel-mongodb/src/payload-types.ts @@ -6,24 +6,94 @@ * and re-run `payload generate:types` to regenerate this file. */ +/** + * Supported timezones in IANA format. + * + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "supportedTimezones". + */ +export type SupportedTimezones = + | 'Pacific/Midway' + | 'Pacific/Niue' + | 'Pacific/Honolulu' + | 'Pacific/Rarotonga' + | 'America/Anchorage' + | 'Pacific/Gambier' + | 'America/Los_Angeles' + | 'America/Tijuana' + | 'America/Denver' + | 'America/Phoenix' + | 'America/Chicago' + | 'America/Guatemala' + | 'America/New_York' + | 'America/Bogota' + | 'America/Caracas' + | 'America/Santiago' + | 'America/Buenos_Aires' + | 'America/Sao_Paulo' + | 'Atlantic/South_Georgia' + | 'Atlantic/Azores' + | 'Atlantic/Cape_Verde' + | 'Europe/London' + | 'Europe/Berlin' + | 'Africa/Lagos' + | 'Europe/Athens' + | 'Africa/Cairo' + | 'Europe/Moscow' + | 'Asia/Riyadh' + | 'Asia/Dubai' + | 'Asia/Baku' + | 'Asia/Karachi' + | 'Asia/Tashkent' + | 'Asia/Calcutta' + | 'Asia/Dhaka' + | 'Asia/Almaty' + | 'Asia/Jakarta' + | 'Asia/Bangkok' + | 'Asia/Shanghai' + | 'Asia/Singapore' + | 'Asia/Tokyo' + | 'Asia/Seoul' + | 'Australia/Brisbane' + | 'Australia/Sydney' + | 'Pacific/Guam' + | 'Pacific/Noumea' + | 'Pacific/Auckland' + | 'Pacific/Fiji'; + export interface Config { auth: { users: UserAuthOperations; }; + blocks: {}; collections: { users: User; media: Media; + 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; + collectionsJoins: {}; + collectionsSelect: { + users: UsersSelect | UsersSelect; + media: MediaSelect | MediaSelect; + 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; + 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; + 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; + }; db: { defaultIDType: string; }; globals: {}; + globalsSelect: {}; locale: null; user: User & { collection: 'users'; }; + jobs: { + tasks: unknown; + workflows: unknown; + }; } export interface UserAuthOperations { forgotPassword: { @@ -58,6 +128,13 @@ export interface User { hash?: string | null; loginAttempts?: number | null; lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; password?: string | null; } /** @@ -79,6 +156,29 @@ export interface Media { 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: '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". @@ -113,6 +213,78 @@ export interface PayloadMigration { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "users_select". + */ +export interface UsersSelect { + updatedAt?: T; + createdAt?: T; + email?: T; + resetPasswordToken?: T; + resetPasswordExpiration?: T; + salt?: T; + hash?: T; + loginAttempts?: T; + lockUntil?: T; + sessions?: + | T + | { + id?: T; + createdAt?: T; + expiresAt?: T; + }; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "media_select". + */ +export interface MediaSelect { + alt?: T; + 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 { + 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 { + 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 { + name?: T; + batch?: T; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "auth". diff --git a/templates/with-vercel-postgres/package.json b/templates/with-vercel-postgres/package.json index 3a248f5bb..fa808efcf 100644 --- a/templates/with-vercel-postgres/package.json +++ b/templates/with-vercel-postgres/package.json @@ -6,7 +6,6 @@ "type": "module", "scripts": { "build": "cross-env NODE_OPTIONS=--no-deprecation next build", - "ci": "payload migrate && pnpm build", "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev", "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev", "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap", @@ -16,21 +15,22 @@ "start": "cross-env NODE_OPTIONS=--no-deprecation next start", "test": "pnpm run test:int && pnpm run test:e2e", "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test", - "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" + "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts", + "ci": "payload migrate && pnpm build" }, "dependencies": { - "@payloadcms/db-vercel-postgres": "3.43.0", - "@payloadcms/next": "3.43.0", - "@payloadcms/payload-cloud": "3.43.0", - "@payloadcms/richtext-lexical": "3.43.0", - "@payloadcms/storage-vercel-blob": "3.43.0", - "@payloadcms/ui": "3.43.0", + "@payloadcms/next": "3.44.0", + "@payloadcms/payload-cloud": "3.44.0", + "@payloadcms/richtext-lexical": "3.44.0", + "@payloadcms/ui": "3.44.0", "cross-env": "^7.0.3", "graphql": "^16.8.1", "next": "15.3.0", - "payload": "3.43.0", + "payload": "3.44.0", "react": "19.1.0", - "react-dom": "19.1.0" + "react-dom": "19.1.0", + "@payloadcms/db-vercel-postgres": "3.44.0", + "@payloadcms/storage-vercel-blob": "3.44.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -50,7 +50,6 @@ "vite-tsconfig-paths": "5.1.4", "vitest": "3.2.3" }, - "packageManager": "pnpm@10.12.3", "engines": { "node": "^18.20.2 || >=20.9.0" }, @@ -58,5 +57,6 @@ "onlyBuiltDependencies": [ "sharp" ] - } + }, + "packageManager": "pnpm@10.12.4" } diff --git a/templates/with-vercel-postgres/src/migrations/20250624_171210_initial.json b/templates/with-vercel-postgres/src/migrations/20250629_202637_initial.json similarity index 90% rename from templates/with-vercel-postgres/src/migrations/20250624_171210_initial.json rename to templates/with-vercel-postgres/src/migrations/20250629_202637_initial.json index d60cff505..7d705c248 100644 --- a/templates/with-vercel-postgres/src/migrations/20250624_171210_initial.json +++ b/templates/with-vercel-postgres/src/migrations/20250629_202637_initial.json @@ -1,9 +1,93 @@ { - "id": "64c3b19c-9d51-4e55-9c9c-333d9b73bc28", + "id": "f6e3ff1c-ee75-4b76-9e36-3e30ac7f87a1", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", "tables": { + "public.users_sessions": { + "name": "users_sessions", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "users_sessions_order_idx": { + "name": "users_sessions_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_sessions_parent_id_idx": { + "name": "users_sessions_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_sessions_parent_id_fk": { + "name": "users_sessions_parent_id_fk", + "tableFrom": "users_sessions", + "tableTo": "users", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.users": { "name": "users", "schema": "", diff --git a/templates/with-vercel-postgres/src/migrations/20250624_171210_initial.ts b/templates/with-vercel-postgres/src/migrations/20250629_202637_initial.ts similarity index 89% rename from templates/with-vercel-postgres/src/migrations/20250624_171210_initial.ts rename to templates/with-vercel-postgres/src/migrations/20250629_202637_initial.ts index 98ded729f..4d73fb6f4 100644 --- a/templates/with-vercel-postgres/src/migrations/20250624_171210_initial.ts +++ b/templates/with-vercel-postgres/src/migrations/20250629_202637_initial.ts @@ -2,7 +2,15 @@ import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-vercel-postg export async function up({ db, payload, req }: MigrateUpArgs): Promise { await db.execute(sql` - CREATE TABLE "users" ( + CREATE TABLE "users_sessions" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "created_at" timestamp(3) with time zone, + "expires_at" timestamp(3) with time zone NOT NULL + ); + + CREATE TABLE "users" ( "id" serial PRIMARY KEY NOT NULL, "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, @@ -71,11 +79,14 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL ); + ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_media_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order"); + CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id"); CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at"); CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at"); CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email"); @@ -103,7 +114,8 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { export async function down({ db, payload, req }: MigrateDownArgs): Promise { await db.execute(sql` - DROP TABLE "users" CASCADE; + DROP TABLE "users_sessions" CASCADE; + DROP TABLE "users" CASCADE; DROP TABLE "media" CASCADE; DROP TABLE "payload_locked_documents" CASCADE; DROP TABLE "payload_locked_documents_rels" CASCADE; diff --git a/templates/with-vercel-postgres/src/migrations/index.ts b/templates/with-vercel-postgres/src/migrations/index.ts index 20e969e83..064143082 100644 --- a/templates/with-vercel-postgres/src/migrations/index.ts +++ b/templates/with-vercel-postgres/src/migrations/index.ts @@ -1,9 +1,9 @@ -import * as migration_20250624_171210_initial from './20250624_171210_initial' +import * as migration_20250629_202637_initial from './20250629_202637_initial' export const migrations = [ { - up: migration_20250624_171210_initial.up, - down: migration_20250624_171210_initial.down, - name: '20250624_171210_initial', + up: migration_20250629_202637_initial.up, + down: migration_20250629_202637_initial.down, + name: '20250629_202637_initial', }, ] diff --git a/templates/with-vercel-postgres/src/payload-types.ts b/templates/with-vercel-postgres/src/payload-types.ts index d3cacc91c..72b7ce4b6 100644 --- a/templates/with-vercel-postgres/src/payload-types.ts +++ b/templates/with-vercel-postgres/src/payload-types.ts @@ -128,6 +128,13 @@ export interface User { hash?: string | null; loginAttempts?: number | null; lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; password?: string | null; } /** @@ -220,6 +227,13 @@ export interface UsersSelect { hash?: T; loginAttempts?: T; lockUntil?: T; + sessions?: + | T + | { + id?: T; + createdAt?: T; + expiresAt?: T; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/templates/with-vercel-website/package.json b/templates/with-vercel-website/package.json index 54b389f20..85b057c88 100644 --- a/templates/with-vercel-website/package.json +++ b/templates/with-vercel-website/package.json @@ -7,7 +7,6 @@ "scripts": { "build": "cross-env NODE_OPTIONS=--no-deprecation next build", "postbuild": "next-sitemap --config next-sitemap.config.cjs", - "ci": "payload migrate && pnpm build", "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev", "dev:prod": "cross-env NODE_OPTIONS=--no-deprecation rm -rf .next && pnpm build && pnpm start", "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap", @@ -20,22 +19,21 @@ "start": "cross-env NODE_OPTIONS=--no-deprecation next start", "test": "pnpm run test:int && pnpm run test:e2e", "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test --config=playwright.config.ts", - "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" + "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts", + "ci": "payload migrate && pnpm build" }, "dependencies": { - "@payloadcms/admin-bar": "3.43.0", - "@payloadcms/db-vercel-postgres": "3.43.0", - "@payloadcms/live-preview-react": "3.43.0", - "@payloadcms/next": "3.43.0", - "@payloadcms/payload-cloud": "3.43.0", - "@payloadcms/plugin-form-builder": "3.43.0", - "@payloadcms/plugin-nested-docs": "3.43.0", - "@payloadcms/plugin-redirects": "3.43.0", - "@payloadcms/plugin-search": "3.43.0", - "@payloadcms/plugin-seo": "3.43.0", - "@payloadcms/richtext-lexical": "3.43.0", - "@payloadcms/storage-vercel-blob": "3.43.0", - "@payloadcms/ui": "3.43.0", + "@payloadcms/admin-bar": "3.44.0", + "@payloadcms/live-preview-react": "3.44.0", + "@payloadcms/next": "3.44.0", + "@payloadcms/payload-cloud": "3.44.0", + "@payloadcms/plugin-form-builder": "3.44.0", + "@payloadcms/plugin-nested-docs": "3.44.0", + "@payloadcms/plugin-redirects": "3.44.0", + "@payloadcms/plugin-search": "3.44.0", + "@payloadcms/plugin-seo": "3.44.0", + "@payloadcms/richtext-lexical": "3.44.0", + "@payloadcms/ui": "3.44.0", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", @@ -49,14 +47,16 @@ "lucide-react": "^0.378.0", "next": "15.3.3", "next-sitemap": "^4.2.3", - "payload": "3.43.0", + "payload": "3.44.0", "prism-react-renderer": "^2.3.1", "react": "19.1.0", "react-dom": "19.1.0", "react-hook-form": "7.45.4", "sharp": "0.32.6", "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "@payloadcms/db-vercel-postgres": "3.44.0", + "@payloadcms/storage-vercel-blob": "3.44.0" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -82,7 +82,6 @@ "vite-tsconfig-paths": "5.1.4", "vitest": "3.2.3" }, - "packageManager": "pnpm@10.12.3", "engines": { "node": "^18.20.2 || >=20.9.0" }, @@ -90,5 +89,6 @@ "onlyBuiltDependencies": [ "sharp" ] - } + }, + "packageManager": "pnpm@10.12.4" } diff --git a/templates/with-vercel-website/src/migrations/20250616_201710_initial.json b/templates/with-vercel-website/src/migrations/20250629_202644_initial.json similarity index 99% rename from templates/with-vercel-website/src/migrations/20250616_201710_initial.json rename to templates/with-vercel-website/src/migrations/20250629_202644_initial.json index 0314d7e8f..cb61946d1 100644 --- a/templates/with-vercel-website/src/migrations/20250616_201710_initial.json +++ b/templates/with-vercel-website/src/migrations/20250629_202644_initial.json @@ -1,5 +1,5 @@ { - "id": "6a44166d-9436-4c05-8f1e-a79a4d523d1c", + "id": "a31dfac2-cdc9-41d5-97e2-2aa6f567d3f8", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", @@ -4639,6 +4639,90 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.users_sessions": { + "name": "users_sessions", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "users_sessions_order_idx": { + "name": "users_sessions_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_sessions_parent_id_idx": { + "name": "users_sessions_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_sessions_parent_id_fk": { + "name": "users_sessions_parent_id_fk", + "tableFrom": "users_sessions", + "tableTo": "users", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.users": { "name": "users", "schema": "", diff --git a/templates/with-vercel-website/src/migrations/20250616_201710_initial.ts b/templates/with-vercel-website/src/migrations/20250629_202644_initial.ts similarity index 99% rename from templates/with-vercel-website/src/migrations/20250616_201710_initial.ts rename to templates/with-vercel-website/src/migrations/20250629_202644_initial.ts index 471b0a139..0aea12f95 100644 --- a/templates/with-vercel-website/src/migrations/20250616_201710_initial.ts +++ b/templates/with-vercel-website/src/migrations/20250629_202644_initial.ts @@ -420,6 +420,14 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL ); + CREATE TABLE "users_sessions" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "created_at" timestamp(3) with time zone, + "expires_at" timestamp(3) with time zone NOT NULL + ); + CREATE TABLE "users" ( "id" serial PRIMARY KEY NOT NULL, "name" varchar, @@ -817,6 +825,7 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { ALTER TABLE "categories_breadcrumbs" ADD CONSTRAINT "categories_breadcrumbs_doc_id_categories_id_fk" FOREIGN KEY ("doc_id") REFERENCES "public"."categories"("id") ON DELETE set null ON UPDATE no action; ALTER TABLE "categories_breadcrumbs" ADD CONSTRAINT "categories_breadcrumbs_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "categories" ADD CONSTRAINT "categories_parent_id_categories_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."categories"("id") ON DELETE set null ON UPDATE no action; + ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "redirects_rels" ADD CONSTRAINT "redirects_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."redirects"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "redirects_rels" ADD CONSTRAINT "redirects_rels_pages_fk" FOREIGN KEY ("pages_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "redirects_rels" ADD CONSTRAINT "redirects_rels_posts_fk" FOREIGN KEY ("posts_id") REFERENCES "public"."posts"("id") ON DELETE cascade ON UPDATE no action; @@ -984,6 +993,8 @@ export async function up({ db, payload, req }: MigrateUpArgs): Promise { CREATE INDEX "categories_parent_idx" ON "categories" USING btree ("parent_id"); CREATE INDEX "categories_updated_at_idx" ON "categories" USING btree ("updated_at"); CREATE INDEX "categories_created_at_idx" ON "categories" USING btree ("created_at"); + CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order"); + CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id"); CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at"); CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at"); CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email"); @@ -1126,6 +1137,7 @@ export async function down({ db, payload, req }: MigrateDownArgs): Promise DROP TABLE "media" CASCADE; DROP TABLE "categories_breadcrumbs" CASCADE; DROP TABLE "categories" CASCADE; + DROP TABLE "users_sessions" CASCADE; DROP TABLE "users" CASCADE; DROP TABLE "redirects" CASCADE; DROP TABLE "redirects_rels" CASCADE; diff --git a/templates/with-vercel-website/src/migrations/index.ts b/templates/with-vercel-website/src/migrations/index.ts index c6781eeb6..2bf4a5d3a 100644 --- a/templates/with-vercel-website/src/migrations/index.ts +++ b/templates/with-vercel-website/src/migrations/index.ts @@ -1,9 +1,9 @@ -import * as migration_20250616_201710_initial from './20250616_201710_initial' +import * as migration_20250629_202644_initial from './20250629_202644_initial' export const migrations = [ { - up: migration_20250616_201710_initial.up, - down: migration_20250616_201710_initial.down, - name: '20250616_201710_initial', + up: migration_20250629_202644_initial.up, + down: migration_20250629_202644_initial.down, + name: '20250629_202644_initial', }, ] diff --git a/templates/with-vercel-website/src/payload-types.ts b/templates/with-vercel-website/src/payload-types.ts index 7ac31f6ed..d16226609 100644 --- a/templates/with-vercel-website/src/payload-types.ts +++ b/templates/with-vercel-website/src/payload-types.ts @@ -383,6 +383,13 @@ export interface User { hash?: string | null; loginAttempts?: number | null; lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; password?: string | null; } /** @@ -1276,6 +1283,13 @@ export interface UsersSelect { hash?: T; loginAttempts?: T; lockUntil?: T; + sessions?: + | T + | { + id?: T; + createdAt?: T; + expiresAt?: T; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/tools/scripts/src/build-template-with-local-pkgs.ts b/tools/scripts/src/build-template-with-local-pkgs.ts index 20a5bcc1c..25e1f2503 100644 --- a/tools/scripts/src/build-template-with-local-pkgs.ts +++ b/tools/scripts/src/build-template-with-local-pkgs.ts @@ -1,6 +1,6 @@ import { TEMPLATES_DIR } from '@tools/constants' import chalk from 'chalk' -import { exec as execOrig, execSync } from 'child_process' +import { execSync } from 'child_process' import fs from 'fs/promises' import path from 'path' @@ -46,6 +46,8 @@ async function main() { const initialPackageJson = await fs.readFile(packageJsonPath, 'utf-8') const initialPackageJsonObj = JSON.parse(initialPackageJson) + // Update the package.json dependencies to use any specific version instead of `workspace:*`, so that + // the next pnpm add command can install the local packages correctly. updatePackageJSONDependencies({ latestVersion: '3.42.0', packageJson: initialPackageJsonObj }) await fs.writeFile(packageJsonPath, JSON.stringify(initialPackageJsonObj, null, 2)) @@ -85,7 +87,14 @@ DATABASE_URI=${databaseConnection} POSTGRES_URL=${databaseConnection} BLOB_READ_WRITE_TOKEN=vercel_blob_rw_TEST_asdf`, ) - execSync('pnpm run build', execOpts) + // Important: run generate:types and generate:importmap first + if (templateName !== 'plugin') { + // TODO: fix in a separate PR - these commands currently fail in the plugin template + execSync('pnpm --ignore-workspace run generate:types', execOpts) + execSync('pnpm --ignore-workspace run generate:importmap', execOpts) + } + + execSync('pnpm --ignore-workspace run build', execOpts) header(`\nšŸŽ‰ Done!`) } diff --git a/tools/scripts/src/generate-template-variations.ts b/tools/scripts/src/generate-template-variations.ts index 76bac5a9e..74c5f7a01 100644 --- a/tools/scripts/src/generate-template-variations.ts +++ b/tools/scripts/src/generate-template-variations.ts @@ -323,13 +323,19 @@ async function main() { // Generate importmap log('Generating import map') - execSyncSafe(`pnpm ${workspace ? '' : '--ignore-workspace'} generate:importmap`, { + execSyncSafe(`pnpm ${workspace ? '' : '--ignore-workspace '}generate:importmap`, { + cwd: destDir, + }) + + // Generate types + log('Generating types') + execSyncSafe(`pnpm ${workspace ? '' : '--ignore-workspace '}generate:types`, { cwd: destDir, }) if (shouldBuild) { log('Building...') - execSyncSafe(`pnpm ${workspace ? '' : '--ignore-workspace'} build`, { cwd: destDir }) + execSyncSafe(`pnpm ${workspace ? '' : '--ignore-workspace '}build`, { cwd: destDir }) } // TODO: Email?