@payloadcms/figma 0.0.1-alpha.41 → 0.0.1-alpha.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/oauth-flow.d.ts.map +1 -1
- package/dist/auth/oauth-flow.js +6 -3
- package/dist/auth/oauth-flow.js.map +1 -1
- package/dist/auth/refresh.d.ts +2 -1
- package/dist/auth/refresh.d.ts.map +1 -1
- package/dist/auth/refresh.js +11 -3
- package/dist/auth/refresh.js.map +1 -1
- package/dist/auth/token-store.d.ts +6 -4
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +17 -14
- package/dist/auth/token-store.js.map +1 -1
- package/dist/auth/types.d.ts +7 -4
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js.map +1 -1
- package/dist/cli.js +8 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +2 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list-tokens.d.ts +3 -3
- package/dist/commands/list-tokens.d.ts.map +1 -1
- package/dist/commands/list-tokens.js +20 -8
- package/dist/commands/list-tokens.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +12 -4
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts +5 -4
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +55 -12
- package/dist/commands/logout.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +19 -4
- package/dist/constants.js.map +1 -1
- package/dist/db-content-api/generated/content-api-types.d.ts +166 -277
- package/dist/db-content-api/generated/content-api-types.d.ts.map +1 -1
- package/dist/db-content-api/generated/content-api-types.js.map +1 -1
- package/dist/db-content-api/index.d.ts.map +1 -1
- package/dist/db-content-api/index.js +116 -64
- package/dist/db-content-api/index.js.map +1 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts +31 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.d.ts.map +1 -1
- package/dist/db-content-api/temp-utilities/unwrapDocument.js +24 -24
- package/dist/db-content-api/temp-utilities/unwrapDocument.js.map +1 -1
- package/dist/db-content-api/utilities/data/castFieldValue.d.ts.map +1 -1
- package/dist/db-content-api/utilities/data/castFieldValue.js +4 -1
- package/dist/db-content-api/utilities/data/castFieldValue.js.map +1 -1
- package/dist/db-content-api/utilities/data/index.d.ts +2 -0
- package/dist/db-content-api/utilities/data/index.d.ts.map +1 -1
- package/dist/db-content-api/utilities/data/index.js +23 -2
- package/dist/db-content-api/utilities/data/index.js.map +1 -1
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.d.ts +11 -0
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.js +55 -0
- package/dist/db-content-api/utilities/meta/buildLocalizedPaths.js.map +1 -0
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts +22 -0
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/buildMeta.js +30 -0
- package/dist/db-content-api/utilities/meta/buildMeta.js.map +1 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts +7 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.js +151 -0
- package/dist/db-content-api/utilities/meta/buildPathTypes.js.map +1 -0
- package/dist/db-content-api/utilities/meta/index.d.ts +5 -0
- package/dist/db-content-api/utilities/meta/index.d.ts.map +1 -0
- package/dist/db-content-api/utilities/meta/index.js +5 -0
- package/dist/db-content-api/utilities/meta/index.js.map +1 -0
- package/dist/db-content-api/utilities/where.d.ts.map +1 -1
- package/dist/db-content-api/utilities/where.js +17 -3
- package/dist/db-content-api/utilities/where.js.map +1 -1
- package/dist/oauth/components/LogoutButton/index.d.ts.map +1 -1
- package/dist/oauth/components/LogoutButton/index.js +5 -3
- package/dist/oauth/components/LogoutButton/index.js.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLoginEndpoint.js +17 -7
- package/dist/oauth/endpoints/getLoginEndpoint.js.map +1 -1
- package/dist/oauth/endpoints/getLogoutEndpoint.d.ts.map +1 -1
- package/dist/oauth/endpoints/getLogoutEndpoint.js +7 -4
- package/dist/oauth/endpoints/getLogoutEndpoint.js.map +1 -1
- package/dist/plugin/build-config.js +3 -3
- package/dist/plugin/build-config.js.map +1 -1
- package/dist/storage-content-api/api-types.d.ts +8 -1
- package/dist/storage-content-api/api-types.d.ts.map +1 -1
- package/dist/storage-content-api/api-types.js.map +1 -1
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.d.ts.map +1 -1
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js +3 -3
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js.map +1 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.d.ts.map +1 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.js +9 -1
- package/dist/storage-content-api/client-uploads/generateSignedURL.js.map +1 -1
- package/dist/storage-content-api/generateURL.d.ts.map +1 -1
- package/dist/storage-content-api/generateURL.js +3 -2
- package/dist/storage-content-api/generateURL.js.map +1 -1
- package/dist/storage-content-api/handleDelete.d.ts.map +1 -1
- package/dist/storage-content-api/handleDelete.js +4 -2
- package/dist/storage-content-api/handleDelete.js.map +1 -1
- package/dist/storage-content-api/handleUpload.d.ts +1 -1
- package/dist/storage-content-api/handleUpload.d.ts.map +1 -1
- package/dist/storage-content-api/handleUpload.js +11 -3
- package/dist/storage-content-api/handleUpload.js.map +1 -1
- package/dist/storage-content-api/staticHandler.js +1 -0
- package/dist/storage-content-api/staticHandler.js.map +1 -1
- package/dist/storage-content-api/utilities/getSafeFilename.d.ts +12 -0
- package/dist/storage-content-api/utilities/getSafeFilename.d.ts.map +1 -0
- package/dist/storage-content-api/utilities/getSafeFilename.js +61 -0
- package/dist/storage-content-api/utilities/getSafeFilename.js.map +1 -0
- package/dist/storage-content-api/utilities/index.d.ts +2 -0
- package/dist/storage-content-api/utilities/index.d.ts.map +1 -0
- package/dist/storage-content-api/utilities/index.js +3 -0
- package/dist/storage-content-api/utilities/index.js.map +1 -0
- package/dist/templates/.env.example +11 -0
- package/dist/utils/env-management.d.ts +9 -1
- package/dist/utils/env-management.d.ts.map +1 -1
- package/dist/utils/env-management.js +39 -17
- package/dist/utils/env-management.js.map +1 -1
- package/dist/utils/format-env-suffix.d.ts +12 -0
- package/dist/utils/format-env-suffix.d.ts.map +1 -0
- package/dist/utils/format-env-suffix.js +13 -0
- package/dist/utils/format-env-suffix.js.map +1 -0
- package/dist/utils/lambda-config.js +26 -12
- package/dist/utils/lambda-config.js.map +1 -1
- package/dist/utils/messages.d.ts.map +1 -1
- package/dist/utils/messages.js +1 -2
- package/dist/utils/messages.js.map +1 -1
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +3 -0
- package/dist/utils/project.js.map +1 -1
- package/package.json +2 -2
- package/dist/db-content-api/temp-utilities/pagination.d.ts +0 -13
- package/dist/db-content-api/temp-utilities/pagination.d.ts.map +0 -1
- package/dist/db-content-api/temp-utilities/pagination.js +0 -45
- package/dist/db-content-api/temp-utilities/pagination.js.map +0 -1
- package/dist/oauth/utilities/getAdminPath.d.ts +0 -8
- package/dist/oauth/utilities/getAdminPath.d.ts.map +0 -1
- package/dist/oauth/utilities/getAdminPath.js +0 -9
- package/dist/oauth/utilities/getAdminPath.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { dataFromContentAPI } from '../utilities/data/index.js';
|
|
2
2
|
// Helper to unwrap Content API document format to Payload format
|
|
3
3
|
// Content API returns:
|
|
4
|
-
// - For documents: { id,
|
|
5
|
-
// - For versions: { id,
|
|
4
|
+
// - For documents: { id, contentSystemId, collection, doc: { ...fields, createdAt, updatedAt } }
|
|
5
|
+
// - For versions: { id, contentSystemId, collection, versionDoc: { id, createdAt, updatedAt, latest, parent, version: {...fields} } }
|
|
6
6
|
// Payload expects:
|
|
7
7
|
// - For documents: { id, ...fields, createdAt, updatedAt }
|
|
8
8
|
// - For versions: { id, parent, latest, version: { ...fields }, createdAt, updatedAt }
|
|
@@ -10,18 +10,13 @@ export function unwrapDocument(payload, doc, collectionSlug) {
|
|
|
10
10
|
if (!doc) {
|
|
11
11
|
return doc;
|
|
12
12
|
}
|
|
13
|
-
// Check if this is a DocumentVersion (has
|
|
14
|
-
const isVersion = '
|
|
13
|
+
// Check if this is a DocumentVersion (has versionDoc field)
|
|
14
|
+
const isVersion = 'versionDoc' in doc;
|
|
15
15
|
if (isVersion) {
|
|
16
16
|
// For versions, Payload expects nested structure with 'version' field
|
|
17
|
-
const versionDoc = doc;
|
|
17
|
+
const { versionDoc } = doc;
|
|
18
18
|
const baseDoc = {
|
|
19
|
-
|
|
20
|
-
createdAt: versionDoc.createdAt,
|
|
21
|
-
latest: versionDoc.latest,
|
|
22
|
-
parent: versionDoc.documentId,
|
|
23
|
-
updatedAt: versionDoc.updatedAt,
|
|
24
|
-
version: versionDoc.data
|
|
19
|
+
...versionDoc
|
|
25
20
|
};
|
|
26
21
|
// Transform data from Content API format to Payload format
|
|
27
22
|
if (collectionSlug) {
|
|
@@ -34,20 +29,25 @@ export function unwrapDocument(payload, doc, collectionSlug) {
|
|
|
34
29
|
}
|
|
35
30
|
}
|
|
36
31
|
return baseDoc;
|
|
37
|
-
} else {
|
|
38
|
-
// For regular documents, spread data at root level
|
|
39
|
-
const baseDoc = {
|
|
40
|
-
...doc.data,
|
|
41
|
-
id: doc.id,
|
|
42
|
-
createdAt: doc.createdAt,
|
|
43
|
-
updatedAt: doc.updatedAt
|
|
44
|
-
};
|
|
45
|
-
// Transform data from Content API format to Payload format
|
|
46
|
-
if (collectionSlug) {
|
|
47
|
-
return dataFromContentAPI(payload, collectionSlug, baseDoc);
|
|
48
|
-
}
|
|
49
|
-
return baseDoc;
|
|
50
32
|
}
|
|
33
|
+
// For regular documents, spread doc data at root level
|
|
34
|
+
const { id, doc: docData } = doc;
|
|
35
|
+
const baseDoc = {
|
|
36
|
+
...docData,
|
|
37
|
+
id
|
|
38
|
+
};
|
|
39
|
+
// Transform data from Content API format to Payload format
|
|
40
|
+
if (collectionSlug) {
|
|
41
|
+
return dataFromContentAPI(payload, collectionSlug, baseDoc);
|
|
42
|
+
}
|
|
43
|
+
return baseDoc;
|
|
51
44
|
}
|
|
45
|
+
export const unwrapFindResponse = (payload, response, collection)=>{
|
|
46
|
+
const { data, ...paginationFields } = response;
|
|
47
|
+
return {
|
|
48
|
+
docs: data.map((doc)=>unwrapDocument(payload, doc, collection)),
|
|
49
|
+
...paginationFields
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
52
|
|
|
53
53
|
//# sourceMappingURL=unwrapDocument.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/db-content-api/temp-utilities/unwrapDocument.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport type { components } from '../generated/content-api-types.js'\n\nimport { dataFromContentAPI } from '../utilities/data/index.js'\n\n// Helper to unwrap Content API document format to Payload format\n// Content API returns:\n// - For documents: { id,
|
|
1
|
+
{"version":3,"sources":["../../../src/db-content-api/temp-utilities/unwrapDocument.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport type { components } from '../generated/content-api-types.js'\n\nimport { dataFromContentAPI } from '../utilities/data/index.js'\n\ntype DocumentResponse = { id: string } & components['schemas']['Document']\ntype DocumentVersionResponse = { id: string } & components['schemas']['DocumentVersion']\n\n// Helper to unwrap Content API document format to Payload format\n// Content API returns:\n// - For documents: { id, contentSystemId, collection, doc: { ...fields, createdAt, updatedAt } }\n// - For versions: { id, contentSystemId, collection, versionDoc: { id, createdAt, updatedAt, latest, parent, version: {...fields} } }\n// Payload expects:\n// - For documents: { id, ...fields, createdAt, updatedAt }\n// - For versions: { id, parent, latest, version: { ...fields }, createdAt, updatedAt }\nexport function unwrapDocument(\n payload: Payload,\n doc: DocumentResponse | DocumentVersionResponse,\n collectionSlug?: string,\n): Record<string, unknown> {\n if (!doc) {\n return doc as unknown as Record<string, unknown>\n }\n\n // Check if this is a DocumentVersion (has versionDoc field)\n const isVersion = 'versionDoc' in doc\n\n if (isVersion) {\n // For versions, Payload expects nested structure with 'version' field\n const { versionDoc } = doc\n const baseDoc = { ...versionDoc } as Record<string, unknown>\n\n // Transform data from Content API format to Payload format\n if (collectionSlug) {\n // Only transform the nested 'version' field\n baseDoc.version = dataFromContentAPI(payload, collectionSlug, baseDoc.version) as Record<\n string,\n unknown\n >\n\n // Also convert version ID if collection uses numeric custom ID\n const customIDType = payload.collections?.[collectionSlug]?.customIDType\n if (customIDType === 'number') {\n baseDoc.id = Number(baseDoc.id)\n }\n }\n\n return baseDoc\n }\n\n // For regular documents, spread doc data at root level\n const { id, doc: docData } = doc\n const baseDoc = { ...docData, id }\n\n // Transform data from Content API format to Payload format\n if (collectionSlug) {\n return dataFromContentAPI(payload, collectionSlug, baseDoc) as Record<string, unknown>\n }\n\n return baseDoc\n}\n\nexport const unwrapFindResponse = (\n payload: Payload,\n response:\n | components['schemas']['FindDocumentsResponse']['result']\n | components['schemas']['FindDocumentVersionsResponse']['result'],\n collection: string,\n) => {\n const { data, ...paginationFields } = response\n return {\n docs: data.map((doc) =>\n unwrapDocument(payload, doc as DocumentResponse | DocumentVersionResponse, collection),\n ),\n ...paginationFields,\n }\n}\n"],"names":["dataFromContentAPI","unwrapDocument","payload","doc","collectionSlug","isVersion","versionDoc","baseDoc","version","customIDType","collections","id","Number","docData","unwrapFindResponse","response","collection","data","paginationFields","docs","map"],"mappings":"AAIA,SAASA,kBAAkB,QAAQ,6BAA4B;AAK/D,iEAAiE;AACjE,uBAAuB;AACvB,mGAAmG;AACnG,wIAAwI;AACxI,mBAAmB;AACnB,6DAA6D;AAC7D,yFAAyF;AACzF,OAAO,SAASC,eACdC,OAAgB,EAChBC,GAA+C,EAC/CC,cAAuB;IAEvB,IAAI,CAACD,KAAK;QACR,OAAOA;IACT;IAEA,4DAA4D;IAC5D,MAAME,YAAY,gBAAgBF;IAElC,IAAIE,WAAW;QACb,sEAAsE;QACtE,MAAM,EAAEC,UAAU,EAAE,GAAGH;QACvB,MAAMI,UAAU;YAAE,GAAGD,UAAU;QAAC;QAEhC,2DAA2D;QAC3D,IAAIF,gBAAgB;YAClB,4CAA4C;YAC5CG,QAAQC,OAAO,GAAGR,mBAAmBE,SAASE,gBAAgBG,QAAQC,OAAO;YAK7E,+DAA+D;YAC/D,MAAMC,eAAeP,QAAQQ,WAAW,EAAE,CAACN,eAAe,EAAEK;YAC5D,IAAIA,iBAAiB,UAAU;gBAC7BF,QAAQI,EAAE,GAAGC,OAAOL,QAAQI,EAAE;YAChC;QACF;QAEA,OAAOJ;IACT;IAEA,uDAAuD;IACvD,MAAM,EAAEI,EAAE,EAAER,KAAKU,OAAO,EAAE,GAAGV;IAC7B,MAAMI,UAAU;QAAE,GAAGM,OAAO;QAAEF;IAAG;IAEjC,2DAA2D;IAC3D,IAAIP,gBAAgB;QAClB,OAAOJ,mBAAmBE,SAASE,gBAAgBG;IACrD;IAEA,OAAOA;AACT;AAEA,OAAO,MAAMO,qBAAqB,CAChCZ,SACAa,UAGAC;IAEA,MAAM,EAAEC,IAAI,EAAE,GAAGC,kBAAkB,GAAGH;IACtC,OAAO;QACLI,MAAMF,KAAKG,GAAG,CAAC,CAACjB,MACdF,eAAeC,SAASC,KAAmDa;QAE7E,GAAGE,gBAAgB;IACrB;AACF,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"castFieldValue.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/data/castFieldValue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"castFieldValue.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/data/castFieldValue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAwEjF"}
|
|
@@ -56,7 +56,10 @@
|
|
|
56
56
|
}
|
|
57
57
|
// Text-like fields: convert non-strings to JSON strings
|
|
58
58
|
// This handles text, textarea, email, and code fields
|
|
59
|
-
|
|
59
|
+
// EXCEPTIONS:
|
|
60
|
+
// - Localized fields are stored as objects like {"en": "value", "es": "valor"}
|
|
61
|
+
// - hasMany fields are stored as arrays like ["value1", "value2"]
|
|
62
|
+
if ((field.type === 'text' || field.type === 'textarea' || field.type === 'email' || field.type === 'code') && typeof value !== 'string' && !('localized' in field && field.localized) && !('hasMany' in field && field.hasMany && Array.isArray(value))) {
|
|
60
63
|
return JSON.stringify(value);
|
|
61
64
|
}
|
|
62
65
|
// No casting needed for other types or already correct types
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/db-content-api/utilities/data/castFieldValue.ts"],"sourcesContent":["import type { Field, TabAsField } from 'payload'\n\n/**\n * Cast field values to the appropriate type for Content API storage.\n *\n * This function implements type coercion similar to Mongoose (used by MongoDB adapter)\n * to ensure data consistency. When users ignore TypeScript types and pass incorrect types,\n * we automatically convert them to the expected type.\n *\n */\nexport function castFieldValue(field: Field | TabAsField, value: unknown): unknown {\n if (value === null || value === undefined) {\n return value\n }\n\n if (field.type === 'number') {\n // Handle hasMany: true - array of numbers\n if ('hasMany' in field && field.hasMany && Array.isArray(value)) {\n return value.map((item) => {\n if (typeof item === 'number') {\n return item\n }\n if (typeof item === 'string' || typeof item === 'boolean') {\n return Number(item)\n }\n return item // Pass through other types (will likely fail validation)\n })\n }\n\n // Handle single number\n if (typeof value !== 'number') {\n if (typeof value === 'string' || typeof value === 'boolean') {\n return Number(value)\n }\n // For objects/arrays (when not hasMany), return as-is (will likely fail validation later)\n return value\n }\n }\n\n if (field.type === 'checkbox' && typeof value !== 'boolean') {\n if (typeof value === 'string') {\n if (value.toLowerCase() === 'true' || value === '1') {\n return true\n }\n if (value.toLowerCase() === 'false' || value === '0' || value === '') {\n return false\n }\n }\n return Boolean(value)\n }\n\n // Matches Mongoose behavior: stores dates as ISO strings\n if (field.type === 'date' && typeof value !== 'string') {\n if (value instanceof Date) {\n return value.toISOString()\n }\n if (typeof value === 'number') {\n return new Date(value).toISOString()\n }\n // For other types, return as-is (will likely fail validation)\n return value\n }\n\n // Text-like fields: convert non-strings to JSON strings\n // This handles text, textarea, email, and code fields\n if (\n (field.type === 'text' ||\n field.type === 'textarea' ||\n field.type === 'email' ||\n field.type === 'code') &&\n typeof value !== 'string'\n ) {\n return JSON.stringify(value)\n }\n\n // No casting needed for other types or already correct types\n return value\n}\n"],"names":["castFieldValue","field","value","undefined","type","hasMany","Array","isArray","map","item","Number","toLowerCase","Boolean","Date","toISOString","JSON","stringify"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,SAASA,eAAeC,KAAyB,EAAEC,KAAc;IACtE,IAAIA,UAAU,QAAQA,UAAUC,WAAW;QACzC,OAAOD;IACT;IAEA,IAAID,MAAMG,IAAI,KAAK,UAAU;QAC3B,0CAA0C;QAC1C,IAAI,aAAaH,SAASA,MAAMI,OAAO,IAAIC,MAAMC,OAAO,CAACL,QAAQ;YAC/D,OAAOA,MAAMM,GAAG,CAAC,CAACC;gBAChB,IAAI,OAAOA,SAAS,UAAU;oBAC5B,OAAOA;gBACT;gBACA,IAAI,OAAOA,SAAS,YAAY,OAAOA,SAAS,WAAW;oBACzD,OAAOC,OAAOD;gBAChB;gBACA,OAAOA,KAAK,yDAAyD;;YACvE;QACF;QAEA,uBAAuB;QACvB,IAAI,OAAOP,UAAU,UAAU;YAC7B,IAAI,OAAOA,UAAU,YAAY,OAAOA,UAAU,WAAW;gBAC3D,OAAOQ,OAAOR;YAChB;YACA,0FAA0F;YAC1F,OAAOA;QACT;IACF;IAEA,IAAID,MAAMG,IAAI,KAAK,cAAc,OAAOF,UAAU,WAAW;QAC3D,IAAI,OAAOA,UAAU,UAAU;YAC7B,IAAIA,MAAMS,WAAW,OAAO,UAAUT,UAAU,KAAK;gBACnD,OAAO;YACT;YACA,IAAIA,MAAMS,WAAW,OAAO,WAAWT,UAAU,OAAOA,UAAU,IAAI;gBACpE,OAAO;YACT;QACF;QACA,OAAOU,QAAQV;IACjB;IAEA,yDAAyD;IACzD,IAAID,MAAMG,IAAI,KAAK,UAAU,OAAOF,UAAU,UAAU;QACtD,IAAIA,iBAAiBW,MAAM;YACzB,OAAOX,MAAMY,WAAW;QAC1B;QACA,IAAI,OAAOZ,UAAU,UAAU;YAC7B,OAAO,IAAIW,KAAKX,OAAOY,WAAW;QACpC;QACA,8DAA8D;QAC9D,OAAOZ;IACT;IAEA,wDAAwD;IACxD,sDAAsD;IACtD,IACE,AAACD,CAAAA,MAAMG,IAAI,KAAK,UACdH,MAAMG,IAAI,KAAK,cACfH,MAAMG,IAAI,KAAK,WACfH,MAAMG,IAAI,KAAK,MAAK,KACtB,OAAOF,UAAU,
|
|
1
|
+
{"version":3,"sources":["../../../../src/db-content-api/utilities/data/castFieldValue.ts"],"sourcesContent":["import type { Field, TabAsField } from 'payload'\n\n/**\n * Cast field values to the appropriate type for Content API storage.\n *\n * This function implements type coercion similar to Mongoose (used by MongoDB adapter)\n * to ensure data consistency. When users ignore TypeScript types and pass incorrect types,\n * we automatically convert them to the expected type.\n *\n */\nexport function castFieldValue(field: Field | TabAsField, value: unknown): unknown {\n if (value === null || value === undefined) {\n return value\n }\n\n if (field.type === 'number') {\n // Handle hasMany: true - array of numbers\n if ('hasMany' in field && field.hasMany && Array.isArray(value)) {\n return value.map((item) => {\n if (typeof item === 'number') {\n return item\n }\n if (typeof item === 'string' || typeof item === 'boolean') {\n return Number(item)\n }\n return item // Pass through other types (will likely fail validation)\n })\n }\n\n // Handle single number\n if (typeof value !== 'number') {\n if (typeof value === 'string' || typeof value === 'boolean') {\n return Number(value)\n }\n // For objects/arrays (when not hasMany), return as-is (will likely fail validation later)\n return value\n }\n }\n\n if (field.type === 'checkbox' && typeof value !== 'boolean') {\n if (typeof value === 'string') {\n if (value.toLowerCase() === 'true' || value === '1') {\n return true\n }\n if (value.toLowerCase() === 'false' || value === '0' || value === '') {\n return false\n }\n }\n return Boolean(value)\n }\n\n // Matches Mongoose behavior: stores dates as ISO strings\n if (field.type === 'date' && typeof value !== 'string') {\n if (value instanceof Date) {\n return value.toISOString()\n }\n if (typeof value === 'number') {\n return new Date(value).toISOString()\n }\n // For other types, return as-is (will likely fail validation)\n return value\n }\n\n // Text-like fields: convert non-strings to JSON strings\n // This handles text, textarea, email, and code fields\n // EXCEPTIONS:\n // - Localized fields are stored as objects like {\"en\": \"value\", \"es\": \"valor\"}\n // - hasMany fields are stored as arrays like [\"value1\", \"value2\"]\n if (\n (field.type === 'text' ||\n field.type === 'textarea' ||\n field.type === 'email' ||\n field.type === 'code') &&\n typeof value !== 'string' &&\n !('localized' in field && field.localized) &&\n !('hasMany' in field && field.hasMany && Array.isArray(value))\n ) {\n return JSON.stringify(value)\n }\n\n // No casting needed for other types or already correct types\n return value\n}\n"],"names":["castFieldValue","field","value","undefined","type","hasMany","Array","isArray","map","item","Number","toLowerCase","Boolean","Date","toISOString","localized","JSON","stringify"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,SAASA,eAAeC,KAAyB,EAAEC,KAAc;IACtE,IAAIA,UAAU,QAAQA,UAAUC,WAAW;QACzC,OAAOD;IACT;IAEA,IAAID,MAAMG,IAAI,KAAK,UAAU;QAC3B,0CAA0C;QAC1C,IAAI,aAAaH,SAASA,MAAMI,OAAO,IAAIC,MAAMC,OAAO,CAACL,QAAQ;YAC/D,OAAOA,MAAMM,GAAG,CAAC,CAACC;gBAChB,IAAI,OAAOA,SAAS,UAAU;oBAC5B,OAAOA;gBACT;gBACA,IAAI,OAAOA,SAAS,YAAY,OAAOA,SAAS,WAAW;oBACzD,OAAOC,OAAOD;gBAChB;gBACA,OAAOA,KAAK,yDAAyD;;YACvE;QACF;QAEA,uBAAuB;QACvB,IAAI,OAAOP,UAAU,UAAU;YAC7B,IAAI,OAAOA,UAAU,YAAY,OAAOA,UAAU,WAAW;gBAC3D,OAAOQ,OAAOR;YAChB;YACA,0FAA0F;YAC1F,OAAOA;QACT;IACF;IAEA,IAAID,MAAMG,IAAI,KAAK,cAAc,OAAOF,UAAU,WAAW;QAC3D,IAAI,OAAOA,UAAU,UAAU;YAC7B,IAAIA,MAAMS,WAAW,OAAO,UAAUT,UAAU,KAAK;gBACnD,OAAO;YACT;YACA,IAAIA,MAAMS,WAAW,OAAO,WAAWT,UAAU,OAAOA,UAAU,IAAI;gBACpE,OAAO;YACT;QACF;QACA,OAAOU,QAAQV;IACjB;IAEA,yDAAyD;IACzD,IAAID,MAAMG,IAAI,KAAK,UAAU,OAAOF,UAAU,UAAU;QACtD,IAAIA,iBAAiBW,MAAM;YACzB,OAAOX,MAAMY,WAAW;QAC1B;QACA,IAAI,OAAOZ,UAAU,UAAU;YAC7B,OAAO,IAAIW,KAAKX,OAAOY,WAAW;QACpC;QACA,8DAA8D;QAC9D,OAAOZ;IACT;IAEA,wDAAwD;IACxD,sDAAsD;IACtD,cAAc;IACd,+EAA+E;IAC/E,kEAAkE;IAClE,IACE,AAACD,CAAAA,MAAMG,IAAI,KAAK,UACdH,MAAMG,IAAI,KAAK,cACfH,MAAMG,IAAI,KAAK,WACfH,MAAMG,IAAI,KAAK,MAAK,KACtB,OAAOF,UAAU,YACjB,CAAE,CAAA,eAAeD,SAASA,MAAMc,SAAS,AAAD,KACxC,CAAE,CAAA,aAAad,SAASA,MAAMI,OAAO,IAAIC,MAAMC,OAAO,CAACL,MAAK,GAC5D;QACA,OAAOc,KAAKC,SAAS,CAACf;IACxB;IAEA,6DAA6D;IAC7D,OAAOA;AACT"}
|
|
@@ -9,11 +9,13 @@ type DataWithOperations = components['schemas']['DataWithOperations'];
|
|
|
9
9
|
* - Type casting: Convert values to match field types (e.g., number -> string for text fields)
|
|
10
10
|
* - RichText fields: Objects -> JSON strings
|
|
11
11
|
* - Virtual fields: Remove fields with `virtual: true` (pure virtual fields should never be saved)
|
|
12
|
+
* - Timestamps: Add updatedAt (always), and createdAt (when createdAt=true)
|
|
12
13
|
*
|
|
13
14
|
* Note: Date fields are automatically converted to ISO strings by JSON.stringify()
|
|
14
15
|
*/
|
|
15
16
|
export declare function dataToContentAPI(payload: Payload, collectionSlug: string, data: unknown, options?: {
|
|
16
17
|
applyDefaults?: boolean;
|
|
18
|
+
createdAt?: boolean;
|
|
17
19
|
}): DataWithOperations;
|
|
18
20
|
/**
|
|
19
21
|
* Transform data received from Content API (READ operations)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/data/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkC,OAAO,EAA0B,MAAM,SAAS,CAAA;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAA;AAOtE,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAA;AAErE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/data/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkC,OAAO,EAA0B,MAAM,SAAS,CAAA;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAA;AAOtE,KAAK,kBAAkB,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,CAAA;AAErE;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GACzD,kBAAkB,CA8EpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,OAAO,GACZ,OAAO,CAyGT"}
|
|
@@ -11,6 +11,7 @@ import { stripFields } from './stripFields.js';
|
|
|
11
11
|
* - Type casting: Convert values to match field types (e.g., number -> string for text fields)
|
|
12
12
|
* - RichText fields: Objects -> JSON strings
|
|
13
13
|
* - Virtual fields: Remove fields with `virtual: true` (pure virtual fields should never be saved)
|
|
14
|
+
* - Timestamps: Add updatedAt (always), and createdAt (when createdAt=true)
|
|
14
15
|
*
|
|
15
16
|
* Note: Date fields are automatically converted to ISO strings by JSON.stringify()
|
|
16
17
|
*/ export function dataToContentAPI(payload, collectionSlug, data, options) {
|
|
@@ -78,6 +79,13 @@ import { stripFields } from './stripFields.js';
|
|
|
78
79
|
if (customIDType === 'number') {
|
|
79
80
|
transformed.id = String(transformed.id);
|
|
80
81
|
}
|
|
82
|
+
// Add timestamps (Content API no longer auto-sets these in DB)
|
|
83
|
+
// updatedAt is always set, createdAt only on create operations
|
|
84
|
+
const now = new Date().toISOString();
|
|
85
|
+
transformed.updatedAt = now;
|
|
86
|
+
if (options?.createdAt) {
|
|
87
|
+
transformed.createdAt = now;
|
|
88
|
+
}
|
|
81
89
|
return transformed;
|
|
82
90
|
}
|
|
83
91
|
/**
|
|
@@ -122,8 +130,21 @@ import { stripFields } from './stripFields.js';
|
|
|
122
130
|
return;
|
|
123
131
|
}
|
|
124
132
|
if (value !== null) {
|
|
125
|
-
//
|
|
126
|
-
|
|
133
|
+
// Localized fields: JSON string -> parsed object
|
|
134
|
+
// Content API may store localized fields as JSON strings like "{\"en\":\"value\"}"
|
|
135
|
+
// so we need to parse them back to objects.
|
|
136
|
+
// Note: We return the full locale object - Payload Core handles flattening to requested locale.
|
|
137
|
+
if ('localized' in field && field.localized && typeof value === 'string') {
|
|
138
|
+
try {
|
|
139
|
+
current[field.name] = JSON.parse(value);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
|
|
142
|
+
payload.logger.warn({
|
|
143
|
+
err: error instanceof Error ? error : new Error(String(error)),
|
|
144
|
+
msg: `Failed to parse localized field '${fieldPath}' in collection '${collectionSlug}'`
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
} else if (field.type === 'richText' && typeof value === 'string') {
|
|
127
148
|
try {
|
|
128
149
|
current[field.name] = JSON.parse(value);
|
|
129
150
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/db-content-api/utilities/data/index.ts"],"sourcesContent":["import type { CollectionConfig, GlobalConfig, Payload, TraverseFieldsCallback } from 'payload'\n\nimport { flattenAllFields, traverseFields } from 'payload'\n\nimport type { components } from '../../generated/content-api-types.js'\n\nimport { applyDefaults } from './applyDefaults.js'\nimport { castFieldValue } from './castFieldValue.js'\nimport { removeVirtualFields } from './removeVirtualFields.js'\nimport { stripFields } from './stripFields.js'\n\ntype DataWithOperations = components['schemas']['DataWithOperations']\n\n/**\n * Transform data before sending to Content API (WRITE operations)\n *\n * Conversions applied:\n * - Default values: Apply static defaults for undefined fields (when applyDefaults=true)\n * - Type casting: Convert values to match field types (e.g., number -> string for text fields)\n * - RichText fields: Objects -> JSON strings\n * - Virtual fields: Remove fields with `virtual: true` (pure virtual fields should never be saved)\n *\n * Note: Date fields are automatically converted to ISO strings by JSON.stringify()\n */\nexport function dataToContentAPI(\n payload: Payload,\n collectionSlug: string,\n data: unknown,\n options?: { applyDefaults?: boolean },\n): DataWithOperations {\n if (!data || typeof data !== 'object') {\n return data as DataWithOperations\n }\n\n // Deep clone to avoid mutating original data\n const transformed = JSON.parse(JSON.stringify(data))\n\n // Get collection config\n const isGlobal = collectionSlug.startsWith('_global-')\n const actualSlug = isGlobal ? collectionSlug.substring(8) : collectionSlug\n const collectionConfig: CollectionConfig | GlobalConfig | undefined = isGlobal\n ? payload.config.globals?.find((g) => g.slug === actualSlug)\n : payload.config.collections.find((c) => c.slug === actualSlug)\n\n if (!collectionConfig?.fields) {\n return transformed as DataWithOperations\n }\n\n // Apply static defaults recursively to handle nested structures (arrays, groups)\n // This must happen before traverseFields to ensure defaults are in place\n if (options?.applyDefaults) {\n applyDefaults(transformed, collectionConfig.fields)\n }\n\n // Use Payload's traverseFields to iterate over all fields for transformations\n const callback: TraverseFieldsCallback = ({ field, ref }) => {\n if (!('name' in field) || !field.name) {\n return\n }\n if (!ref || typeof ref !== 'object') {\n return\n }\n\n const current = ref as Record<string, unknown>\n let value = current[field.name]\n\n // Transform existing values\n if (value !== null && value !== undefined) {\n // Step 1: Apply type casting (e.g., number -> string for text fields)\n value = castFieldValue(field, value)\n current[field.name] = value\n\n // Step 2: Apply Content API specific transformations\n // RichText: object -> JSON string\n if (field.type === 'richText' && typeof value !== 'string') {\n current[field.name] = JSON.stringify(value)\n }\n // Date fields are already handled by JSON.stringify() which converts Date -> ISO string\n }\n }\n\n traverseFields({ callback, fields: collectionConfig.fields, fillEmpty: false, ref: transformed })\n\n stripFields({\n config: payload.config,\n data: transformed,\n fields: flattenAllFields({ cache: true, fields: collectionConfig.fields }),\n reservedKeys: ['id', 'globalType'],\n })\n\n removeVirtualFields(transformed, collectionConfig.fields)\n\n // Convert numeric ID to string for Content API\n const customIDType = payload.collections?.[collectionSlug]?.customIDType\n if (customIDType === 'number') {\n transformed.id = String(transformed.id)\n }\n\n return transformed as DataWithOperations\n}\n\n/**\n * Transform data received from Content API (READ operations)\n *\n * Conversions applied:\n * - RichText fields: JSON strings -> Objects\n * - Virtual fields: Remove fields with `virtual: true` (should not be in DB but Content API backend may add them)\n *\n * Note: Date fields come as ISO strings from Content API and stay as strings\n * (matching MongoDB and other adapters' behavior)\n */\nexport function dataFromContentAPI(\n payload: Payload,\n collectionSlug: string,\n data: unknown,\n): unknown {\n if (!data || typeof data !== 'object') {\n return data\n }\n\n // Deep clone to avoid mutating original data\n const transformed = JSON.parse(JSON.stringify(data))\n\n // Get collection config\n const isGlobal = collectionSlug.startsWith('_global-')\n const actualSlug = isGlobal ? collectionSlug.substring(8) : collectionSlug\n const collectionConfig: CollectionConfig | GlobalConfig | undefined = isGlobal\n ? payload.config.globals?.find((g) => g.slug === actualSlug)\n : payload.config.collections.find((c) => c.slug === actualSlug)\n\n if (!collectionConfig?.fields) {\n return transformed\n }\n\n // Use Payload's traverseFields to iterate over all fields\n const callback: TraverseFieldsCallback = ({ field, parentPath, ref }) => {\n if (!('name' in field) || !field.name) {\n return\n }\n if (!ref || typeof ref !== 'object') {\n return\n }\n\n const current = ref as Record<string, unknown>\n const value = current[field.name]\n\n // Normalize undefined → null for optional fields\n // Content API may omit fields (undefined) but Payload expects null for optional fields\n if (value === undefined) {\n const isRequired = 'required' in field && field.required\n if (!isRequired) {\n current[field.name] = null\n }\n return\n }\n\n if (value !== null) {\n // RichText: JSON string -> object\n if (field.type === 'richText' && typeof value === 'string') {\n try {\n current[field.name] = JSON.parse(value)\n } catch (error) {\n const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name\n payload.logger.warn({\n err: error instanceof Error ? error : new Error(String(error)),\n msg: `Failed to parse richtext field '${fieldPath}' in collection '${collectionSlug}'`,\n })\n }\n }\n // Date fields: Already ISO strings from Content API, no conversion needed\n }\n }\n\n traverseFields({ callback, fields: collectionConfig.fields, ref: transformed })\n\n // Convert string ID to number if collection uses numeric custom ID\n const customIDType = payload.collections?.[collectionSlug]?.customIDType\n if (customIDType === 'number') {\n transformed.id = Number(transformed.id)\n }\n\n stripFields({\n config: payload.config,\n data: transformed,\n fields: flattenAllFields({ cache: true, fields: collectionConfig.fields }),\n reservedKeys: ['id', 'globalType'],\n })\n\n // Remove virtual fields - must happen AFTER stripFields\n removeVirtualFields(transformed, collectionConfig.fields)\n\n // Handle auth-specific fields that aren't in the field schema\n // When these fields are null in DB, Content API omits them from response (undefined)\n // But Payload expects them to be null, not undefined\n // Only add them if the collection has auth enabled\n if ('auth' in collectionConfig && collectionConfig.auth) {\n const authFields = ['resetPasswordExpiration', 'lockUntil']\n for (const fieldName of authFields) {\n if (!(fieldName in transformed)) {\n transformed[fieldName] = null\n }\n }\n }\n\n return transformed\n}\n"],"names":["flattenAllFields","traverseFields","applyDefaults","castFieldValue","removeVirtualFields","stripFields","dataToContentAPI","payload","collectionSlug","data","options","transformed","JSON","parse","stringify","isGlobal","startsWith","actualSlug","substring","collectionConfig","config","globals","find","g","slug","collections","c","fields","callback","field","ref","name","current","value","undefined","type","fillEmpty","cache","reservedKeys","customIDType","id","String","dataFromContentAPI","parentPath","isRequired","required","error","fieldPath","logger","warn","err","Error","msg","Number","auth","authFields","fieldName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/db-content-api/utilities/data/index.ts"],"sourcesContent":["import type { CollectionConfig, GlobalConfig, Payload, TraverseFieldsCallback } from 'payload'\n\nimport { flattenAllFields, traverseFields } from 'payload'\n\nimport type { components } from '../../generated/content-api-types.js'\n\nimport { applyDefaults } from './applyDefaults.js'\nimport { castFieldValue } from './castFieldValue.js'\nimport { removeVirtualFields } from './removeVirtualFields.js'\nimport { stripFields } from './stripFields.js'\n\ntype DataWithOperations = components['schemas']['DataWithOperations']\n\n/**\n * Transform data before sending to Content API (WRITE operations)\n *\n * Conversions applied:\n * - Default values: Apply static defaults for undefined fields (when applyDefaults=true)\n * - Type casting: Convert values to match field types (e.g., number -> string for text fields)\n * - RichText fields: Objects -> JSON strings\n * - Virtual fields: Remove fields with `virtual: true` (pure virtual fields should never be saved)\n * - Timestamps: Add updatedAt (always), and createdAt (when createdAt=true)\n *\n * Note: Date fields are automatically converted to ISO strings by JSON.stringify()\n */\nexport function dataToContentAPI(\n payload: Payload,\n collectionSlug: string,\n data: unknown,\n options?: { applyDefaults?: boolean; createdAt?: boolean },\n): DataWithOperations {\n if (!data || typeof data !== 'object') {\n return data as DataWithOperations\n }\n\n // Deep clone to avoid mutating original data\n const transformed = JSON.parse(JSON.stringify(data))\n\n // Get collection config\n const isGlobal = collectionSlug.startsWith('_global-')\n const actualSlug = isGlobal ? collectionSlug.substring(8) : collectionSlug\n const collectionConfig: CollectionConfig | GlobalConfig | undefined = isGlobal\n ? payload.config.globals?.find((g) => g.slug === actualSlug)\n : payload.config.collections.find((c) => c.slug === actualSlug)\n\n if (!collectionConfig?.fields) {\n return transformed as DataWithOperations\n }\n\n // Apply static defaults recursively to handle nested structures (arrays, groups)\n // This must happen before traverseFields to ensure defaults are in place\n if (options?.applyDefaults) {\n applyDefaults(transformed, collectionConfig.fields)\n }\n\n // Use Payload's traverseFields to iterate over all fields for transformations\n const callback: TraverseFieldsCallback = ({ field, ref }) => {\n if (!('name' in field) || !field.name) {\n return\n }\n if (!ref || typeof ref !== 'object') {\n return\n }\n\n const current = ref as Record<string, unknown>\n let value = current[field.name]\n\n // Transform existing values\n if (value !== null && value !== undefined) {\n // Step 1: Apply type casting (e.g., number -> string for text fields)\n value = castFieldValue(field, value)\n current[field.name] = value\n\n // Step 2: Apply Content API specific transformations\n // RichText: object -> JSON string\n if (field.type === 'richText' && typeof value !== 'string') {\n current[field.name] = JSON.stringify(value)\n }\n // Date fields are already handled by JSON.stringify() which converts Date -> ISO string\n }\n }\n\n traverseFields({ callback, fields: collectionConfig.fields, fillEmpty: false, ref: transformed })\n\n stripFields({\n config: payload.config,\n data: transformed,\n fields: flattenAllFields({ cache: true, fields: collectionConfig.fields }),\n reservedKeys: ['id', 'globalType'],\n })\n\n removeVirtualFields(transformed, collectionConfig.fields)\n\n // Convert numeric ID to string for Content API\n const customIDType = payload.collections?.[collectionSlug]?.customIDType\n if (customIDType === 'number') {\n transformed.id = String(transformed.id)\n }\n\n // Add timestamps (Content API no longer auto-sets these in DB)\n // updatedAt is always set, createdAt only on create operations\n const now = new Date().toISOString()\n transformed.updatedAt = now\n if (options?.createdAt) {\n transformed.createdAt = now\n }\n\n return transformed as DataWithOperations\n}\n\n/**\n * Transform data received from Content API (READ operations)\n *\n * Conversions applied:\n * - RichText fields: JSON strings -> Objects\n * - Virtual fields: Remove fields with `virtual: true` (should not be in DB but Content API backend may add them)\n *\n * Note: Date fields come as ISO strings from Content API and stay as strings\n * (matching MongoDB and other adapters' behavior)\n */\nexport function dataFromContentAPI(\n payload: Payload,\n collectionSlug: string,\n data: unknown,\n): unknown {\n if (!data || typeof data !== 'object') {\n return data\n }\n\n // Deep clone to avoid mutating original data\n const transformed = JSON.parse(JSON.stringify(data))\n\n // Get collection config\n const isGlobal = collectionSlug.startsWith('_global-')\n const actualSlug = isGlobal ? collectionSlug.substring(8) : collectionSlug\n const collectionConfig: CollectionConfig | GlobalConfig | undefined = isGlobal\n ? payload.config.globals?.find((g) => g.slug === actualSlug)\n : payload.config.collections.find((c) => c.slug === actualSlug)\n\n if (!collectionConfig?.fields) {\n return transformed\n }\n\n // Use Payload's traverseFields to iterate over all fields\n const callback: TraverseFieldsCallback = ({ field, parentPath, ref }) => {\n if (!('name' in field) || !field.name) {\n return\n }\n if (!ref || typeof ref !== 'object') {\n return\n }\n\n const current = ref as Record<string, unknown>\n const value = current[field.name]\n\n // Normalize undefined → null for optional fields\n // Content API may omit fields (undefined) but Payload expects null for optional fields\n if (value === undefined) {\n const isRequired = 'required' in field && field.required\n if (!isRequired) {\n current[field.name] = null\n }\n return\n }\n\n if (value !== null) {\n // Localized fields: JSON string -> parsed object\n // Content API may store localized fields as JSON strings like \"{\\\"en\\\":\\\"value\\\"}\"\n // so we need to parse them back to objects.\n // Note: We return the full locale object - Payload Core handles flattening to requested locale.\n if ('localized' in field && field.localized && typeof value === 'string') {\n try {\n current[field.name] = JSON.parse(value)\n } catch (error) {\n const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name\n payload.logger.warn({\n err: error instanceof Error ? error : new Error(String(error)),\n msg: `Failed to parse localized field '${fieldPath}' in collection '${collectionSlug}'`,\n })\n }\n }\n // RichText: JSON string -> object\n else if (field.type === 'richText' && typeof value === 'string') {\n try {\n current[field.name] = JSON.parse(value)\n } catch (error) {\n const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name\n payload.logger.warn({\n err: error instanceof Error ? error : new Error(String(error)),\n msg: `Failed to parse richtext field '${fieldPath}' in collection '${collectionSlug}'`,\n })\n }\n }\n // Date fields: Already ISO strings from Content API, no conversion needed\n }\n }\n\n traverseFields({ callback, fields: collectionConfig.fields, ref: transformed })\n\n // Convert string ID to number if collection uses numeric custom ID\n const customIDType = payload.collections?.[collectionSlug]?.customIDType\n if (customIDType === 'number') {\n transformed.id = Number(transformed.id)\n }\n\n stripFields({\n config: payload.config,\n data: transformed,\n fields: flattenAllFields({ cache: true, fields: collectionConfig.fields }),\n reservedKeys: ['id', 'globalType'],\n })\n\n // Remove virtual fields - must happen AFTER stripFields\n removeVirtualFields(transformed, collectionConfig.fields)\n\n // Handle auth-specific fields that aren't in the field schema\n // When these fields are null in DB, Content API omits them from response (undefined)\n // But Payload expects them to be null, not undefined\n // Only add them if the collection has auth enabled\n if ('auth' in collectionConfig && collectionConfig.auth) {\n const authFields = ['resetPasswordExpiration', 'lockUntil']\n for (const fieldName of authFields) {\n if (!(fieldName in transformed)) {\n transformed[fieldName] = null\n }\n }\n }\n\n return transformed\n}\n"],"names":["flattenAllFields","traverseFields","applyDefaults","castFieldValue","removeVirtualFields","stripFields","dataToContentAPI","payload","collectionSlug","data","options","transformed","JSON","parse","stringify","isGlobal","startsWith","actualSlug","substring","collectionConfig","config","globals","find","g","slug","collections","c","fields","callback","field","ref","name","current","value","undefined","type","fillEmpty","cache","reservedKeys","customIDType","id","String","now","Date","toISOString","updatedAt","createdAt","dataFromContentAPI","parentPath","isRequired","required","localized","error","fieldPath","logger","warn","err","Error","msg","Number","auth","authFields","fieldName"],"mappings":"AAEA,SAASA,gBAAgB,EAAEC,cAAc,QAAQ,UAAS;AAI1D,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,cAAc,QAAQ,sBAAqB;AACpD,SAASC,mBAAmB,QAAQ,2BAA0B;AAC9D,SAASC,WAAW,QAAQ,mBAAkB;AAI9C;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,iBACdC,OAAgB,EAChBC,cAAsB,EACtBC,IAAa,EACbC,OAA0D;IAE1D,IAAI,CAACD,QAAQ,OAAOA,SAAS,UAAU;QACrC,OAAOA;IACT;IAEA,6CAA6C;IAC7C,MAAME,cAAcC,KAAKC,KAAK,CAACD,KAAKE,SAAS,CAACL;IAE9C,wBAAwB;IACxB,MAAMM,WAAWP,eAAeQ,UAAU,CAAC;IAC3C,MAAMC,aAAaF,WAAWP,eAAeU,SAAS,CAAC,KAAKV;IAC5D,MAAMW,mBAAgEJ,WAClER,QAAQa,MAAM,CAACC,OAAO,EAAEC,KAAK,CAACC,IAAMA,EAAEC,IAAI,KAAKP,cAC/CV,QAAQa,MAAM,CAACK,WAAW,CAACH,IAAI,CAAC,CAACI,IAAMA,EAAEF,IAAI,KAAKP;IAEtD,IAAI,CAACE,kBAAkBQ,QAAQ;QAC7B,OAAOhB;IACT;IAEA,iFAAiF;IACjF,yEAAyE;IACzE,IAAID,SAASR,eAAe;QAC1BA,cAAcS,aAAaQ,iBAAiBQ,MAAM;IACpD;IAEA,8EAA8E;IAC9E,MAAMC,WAAmC,CAAC,EAAEC,KAAK,EAAEC,GAAG,EAAE;QACtD,IAAI,CAAE,CAAA,UAAUD,KAAI,KAAM,CAACA,MAAME,IAAI,EAAE;YACrC;QACF;QACA,IAAI,CAACD,OAAO,OAAOA,QAAQ,UAAU;YACnC;QACF;QAEA,MAAME,UAAUF;QAChB,IAAIG,QAAQD,OAAO,CAACH,MAAME,IAAI,CAAC;QAE/B,4BAA4B;QAC5B,IAAIE,UAAU,QAAQA,UAAUC,WAAW;YACzC,sEAAsE;YACtED,QAAQ9B,eAAe0B,OAAOI;YAC9BD,OAAO,CAACH,MAAME,IAAI,CAAC,GAAGE;YAEtB,qDAAqD;YACrD,kCAAkC;YAClC,IAAIJ,MAAMM,IAAI,KAAK,cAAc,OAAOF,UAAU,UAAU;gBAC1DD,OAAO,CAACH,MAAME,IAAI,CAAC,GAAGnB,KAAKE,SAAS,CAACmB;YACvC;QACA,wFAAwF;QAC1F;IACF;IAEAhC,eAAe;QAAE2B;QAAUD,QAAQR,iBAAiBQ,MAAM;QAAES,WAAW;QAAON,KAAKnB;IAAY;IAE/FN,YAAY;QACVe,QAAQb,QAAQa,MAAM;QACtBX,MAAME;QACNgB,QAAQ3B,iBAAiB;YAAEqC,OAAO;YAAMV,QAAQR,iBAAiBQ,MAAM;QAAC;QACxEW,cAAc;YAAC;YAAM;SAAa;IACpC;IAEAlC,oBAAoBO,aAAaQ,iBAAiBQ,MAAM;IAExD,+CAA+C;IAC/C,MAAMY,eAAehC,QAAQkB,WAAW,EAAE,CAACjB,eAAe,EAAE+B;IAC5D,IAAIA,iBAAiB,UAAU;QAC7B5B,YAAY6B,EAAE,GAAGC,OAAO9B,YAAY6B,EAAE;IACxC;IAEA,+DAA+D;IAC/D,+DAA+D;IAC/D,MAAME,MAAM,IAAIC,OAAOC,WAAW;IAClCjC,YAAYkC,SAAS,GAAGH;IACxB,IAAIhC,SAASoC,WAAW;QACtBnC,YAAYmC,SAAS,GAAGJ;IAC1B;IAEA,OAAO/B;AACT;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASoC,mBACdxC,OAAgB,EAChBC,cAAsB,EACtBC,IAAa;IAEb,IAAI,CAACA,QAAQ,OAAOA,SAAS,UAAU;QACrC,OAAOA;IACT;IAEA,6CAA6C;IAC7C,MAAME,cAAcC,KAAKC,KAAK,CAACD,KAAKE,SAAS,CAACL;IAE9C,wBAAwB;IACxB,MAAMM,WAAWP,eAAeQ,UAAU,CAAC;IAC3C,MAAMC,aAAaF,WAAWP,eAAeU,SAAS,CAAC,KAAKV;IAC5D,MAAMW,mBAAgEJ,WAClER,QAAQa,MAAM,CAACC,OAAO,EAAEC,KAAK,CAACC,IAAMA,EAAEC,IAAI,KAAKP,cAC/CV,QAAQa,MAAM,CAACK,WAAW,CAACH,IAAI,CAAC,CAACI,IAAMA,EAAEF,IAAI,KAAKP;IAEtD,IAAI,CAACE,kBAAkBQ,QAAQ;QAC7B,OAAOhB;IACT;IAEA,0DAA0D;IAC1D,MAAMiB,WAAmC,CAAC,EAAEC,KAAK,EAAEmB,UAAU,EAAElB,GAAG,EAAE;QAClE,IAAI,CAAE,CAAA,UAAUD,KAAI,KAAM,CAACA,MAAME,IAAI,EAAE;YACrC;QACF;QACA,IAAI,CAACD,OAAO,OAAOA,QAAQ,UAAU;YACnC;QACF;QAEA,MAAME,UAAUF;QAChB,MAAMG,QAAQD,OAAO,CAACH,MAAME,IAAI,CAAC;QAEjC,iDAAiD;QACjD,uFAAuF;QACvF,IAAIE,UAAUC,WAAW;YACvB,MAAMe,aAAa,cAAcpB,SAASA,MAAMqB,QAAQ;YACxD,IAAI,CAACD,YAAY;gBACfjB,OAAO,CAACH,MAAME,IAAI,CAAC,GAAG;YACxB;YACA;QACF;QAEA,IAAIE,UAAU,MAAM;YAClB,iDAAiD;YACjD,mFAAmF;YACnF,4CAA4C;YAC5C,gGAAgG;YAChG,IAAI,eAAeJ,SAASA,MAAMsB,SAAS,IAAI,OAAOlB,UAAU,UAAU;gBACxE,IAAI;oBACFD,OAAO,CAACH,MAAME,IAAI,CAAC,GAAGnB,KAAKC,KAAK,CAACoB;gBACnC,EAAE,OAAOmB,OAAO;oBACd,MAAMC,YAAYL,aAAa,GAAGA,WAAW,CAAC,EAAEnB,MAAME,IAAI,EAAE,GAAGF,MAAME,IAAI;oBACzExB,QAAQ+C,MAAM,CAACC,IAAI,CAAC;wBAClBC,KAAKJ,iBAAiBK,QAAQL,QAAQ,IAAIK,MAAMhB,OAAOW;wBACvDM,KAAK,CAAC,iCAAiC,EAAEL,UAAU,iBAAiB,EAAE7C,eAAe,CAAC,CAAC;oBACzF;gBACF;YACF,OAEK,IAAIqB,MAAMM,IAAI,KAAK,cAAc,OAAOF,UAAU,UAAU;gBAC/D,IAAI;oBACFD,OAAO,CAACH,MAAME,IAAI,CAAC,GAAGnB,KAAKC,KAAK,CAACoB;gBACnC,EAAE,OAAOmB,OAAO;oBACd,MAAMC,YAAYL,aAAa,GAAGA,WAAW,CAAC,EAAEnB,MAAME,IAAI,EAAE,GAAGF,MAAME,IAAI;oBACzExB,QAAQ+C,MAAM,CAACC,IAAI,CAAC;wBAClBC,KAAKJ,iBAAiBK,QAAQL,QAAQ,IAAIK,MAAMhB,OAAOW;wBACvDM,KAAK,CAAC,gCAAgC,EAAEL,UAAU,iBAAiB,EAAE7C,eAAe,CAAC,CAAC;oBACxF;gBACF;YACF;QACA,0EAA0E;QAC5E;IACF;IAEAP,eAAe;QAAE2B;QAAUD,QAAQR,iBAAiBQ,MAAM;QAAEG,KAAKnB;IAAY;IAE7E,mEAAmE;IACnE,MAAM4B,eAAehC,QAAQkB,WAAW,EAAE,CAACjB,eAAe,EAAE+B;IAC5D,IAAIA,iBAAiB,UAAU;QAC7B5B,YAAY6B,EAAE,GAAGmB,OAAOhD,YAAY6B,EAAE;IACxC;IAEAnC,YAAY;QACVe,QAAQb,QAAQa,MAAM;QACtBX,MAAME;QACNgB,QAAQ3B,iBAAiB;YAAEqC,OAAO;YAAMV,QAAQR,iBAAiBQ,MAAM;QAAC;QACxEW,cAAc;YAAC;YAAM;SAAa;IACpC;IAEA,wDAAwD;IACxDlC,oBAAoBO,aAAaQ,iBAAiBQ,MAAM;IAExD,8DAA8D;IAC9D,qFAAqF;IACrF,qDAAqD;IACrD,mDAAmD;IACnD,IAAI,UAAUR,oBAAoBA,iBAAiByC,IAAI,EAAE;QACvD,MAAMC,aAAa;YAAC;YAA2B;SAAY;QAC3D,KAAK,MAAMC,aAAaD,WAAY;YAClC,IAAI,CAAEC,CAAAA,aAAanD,WAAU,GAAI;gBAC/BA,WAAW,CAACmD,UAAU,GAAG;YAC3B;QACF;IACF;IAEA,OAAOnD;AACT"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts all localized field paths from a collection's field configuration.
|
|
4
|
+
* Recursively traverses nested structures (groups, arrays, blocks, tabs) to find fields marked as localized.
|
|
5
|
+
*
|
|
6
|
+
* @param payload - The Payload instance containing collection configurations
|
|
7
|
+
* @param collectionSlug - The slug of the collection to extract localized paths from
|
|
8
|
+
* @returns Array of dot-notation paths to localized fields (e.g., ['title', 'metadata.description'])
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildLocalizedPaths(payload: Payload, collectionSlug: string): string[];
|
|
11
|
+
//# sourceMappingURL=buildLocalizedPaths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildLocalizedPaths.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/meta/buildLocalizedPaths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,OAAO,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAStF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts all localized field paths from a collection's field configuration.
|
|
3
|
+
* Recursively traverses nested structures (groups, arrays, blocks, tabs) to find fields marked as localized.
|
|
4
|
+
*
|
|
5
|
+
* @param payload - The Payload instance containing collection configurations
|
|
6
|
+
* @param collectionSlug - The slug of the collection to extract localized paths from
|
|
7
|
+
* @returns Array of dot-notation paths to localized fields (e.g., ['title', 'metadata.description'])
|
|
8
|
+
*/ export function buildLocalizedPaths(payload, collectionSlug) {
|
|
9
|
+
const collectionConfig = payload.config.collections.find((c)=>c.slug === collectionSlug);
|
|
10
|
+
if (!collectionConfig) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
const localizedPaths = [];
|
|
14
|
+
traverseFields(collectionConfig.fields, localizedPaths);
|
|
15
|
+
return localizedPaths;
|
|
16
|
+
}
|
|
17
|
+
/** Recursively traverses fields to find and collect localized paths */ function traverseFields(fields, localizedPaths, parentPath = '', parentIsLocalized = false) {
|
|
18
|
+
for (const field of fields){
|
|
19
|
+
// Only field types that have names can support localization
|
|
20
|
+
// See: https://payloadcms.com/docs/configuration/localization
|
|
21
|
+
//
|
|
22
|
+
// This will skip presentational fields like ui, collapsible, row, tabs (if unnamed), groups (if unnamed)
|
|
23
|
+
// which do not store data in the database and thus are not relevant for querying localized paths
|
|
24
|
+
// See: https://payloadcms.com/docs/fields/overview#presentational-fields
|
|
25
|
+
if (!('name' in field)) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const fieldPath = buildPath(parentPath, field.name);
|
|
29
|
+
const isLocalized = 'localized' in field && field.localized === true;
|
|
30
|
+
const includeNestedPaths = isLocalized || parentIsLocalized;
|
|
31
|
+
// Add field to results if localized or parent is localized
|
|
32
|
+
if (includeNestedPaths) {
|
|
33
|
+
localizedPaths.push(fieldPath);
|
|
34
|
+
}
|
|
35
|
+
// Handle blocks field - has a special 'blocks' array structure and each block has its own nested fields
|
|
36
|
+
if (field.type === 'blocks' && 'blocks' in field) {
|
|
37
|
+
for (const block of field.blocks){
|
|
38
|
+
if ('fields' in block) {
|
|
39
|
+
traverseFields(block.fields, localizedPaths, fieldPath, includeNestedPaths);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Handle field type that support nested fields
|
|
45
|
+
// Recursively handle nested structures (groups, arrays, tab, etc)
|
|
46
|
+
if ('fields' in field) {
|
|
47
|
+
traverseFields(field.fields, localizedPaths, fieldPath, includeNestedPaths);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Builds a dot-notation path from parent path and field name */ function buildPath(parentPath, fieldName) {
|
|
52
|
+
return parentPath ? `${parentPath}.${fieldName}` : fieldName;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=buildLocalizedPaths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/db-content-api/utilities/meta/buildLocalizedPaths.ts"],"sourcesContent":["import type { Field, Payload } from 'payload'\n\n/**\n * Extracts all localized field paths from a collection's field configuration.\n * Recursively traverses nested structures (groups, arrays, blocks, tabs) to find fields marked as localized.\n *\n * @param payload - The Payload instance containing collection configurations\n * @param collectionSlug - The slug of the collection to extract localized paths from\n * @returns Array of dot-notation paths to localized fields (e.g., ['title', 'metadata.description'])\n */\nexport function buildLocalizedPaths(payload: Payload, collectionSlug: string): string[] {\n const collectionConfig = payload.config.collections.find((c) => c.slug === collectionSlug)\n if (!collectionConfig) {\n return []\n }\n\n const localizedPaths: string[] = []\n traverseFields(collectionConfig.fields, localizedPaths)\n return localizedPaths\n}\n\n/** Recursively traverses fields to find and collect localized paths */\nfunction traverseFields(\n fields: Field[],\n localizedPaths: string[],\n parentPath = '',\n parentIsLocalized = false,\n): void {\n for (const field of fields) {\n // Only field types that have names can support localization\n // See: https://payloadcms.com/docs/configuration/localization\n //\n // This will skip presentational fields like ui, collapsible, row, tabs (if unnamed), groups (if unnamed)\n // which do not store data in the database and thus are not relevant for querying localized paths\n // See: https://payloadcms.com/docs/fields/overview#presentational-fields\n if (!('name' in field)) {\n continue\n }\n\n const fieldPath = buildPath(parentPath, field.name)\n const isLocalized = 'localized' in field && field.localized === true\n const includeNestedPaths = isLocalized || parentIsLocalized\n\n // Add field to results if localized or parent is localized\n if (includeNestedPaths) {\n localizedPaths.push(fieldPath)\n }\n\n // Handle blocks field - has a special 'blocks' array structure and each block has its own nested fields\n if (field.type === 'blocks' && 'blocks' in field) {\n for (const block of field.blocks) {\n if ('fields' in block) {\n traverseFields(block.fields, localizedPaths, fieldPath, includeNestedPaths)\n }\n }\n continue\n }\n\n // Handle field type that support nested fields\n // Recursively handle nested structures (groups, arrays, tab, etc)\n if ('fields' in field) {\n traverseFields(field.fields, localizedPaths, fieldPath, includeNestedPaths)\n }\n }\n}\n\n/** Builds a dot-notation path from parent path and field name */\nfunction buildPath(parentPath: string, fieldName: string): string {\n return parentPath ? `${parentPath}.${fieldName}` : fieldName\n}\n"],"names":["buildLocalizedPaths","payload","collectionSlug","collectionConfig","config","collections","find","c","slug","localizedPaths","traverseFields","fields","parentPath","parentIsLocalized","field","fieldPath","buildPath","name","isLocalized","localized","includeNestedPaths","push","type","block","blocks","fieldName"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,SAASA,oBAAoBC,OAAgB,EAAEC,cAAsB;IAC1E,MAAMC,mBAAmBF,QAAQG,MAAM,CAACC,WAAW,CAACC,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKN;IAC3E,IAAI,CAACC,kBAAkB;QACrB,OAAO,EAAE;IACX;IAEA,MAAMM,iBAA2B,EAAE;IACnCC,eAAeP,iBAAiBQ,MAAM,EAAEF;IACxC,OAAOA;AACT;AAEA,qEAAqE,GACrE,SAASC,eACPC,MAAe,EACfF,cAAwB,EACxBG,aAAa,EAAE,EACfC,oBAAoB,KAAK;IAEzB,KAAK,MAAMC,SAASH,OAAQ;QAC1B,4DAA4D;QAC5D,8DAA8D;QAC9D,EAAE;QACF,yGAAyG;QACzG,iGAAiG;QACjG,yEAAyE;QACzE,IAAI,CAAE,CAAA,UAAUG,KAAI,GAAI;YACtB;QACF;QAEA,MAAMC,YAAYC,UAAUJ,YAAYE,MAAMG,IAAI;QAClD,MAAMC,cAAc,eAAeJ,SAASA,MAAMK,SAAS,KAAK;QAChE,MAAMC,qBAAqBF,eAAeL;QAE1C,2DAA2D;QAC3D,IAAIO,oBAAoB;YACtBX,eAAeY,IAAI,CAACN;QACtB;QAEA,wGAAwG;QACxG,IAAID,MAAMQ,IAAI,KAAK,YAAY,YAAYR,OAAO;YAChD,KAAK,MAAMS,SAAST,MAAMU,MAAM,CAAE;gBAChC,IAAI,YAAYD,OAAO;oBACrBb,eAAea,MAAMZ,MAAM,EAAEF,gBAAgBM,WAAWK;gBAC1D;YACF;YACA;QACF;QAEA,+CAA+C;QAC/C,kEAAkE;QAClE,IAAI,YAAYN,OAAO;YACrBJ,eAAeI,MAAMH,MAAM,EAAEF,gBAAgBM,WAAWK;QAC1D;IACF;AACF;AAEA,+DAA+D,GAC/D,SAASJ,UAAUJ,UAAkB,EAAEa,SAAiB;IACtD,OAAOb,aAAa,GAAGA,WAAW,CAAC,EAAEa,WAAW,GAAGA;AACrD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Payload, Where } from 'payload';
|
|
2
|
+
export interface ContentAPIMeta {
|
|
3
|
+
localizedPaths?: string[];
|
|
4
|
+
pathTypes?: Record<string, 'array'>;
|
|
5
|
+
}
|
|
6
|
+
export interface BuildMetaOptions {
|
|
7
|
+
collection: string;
|
|
8
|
+
locale?: string;
|
|
9
|
+
where?: Where;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds the `meta` object for Content API requests.
|
|
13
|
+
* Combines pathTypes (for array field handling) and localizedPaths (for locale queries).
|
|
14
|
+
*
|
|
15
|
+
* @param payload - The Payload instance
|
|
16
|
+
* @param options - Options including collection slug, locale, and where clause
|
|
17
|
+
* @returns Object with meta property ready to spread into request body, or empty object if no meta needed
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildMeta(payload: Payload, options: BuildMetaOptions): {
|
|
20
|
+
meta?: ContentAPIMeta;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=buildMeta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildMeta.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/meta/buildMeta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAK7C,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAoBhG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { buildLocalizedPaths } from './buildLocalizedPaths.js';
|
|
2
|
+
import { buildPathTypes } from './buildPathTypes.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the `meta` object for Content API requests.
|
|
5
|
+
* Combines pathTypes (for array field handling) and localizedPaths (for locale queries).
|
|
6
|
+
*
|
|
7
|
+
* @param payload - The Payload instance
|
|
8
|
+
* @param options - Options including collection slug, locale, and where clause
|
|
9
|
+
* @returns Object with meta property ready to spread into request body, or empty object if no meta needed
|
|
10
|
+
*/ export function buildMeta(payload, options) {
|
|
11
|
+
const { collection, locale, where } = options;
|
|
12
|
+
const meta = {};
|
|
13
|
+
// Add pathTypes if there are array fields in the where clause
|
|
14
|
+
const pathTypes = buildPathTypes(payload, collection, where);
|
|
15
|
+
if (Object.keys(pathTypes).length > 0) {
|
|
16
|
+
meta.pathTypes = pathTypes;
|
|
17
|
+
}
|
|
18
|
+
// Add localizedPaths if locale is specified (but not 'all')
|
|
19
|
+
if (locale && locale !== 'all') {
|
|
20
|
+
const localizedPaths = buildLocalizedPaths(payload, collection);
|
|
21
|
+
if (localizedPaths.length > 0) {
|
|
22
|
+
meta.localizedPaths = localizedPaths;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return Object.keys(meta).length > 0 ? {
|
|
26
|
+
meta
|
|
27
|
+
} : {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=buildMeta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/db-content-api/utilities/meta/buildMeta.ts"],"sourcesContent":["import type { Payload, Where } from 'payload'\n\nimport { buildLocalizedPaths } from './buildLocalizedPaths.js'\nimport { buildPathTypes } from './buildPathTypes.js'\n\nexport interface ContentAPIMeta {\n localizedPaths?: string[]\n pathTypes?: Record<string, 'array'>\n}\n\nexport interface BuildMetaOptions {\n collection: string\n locale?: string\n where?: Where\n}\n\n/**\n * Builds the `meta` object for Content API requests.\n * Combines pathTypes (for array field handling) and localizedPaths (for locale queries).\n *\n * @param payload - The Payload instance\n * @param options - Options including collection slug, locale, and where clause\n * @returns Object with meta property ready to spread into request body, or empty object if no meta needed\n */\nexport function buildMeta(payload: Payload, options: BuildMetaOptions): { meta?: ContentAPIMeta } {\n const { collection, locale, where } = options\n\n const meta: ContentAPIMeta = {}\n\n // Add pathTypes if there are array fields in the where clause\n const pathTypes = buildPathTypes(payload, collection, where)\n if (Object.keys(pathTypes).length > 0) {\n meta.pathTypes = pathTypes\n }\n\n // Add localizedPaths if locale is specified (but not 'all')\n if (locale && locale !== 'all') {\n const localizedPaths = buildLocalizedPaths(payload, collection)\n if (localizedPaths.length > 0) {\n meta.localizedPaths = localizedPaths\n }\n }\n\n return Object.keys(meta).length > 0 ? { meta } : {}\n}\n"],"names":["buildLocalizedPaths","buildPathTypes","buildMeta","payload","options","collection","locale","where","meta","pathTypes","Object","keys","length","localizedPaths"],"mappings":"AAEA,SAASA,mBAAmB,QAAQ,2BAA0B;AAC9D,SAASC,cAAc,QAAQ,sBAAqB;AAapD;;;;;;;CAOC,GACD,OAAO,SAASC,UAAUC,OAAgB,EAAEC,OAAyB;IACnE,MAAM,EAAEC,UAAU,EAAEC,MAAM,EAAEC,KAAK,EAAE,GAAGH;IAEtC,MAAMI,OAAuB,CAAC;IAE9B,8DAA8D;IAC9D,MAAMC,YAAYR,eAAeE,SAASE,YAAYE;IACtD,IAAIG,OAAOC,IAAI,CAACF,WAAWG,MAAM,GAAG,GAAG;QACrCJ,KAAKC,SAAS,GAAGA;IACnB;IAEA,4DAA4D;IAC5D,IAAIH,UAAUA,WAAW,OAAO;QAC9B,MAAMO,iBAAiBb,oBAAoBG,SAASE;QACpD,IAAIQ,eAAeD,MAAM,GAAG,GAAG;YAC7BJ,KAAKK,cAAc,GAAGA;QACxB;IACF;IAEA,OAAOH,OAAOC,IAAI,CAACH,MAAMI,MAAM,GAAG,IAAI;QAAEJ;IAAK,IAAI,CAAC;AACpD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Payload, Where } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Builds pathTypes metadata for Content API request.
|
|
4
|
+
* Returns a record of paths that are arrays for proper query generation.
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildPathTypes(payload: Payload, collectionSlug: string, where: undefined | Where): Record<string, 'array'>;
|
|
7
|
+
//# sourceMappingURL=buildPathTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildPathTypes.d.ts","sourceRoot":"","sources":["../../../../src/db-content-api/utilities/meta/buildPathTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AA8JpD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,SAAS,GAAG,KAAK,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0BzB"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts all field paths from a where clause recursively
|
|
3
|
+
*/ function extractPathsFromWhere(where, paths = new Set()) {
|
|
4
|
+
if (!where || typeof where !== 'object') {
|
|
5
|
+
return paths;
|
|
6
|
+
}
|
|
7
|
+
// Handle 'and' operator
|
|
8
|
+
if ('and' in where && Array.isArray(where.and)) {
|
|
9
|
+
for (const clause of where.and){
|
|
10
|
+
extractPathsFromWhere(clause, paths);
|
|
11
|
+
}
|
|
12
|
+
return paths;
|
|
13
|
+
}
|
|
14
|
+
// Handle 'or' operator
|
|
15
|
+
if ('or' in where && Array.isArray(where.or)) {
|
|
16
|
+
for (const clause of where.or){
|
|
17
|
+
extractPathsFromWhere(clause, paths);
|
|
18
|
+
}
|
|
19
|
+
return paths;
|
|
20
|
+
}
|
|
21
|
+
// Handle regular conditions - keys are field paths
|
|
22
|
+
for(const key in where){
|
|
23
|
+
if (key === 'and' || key === 'or') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// Add the path
|
|
27
|
+
paths.add(key);
|
|
28
|
+
// Check if the value is a nested where clause (for relationships)
|
|
29
|
+
const value = where[key];
|
|
30
|
+
if (value && typeof value === 'object' && !('equals' in value) && !('in' in value)) {
|
|
31
|
+
// Might be a nested where, recurse
|
|
32
|
+
extractPathsFromWhere(value, paths);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return paths;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Determines the type of a field based on its configuration
|
|
39
|
+
*/ function getFieldType(field) {
|
|
40
|
+
// Array field
|
|
41
|
+
if (field.type === 'array') {
|
|
42
|
+
return 'array';
|
|
43
|
+
}
|
|
44
|
+
// Blocks field (array of objects)
|
|
45
|
+
if (field.type === 'blocks') {
|
|
46
|
+
return 'array';
|
|
47
|
+
}
|
|
48
|
+
// Relationship field with hasMany (array)
|
|
49
|
+
if (field.type === 'relationship' && 'hasMany' in field && field.hasMany) {
|
|
50
|
+
return 'array';
|
|
51
|
+
}
|
|
52
|
+
// Select field with hasMany (array)
|
|
53
|
+
if (field.type === 'select' && 'hasMany' in field && field.hasMany) {
|
|
54
|
+
return 'array';
|
|
55
|
+
}
|
|
56
|
+
// Upload field with hasMany (array)
|
|
57
|
+
if (field.type === 'upload' && 'hasMany' in field && field.hasMany) {
|
|
58
|
+
return 'array';
|
|
59
|
+
}
|
|
60
|
+
// Objects (group, collapsible, row, tabs) don't need traversal
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Finds a field in the collection schema by its path
|
|
65
|
+
* Supports nested paths like "access.read.users"
|
|
66
|
+
*/ function findFieldByPath(payload, collectionSlug, path) {
|
|
67
|
+
const collectionConfig = payload.config.collections.find((c)=>c.slug === collectionSlug);
|
|
68
|
+
if (!collectionConfig) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const pathSegments = path.split('.');
|
|
72
|
+
// Walk through the path segments to find the field
|
|
73
|
+
let currentFields = collectionConfig.fields;
|
|
74
|
+
let currentField = undefined;
|
|
75
|
+
for(let i = 0; i < pathSegments.length; i++){
|
|
76
|
+
const segment = pathSegments[i];
|
|
77
|
+
// First try to find a direct match
|
|
78
|
+
currentField = currentFields.find((f)=>'name' in f && f.name === segment);
|
|
79
|
+
// If not found, check if any unnamed container fields (collapsible, row) contain it
|
|
80
|
+
if (!currentField) {
|
|
81
|
+
for (const field of currentFields){
|
|
82
|
+
if (!('name' in field) || !field.name) {
|
|
83
|
+
// This is an unnamed field, check its contents
|
|
84
|
+
if ('fields' in field && field.type === 'collapsible' || 'fields' in field && field.type === 'row') {
|
|
85
|
+
currentField = field.fields.find((f)=>'name' in f && f.name === segment);
|
|
86
|
+
if (currentField) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
} else if ('tabs' in field && field.type === 'tabs') {
|
|
90
|
+
// Check all tabs
|
|
91
|
+
for (const tab of field.tabs){
|
|
92
|
+
currentField = tab.fields.find((f)=>'name' in f && f.name === segment);
|
|
93
|
+
if (currentField) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (currentField) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!currentField) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
// If we're not at the end, get nested fields
|
|
108
|
+
if (i < pathSegments.length - 1) {
|
|
109
|
+
if (currentField.type === 'group' && 'fields' in currentField) {
|
|
110
|
+
currentFields = currentField.fields;
|
|
111
|
+
} else if (currentField.type === 'array' && 'fields' in currentField) {
|
|
112
|
+
currentFields = currentField.fields;
|
|
113
|
+
} else if (currentField.type === 'blocks' && 'blocks' in currentField) {
|
|
114
|
+
// For blocks, we can't determine the exact field without knowing the block type
|
|
115
|
+
// Return the blocks field itself
|
|
116
|
+
return currentField;
|
|
117
|
+
} else {
|
|
118
|
+
// Field doesn't have nested fields, can't continue
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return currentField;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Builds pathTypes metadata for Content API request.
|
|
127
|
+
* Returns a record of paths that are arrays for proper query generation.
|
|
128
|
+
*/ export function buildPathTypes(payload, collectionSlug, where) {
|
|
129
|
+
if (!where) {
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
const pathTypes = {};
|
|
133
|
+
const paths = extractPathsFromWhere(where);
|
|
134
|
+
for (const path of paths){
|
|
135
|
+
// For each path, we need to check all segments to see if any intermediate ones are arrays/objects
|
|
136
|
+
const segments = path.split('.');
|
|
137
|
+
for(let i = 0; i < segments.length; i++){
|
|
138
|
+
const partialPath = segments.slice(0, i + 1).join('.');
|
|
139
|
+
const field = findFieldByPath(payload, collectionSlug, partialPath);
|
|
140
|
+
if (field) {
|
|
141
|
+
const fieldType = getFieldType(field);
|
|
142
|
+
if (fieldType === 'array') {
|
|
143
|
+
pathTypes[partialPath] = fieldType;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return pathTypes;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=buildPathTypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/db-content-api/utilities/meta/buildPathTypes.ts"],"sourcesContent":["import type { Field, Payload, Where } from 'payload'\n\n/**\n * Extracts all field paths from a where clause recursively\n */\nfunction extractPathsFromWhere(where: Where, paths: Set<string> = new Set()): Set<string> {\n if (!where || typeof where !== 'object') {\n return paths\n }\n\n // Handle 'and' operator\n if ('and' in where && Array.isArray(where.and)) {\n for (const clause of where.and) {\n extractPathsFromWhere(clause, paths)\n }\n return paths\n }\n\n // Handle 'or' operator\n if ('or' in where && Array.isArray(where.or)) {\n for (const clause of where.or) {\n extractPathsFromWhere(clause, paths)\n }\n return paths\n }\n\n // Handle regular conditions - keys are field paths\n for (const key in where) {\n if (key === 'and' || key === 'or') {\n continue\n }\n\n // Add the path\n paths.add(key)\n\n // Check if the value is a nested where clause (for relationships)\n const value = where[key]\n if (value && typeof value === 'object' && !('equals' in value) && !('in' in value)) {\n // Might be a nested where, recurse\n extractPathsFromWhere(value as Where, paths)\n }\n }\n\n return paths\n}\n\n/**\n * Determines the type of a field based on its configuration\n */\nfunction getFieldType(field: Field): 'array' | null {\n // Array field\n if (field.type === 'array') {\n return 'array'\n }\n\n // Blocks field (array of objects)\n if (field.type === 'blocks') {\n return 'array'\n }\n\n // Relationship field with hasMany (array)\n if (field.type === 'relationship' && 'hasMany' in field && field.hasMany) {\n return 'array'\n }\n\n // Select field with hasMany (array)\n if (field.type === 'select' && 'hasMany' in field && field.hasMany) {\n return 'array'\n }\n\n // Upload field with hasMany (array)\n if (field.type === 'upload' && 'hasMany' in field && field.hasMany) {\n return 'array'\n }\n\n // Objects (group, collapsible, row, tabs) don't need traversal\n return null\n}\n\n/**\n * Finds a field in the collection schema by its path\n * Supports nested paths like \"access.read.users\"\n */\nfunction findFieldByPath(\n payload: Payload,\n collectionSlug: string,\n path: string,\n): Field | undefined {\n const collectionConfig = payload.config.collections.find((c) => c.slug === collectionSlug)\n if (!collectionConfig) {\n return undefined\n }\n\n const pathSegments = path.split('.')\n\n // Walk through the path segments to find the field\n let currentFields = collectionConfig.fields\n let currentField: Field | undefined = undefined\n\n for (let i = 0; i < pathSegments.length; i++) {\n const segment = pathSegments[i]\n\n // First try to find a direct match\n currentField = currentFields.find((f) => 'name' in f && f.name === segment)\n\n // If not found, check if any unnamed container fields (collapsible, row) contain it\n if (!currentField) {\n for (const field of currentFields) {\n if (!('name' in field) || !field.name) {\n // This is an unnamed field, check its contents\n if (\n ('fields' in field && field.type === 'collapsible') ||\n ('fields' in field && field.type === 'row')\n ) {\n currentField = field.fields.find((f) => 'name' in f && f.name === segment)\n if (currentField) {\n break\n }\n } else if ('tabs' in field && field.type === 'tabs') {\n // Check all tabs\n for (const tab of field.tabs) {\n currentField = tab.fields.find((f) => 'name' in f && f.name === segment)\n if (currentField) {\n break\n }\n }\n if (currentField) {\n break\n }\n }\n }\n }\n }\n\n if (!currentField) {\n return undefined\n }\n\n // If we're not at the end, get nested fields\n if (i < pathSegments.length - 1) {\n if (currentField.type === 'group' && 'fields' in currentField) {\n currentFields = currentField.fields\n } else if (currentField.type === 'array' && 'fields' in currentField) {\n currentFields = currentField.fields\n } else if (currentField.type === 'blocks' && 'blocks' in currentField) {\n // For blocks, we can't determine the exact field without knowing the block type\n // Return the blocks field itself\n return currentField\n } else {\n // Field doesn't have nested fields, can't continue\n return undefined\n }\n }\n }\n\n return currentField\n}\n\n/**\n * Builds pathTypes metadata for Content API request.\n * Returns a record of paths that are arrays for proper query generation.\n */\nexport function buildPathTypes(\n payload: Payload,\n collectionSlug: string,\n where: undefined | Where,\n): Record<string, 'array'> {\n if (!where) {\n return {}\n }\n\n const pathTypes: Record<string, 'array'> = {}\n const paths = extractPathsFromWhere(where)\n\n for (const path of paths) {\n // For each path, we need to check all segments to see if any intermediate ones are arrays/objects\n const segments = path.split('.')\n\n for (let i = 0; i < segments.length; i++) {\n const partialPath = segments.slice(0, i + 1).join('.')\n const field = findFieldByPath(payload, collectionSlug, partialPath)\n\n if (field) {\n const fieldType = getFieldType(field)\n if (fieldType === 'array') {\n pathTypes[partialPath] = fieldType\n }\n }\n }\n }\n\n return pathTypes\n}\n"],"names":["extractPathsFromWhere","where","paths","Set","Array","isArray","and","clause","or","key","add","value","getFieldType","field","type","hasMany","findFieldByPath","payload","collectionSlug","path","collectionConfig","config","collections","find","c","slug","undefined","pathSegments","split","currentFields","fields","currentField","i","length","segment","f","name","tab","tabs","buildPathTypes","pathTypes","segments","partialPath","slice","join","fieldType"],"mappings":"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"}
|