Files
payloadcms/packages/payload/src/utilities/createLocalReq.ts
Alessio Gravili 4458f74cef ci: template errors not being caught due. fix: error due to updated generated-types User type (#12973)
This PR consists of two separate changes. One change cannot pass CI
without the other, so both are included in this single PR.


## CI - ensure types are generated

Our website template is currently failing to build due to a type error.
This error was introduced by a change in our generated types.

Our CI did not catch this issue because it wasn't generating types /
import map before attempting to build the templates. This PR updates the
CI to generate types first.

It also updates some CI step names for improved clarity.

## Fix: type error

![Screenshot 2025-06-29 at 12 53
49@2x](https://github.com/user-attachments/assets/962f1513-bc6c-4e12-9b74-9b891c49900b)


This fixes the type error by ensuring we consistently use the _same_
generated `TypedUser` object within payload, instead of `BaseUser`.
Previously, we sometimes used the generated-types user and sometimes the
base user, which was causing type conflicts depending on what the
generated user type was.

It also deprecates the `User` type (which was essentially just
`BaseUser`), as consumers should use `TypedUser` instead. `TypedUser`
will automatically fall back to `BaseUser` if no generated types exists,
but will accept passing it a generated-types User.

Without this change, additional properties added to the user via
generated-types may cause the user object to not be accepted by
functions that only accept a `User` instead of a `TypedUser`, which is
what failed here.

## Templates: re-generate templates to update generated types

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210668927737258
2025-06-29 14:27:50 -07:00

144 lines
4.1 KiB
TypeScript

import type { Payload, RequestContext, TypedLocale, TypedUser } from '../index.js'
import type { PayloadRequest } from '../types/index.js'
import { getDataLoader } from '../collections/dataloader.js'
import { getLocalI18n } from '../translations/getLocalI18n.js'
import { sanitizeFallbackLocale } from '../utilities/sanitizeFallbackLocale.js'
function getRequestContext(
req: Partial<PayloadRequest> = { context: null } as unknown as PayloadRequest,
context: RequestContext = {},
): RequestContext {
if (req.context) {
if (Object.keys(req.context).length === 0 && req.context.constructor === Object) {
// if req.context is `{}` avoid unnecessary spread
return context
} else {
return { ...req.context, ...context }
}
} else {
return context
}
}
const attachFakeURLProperties = (req: Partial<PayloadRequest>, urlSuffix?: string) => {
/**
* *NOTE*
* If no URL is provided, the local API was called outside
* the context of a request. Therefore we create a fake URL object.
* `ts-expect-error` is used below for properties that are 'read-only'.
* Since they do not exist yet we can safely ignore the error.
*/
let urlObject: undefined | URL
function getURLObject() {
if (urlObject) {
return urlObject
}
const fallbackURL = `http://${req.host || 'localhost'}${urlSuffix || ''}`
const urlToUse =
req?.url || req.payload?.config?.serverURL
? `${req.payload?.config.serverURL}${urlSuffix || ''}`
: fallbackURL
try {
urlObject = new URL(urlToUse)
} catch (_err) {
req.payload?.logger.error(
`Failed to create URL object from URL: ${urlToUse}, falling back to ${fallbackURL}`,
)
urlObject = new URL(fallbackURL)
}
return urlObject
}
if (!req.host) {
req.host = getURLObject().host
}
if (!req.protocol) {
req.protocol = getURLObject().protocol
}
if (!req.pathname) {
req.pathname = getURLObject().pathname
}
if (!req.searchParams) {
// @ts-expect-error eslint-disable-next-line no-param-reassign
req.searchParams = getURLObject().searchParams
}
if (!req.origin) {
// @ts-expect-error eslint-disable-next-line no-param-reassign
req.origin = getURLObject().origin
}
if (!req?.url) {
// @ts-expect-error eslint-disable-next-line no-param-reassign
req.url = getURLObject().href
}
}
export type CreateLocalReqOptions = {
context?: RequestContext
fallbackLocale?: false | TypedLocale
locale?: string
req?: Partial<PayloadRequest>
urlSuffix?: string
user?: TypedUser
}
type CreateLocalReq = (options: CreateLocalReqOptions, payload: Payload) => Promise<PayloadRequest>
export const createLocalReq: CreateLocalReq = async (
{ context, fallbackLocale, locale: localeArg, req = {} as PayloadRequest, urlSuffix, user },
payload,
): Promise<PayloadRequest> => {
const localization = payload.config?.localization
if (localization) {
const locale = localeArg === '*' ? 'all' : localeArg
const defaultLocale = localization.defaultLocale
const localeCandidate = locale || req?.locale || req?.query?.locale
req.locale =
localeCandidate && typeof localeCandidate === 'string' ? localeCandidate : defaultLocale
const sanitizedFallback = sanitizeFallbackLocale({
fallbackLocale: fallbackLocale!,
locale: req.locale,
localization,
})
req.fallbackLocale = sanitizedFallback!
}
const i18n =
req?.i18n ||
(await getLocalI18n({ config: payload.config, language: payload.config.i18n.fallbackLanguage }))
if (!req.headers) {
// @ts-expect-error eslint-disable-next-line no-param-reassign
req.headers = new Headers()
}
req.context = getRequestContext(req, context)
req.payloadAPI = req?.payloadAPI || 'local'
req.payload = payload
req.i18n = i18n
req.t = i18n.t
req.user = user || req?.user || null
req.payloadDataLoader = req?.payloadDataLoader || getDataLoader(req as PayloadRequest)
req.routeParams = req?.routeParams || {}
req.query = req?.query || {}
attachFakeURLProperties(req, urlSuffix)
return req as PayloadRequest
}