@payloadcms/figma 0.0.1-alpha.51 → 0.0.1-alpha.53

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.
@@ -151,4 +151,12 @@ export type BootstrapInfo = {
151
151
  * @param environmentName - Optional environment name filter
152
152
  */
153
153
  export declare function getBootstrapInfo(accessToken: string, cmsResourceId: string, environmentName?: string): Promise<BootstrapInfo>;
154
+ /**
155
+ * Resolve a CMS Resource ID from a legacy dataset (content system) ID.
156
+ * Maps to: GET /v1/cms/dataset/:datasetId
157
+ *
158
+ * Used during upgrade migration to convert old FIGMA_CONTENT_API_CONTENT_SYSTEM_ID
159
+ * into the new FIGMA_PROJECT_ID (CMS Resource ID).
160
+ */
161
+ export declare function getCmsResourceId(accessToken: string, datasetId: string): Promise<string>;
154
162
  //# sourceMappingURL=control-plane.d.ts.map
@@ -276,6 +276,27 @@ import * as log from '../utils/log.js';
276
276
  }
277
277
  };
278
278
  }
279
+ /**
280
+ * Resolve a CMS Resource ID from a legacy dataset (content system) ID.
281
+ * Maps to: GET /v1/cms/dataset/:datasetId
282
+ *
283
+ * Used during upgrade migration to convert old FIGMA_CONTENT_API_CONTENT_SYSTEM_ID
284
+ * into the new FIGMA_PROJECT_ID (CMS Resource ID).
285
+ */ export async function getCmsResourceId(accessToken, datasetId) {
286
+ const url = `${getControlPlaneBaseUrl()}/v1/cms/dataset/${datasetId}`;
287
+ log.debug(`Calling getCmsResourceId API at ${url}`);
288
+ const response = await controlPlaneFetch({
289
+ context: 'get CMS resource ID from dataset',
290
+ options: {
291
+ headers: {
292
+ Authorization: `Bearer ${accessToken}`
293
+ }
294
+ },
295
+ url
296
+ });
297
+ const data = await response.json();
298
+ return data.meta.cms_resource_id;
299
+ }
279
300
  const MAX_RETRIES = 3;
280
301
  const INITIAL_RETRY_DELAY = 1000;
281
302
  function sleep(ms) {
package/dist/cli.js CHANGED
@@ -9,6 +9,7 @@ import { initCommand } from './commands/init.js';
9
9
  import { listTokensCommand } from './commands/list-tokens.js';
10
10
  import { loginCommand } from './commands/login.js';
11
11
  import { logoutCommand } from './commands/logout.js';
12
+ import { upgradeCommand } from './commands/upgrade.js';
12
13
  import { setInfraEnvironment } from './constants.js';
13
14
  import { helpMessage } from './utils/messages.js';
14
15
  /**
@@ -30,6 +31,7 @@ class Main {
30
31
  this.args = arg({
31
32
  '--all': Boolean,
32
33
  '--debug': Boolean,
34
+ '--dry-run': Boolean,
33
35
  '--env': String,
34
36
  '--force': Boolean,
35
37
  '--help': Boolean,
@@ -137,6 +139,11 @@ class Main {
137
139
  debug: this.args['--debug']
138
140
  });
139
141
  break;
142
+ case 'upgrade':
143
+ await upgradeCommand({
144
+ isDryRun: this.args['--dry-run']
145
+ });
146
+ break;
140
147
  default:
141
148
  p.log.error(pc.red(`Unknown command: ${subcommand}`));
142
149
  p.note('Use --help to see available commands', 'Tip');
@@ -0,0 +1,5 @@
1
+ export type UpgradeCommandOptions = {
2
+ isDryRun?: boolean;
3
+ };
4
+ export declare function upgradeCommand(options?: UpgradeCommandOptions): Promise<void>;
5
+ //# sourceMappingURL=upgrade.d.ts.map
@@ -0,0 +1,331 @@
1
+ import * as p from '@clack/prompts';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import pc from 'picocolors';
5
+ import { Project } from 'ts-morph';
6
+ import { fileURLToPath } from 'url';
7
+ import { getBootstrapInfo, getCmsResourceId } from '../api/control-plane.js';
8
+ import { getValidAccessToken } from '../auth/oauth-flow.js';
9
+ import { getTokenStore } from '../auth/token-store.js';
10
+ import { cacheAllEnvironments } from '../utils/cache-bootstrap.js';
11
+ import { addOrUpdateEnvVar, getEnvVar, removeEnvVar } from '../utils/env-management.js';
12
+ import { formatFile } from '../utils/formatter.js';
13
+ import { getPackageManager } from '../utils/package-manager.js';
14
+ import { removeFigmaContentSystemId } from '../utils/payload-config-ast.js';
15
+ import { findPayloadConfig } from '../utils/payload-config-finder.js';
16
+ import { checkPackageInstalled, installPackage } from '../utils/payload-package-check.js';
17
+ import { getOwnVersion } from '../utils/version-check.js';
18
+ export async function upgradeCommand(options = {}) {
19
+ const cwd = process.cwd();
20
+ const { isDryRun = false } = options;
21
+ const changes = [];
22
+ const warnings = [];
23
+ const spinner = p.spinner();
24
+ spinner.start('Checking for needed migrations...');
25
+ spinner.stop('Checking for needed migrations...');
26
+ await migrateEnvRename({
27
+ changes,
28
+ cwd,
29
+ isDryRun
30
+ });
31
+ await migrateProjectId({
32
+ changes,
33
+ cwd,
34
+ isDryRun,
35
+ warnings
36
+ });
37
+ await ensureBootstrapCached({
38
+ changes,
39
+ cwd,
40
+ isDryRun
41
+ });
42
+ await migrateRemoveDeprecatedEnvVars({
43
+ changes,
44
+ cwd,
45
+ isDryRun,
46
+ warnings
47
+ });
48
+ await migrateConfigAst({
49
+ changes,
50
+ cwd,
51
+ isDryRun
52
+ });
53
+ await migratePackageVersion({
54
+ changes,
55
+ cwd,
56
+ isDryRun
57
+ });
58
+ for (const warning of warnings){
59
+ p.log.warning(warning);
60
+ }
61
+ if (changes.length === 0) {
62
+ p.log.success('Everything is up to date.');
63
+ return;
64
+ }
65
+ if (isDryRun) {
66
+ p.log.info(`Dry run: ${changes.length} change(s) detected.`);
67
+ } else {
68
+ p.log.success(`${changes.length} change(s) applied.`);
69
+ }
70
+ }
71
+ const DEPRECATED_ENV_VARS = [
72
+ 'FIGMA_CONTENT_API_CONTENT_SYSTEM_ID',
73
+ 'FIGMA_TENANT_ID',
74
+ 'FIGMA_OAUTH_CLIENT_ID',
75
+ 'FIGMA_OAUTH_CLIENT_SECRET'
76
+ ];
77
+ async function migrateEnvRename({ changes, cwd, isDryRun }) {
78
+ try {
79
+ const oldValue = await getEnvVar(cwd, 'FIGMA_ENV');
80
+ if (oldValue === null) {
81
+ return;
82
+ }
83
+ if (isDryRun) {
84
+ p.log.message(`${pc.dim('○')} Would rename FIGMA_ENV → FIGMA_INFRA_ENV`);
85
+ changes.push('Rename FIGMA_ENV → FIGMA_INFRA_ENV');
86
+ return;
87
+ }
88
+ await addOrUpdateEnvVar(cwd, 'FIGMA_INFRA_ENV', oldValue);
89
+ await removeEnvVar(cwd, 'FIGMA_ENV');
90
+ p.log.message(`${pc.green('●')} Env: Renamed FIGMA_ENV → FIGMA_INFRA_ENV`);
91
+ changes.push('Renamed FIGMA_ENV → FIGMA_INFRA_ENV');
92
+ } catch (error) {
93
+ const msg = error instanceof Error ? error.message : 'Unknown error';
94
+ p.log.warning(`Env rename migration failed: ${msg}`);
95
+ }
96
+ }
97
+ async function migrateProjectId({ changes, cwd, isDryRun, warnings = [] }) {
98
+ try {
99
+ const existingProjectId = await getEnvVar(cwd, 'FIGMA_PROJECT_ID');
100
+ if (existingProjectId) {
101
+ return;
102
+ }
103
+ const oldContentSystemId = await getEnvVar(cwd, 'FIGMA_CONTENT_API_CONTENT_SYSTEM_ID');
104
+ if (!oldContentSystemId) {
105
+ return;
106
+ }
107
+ if (isDryRun) {
108
+ p.log.message(`${pc.dim('○')} Would resolve FIGMA_PROJECT_ID from content system ID`);
109
+ changes.push('Resolve FIGMA_PROJECT_ID from content system ID');
110
+ return;
111
+ }
112
+ const store = getTokenStore();
113
+ const accessToken = await getValidAccessToken(store);
114
+ if (!accessToken) {
115
+ warnings.push('Not authenticated — could not resolve FIGMA_PROJECT_ID. Run `npx @payloadcms/figma login` first.');
116
+ return;
117
+ }
118
+ const cmsResourceId = await getCmsResourceId(accessToken, oldContentSystemId);
119
+ await addOrUpdateEnvVar(cwd, 'FIGMA_PROJECT_ID', cmsResourceId);
120
+ // Resolve actual environment name from bootstrap API
121
+ try {
122
+ const bootstrapInfo = await getBootstrapInfo(accessToken, cmsResourceId);
123
+ cacheAllEnvironments(store, cmsResourceId, bootstrapInfo);
124
+ const envName = bootstrapInfo.environments[0]?.name ?? 'production';
125
+ await addOrUpdateEnvVar(cwd, 'FIGMA_ENVIRONMENT_NAME', envName);
126
+ } catch {
127
+ await addOrUpdateEnvVar(cwd, 'FIGMA_ENVIRONMENT_NAME', 'production');
128
+ }
129
+ p.log.message(`${pc.green('●')} Env: Resolved FIGMA_PROJECT_ID from content system ID`);
130
+ changes.push('Resolved FIGMA_PROJECT_ID from content system ID');
131
+ } catch (error) {
132
+ const msg = error instanceof Error ? error.message : 'Unknown error';
133
+ p.log.warning(`Project ID migration failed: ${msg}`);
134
+ }
135
+ }
136
+ async function ensureBootstrapCached(params) {
137
+ try {
138
+ const projectId = await getEnvVar(params.cwd, 'FIGMA_PROJECT_ID');
139
+ if (!projectId) {
140
+ return;
141
+ }
142
+ const environmentName = await getEnvVar(params.cwd, 'FIGMA_ENVIRONMENT_NAME') ?? 'production';
143
+ const store = getTokenStore();
144
+ if (store.getBootstrapData(projectId, environmentName)) {
145
+ return;
146
+ }
147
+ const accessToken = await getValidAccessToken(store);
148
+ if (!accessToken) {
149
+ return;
150
+ }
151
+ const bootstrapInfo = await getBootstrapInfo(accessToken, projectId);
152
+ cacheAllEnvironments(store, projectId, bootstrapInfo);
153
+ // Fix FIGMA_ENVIRONMENT_NAME if it doesn't match any actual environment
154
+ const envNames = bootstrapInfo.environments.map((e)=>e.name);
155
+ if (!envNames.includes(environmentName) && envNames.length > 0) {
156
+ const correctName = envNames[0];
157
+ if (params.isDryRun) {
158
+ p.log.message(`${pc.dim('○')} Would fix FIGMA_ENVIRONMENT_NAME: ${environmentName} → ${correctName}`);
159
+ params.changes.push(`Fix FIGMA_ENVIRONMENT_NAME`);
160
+ return;
161
+ }
162
+ await addOrUpdateEnvVar(params.cwd, 'FIGMA_ENVIRONMENT_NAME', correctName);
163
+ p.log.message(`${pc.green('●')} Env: Fixed FIGMA_ENVIRONMENT_NAME: ${environmentName} → ${correctName}`);
164
+ params.changes.push(`Fixed FIGMA_ENVIRONMENT_NAME: ${environmentName} → ${correctName}`);
165
+ }
166
+ } catch {
167
+ // Best-effort — runtime will retry if needed
168
+ }
169
+ }
170
+ async function migrateRemoveDeprecatedEnvVars({ changes, cwd, isDryRun, warnings = [] }) {
171
+ try {
172
+ const presentVars = [];
173
+ for (const key of DEPRECATED_ENV_VARS){
174
+ const value = await getEnvVar(cwd, key);
175
+ if (value !== null) {
176
+ presentVars.push(key);
177
+ }
178
+ }
179
+ if (presentVars.length === 0) {
180
+ return;
181
+ }
182
+ // Only remove when bootstrap data is available (proves the project is set up correctly)
183
+ const projectId = await getEnvVar(cwd, 'FIGMA_PROJECT_ID');
184
+ if (!projectId) {
185
+ warnings.push('Cannot remove deprecated env vars without FIGMA_PROJECT_ID. Run upgrade again after project ID migration.');
186
+ return;
187
+ }
188
+ const environmentName = await getEnvVar(cwd, 'FIGMA_ENVIRONMENT_NAME') ?? 'production';
189
+ const store = getTokenStore();
190
+ const bootstrapData = store.getBootstrapData(projectId, environmentName);
191
+ if (!bootstrapData) {
192
+ // Try fetching from API
193
+ const accessToken = await getValidAccessToken(store);
194
+ if (accessToken) {
195
+ try {
196
+ const bootstrapInfo = await getBootstrapInfo(accessToken, projectId);
197
+ cacheAllEnvironments(store, projectId, bootstrapInfo);
198
+ } catch {
199
+ warnings.push('Could not verify bootstrap data — keeping deprecated env vars. Run `npx @payloadcms/figma init` to complete migration.');
200
+ return;
201
+ }
202
+ } else {
203
+ warnings.push('Not authenticated — keeping deprecated env vars. Run `npx @payloadcms/figma login` first.');
204
+ return;
205
+ }
206
+ }
207
+ if (isDryRun) {
208
+ p.log.message(`${pc.dim('○')} Would remove deprecated env vars: ${presentVars.join(', ')}`);
209
+ changes.push(`Remove deprecated env vars: ${presentVars.join(', ')}`);
210
+ return;
211
+ }
212
+ const removed = [];
213
+ for (const key of presentVars){
214
+ await removeEnvVar(cwd, key);
215
+ removed.push(key);
216
+ }
217
+ p.log.message(`${pc.green('●')} Env: Removed deprecated env vars: ${removed.join(', ')}`);
218
+ changes.push(`Removed deprecated env vars: ${removed.join(', ')}`);
219
+ } catch (error) {
220
+ const msg = error instanceof Error ? error.message : 'Unknown error';
221
+ p.log.warning(`Deprecated env var removal failed: ${msg}`);
222
+ }
223
+ }
224
+ async function migrateConfigAst({ changes, cwd, isDryRun }) {
225
+ try {
226
+ const configPath = await findPayloadConfig(cwd);
227
+ if (!configPath) {
228
+ return;
229
+ }
230
+ const project = new Project({
231
+ skipAddingFilesFromTsConfig: true
232
+ });
233
+ const sourceFile = project.addSourceFileAtPath(configPath);
234
+ const wouldRemove = removeFigmaContentSystemId(sourceFile);
235
+ if (!wouldRemove) {
236
+ return;
237
+ }
238
+ if (isDryRun) {
239
+ p.log.message(`${pc.dim('○')} Would remove contentSystemId from payload.config.ts`);
240
+ changes.push('Remove contentSystemId from payload.config.ts');
241
+ return;
242
+ }
243
+ // Re-parse fresh since the detection already mutated the AST
244
+ const freshProject = new Project({
245
+ skipAddingFilesFromTsConfig: true
246
+ });
247
+ const freshSourceFile = freshProject.addSourceFileAtPath(configPath);
248
+ removeFigmaContentSystemId(freshSourceFile);
249
+ await freshSourceFile.save();
250
+ try {
251
+ const packageManager = await getPackageManager(cwd);
252
+ await formatFile(configPath, packageManager);
253
+ } catch {
254
+ // Formatting is best-effort
255
+ }
256
+ p.log.message(`${pc.green('●')} Config: Removed contentSystemId from payload.config.ts`);
257
+ changes.push('Removed contentSystemId from payload.config.ts');
258
+ } catch (error) {
259
+ const msg = error instanceof Error ? error.message : 'Unknown error';
260
+ p.log.warning(`Config AST migration failed: ${msg}`);
261
+ }
262
+ }
263
+ async function migratePackageVersion({ changes, cwd, isDryRun }) {
264
+ try {
265
+ const isInstalled = await checkPackageInstalled(cwd, '@payloadcms/figma');
266
+ if (!isInstalled) {
267
+ return;
268
+ }
269
+ const cliDir = path.dirname(fileURLToPath(import.meta.url));
270
+ const isFromRegistry = cliDir.includes('node_modules') || cliDir.includes('.npm');
271
+ if (isFromRegistry) {
272
+ await migrateRegistryPackageVersion({
273
+ changes,
274
+ cliDir,
275
+ cwd,
276
+ isDryRun
277
+ });
278
+ } else {
279
+ await migrateLocalPackageVersion({
280
+ changes,
281
+ cliDir,
282
+ cwd,
283
+ isDryRun
284
+ });
285
+ }
286
+ } catch (error) {
287
+ const msg = error instanceof Error ? error.message : 'Unknown error';
288
+ p.log.warning(`Package version sync failed: ${msg}`);
289
+ }
290
+ }
291
+ async function migrateRegistryPackageVersion({ changes, cliDir: _cliDir, cwd, isDryRun }) {
292
+ const cliVersion = await getOwnVersion();
293
+ const pkgJsonPath = path.join(cwd, 'package.json');
294
+ const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'));
295
+ const installedVersion = pkgJson.dependencies?.['@payloadcms/figma']?.replace(/^[\^~>=<]+/, '');
296
+ if (!installedVersion || installedVersion === cliVersion) {
297
+ return;
298
+ }
299
+ if (isDryRun) {
300
+ p.log.message(`${pc.dim('○')} Would update @payloadcms/figma ${installedVersion} → ${cliVersion}`);
301
+ changes.push(`Update @payloadcms/figma ${installedVersion} → ${cliVersion}`);
302
+ return;
303
+ }
304
+ const packageManager = await getPackageManager(cwd);
305
+ await installPackage(cwd, '@payloadcms/figma', packageManager, cliVersion);
306
+ p.log.message(`${pc.green('●')} Package: Updated @payloadcms/figma ${installedVersion} → ${cliVersion}`);
307
+ changes.push(`Updated @payloadcms/figma ${installedVersion} → ${cliVersion}`);
308
+ }
309
+ async function migrateLocalPackageVersion({ changes, cliDir, cwd, isDryRun }) {
310
+ if (isDryRun) {
311
+ p.log.message(`${pc.dim('○')} Would install @payloadcms/figma from local build`);
312
+ changes.push('Install @payloadcms/figma from local build');
313
+ return;
314
+ }
315
+ // Dynamic import — findPackageRoot and packLocalPackage will be exported in a later task
316
+ const { findPackageRoot, packLocalPackage } = await import('../utils/handle-upgrade.js');
317
+ const pkgDir = await findPackageRoot(cliDir);
318
+ if (!pkgDir) {
319
+ return;
320
+ }
321
+ const packageManager = await getPackageManager(cwd);
322
+ const tgzPath = packLocalPackage(pkgDir);
323
+ const destTgz = path.join(cwd, path.basename(tgzPath));
324
+ await fs.rename(tgzPath, destTgz);
325
+ await installPackage(cwd, destTgz, packageManager);
326
+ await fs.unlink(destTgz).catch(()=>{});
327
+ p.log.message(`${pc.green('●')} Package: Installed @payloadcms/figma from local build`);
328
+ changes.push('Installed @payloadcms/figma from local build');
329
+ }
330
+
331
+ //# sourceMappingURL=upgrade.js.map
@@ -1494,6 +1494,117 @@ export type paths = {
1494
1494
  };
1495
1495
  };
1496
1496
  };
1497
+ /** @description Unauthorized */
1498
+ 401: {
1499
+ headers: {
1500
+ [name: string]: unknown;
1501
+ };
1502
+ content: {
1503
+ 'application/json': {
1504
+ /** @example Error message describing the issue. */
1505
+ message: string;
1506
+ };
1507
+ };
1508
+ };
1509
+ /** @description Forbidden */
1510
+ 403: {
1511
+ headers: {
1512
+ [name: string]: unknown;
1513
+ };
1514
+ content: {
1515
+ 'application/json': {
1516
+ /** @example Error message describing the issue. */
1517
+ message: string;
1518
+ };
1519
+ };
1520
+ };
1521
+ /** @description Not Found */
1522
+ 404: {
1523
+ headers: {
1524
+ [name: string]: unknown;
1525
+ };
1526
+ content: {
1527
+ 'application/json': {
1528
+ /** @example Error message describing the issue. */
1529
+ message: string;
1530
+ };
1531
+ };
1532
+ };
1533
+ /** @description Internal Server Error */
1534
+ 500: {
1535
+ headers: {
1536
+ [name: string]: unknown;
1537
+ };
1538
+ content: {
1539
+ 'application/json': {
1540
+ /** @example Error message describing the issue. */
1541
+ message: string;
1542
+ };
1543
+ };
1544
+ };
1545
+ };
1546
+ };
1547
+ delete?: never;
1548
+ options?: never;
1549
+ head?: never;
1550
+ patch?: never;
1551
+ trace?: never;
1552
+ };
1553
+ '/api/v0/documents:findDistinct': {
1554
+ parameters: {
1555
+ query?: never;
1556
+ header?: never;
1557
+ path?: never;
1558
+ cookie?: never;
1559
+ };
1560
+ get?: never;
1561
+ put?: never;
1562
+ post: {
1563
+ parameters: {
1564
+ query?: never;
1565
+ header?: never;
1566
+ path?: never;
1567
+ cookie?: never;
1568
+ };
1569
+ requestBody?: {
1570
+ content: {
1571
+ 'application/json': components['schemas']['FindDistinctDocumentsRequest'];
1572
+ };
1573
+ };
1574
+ responses: {
1575
+ /** @description Distinct values found */
1576
+ 200: {
1577
+ headers: {
1578
+ [name: string]: unknown;
1579
+ };
1580
+ content: {
1581
+ 'application/json': components['schemas']['FindDistinctDocumentsResponse'];
1582
+ };
1583
+ };
1584
+ /** @description Bad Request */
1585
+ 400: {
1586
+ headers: {
1587
+ [name: string]: unknown;
1588
+ };
1589
+ content: {
1590
+ 'application/json': {
1591
+ /** @example Error message describing the issue. */
1592
+ message: string;
1593
+ };
1594
+ };
1595
+ };
1596
+ /** @description Unauthorized */
1597
+ 401: {
1598
+ headers: {
1599
+ [name: string]: unknown;
1600
+ };
1601
+ content: {
1602
+ 'application/json': {
1603
+ /** @example Error message describing the issue. */
1604
+ message: string;
1605
+ };
1606
+ };
1607
+ };
1497
1608
  /** @description Forbidden */
1498
1609
  403: {
1499
1610
  headers: {
@@ -2953,12 +3064,21 @@ export type components = {
2953
3064
  contentSystemScope?: string;
2954
3065
  };
2955
3066
  HealthResponse: {
2956
- /** @example ok */
2957
- status: string;
2958
- /** @example connected */
2959
- database: string;
2960
- /** @example available */
2961
- s3: string;
3067
+ /**
3068
+ * @example ok
3069
+ * @enum {string}
3070
+ */
3071
+ status: 'ok';
3072
+ /**
3073
+ * @example connected
3074
+ * @enum {string}
3075
+ */
3076
+ database: 'connected' | 'disconnected';
3077
+ /**
3078
+ * @example available
3079
+ * @enum {string}
3080
+ */
3081
+ s3: 'available' | 'unavailable';
2962
3082
  /** @example 2026-01-01T00:00:00.000Z */
2963
3083
  timestamp: string;
2964
3084
  };
@@ -3177,14 +3297,51 @@ export type components = {
3177
3297
  count: number;
3178
3298
  };
3179
3299
  };
3300
+ /** @example es */
3301
+ LocaleClause: string;
3180
3302
  /**
3181
- * @example array
3182
- * @enum {string}
3303
+ * @example {
3304
+ * "paths": [
3305
+ * "field_a",
3306
+ * "field_b"
3307
+ * ]
3308
+ * }
3183
3309
  */
3184
- PathType: 'array';
3310
+ UniquePath: {
3311
+ paths: string[];
3312
+ };
3313
+ PathTypeRelationship: {
3314
+ /** @enum {string} */
3315
+ type: 'relationship';
3316
+ collection: string | string[];
3317
+ hasMany?: boolean;
3318
+ };
3319
+ PathTypeJoin: {
3320
+ /** @enum {string} */
3321
+ type: 'join';
3322
+ collection: string | string[];
3323
+ hasMany?: boolean;
3324
+ on: string;
3325
+ };
3326
+ /** @enum {string} */
3327
+ PathTypeBlocks: 'blocks';
3328
+ /** @example array */
3329
+ PathType: 'array' | components['schemas']['PathTypeRelationship'] | components['schemas']['PathTypeJoin'] | components['schemas']['PathTypeBlocks'];
3185
3330
  /**
3186
3331
  * @example {
3187
- * "author.tagIds": "array"
3332
+ * "author.tagIds": "array",
3333
+ * "author": {
3334
+ * "type": "relationship",
3335
+ * "collection": "users",
3336
+ * "hasMany": false
3337
+ * },
3338
+ * "relatedPosts": {
3339
+ * "type": "join",
3340
+ * "collection": "posts",
3341
+ * "hasMany": true,
3342
+ * "on": "category"
3343
+ * },
3344
+ * "blocks": "blocks"
3188
3345
  * }
3189
3346
  */
3190
3347
  PathTypesMeta: {
@@ -3192,6 +3349,7 @@ export type components = {
3192
3349
  };
3193
3350
  RequestMeta: {
3194
3351
  localizedPaths?: string[];
3352
+ uniquePaths?: components['schemas']['UniquePath'][];
3195
3353
  pathTypes?: components['schemas']['PathTypesMeta'];
3196
3354
  };
3197
3355
  DataValue: string | number | boolean | unknown | components['schemas']['DataValue'][] | {
@@ -3227,6 +3385,7 @@ export type components = {
3227
3385
  contentSystemId: string;
3228
3386
  /** @example posts */
3229
3387
  collection: string;
3388
+ locale?: components['schemas']['LocaleClause'];
3230
3389
  meta?: components['schemas']['RequestMeta'];
3231
3390
  where?: components['schemas']['WhereClause'];
3232
3391
  };
@@ -3291,29 +3450,46 @@ export type components = {
3291
3450
  page?: number;
3292
3451
  sort?: components['schemas']['SortClause'];
3293
3452
  }[];
3294
- /** @example es */
3295
- LocaleClause: string;
3296
3453
  /**
3297
3454
  * @example {
3298
- * "mode": "include",
3299
- * "paths": [
3300
- * "title",
3301
- * "author.name",
3302
- * "tags"
3303
- * ]
3455
+ * "title": true,
3456
+ * "author": {
3457
+ * "name": true,
3458
+ * "email": true
3459
+ * },
3460
+ * "tags": true
3304
3461
  * }
3305
3462
  */
3306
- SelectClause: {
3307
- /** @enum {string} */
3308
- mode: 'exclude' | 'include';
3309
- paths: string[];
3463
+ IncludeSelectClause: {
3464
+ [key: string]: true | {
3465
+ [key: string]: components['schemas']['IncludeSelectClause'];
3466
+ };
3467
+ };
3468
+ /**
3469
+ * @example {
3470
+ * "title": false,
3471
+ * "author": {
3472
+ * "name": false,
3473
+ * "email": false
3474
+ * },
3475
+ * "tags": false
3476
+ * }
3477
+ */
3478
+ ExcludeSelectClause: {
3479
+ [key: string]: false | {
3480
+ [key: string]: components['schemas']['ExcludeSelectClause'];
3481
+ };
3310
3482
  };
3483
+ /** @description Field selection - use include mode (true) or exclude mode (false), but not both */
3484
+ SelectClause: components['schemas']['IncludeSelectClause'] | components['schemas']['ExcludeSelectClause'];
3311
3485
  CreateDocumentRequest: {
3312
3486
  /** @example cms-xxxxx-xxxxx */
3313
3487
  contentSystemId: string;
3314
3488
  /** @example posts */
3315
3489
  collection: string;
3316
3490
  doc: components['schemas']['DocumentData'];
3491
+ locale?: components['schemas']['LocaleClause'];
3492
+ meta?: components['schemas']['RequestMeta'];
3317
3493
  returning?: false | {
3318
3494
  join?: components['schemas']['JoinClause'];
3319
3495
  locale?: components['schemas']['LocaleClause'];
@@ -3353,6 +3529,47 @@ export type components = {
3353
3529
  sort?: components['schemas']['SortClause'];
3354
3530
  where?: components['schemas']['WhereClause'];
3355
3531
  };
3532
+ FindDistinctDocumentsResponse: {
3533
+ result: {
3534
+ /**
3535
+ * @example [
3536
+ * "value1",
3537
+ * "value2"
3538
+ * ]
3539
+ */
3540
+ data: unknown[];
3541
+ totalDocs: number;
3542
+ limit: number;
3543
+ page: number;
3544
+ totalPages: number;
3545
+ pagingCounter: number;
3546
+ hasPrevPage: boolean;
3547
+ hasNextPage: boolean;
3548
+ prevPage: number | null;
3549
+ nextPage: number | null;
3550
+ };
3551
+ };
3552
+ FindDistinctDocumentsRequest: {
3553
+ /** @example cms-xxxxx-xxxxx */
3554
+ contentSystemId: string;
3555
+ /** @example posts */
3556
+ collection: string;
3557
+ /**
3558
+ * @description Path to the field to get distinct values for
3559
+ * @example title
3560
+ */
3561
+ distinctBy: string;
3562
+ join?: components['schemas']['JoinClause'];
3563
+ /** @example 10 */
3564
+ limit?: number;
3565
+ locale?: components['schemas']['LocaleClause'];
3566
+ /** @example 1 */
3567
+ page?: number;
3568
+ meta?: components['schemas']['RequestMeta'];
3569
+ select?: components['schemas']['SelectClause'];
3570
+ sort?: components['schemas']['SortClause'];
3571
+ where?: components['schemas']['WhereClause'];
3572
+ };
3356
3573
  UpdateDocumentResponse: {
3357
3574
  result?: {
3358
3575
  /** @example 1 */
@@ -3388,6 +3605,7 @@ export type components = {
3388
3605
  doc: components['schemas']['DataWithOperations'];
3389
3606
  /** @example 10 */
3390
3607
  limit?: number;
3608
+ locale?: components['schemas']['LocaleClause'];
3391
3609
  meta?: components['schemas']['RequestMeta'];
3392
3610
  returning?: false | {
3393
3611
  join?: components['schemas']['JoinClause'];
@@ -3396,7 +3614,6 @@ export type components = {
3396
3614
  };
3397
3615
  sort?: components['schemas']['SortClause'];
3398
3616
  where: components['schemas']['WhereClause'];
3399
- locale?: components['schemas']['LocaleClause'];
3400
3617
  };
3401
3618
  DeleteDocumentResponse: {
3402
3619
  result?: {
@@ -3414,6 +3631,7 @@ export type components = {
3414
3631
  contentSystemId: string;
3415
3632
  /** @example posts */
3416
3633
  collection: string;
3634
+ locale?: components['schemas']['LocaleClause'];
3417
3635
  meta?: components['schemas']['RequestMeta'];
3418
3636
  returning?: false | {
3419
3637
  join?: components['schemas']['JoinClause'];
@@ -3421,7 +3639,6 @@ export type components = {
3421
3639
  select?: components['schemas']['SelectClause'];
3422
3640
  };
3423
3641
  where: components['schemas']['WhereClause'];
3424
- locale?: components['schemas']['LocaleClause'];
3425
3642
  };
3426
3643
  CountDocumentVersionResponse: {
3427
3644
  result: {
@@ -3434,6 +3651,7 @@ export type components = {
3434
3651
  contentSystemId: string;
3435
3652
  /** @example posts */
3436
3653
  collection: string;
3654
+ locale?: components['schemas']['LocaleClause'];
3437
3655
  meta?: components['schemas']['RequestMeta'];
3438
3656
  where?: components['schemas']['WhereClause'];
3439
3657
  };
@@ -3495,6 +3713,8 @@ export type components = {
3495
3713
  parent?: string | number;
3496
3714
  version: components['schemas']['DocumentData'] & unknown;
3497
3715
  };
3716
+ locale?: components['schemas']['LocaleClause'];
3717
+ meta?: components['schemas']['RequestMeta'];
3498
3718
  returning?: false | {
3499
3719
  join?: components['schemas']['JoinClause'];
3500
3720
  locale?: components['schemas']['LocaleClause'];
@@ -3569,9 +3789,9 @@ export type components = {
3569
3789
  where: components['schemas']['WhereClause'];
3570
3790
  /** @example 10 */
3571
3791
  limit?: number;
3792
+ locale?: components['schemas']['LocaleClause'];
3572
3793
  meta?: components['schemas']['RequestMeta'];
3573
3794
  sort?: components['schemas']['SortClause'];
3574
- locale?: components['schemas']['LocaleClause'];
3575
3795
  returning?: false | {
3576
3796
  join?: components['schemas']['JoinClause'];
3577
3797
  locale?: components['schemas']['LocaleClause'];
@@ -3594,6 +3814,7 @@ export type components = {
3594
3814
  contentSystemId: string;
3595
3815
  /** @example posts */
3596
3816
  collection: string;
3817
+ locale?: components['schemas']['LocaleClause'];
3597
3818
  meta?: components['schemas']['RequestMeta'];
3598
3819
  returning?: false | {
3599
3820
  join?: components['schemas']['JoinClause'];
@@ -8,20 +8,16 @@ import type { Config, SanitizedConfig } from 'payload';
8
8
  * - `editor`: Optional (defaults to lexicalEditor() if not provided)
9
9
  *
10
10
  * @example
11
- * // Minimal config
11
+ * // Minimal config (contentSystemId resolved from bootstrap data)
12
12
  * const config: FigmaConfig = {
13
- * figma: {
14
- * contentSystemId: process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID!
15
- * },
13
+ * figma: {},
16
14
  * collections: [...]
17
15
  * }
18
16
  *
19
17
  * @example
20
18
  * // With custom editor
21
19
  * const config: FigmaConfig = {
22
- * figma: {
23
- * contentSystemId: process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID!
24
- * },
20
+ * figma: {},
25
21
  * collections: [...],
26
22
  * editor: lexicalEditor({ features: [...] })
27
23
  * }
@@ -30,7 +26,6 @@ import type { Config, SanitizedConfig } from 'payload';
30
26
  * // Disable Content System (use custom db)
31
27
  * const config: FigmaConfig = {
32
28
  * figma: {
33
- * contentSystemId: process.env.FIGMA_CONTENT_API_CONTENT_SYSTEM_ID!,
34
29
  * useContentSystem: false
35
30
  * },
36
31
  * db: mongooseAdapter({ url: process.env.DATABASE_URI }),
@@ -41,7 +36,7 @@ export type FigmaConfig = {
41
36
  editor?: Config['editor'];
42
37
  } & {
43
38
  figma: {
44
- contentSystemId: string;
39
+ contentSystemId?: string;
45
40
  storage?: boolean;
46
41
  useContentSystem?: boolean;
47
42
  };
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type arg from 'arg';
2
2
  export interface Args extends arg.Spec {
3
3
  '--debug': BooleanConstructor;
4
+ '--dry-run': BooleanConstructor;
4
5
  '--env': StringConstructor;
5
6
  '--force': BooleanConstructor;
6
7
  '--help': BooleanConstructor;
@@ -22,4 +22,12 @@ export declare function getEnvVarSync(projectPath: string, key: string): null |
22
22
  * @param value - Environment variable value
23
23
  */
24
24
  export declare function addOrUpdateEnvVar(projectPath: string, key: string, value: string): Promise<void>;
25
+ /**
26
+ * Remove an environment variable from .env file
27
+ *
28
+ * @param projectPath - Path to project directory
29
+ * @param key - Environment variable name to remove
30
+ * @returns true if key was found and removed, false otherwise
31
+ */
32
+ export declare function removeEnvVar(projectPath: string, key: string): Promise<boolean>;
25
33
  //# sourceMappingURL=env-management.d.ts.map
@@ -115,5 +115,39 @@ import path from 'path';
115
115
  throw new Error(`Failed to update .env file: ${error instanceof Error ? error.message : 'Unknown error'}`);
116
116
  }
117
117
  }
118
+ /**
119
+ * Remove an environment variable from .env file
120
+ *
121
+ * @param projectPath - Path to project directory
122
+ * @param key - Environment variable name to remove
123
+ * @returns true if key was found and removed, false otherwise
124
+ */ export async function removeEnvVar(projectPath, key) {
125
+ const envPath = path.join(projectPath, '.env');
126
+ try {
127
+ const contents = await fs.readFile(envPath, 'utf-8');
128
+ const lines = contents.split(/\r?\n/);
129
+ const filteredLines = [];
130
+ let found = false;
131
+ for (const line of lines){
132
+ if (line.trim() && !line.trim().startsWith('#') && line.includes('=')) {
133
+ const equalsIndex = line.indexOf('=');
134
+ const lineKey = line.substring(0, equalsIndex).trim();
135
+ if (lineKey === key) {
136
+ found = true;
137
+ continue;
138
+ }
139
+ }
140
+ filteredLines.push(line);
141
+ }
142
+ if (found) {
143
+ // Collapse consecutive blank lines and trim leading/trailing blank lines
144
+ const collapsed = filteredLines.join('\n').replace(/\n{3,}/g, '\n\n').replace(/^\n+/, '');
145
+ await fs.writeFile(envPath, collapsed, 'utf-8');
146
+ }
147
+ return found;
148
+ } catch {
149
+ return false;
150
+ }
151
+ }
118
152
 
119
153
  //# sourceMappingURL=env-management.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Walk up from a directory to find the nearest package.json with name "@payloadcms/figma".
3
+ */
4
+ export declare function findPackageRoot(startDir: string): Promise<null | string>;
5
+ /**
6
+ * Build and pack the local @payloadcms/figma package, returning the tgz path.
7
+ */
8
+ export declare function packLocalPackage(pkgDir: string): string;
9
+ //# sourceMappingURL=handle-upgrade.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { execSync } from 'child_process';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ /**
5
+ * Walk up from a directory to find the nearest package.json with name "@payloadcms/figma".
6
+ */ export async function findPackageRoot(startDir) {
7
+ let dir = startDir;
8
+ while(true){
9
+ const pkgPath = path.join(dir, 'package.json');
10
+ try {
11
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'));
12
+ if (pkg.name === '@payloadcms/figma') {
13
+ return dir;
14
+ }
15
+ } catch {
16
+ // No package.json here
17
+ }
18
+ const parent = path.dirname(dir);
19
+ if (parent === dir) {
20
+ return null;
21
+ }
22
+ dir = parent;
23
+ }
24
+ }
25
+ /**
26
+ * Build and pack the local @payloadcms/figma package, returning the tgz path.
27
+ */ export function packLocalPackage(pkgDir) {
28
+ execSync('pnpm build', {
29
+ cwd: pkgDir,
30
+ stdio: 'pipe'
31
+ });
32
+ const tgzOutput = execSync('pnpm pack', {
33
+ cwd: pkgDir,
34
+ stdio: 'pipe'
35
+ }).toString().trim();
36
+ const tgzName = tgzOutput.split('\n').pop();
37
+ const tgzPath = path.join(pkgDir, tgzName);
38
+ return tgzPath;
39
+ }
40
+
41
+ //# sourceMappingURL=handle-upgrade.js.map
@@ -17,6 +17,7 @@ export function helpMessage() {
17
17
  ${pc.cyan('debug')} Show debug info for troubleshooting
18
18
  ${pc.cyan('env')} Switch active environment
19
19
  ${pc.cyan('deploy')} Deploy your project to Figma
20
+ ${pc.cyan('upgrade')} Run upgrade migrations
20
21
  ${pc.cyan('build-lambda-zip')} Build Lambda deployment zip
21
22
 
22
23
  ${pc.bold('OPTIONS')}
@@ -50,6 +51,11 @@ export function helpMessage() {
50
51
  ${pc.dim('--yes, -y')} Skip confirmation prompts
51
52
  ${pc.dim('--skip-build')} Skip building and use existing build
52
53
 
54
+ ${pc.bold('UPGRADE COMMAND')}
55
+
56
+ ${pc.cyan('@payloadcms/figma upgrade')} Run all upgrade migrations
57
+ ${pc.dim('--dry-run')} Preview changes without applying
58
+
53
59
  ${pc.bold('GLOBAL OPTIONS')}
54
60
 
55
61
  ${pc.dim('--infra-env <env>')} Target infrastructure (production or staging)
@@ -62,4 +62,11 @@ export declare function addFigmaProperty(sourceFile: SourceFile, config: FigmaPr
62
62
  * Returns the figma object if it exists, null otherwise
63
63
  */
64
64
  export declare function readFigmaConfig(sourceFile: SourceFile): FigmaPropertyConfig | null;
65
+ /**
66
+ * Remove the contentSystemId property from figma config object.
67
+ * Used during upgrade migration — contentSystemId is now resolved from bootstrap data.
68
+ *
69
+ * @returns true if the property was found and removed
70
+ */
71
+ export declare function removeFigmaContentSystemId(sourceFile: SourceFile): boolean;
65
72
  //# sourceMappingURL=payload-config-ast.d.ts.map
@@ -472,5 +472,39 @@ import * as log from './log.js';
472
472
  useContentSystem
473
473
  };
474
474
  }
475
+ /**
476
+ * Remove the contentSystemId property from figma config object.
477
+ * Used during upgrade migration — contentSystemId is now resolved from bootstrap data.
478
+ *
479
+ * @returns true if the property was found and removed
480
+ */ export function removeFigmaContentSystemId(sourceFile) {
481
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
482
+ const buildConfigCall = callExpressions.find((ce)=>{
483
+ const expr = ce.getExpression();
484
+ const text = expr.getText();
485
+ return text === 'buildFigmaConfig' || text.endsWith('.buildFigmaConfig');
486
+ });
487
+ if (!buildConfigCall) {
488
+ return false;
489
+ }
490
+ const configArg = buildConfigCall.getArguments()[0];
491
+ if (!configArg || !Node.isObjectLiteralExpression(configArg)) {
492
+ return false;
493
+ }
494
+ const figmaProperty = configArg.getProperty('figma');
495
+ if (!figmaProperty || !Node.isPropertyAssignment(figmaProperty)) {
496
+ return false;
497
+ }
498
+ const initializer = figmaProperty.getInitializer();
499
+ if (!initializer || !Node.isObjectLiteralExpression(initializer)) {
500
+ return false;
501
+ }
502
+ const contentSystemIdProp = initializer.getProperty('contentSystemId');
503
+ if (!contentSystemIdProp) {
504
+ return false;
505
+ }
506
+ contentSystemIdProp.remove();
507
+ return true;
508
+ }
475
509
 
476
510
  //# sourceMappingURL=payload-config-ast.js.map
@@ -42,7 +42,7 @@ import * as log from './log.js';
42
42
  */ export async function installPackage(projectPath, packageName, packageManager, version = 'latest') {
43
43
  return new Promise((resolve, reject)=>{
44
44
  const command = packageManager;
45
- const packageSpec = `${packageName}@${version}`;
45
+ const packageSpec = packageName.endsWith('.tgz') ? packageName : `${packageName}@${version}`;
46
46
  const args = [
47
47
  'add',
48
48
  packageSpec
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/figma",
3
- "version": "0.0.1-alpha.51",
3
+ "version": "0.0.1-alpha.53",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "type": "module",
6
6
  "exports": {