@payloadcms/figma 0.0.1-alpha.56 → 0.0.1-alpha.57
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/commands/deploy.js +1 -1
- package/dist/db-content-api/index.js +2 -0
- package/dist/db-content-api/utilities/data/index.js +9 -0
- package/dist/db-content-api/utilities/meta/buildMeta.d.ts +5 -0
- package/dist/db-content-api/utilities/meta/buildMeta.js +9 -1
- package/dist/db-content-api/utilities/meta/buildUniquePaths.d.ts +13 -0
- package/dist/db-content-api/utilities/meta/buildUniquePaths.js +62 -0
- package/dist/utils/build-lambda-zip.js +1 -1
- package/dist/utils/lambda-config.js +69 -67
- package/package.json +1 -2
package/dist/commands/deploy.js
CHANGED
|
@@ -184,7 +184,7 @@ import { loginCommand } from './login.js';
|
|
|
184
184
|
buildInfo = await detectBuild(projectPath);
|
|
185
185
|
if (!buildInfo) {
|
|
186
186
|
p.log.error(pc.red('✗ Build completed but standalone build not found'));
|
|
187
|
-
p.note('Ensure next.config
|
|
187
|
+
p.note('Ensure next.config has output: "standalone"', 'Check Lambda Configuration');
|
|
188
188
|
process.exit(1);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
@@ -476,6 +476,7 @@ async function create(args) {
|
|
|
476
476
|
locale,
|
|
477
477
|
...buildMeta(this.payload, {
|
|
478
478
|
collection: args.collection,
|
|
479
|
+
data: args.data,
|
|
479
480
|
locale
|
|
480
481
|
})
|
|
481
482
|
}
|
|
@@ -584,6 +585,7 @@ async function upsert(args) {
|
|
|
584
585
|
where: convertPayloadWhereToContentAPI(args.where),
|
|
585
586
|
...buildMeta(this.payload, {
|
|
586
587
|
collection: args.collection,
|
|
588
|
+
data: args.data,
|
|
587
589
|
locale,
|
|
588
590
|
where: args.where
|
|
589
591
|
})
|
|
@@ -157,6 +157,15 @@ export function dataFromContentAPI(payload, collectionSlug, data) {
|
|
|
157
157
|
return;
|
|
158
158
|
}
|
|
159
159
|
if (value !== null) {
|
|
160
|
+
// hasMany relationships: normalize to array (non-localized only).
|
|
161
|
+
// Unlike Drizzle (which reconstructs arrays from join tables), we store raw JSON,
|
|
162
|
+
// so a single-item input like `{ value, relationTo }` stays as-is. Payload expects arrays.
|
|
163
|
+
// Localized fields are skipped because their value is a locale map, not a relationship value.
|
|
164
|
+
if (field.type === 'relationship' && field.hasMany && !Array.isArray(value) && !('localized' in field && field.localized)) {
|
|
165
|
+
current[field.name] = [
|
|
166
|
+
value
|
|
167
|
+
];
|
|
168
|
+
}
|
|
160
169
|
// Localized fields: JSON string -> parsed object
|
|
161
170
|
// Content API may store localized fields as JSON strings like "{\"en\":\"value\"}"
|
|
162
171
|
// so we need to parse them back to objects.
|
|
@@ -11,6 +11,9 @@ import type { PathTypesRecord } from '../../temp-utilities/types.js';
|
|
|
11
11
|
export interface ContentAPIMeta {
|
|
12
12
|
localizedPaths?: string[];
|
|
13
13
|
pathTypes?: PathTypesRecord;
|
|
14
|
+
uniquePaths?: {
|
|
15
|
+
paths: string[];
|
|
16
|
+
}[];
|
|
14
17
|
}
|
|
15
18
|
/**
|
|
16
19
|
* The type that Content API currently expects (from generated types).
|
|
@@ -19,6 +22,8 @@ export interface ContentAPIMeta {
|
|
|
19
22
|
type ContentAPIMetaGenerated = components['schemas']['RequestMeta'];
|
|
20
23
|
export interface BuildMetaOptions {
|
|
21
24
|
collection: string;
|
|
25
|
+
/** Document data — when provided, unique field constraints are included in meta. */
|
|
26
|
+
data?: Record<string, unknown>;
|
|
22
27
|
locale: string | undefined;
|
|
23
28
|
where?: Where;
|
|
24
29
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildLocalizedPaths } from './buildLocalizedPaths.js';
|
|
2
2
|
import { buildPathTypes } from './buildPathTypes.js';
|
|
3
|
+
import { buildUniquePaths } from './buildUniquePaths.js';
|
|
3
4
|
/**
|
|
4
5
|
* Builds the `meta` object for Content API requests.
|
|
5
6
|
* Combines pathTypes (for array field handling) and localizedPaths (for locale queries).
|
|
@@ -8,7 +9,7 @@ import { buildPathTypes } from './buildPathTypes.js';
|
|
|
8
9
|
* @param options - Options including collection slug, locale, and where clause
|
|
9
10
|
* @returns Object with meta property ready to spread into request body, or empty object if no meta needed
|
|
10
11
|
*/ export function buildMeta(payload, options) {
|
|
11
|
-
const { collection, locale, where } = options;
|
|
12
|
+
const { collection, data, locale, where } = options;
|
|
12
13
|
const meta = {};
|
|
13
14
|
// Add pathTypes if there are array fields in the where clause
|
|
14
15
|
const pathTypes = buildPathTypes(payload, collection, where);
|
|
@@ -22,6 +23,13 @@ import { buildPathTypes } from './buildPathTypes.js';
|
|
|
22
23
|
meta.localizedPaths = localizedPaths;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
26
|
+
// Add uniquePaths when data is provided (create/upsert operations)
|
|
27
|
+
if (data) {
|
|
28
|
+
const uniquePaths = buildUniquePaths(payload, collection, data);
|
|
29
|
+
if (uniquePaths.length > 0) {
|
|
30
|
+
meta.uniquePaths = uniquePaths;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
25
33
|
// Cast to generated type - Content API will need to be updated to handle
|
|
26
34
|
// the new pathTypes format with relationship info. Until then, it will
|
|
27
35
|
// ignore the extra fields but still receive the data for testing.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import type { components } from '../../generated/content-api-types.js';
|
|
3
|
+
type UniquePath = components['schemas']['UniquePath'];
|
|
4
|
+
/**
|
|
5
|
+
* Builds a UniquePath[] from the collection's field config by collecting
|
|
6
|
+
* all fields with `unique: true` that have a non-null value in the data.
|
|
7
|
+
*
|
|
8
|
+
* Fields with null/undefined values are excluded to match sparse unique index
|
|
9
|
+
* behavior (multiple documents can have null for a unique field).
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildUniquePaths(payload: Payload, collectionSlug: string, data: Record<string, unknown>): UniquePath[];
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=buildUniquePaths.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a UniquePath[] from the collection's field config by collecting
|
|
3
|
+
* all fields with `unique: true` that have a non-null value in the data.
|
|
4
|
+
*
|
|
5
|
+
* Fields with null/undefined values are excluded to match sparse unique index
|
|
6
|
+
* behavior (multiple documents can have null for a unique field).
|
|
7
|
+
*/ export function buildUniquePaths(payload, collectionSlug, data) {
|
|
8
|
+
const isGlobal = collectionSlug.startsWith('_global-');
|
|
9
|
+
const actualSlug = isGlobal ? collectionSlug.substring(8) : collectionSlug;
|
|
10
|
+
const config = isGlobal ? payload.config.globals?.find((g)=>g.slug === actualSlug) : payload.config.collections.find((c)=>c.slug === actualSlug);
|
|
11
|
+
if (!config?.fields) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const unique = [];
|
|
15
|
+
collectUniqueFields(config.fields, '', data, unique);
|
|
16
|
+
return unique;
|
|
17
|
+
}
|
|
18
|
+
function getValueAtPath(data, path) {
|
|
19
|
+
const segments = path.split('.');
|
|
20
|
+
let current = data;
|
|
21
|
+
for (const segment of segments){
|
|
22
|
+
if (current == null || typeof current !== 'object') {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
current = current[segment];
|
|
26
|
+
}
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
function collectUniqueFields(fields, prefix, data, result) {
|
|
30
|
+
for (const field of fields){
|
|
31
|
+
if (field.type === 'row' || field.type === 'collapsible') {
|
|
32
|
+
collectUniqueFields(field.fields, prefix, data, result);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (field.type === 'tabs') {
|
|
36
|
+
for (const tab of field.tabs){
|
|
37
|
+
const tabPrefix = 'name' in tab && tab.name ? `${prefix}${tab.name}.` : prefix;
|
|
38
|
+
collectUniqueFields(tab.fields, tabPrefix, data, result);
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!('name' in field) || !field.name) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const path = prefix + field.name;
|
|
46
|
+
if ('unique' in field && field.unique) {
|
|
47
|
+
const value = getValueAtPath(data, path);
|
|
48
|
+
if (value != null) {
|
|
49
|
+
result.push({
|
|
50
|
+
paths: [
|
|
51
|
+
path
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (field.type === 'group' && 'fields' in field) {
|
|
57
|
+
collectUniqueFields(field.fields, `${path}.`, data, result);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=buildUniquePaths.js.map
|
|
@@ -20,7 +20,7 @@ import path from 'path';
|
|
|
20
20
|
const runShPath = path.join(projectPath, 'run.sh');
|
|
21
21
|
const zipPath = path.join(projectPath, 'lambda.zip');
|
|
22
22
|
if (!await isDirectory(standalonePath)) {
|
|
23
|
-
throw new Error('Standalone build not found at .next/standalone. ' + 'Ensure next.config
|
|
23
|
+
throw new Error('Standalone build not found at .next/standalone. ' + 'Ensure next.config has output: "standalone" and run build first.');
|
|
24
24
|
}
|
|
25
25
|
if (await isDirectory(staticPath)) {
|
|
26
26
|
const destStatic = path.join(standalonePath, '.next', 'static');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { parseModule, Syntax } from 'esprima-next';
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
import path from 'path';
|
|
3
|
+
import { IndentationText, Node, Project, SyntaxKind } from 'ts-morph';
|
|
4
4
|
const RUN_SH_CONTENT = `#!/bin/bash -x
|
|
5
5
|
|
|
6
6
|
[ ! -d '/tmp/cache' ] && mkdir -p /tmp/cache
|
|
@@ -36,11 +36,11 @@ NODE_ENV=production exec node server.js
|
|
|
36
36
|
await addLambdaBuildScript(projectPath, packageManager);
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Find the Next.js config file
|
|
40
|
-
*
|
|
41
|
-
* @todo Support TypeScript config files in future
|
|
39
|
+
* Find the Next.js config file, checking TS extensions first
|
|
42
40
|
*/ async function findNextConfigPath(projectPath) {
|
|
43
41
|
const configFiles = [
|
|
42
|
+
'next.config.ts',
|
|
43
|
+
'next.config.mts',
|
|
44
44
|
'next.config.js',
|
|
45
45
|
'next.config.mjs'
|
|
46
46
|
];
|
|
@@ -53,83 +53,85 @@ NODE_ENV=production exec node server.js
|
|
|
53
53
|
// File doesn't exist, try next one
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
throw new Error('Could not find next.config.js or next.config.mjs');
|
|
56
|
+
throw new Error('Could not find next.config.ts, next.config.mts, next.config.js, or next.config.mjs');
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* Add required Next.js config properties using AST parsing
|
|
59
|
+
* Add required Next.js config properties using ts-morph AST parsing
|
|
60
60
|
* - output: 'standalone' (for Lambda deployment)
|
|
61
61
|
* - eslint: { ignoreDuringBuilds: true } (allow builds with lint errors)
|
|
62
62
|
*/ async function addNextConfigProperties(projectPath) {
|
|
63
63
|
const nextConfigPath = await findNextConfigPath(projectPath);
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const ast = parseModule(content, {
|
|
73
|
-
loc: true
|
|
64
|
+
const project = new Project({
|
|
65
|
+
compilerOptions: {
|
|
66
|
+
allowJs: true
|
|
67
|
+
},
|
|
68
|
+
manipulationSettings: {
|
|
69
|
+
indentationText: IndentationText.TwoSpaces
|
|
70
|
+
},
|
|
71
|
+
skipAddingFilesFromTsConfig: true
|
|
74
72
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
// Find the object expression to modify
|
|
81
|
-
let targetObjectExpression;
|
|
82
|
-
if (exportDefaultDeclaration.declaration.type === 'ObjectExpression') {
|
|
83
|
-
// Direct export: export default { ... }
|
|
84
|
-
targetObjectExpression = exportDefaultDeclaration.declaration;
|
|
85
|
-
} else if (exportDefaultDeclaration.declaration.type === 'CallExpression') {
|
|
86
|
-
// Wrapped export: export default withPayload(nextConfig, ...)
|
|
87
|
-
const callExpr = exportDefaultDeclaration.declaration;
|
|
88
|
-
const firstArg = callExpr.arguments?.[0];
|
|
89
|
-
if (firstArg?.type === 'Identifier') {
|
|
90
|
-
// Find the variable declaration for this identifier
|
|
91
|
-
const configVarName = firstArg.name;
|
|
92
|
-
const varDeclaration = ast.body.find((node)=>{
|
|
93
|
-
if (node.type === Syntax.VariableDeclaration) {
|
|
94
|
-
const varNode = node;
|
|
95
|
-
return varNode.declarations?.some((decl)=>decl.id?.type === 'Identifier' && decl.id.name === configVarName && decl.init?.type === 'ObjectExpression');
|
|
96
|
-
}
|
|
97
|
-
return false;
|
|
98
|
-
});
|
|
99
|
-
if (varDeclaration) {
|
|
100
|
-
const declarator = varDeclaration.declarations?.find((decl)=>decl.id?.type === 'Identifier' && decl.id.name === configVarName);
|
|
101
|
-
if (declarator?.init?.type === 'ObjectExpression') {
|
|
102
|
-
targetObjectExpression = declarator.init;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (!targetObjectExpression?.loc) {
|
|
73
|
+
const sourceFile = project.addSourceFileAtPath(nextConfigPath);
|
|
74
|
+
// Find the config object to modify
|
|
75
|
+
const configObject = findConfigObject(sourceFile);
|
|
76
|
+
if (!configObject) {
|
|
108
77
|
throw new Error(`Could not find Next.js config object in ${path.basename(nextConfigPath)}. ` + `Expected either 'export default { ... }' or 'export default wrapper(configVar, ...)'`);
|
|
109
78
|
}
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
79
|
+
// Check which properties already exist using AST (avoids false positives from comments)
|
|
80
|
+
const hasOutput = configObject.getProperty('output') !== undefined;
|
|
81
|
+
const hasEslint = configObject.getProperty('eslint') !== undefined;
|
|
82
|
+
if (hasOutput && hasEslint) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
114
85
|
if (!hasOutput) {
|
|
115
|
-
|
|
86
|
+
configObject.addPropertyAssignment({
|
|
87
|
+
name: 'output',
|
|
88
|
+
initializer: "'standalone'"
|
|
89
|
+
});
|
|
116
90
|
}
|
|
117
91
|
if (!hasEslint) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
92
|
+
configObject.addPropertyAssignment({
|
|
93
|
+
name: 'eslint',
|
|
94
|
+
initializer: `{ ignoreDuringBuilds: true }`
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
await sourceFile.save();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Find the config object literal from either:
|
|
101
|
+
* - Direct export: `export default { ... }`
|
|
102
|
+
* - Wrapped export: `export default withPayload(configVar, ...)`
|
|
103
|
+
* where configVar is a variable declaration with an object literal
|
|
104
|
+
*/ function findConfigObject(sourceFile) {
|
|
105
|
+
const exportDefault = sourceFile.getFirstDescendantByKind(SyntaxKind.ExportAssignment);
|
|
106
|
+
if (!exportDefault) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const expression = exportDefault.getExpression();
|
|
110
|
+
// Direct export: export default { ... }
|
|
111
|
+
if (Node.isObjectLiteralExpression(expression)) {
|
|
112
|
+
return expression;
|
|
121
113
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
114
|
+
// Wrapped export: export default withPayload(configVar, ...)
|
|
115
|
+
if (Node.isCallExpression(expression)) {
|
|
116
|
+
const firstArg = expression.getArguments()[0];
|
|
117
|
+
// Variable reference: export default withPayload(configVar)
|
|
118
|
+
if (firstArg && Node.isIdentifier(firstArg)) {
|
|
119
|
+
const configVarName = firstArg.getText();
|
|
120
|
+
// Find the variable declaration
|
|
121
|
+
const varStatements = sourceFile.getVariableStatements();
|
|
122
|
+
for (const statement of varStatements){
|
|
123
|
+
for (const decl of statement.getDeclarations()){
|
|
124
|
+
if (decl.getName() === configVarName) {
|
|
125
|
+
const initializer = decl.getInitializer();
|
|
126
|
+
if (initializer && Node.isObjectLiteralExpression(initializer)) {
|
|
127
|
+
return initializer;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
|
-
|
|
131
|
-
const modifiedContent = lines.join('\n');
|
|
132
|
-
await fs.writeFile(nextConfigPath, modifiedContent, 'utf-8');
|
|
134
|
+
return undefined;
|
|
133
135
|
}
|
|
134
136
|
/**
|
|
135
137
|
* Add lambda:buildzip script to package.json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/figma",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.57",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"arg": "^5.0.2",
|
|
35
35
|
"conf": "^13.0.1",
|
|
36
36
|
"cross-spawn": "7.0.6",
|
|
37
|
-
"esprima-next": "^6.0.2",
|
|
38
37
|
"figures": "^6.1.0",
|
|
39
38
|
"jose": "6.0.12",
|
|
40
39
|
"jsonwebtoken": "9.0.3",
|