@payloadcms/figma 0.1.0-alpha.2 → 0.1.0-alpha.4

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.
Files changed (219) hide show
  1. package/dist/api/check-project-subdomain-availability.d.ts +12 -0
  2. package/dist/api/check-project-subdomain-availability.js +32 -0
  3. package/dist/api/control-plane-error.d.ts +6 -0
  4. package/dist/api/control-plane-error.js +8 -0
  5. package/dist/api/control-plane-fetch.d.ts +7 -0
  6. package/dist/api/control-plane-fetch.js +37 -0
  7. package/dist/api/control-plane.d.ts +1 -9
  8. package/dist/api/control-plane.js +2 -48
  9. package/dist/api/create-project.d.ts +18 -0
  10. package/dist/api/create-project.js +37 -0
  11. package/dist/api/list-project-workspaces.d.ts +8 -0
  12. package/dist/api/list-project-workspaces.js +28 -0
  13. package/dist/api/project-resource-type.d.ts +1 -0
  14. package/dist/api/project-resource-type.js +1 -0
  15. package/dist/auth/headless-oauth-flow.d.ts +23 -0
  16. package/dist/auth/headless-oauth-flow.js +69 -0
  17. package/dist/auth/oauth-flow.d.ts +2 -0
  18. package/dist/auth/oauth-flow.js +13 -8
  19. package/dist/auth/token-store.d.ts +4 -1
  20. package/dist/auth/token-store.js +14 -0
  21. package/dist/auth/types.d.ts +12 -0
  22. package/dist/cli.d.ts +39 -0
  23. package/dist/cli.js +71 -36
  24. package/dist/commands/bootstrap.js +2 -1
  25. package/dist/commands/deploy.d.ts +12 -0
  26. package/dist/commands/deploy.js +61 -23
  27. package/dist/commands/detect-project-resource-type.d.ts +3 -0
  28. package/dist/commands/detect-project-resource-type.js +28 -0
  29. package/dist/commands/init.d.ts +2 -2
  30. package/dist/commands/init.js +163 -78
  31. package/dist/commands/login.d.ts +11 -1
  32. package/dist/commands/login.js +66 -4
  33. package/dist/commands/prompt-to-create-project/format-project-subdomain.d.ts +1 -0
  34. package/dist/commands/prompt-to-create-project/format-project-subdomain.js +3 -0
  35. package/dist/commands/prompt-to-create-project/format-unavailable-subdomain-reason.d.ts +1 -0
  36. package/dist/commands/prompt-to-create-project/format-unavailable-subdomain-reason.js +7 -0
  37. package/dist/commands/prompt-to-create-project/get-default-project-name.d.ts +1 -0
  38. package/dist/commands/prompt-to-create-project/get-default-project-name.js +13 -0
  39. package/dist/commands/prompt-to-create-project/prompt-for-available-subdomain.d.ts +7 -0
  40. package/dist/commands/prompt-to-create-project/prompt-for-available-subdomain.js +32 -0
  41. package/dist/commands/prompt-to-create-project/resolve-project-region.d.ts +8 -0
  42. package/dist/commands/prompt-to-create-project/resolve-project-region.js +27 -0
  43. package/dist/commands/prompt-to-create-project/resolve-project-subdomain.d.ts +9 -0
  44. package/dist/commands/prompt-to-create-project/resolve-project-subdomain.js +54 -0
  45. package/dist/commands/prompt-to-create-project/resolve-text-input.d.ts +9 -0
  46. package/dist/commands/prompt-to-create-project/resolve-text-input.js +23 -0
  47. package/dist/commands/prompt-to-create-project/resolve-workspace-plan-key.d.ts +12 -0
  48. package/dist/commands/prompt-to-create-project/resolve-workspace-plan-key.js +36 -0
  49. package/dist/commands/prompt-to-create-project/validate-subdomain.d.ts +1 -0
  50. package/dist/commands/prompt-to-create-project/validate-subdomain.js +9 -0
  51. package/dist/commands/prompt-to-create-project.d.ts +24 -0
  52. package/dist/commands/prompt-to-create-project.js +69 -0
  53. package/dist/commands/setup-agent.d.ts +12 -0
  54. package/dist/commands/setup-agent.js +123 -0
  55. package/dist/commands/write-project-configuration.d.ts +12 -0
  56. package/dist/commands/write-project-configuration.js +16 -0
  57. package/dist/db-content-api/index.d.ts +1 -1
  58. package/dist/db-content-api/index.js +90 -55
  59. package/dist/db-content-api/utilities/data/injectGlobalType.d.ts +6 -0
  60. package/dist/db-content-api/utilities/data/injectGlobalType.js +11 -0
  61. package/dist/db-content-api/utilities/data/stringifyNumericIds.d.ts +1 -0
  62. package/dist/db-content-api/utilities/data/stringifyNumericIds.js +19 -0
  63. package/dist/db-content-api/utilities/isRecord.d.ts +9 -0
  64. package/dist/db-content-api/utilities/isRecord.js +19 -0
  65. package/dist/db-content-api/utilities/schema/getDocumentSchema.d.ts +3 -0
  66. package/dist/db-content-api/utilities/schema/getDocumentSchema.js +24 -0
  67. package/dist/db-content-api/utilities/where.js +6 -4
  68. package/dist/exports/client.d.ts +2 -0
  69. package/dist/exports/client.js +2 -0
  70. package/dist/lib/write-agent-config.d.ts +12 -0
  71. package/dist/lib/write-agent-config.js +52 -0
  72. package/dist/plugin/build-config.js +32 -3
  73. package/dist/plugin/cloud-limits/CloudLimitModal/index.d.ts +9 -0
  74. package/dist/plugin/cloud-limits/CloudLimitModal/index.js +78 -0
  75. package/dist/plugin/cloud-limits/CloudLimitModal/index.scss +51 -0
  76. package/dist/plugin/cloud-limits/CloudLimitProvider/index.d.ts +7 -0
  77. package/dist/plugin/cloud-limits/CloudLimitProvider/index.js +169 -0
  78. package/dist/plugin/cloud-limits/SlugRegistryContext/index.d.ts +2 -0
  79. package/dist/plugin/cloud-limits/SlugRegistryContext/index.js +3 -0
  80. package/dist/plugin/cloud-limits/SlugTracker/index.d.ts +9 -0
  81. package/dist/plugin/cloud-limits/SlugTracker/index.js +23 -0
  82. package/dist/plugin/cloud-limits/icons/ExternalLinkIcon.d.ts +2 -0
  83. package/dist/plugin/cloud-limits/icons/ExternalLinkIcon.js +30 -0
  84. package/dist/plugin/cloud-limits/shared.d.ts +10 -0
  85. package/dist/plugin/cloud-limits/shared.js +27 -0
  86. package/dist/skills/deploy-to-figma/SKILL.md +234 -0
  87. package/dist/skills/deploy-to-figma/references/cli-reference.md +146 -0
  88. package/dist/translations/ar.d.ts +12 -0
  89. package/dist/translations/ar.js +12 -0
  90. package/dist/translations/az.d.ts +12 -0
  91. package/dist/translations/az.js +12 -0
  92. package/dist/translations/bg.d.ts +12 -0
  93. package/dist/translations/bg.js +12 -0
  94. package/dist/translations/bnBd.d.ts +12 -0
  95. package/dist/translations/bnBd.js +12 -0
  96. package/dist/translations/bnIn.d.ts +12 -0
  97. package/dist/translations/bnIn.js +12 -0
  98. package/dist/translations/ca.d.ts +12 -0
  99. package/dist/translations/ca.js +12 -0
  100. package/dist/translations/cs.d.ts +12 -0
  101. package/dist/translations/cs.js +12 -0
  102. package/dist/translations/da.d.ts +12 -0
  103. package/dist/translations/da.js +12 -0
  104. package/dist/translations/de.d.ts +12 -0
  105. package/dist/translations/de.js +12 -0
  106. package/dist/translations/en.d.ts +12 -0
  107. package/dist/translations/en.js +12 -0
  108. package/dist/translations/es.d.ts +12 -0
  109. package/dist/translations/es.js +12 -0
  110. package/dist/translations/et.d.ts +12 -0
  111. package/dist/translations/et.js +12 -0
  112. package/dist/translations/fa.d.ts +12 -0
  113. package/dist/translations/fa.js +12 -0
  114. package/dist/translations/fr.d.ts +12 -0
  115. package/dist/translations/fr.js +12 -0
  116. package/dist/translations/he.d.ts +12 -0
  117. package/dist/translations/he.js +12 -0
  118. package/dist/translations/hr.d.ts +12 -0
  119. package/dist/translations/hr.js +12 -0
  120. package/dist/translations/hu.d.ts +12 -0
  121. package/dist/translations/hu.js +12 -0
  122. package/dist/translations/hy.d.ts +12 -0
  123. package/dist/translations/hy.js +12 -0
  124. package/dist/translations/id.d.ts +12 -0
  125. package/dist/translations/id.js +12 -0
  126. package/dist/translations/index.d.ts +533 -0
  127. package/dist/translations/index.js +90 -0
  128. package/dist/translations/is.d.ts +12 -0
  129. package/dist/translations/is.js +12 -0
  130. package/dist/translations/it.d.ts +12 -0
  131. package/dist/translations/it.js +12 -0
  132. package/dist/translations/ja.d.ts +12 -0
  133. package/dist/translations/ja.js +12 -0
  134. package/dist/translations/ko.d.ts +12 -0
  135. package/dist/translations/ko.js +12 -0
  136. package/dist/translations/lt.d.ts +12 -0
  137. package/dist/translations/lt.js +12 -0
  138. package/dist/translations/lv.d.ts +12 -0
  139. package/dist/translations/lv.js +12 -0
  140. package/dist/translations/my.d.ts +12 -0
  141. package/dist/translations/my.js +12 -0
  142. package/dist/translations/nb.d.ts +12 -0
  143. package/dist/translations/nb.js +12 -0
  144. package/dist/translations/nl.d.ts +12 -0
  145. package/dist/translations/nl.js +12 -0
  146. package/dist/translations/pl.d.ts +12 -0
  147. package/dist/translations/pl.js +12 -0
  148. package/dist/translations/pt.d.ts +12 -0
  149. package/dist/translations/pt.js +12 -0
  150. package/dist/translations/ro.d.ts +12 -0
  151. package/dist/translations/ro.js +12 -0
  152. package/dist/translations/rs.d.ts +12 -0
  153. package/dist/translations/rs.js +12 -0
  154. package/dist/translations/rsLatin.d.ts +12 -0
  155. package/dist/translations/rsLatin.js +12 -0
  156. package/dist/translations/ru.d.ts +12 -0
  157. package/dist/translations/ru.js +12 -0
  158. package/dist/translations/sk.d.ts +12 -0
  159. package/dist/translations/sk.js +12 -0
  160. package/dist/translations/sl.d.ts +12 -0
  161. package/dist/translations/sl.js +12 -0
  162. package/dist/translations/sv.d.ts +12 -0
  163. package/dist/translations/sv.js +12 -0
  164. package/dist/translations/ta.d.ts +12 -0
  165. package/dist/translations/ta.js +12 -0
  166. package/dist/translations/th.d.ts +12 -0
  167. package/dist/translations/th.js +12 -0
  168. package/dist/translations/tr.d.ts +12 -0
  169. package/dist/translations/tr.js +12 -0
  170. package/dist/translations/uk.d.ts +12 -0
  171. package/dist/translations/uk.js +12 -0
  172. package/dist/translations/vi.d.ts +12 -0
  173. package/dist/translations/vi.js +12 -0
  174. package/dist/translations/zh.d.ts +12 -0
  175. package/dist/translations/zh.js +12 -0
  176. package/dist/translations/zhTw.d.ts +12 -0
  177. package/dist/translations/zhTw.js +12 -0
  178. package/dist/types.d.ts +11 -0
  179. package/dist/utils/adapters/nextjs.d.ts +3 -3
  180. package/dist/utils/adapters/nextjs.js +9 -7
  181. package/dist/utils/adapters/nitro.d.ts +3 -3
  182. package/dist/utils/adapters/nitro.js +73 -77
  183. package/dist/utils/adapters/vite.d.ts +1 -1
  184. package/dist/utils/adapters/vite.js +8 -7
  185. package/dist/utils/assert-never.d.ts +1 -0
  186. package/dist/utils/assert-never.js +3 -0
  187. package/dist/utils/asset-collection.d.ts +2 -2
  188. package/dist/utils/asset-collection.js +10 -15
  189. package/dist/utils/build-detection.d.ts +6 -1
  190. package/dist/utils/build-detection.js +68 -89
  191. package/dist/utils/build-lambda-zip.d.ts +5 -6
  192. package/dist/utils/build-lambda-zip.js +31 -48
  193. package/dist/utils/deploy-adapter.d.ts +4 -4
  194. package/dist/utils/deploy-adapter.js +12 -8
  195. package/dist/utils/deploy-output/inspectNextjsOutput/getNextRelativeAppDir.d.ts +1 -0
  196. package/dist/utils/deploy-output/inspectNextjsOutput/getNextRelativeAppDir.js +15 -0
  197. package/dist/utils/deploy-output/inspectNextjsOutput.d.ts +12 -0
  198. package/dist/utils/deploy-output/inspectNextjsOutput.js +28 -0
  199. package/dist/utils/deploy-output/inspectNitroOutput.d.ts +9 -0
  200. package/dist/utils/deploy-output/inspectNitroOutput.js +13 -0
  201. package/dist/utils/deploy-output/isFile.d.ts +1 -0
  202. package/dist/utils/deploy-output/isFile.js +4 -0
  203. package/dist/utils/deploy-output/readJson.d.ts +1 -0
  204. package/dist/utils/deploy-output/readJson.js +9 -0
  205. package/dist/utils/deploy-output/resolveNitroOutputLayout.d.ts +8 -0
  206. package/dist/utils/deploy-output/resolveNitroOutputLayout.js +16 -0
  207. package/dist/utils/deploy-output/resolveOutputPath.d.ts +2 -0
  208. package/dist/utils/deploy-output/resolveOutputPath.js +9 -0
  209. package/dist/utils/deploy-output/resolveWithin.d.ts +1 -0
  210. package/dist/utils/deploy-output/resolveWithin.js +12 -0
  211. package/dist/utils/lambda-config.js +4 -14
  212. package/dist/utils/messages.js +24 -3
  213. package/dist/utils/payload-generate.d.ts +10 -0
  214. package/dist/utils/payload-generate.js +59 -0
  215. package/dist/utils/pnpm-builds.d.ts +14 -0
  216. package/dist/utils/pnpm-builds.js +125 -0
  217. package/package.json +6 -5
  218. package/dist/lib/download-skill.d.ts +0 -12
  219. package/dist/lib/download-skill.js +0 -77
@@ -1,5 +1,5 @@
1
1
  import createClient from 'openapi-fetch';
2
- import { APIError, createDatabaseAdapter } from 'payload';
2
+ import { APIError, createDatabaseAdapter, ValidationError } from 'payload';
3
3
  import { v4 as uuid } from 'uuid';
4
4
  import { FigmaApiError } from '../api/figma-api.js';
5
5
  import { CONTENT_SYSTEM_NOT_FOUND_MESSAGE } from '../constants.js';
@@ -9,11 +9,12 @@ import { unwrapDocument, unwrapFindResponse } from './temp-utilities/unwrapDocum
9
9
  import { createAuthMiddleware, createErrorMiddleware } from './utilities/auth.js';
10
10
  import { getClientVersionHeaders } from './utilities/clientVersionHeaders.js';
11
11
  import { dataToContentAPI, resolveVersionContent } from './utilities/data/index.js';
12
+ import { injectGlobalType } from './utilities/data/injectGlobalType.js';
12
13
  import { validateRelationshipIds } from './utilities/data/validateRelationships.js';
13
14
  import { convertPayloadJoinsToContentAPI } from './utilities/joins.js';
14
15
  import { addFallbackLocale } from './utilities/locale/index.js';
15
16
  import { normalizeLocaleInWhere } from './utilities/meta/normalizeLocaleInWhere.js';
16
- import { buildDocumentSchema } from './utilities/schema/buildDocumentSchema.js';
17
+ import { getDocumentSchema } from './utilities/schema/getDocumentSchema.js';
17
18
  import { convertPayloadWhereToContentAPI } from './utilities/where.js';
18
19
  /**
19
20
  * Heads-up logged locally whenever a job is queued. Queued jobs are executed by Figma's deployed runner, so
@@ -109,7 +110,7 @@ async function createCollection(collectionId) {
109
110
  }
110
111
  });
111
112
  if (error) {
112
- throw createContentAPIError('createCollection', error, httpResponse.status);
113
+ throw createDocumentContentAPIError(this, 'createCollection', error, httpResponse.status, collectionId);
113
114
  }
114
115
  this.payload.logger.info(`Created collection: ${collectionId}`);
115
116
  } catch (error) {
@@ -131,13 +132,13 @@ async function findMany({ collection, joins, limit, locale: localeArg, page, pag
131
132
  limit,
132
133
  locale,
133
134
  page: page ?? 1,
134
- schema: this.documentSchema,
135
+ schema: this.documentSchema(),
135
136
  sort: addFallbackSort(sort, this.payload, collection),
136
137
  where: convertPayloadWhereToContentAPI(normalizedWhere ?? {})
137
138
  }
138
139
  });
139
140
  if (error) {
140
- throw createContentAPIError('findMany', error, httpResponse.status);
141
+ throw createDocumentContentAPIError(this, 'findMany', error, httpResponse.status, collection);
141
142
  }
142
143
  if (!response) {
143
144
  throw new Error('No response from findMany');
@@ -176,13 +177,13 @@ async function findVersions(args) {
176
177
  limit: args.limit,
177
178
  locale,
178
179
  page: args.page ?? 1,
179
- schema: this.documentSchema,
180
+ schema: this.documentSchema(),
180
181
  sort: sortWithFallback,
181
182
  where: whereClause
182
183
  }
183
184
  });
184
185
  if (error) {
185
- throw createContentAPIError('findVersions', error, httpResponse.status);
186
+ throw createDocumentContentAPIError(this, 'findVersions', error, httpResponse.status, args.collection);
186
187
  }
187
188
  if (!response) {
188
189
  throw new Error('No response from findVersions');
@@ -230,7 +231,7 @@ async function createVersion(args) {
230
231
  body: {
231
232
  collection: args.collectionSlug,
232
233
  contentSystemId: this.contentSystemId,
233
- schema: this.documentSchema,
234
+ schema: this.documentSchema(),
234
235
  versionDoc: {
235
236
  createdAt: args.createdAt,
236
237
  latest: true,
@@ -241,7 +242,7 @@ async function createVersion(args) {
241
242
  }
242
243
  });
243
244
  if (error) {
244
- throw createContentAPIError('createVersion', error, httpResponse.status);
245
+ throw createDocumentContentAPIError(this, 'createVersion', error, httpResponse.status, args.collectionSlug);
245
246
  }
246
247
  if (!response) {
247
248
  throw new Error('No response from createVersion');
@@ -272,7 +273,7 @@ async function updateVersion(args) {
272
273
  contentSystemId: this.contentSystemId,
273
274
  createOnMissing: false,
274
275
  locale,
275
- schema: this.documentSchema,
276
+ schema: this.documentSchema(),
276
277
  versionDoc: {
277
278
  createdAt: versionMeta.createdAt,
278
279
  latest: versionMeta.latest,
@@ -284,7 +285,7 @@ async function updateVersion(args) {
284
285
  }
285
286
  });
286
287
  if (error) {
287
- throw createContentAPIError('updateVersion', error, httpResponse.status);
288
+ throw createDocumentContentAPIError(this, 'updateVersion', error, httpResponse.status, args.collection);
288
289
  }
289
290
  if (!response?.result || !('data' in response.result)) {
290
291
  throw new Error('No document data in updateVersion response');
@@ -301,21 +302,25 @@ async function updateVersion(args) {
301
302
  });
302
303
  }
303
304
  async function deleteVersions(args) {
304
- if (!args.collection) {
305
- throw new Error('deleteVersions requires a collection');
305
+ // Globals are addressed by `globalSlug` and map to the `_global-<slug>` collection.
306
+ // Only one of `collection`/`globalSlug` is set.
307
+ const globalSlug = args.globalSlug != null ? getGlobalSlug(args.globalSlug) : undefined;
308
+ const collection = args.collection ?? globalSlug;
309
+ if (collection == null) {
310
+ throw new Error('deleteVersions requires a collection or globalSlug');
306
311
  }
307
312
  const locale = addFallbackLocale(args.locale, this.payload);
308
313
  const { error, response: httpResponse } = await this.client.POST('/api/v1/document_versions:delete', {
309
314
  body: {
310
- collection: args.collection,
315
+ collection,
311
316
  contentSystemId: this.contentSystemId,
312
317
  locale,
313
- schema: this.documentSchema,
318
+ schema: this.documentSchema(),
314
319
  where: convertPayloadWhereToContentAPI(args.where)
315
320
  }
316
321
  });
317
322
  if (error) {
318
- throw createContentAPIError('deleteVersions', error, httpResponse.status);
323
+ throw createDocumentContentAPIError(this, 'deleteVersions', error, httpResponse.status, collection);
319
324
  }
320
325
  }
321
326
  async function findOne(args) {
@@ -339,13 +344,13 @@ async function findDistinct(args) {
339
344
  limit: args.limit,
340
345
  locale,
341
346
  page: args.page ?? 1,
342
- schema: this.documentSchema,
347
+ schema: this.documentSchema(),
343
348
  sort: addFallbackSort(args.sort, this.payload, args.collection),
344
349
  where: convertPayloadWhereToContentAPI(args.where ?? {})
345
350
  }
346
351
  });
347
352
  if (error) {
348
- throw createContentAPIError('findDistinct', error, httpResponse.status);
353
+ throw createDocumentContentAPIError(this, 'findDistinct', error, httpResponse.status, args.collection);
349
354
  }
350
355
  const { data, ...pagination } = response.result;
351
356
  return {
@@ -372,13 +377,13 @@ async function updateMany(args) {
372
377
  environmentName: this.environmentName,
373
378
  limit: args.limit,
374
379
  locale,
375
- schema: this.documentSchema,
380
+ schema: this.documentSchema(),
376
381
  sort: addFallbackSort(args.sort || '-updatedAt', this.payload, args.collection),
377
382
  where: convertPayloadWhereToContentAPI(args.where)
378
383
  }
379
384
  });
380
385
  if (error) {
381
- throw createContentAPIError('updateMany', error, httpResponse.status);
386
+ throw createDocumentContentAPIError(this, 'updateMany', error, httpResponse.status, args.collection);
382
387
  }
383
388
  if (!contentAPIResponse) {
384
389
  throw new Error('No response from updateMany');
@@ -413,12 +418,12 @@ async function updateOne(args) {
413
418
  doc: dataToContentAPI(this.payload, args.collection, args.data),
414
419
  environmentName: this.environmentName,
415
420
  locale,
416
- schema: this.documentSchema,
421
+ schema: this.documentSchema(),
417
422
  where: convertPayloadWhereToContentAPI(where)
418
423
  }
419
424
  });
420
425
  if (error) {
421
- throw createContentAPIError('updateOne', error, httpResponse.status);
426
+ throw createDocumentContentAPIError(this, 'updateOne', error, httpResponse.status, args.collection);
422
427
  }
423
428
  if (!contentAPIResponse) {
424
429
  throw new Error('No response from updateOne');
@@ -445,12 +450,12 @@ async function deleteMany(args) {
445
450
  contentSystemId: this.contentSystemId,
446
451
  environmentName: this.environmentName,
447
452
  locale,
448
- schema: this.documentSchema,
453
+ schema: this.documentSchema(),
449
454
  where: convertPayloadWhereToContentAPI(args.where)
450
455
  }
451
456
  });
452
457
  if (error) {
453
- throw createContentAPIError('deleteMany', error, httpResponse.status);
458
+ throw createDocumentContentAPIError(this, 'deleteMany', error, httpResponse.status, args.collection);
454
459
  }
455
460
  }
456
461
  async function deleteOne(args) {
@@ -461,12 +466,12 @@ async function deleteOne(args) {
461
466
  contentSystemId: this.contentSystemId,
462
467
  environmentName: this.environmentName,
463
468
  locale,
464
- schema: this.documentSchema,
469
+ schema: this.documentSchema(),
465
470
  where: convertPayloadWhereToContentAPI(args.where)
466
471
  }
467
472
  });
468
473
  if (error) {
469
- throw createContentAPIError('deleteOne', error, httpResponse.status);
474
+ throw createDocumentContentAPIError(this, 'deleteOne', error, httpResponse.status, args.collection);
470
475
  }
471
476
  if (!response?.result || !('data' in response.result)) {
472
477
  throw new Error('No document data in deleteOne response');
@@ -516,11 +521,11 @@ async function create(args) {
516
521
  },
517
522
  environmentName: this.environmentName,
518
523
  locale,
519
- schema: this.documentSchema
524
+ schema: this.documentSchema()
520
525
  }
521
526
  });
522
527
  if (error) {
523
- throw createContentAPIError('create', error, httpResponse.status);
528
+ throw createDocumentContentAPIError(this, 'create', error, httpResponse.status, args.collection);
524
529
  }
525
530
  if (!contentAPIResponse) {
526
531
  throw new Error('No response from create');
@@ -568,12 +573,12 @@ async function count(args) {
568
573
  collection: args.collection,
569
574
  contentSystemId: this.contentSystemId,
570
575
  locale,
571
- schema: this.documentSchema,
576
+ schema: this.documentSchema(),
572
577
  where: convertPayloadWhereToContentAPI(args.where)
573
578
  }
574
579
  });
575
580
  if (error) {
576
- throw createContentAPIError('count', error, httpResponse.status);
581
+ throw createDocumentContentAPIError(this, 'count', error, httpResponse.status, args.collection);
577
582
  }
578
583
  if (!response) {
579
584
  throw new Error('No response from count');
@@ -589,12 +594,12 @@ async function countVersions(args) {
589
594
  collection: args.collection,
590
595
  contentSystemId: this.contentSystemId,
591
596
  locale,
592
- schema: this.documentSchema,
597
+ schema: this.documentSchema(),
593
598
  where: convertPayloadWhereToContentAPI(args.where)
594
599
  }
595
600
  });
596
601
  if (error) {
597
- throw createContentAPIError('countVersions', error, httpResponse.status);
602
+ throw createDocumentContentAPIError(this, 'countVersions', error, httpResponse.status, args.collection);
598
603
  }
599
604
  if (!response) {
600
605
  throw new Error('No response from countVersions');
@@ -620,25 +625,31 @@ async function upsert(args) {
620
625
  data: args.data,
621
626
  payload: this.payload
622
627
  });
628
+ // `createdAt` is create-time metadata, so seed it on `createOnMissing` (used only when no
629
+ // document matches) rather than on `doc`. The server preserves an existing document's
630
+ // `createdAt` on update precisely because `doc` omits it; `updatedAt` is still refreshed on
631
+ // every write via `dataToContentAPI`. Sending `createdAt` on `doc` would overwrite it on
632
+ // every update (e.g. repeated `updateGlobal` calls).
633
+ const createdAt = new Date().toISOString();
623
634
  const { data: contentAPIResponse, error, response: httpResponse } = await this.client.POST('/api/v1/documents:update', {
624
635
  body: {
625
636
  collection: args.collection,
626
637
  contentSystemId: this.contentSystemId,
627
638
  createOnMissing: {
628
- id: documentId
639
+ id: documentId,
640
+ createdAt
629
641
  },
630
642
  doc: dataToContentAPI(this.payload, args.collection, args.data, {
631
- applyDefaults: true,
632
- createdAt: true
643
+ applyDefaults: true
633
644
  }),
634
645
  environmentName: this.environmentName,
635
646
  locale,
636
- schema: this.documentSchema,
647
+ schema: this.documentSchema(),
637
648
  where: convertPayloadWhereToContentAPI(args.where)
638
649
  }
639
650
  });
640
651
  if (error) {
641
- throw createContentAPIError('upsert', error, httpResponse.status);
652
+ throw createDocumentContentAPIError(this, 'upsert', error, httpResponse.status, args.collection);
642
653
  }
643
654
  if (!contentAPIResponse) {
644
655
  throw new Error('No response from upsert');
@@ -658,6 +669,7 @@ async function upsert(args) {
658
669
  });
659
670
  }
660
671
  async function updateJobs(args) {
672
+ const collection = 'payload-jobs';
661
673
  const { id, limit, returning, sort, where } = args;
662
674
  if (id == null && where == null) {
663
675
  throw new Error('updateJobs requires either id or a where clause');
@@ -678,12 +690,12 @@ async function updateJobs(args) {
678
690
  equals: String(id)
679
691
  }
680
692
  } : where;
681
- const docData = dataToContentAPI(this.payload, 'payload-jobs', data);
682
- const sortClause = addFallbackSort(sort || '-updatedAt', this.payload, 'payload-jobs');
693
+ const docData = dataToContentAPI(this.payload, collection, data);
694
+ const sortClause = addFallbackSort(sort || '-updatedAt', this.payload, collection);
683
695
  const whereQuery = convertPayloadWhereToContentAPI(whereClause);
684
696
  const { data: response, error, response: httpResponse } = await this.client.POST('/api/v1/documents:update', {
685
697
  body: {
686
- collection: 'payload-jobs',
698
+ collection,
687
699
  contentSystemId: this.contentSystemId,
688
700
  createOnMissing: false,
689
701
  doc: docData,
@@ -691,13 +703,13 @@ async function updateJobs(args) {
691
703
  ...limit != null && {
692
704
  limit
693
705
  },
694
- schema: this.documentSchema,
706
+ schema: this.documentSchema(),
695
707
  sort: sortClause,
696
708
  where: whereQuery
697
709
  }
698
710
  });
699
711
  if (error) {
700
- throw createContentAPIError('updateJobs', error, httpResponse.status);
712
+ throw createDocumentContentAPIError(this, 'updateJobs', error, httpResponse.status, collection);
701
713
  }
702
714
  if (!response) {
703
715
  throw new Error('No response from updateJobs');
@@ -709,18 +721,18 @@ async function updateJobs(args) {
709
721
  return [];
710
722
  }
711
723
  return response.result.data.map((doc)=>unwrapDocument({
712
- collectionSlug: 'payload-jobs',
724
+ collectionSlug: collection,
713
725
  doc,
714
726
  payload: this.payload
715
727
  }));
716
728
  }
717
- function createGlobal(args) {
729
+ async function createGlobal(args) {
718
730
  // Globals are singletons identified by their slug. Use upsert so concurrent calls
719
731
  // (e.g. handleSchedules processing multiple queueables with the same null jobStats)
720
732
  // don't fail with "A record with this field already exists". The deep-merge semantics
721
733
  // of the update path are safe here: callers that want a fresh create (first run) get
722
734
  // an effective create, and callers that race a prior create get a merge instead.
723
- return this.upsert({
735
+ const doc = await this.upsert({
724
736
  collection: getGlobalSlug(args.slug),
725
737
  data: {
726
738
  ...args.data,
@@ -732,6 +744,7 @@ function createGlobal(args) {
732
744
  }
733
745
  }
734
746
  });
747
+ return injectGlobalType(doc, args.slug);
735
748
  }
736
749
  async function findGlobal(args) {
737
750
  const doc = await this.findOne({
@@ -739,17 +752,10 @@ async function findGlobal(args) {
739
752
  locale: args.locale,
740
753
  where: args.where ?? {}
741
754
  });
742
- if (doc != null) {
743
- ;
744
- doc.globalType = args.slug;
745
- }
746
- return doc;
755
+ return injectGlobalType(doc, args.slug);
747
756
  }
748
757
  async function updateGlobal(args) {
749
- // TODO: upsert's `createOnMissing` path generates a new `createdAt` on every call,
750
- // which overwrites the original timestamp on updates. The fix belongs in the Content API:
751
- // `createOnMissing` should preserve `createdAt` on updates and only set it on creates.
752
- return this.upsert({
758
+ const doc = await this.upsert({
753
759
  collection: getGlobalSlug(args.slug),
754
760
  data: args.data,
755
761
  where: {
@@ -758,6 +764,7 @@ async function updateGlobal(args) {
758
764
  }
759
765
  }
760
766
  });
767
+ return injectGlobalType(doc, args.slug);
761
768
  }
762
769
  // TODO: remove Omit when this PR is released: https://github.com/payloadcms/payload/pull/15183
763
770
  function findGlobalVersions(args) {
@@ -809,6 +816,13 @@ function countGlobalVersions(args) {
809
816
  where: args.where
810
817
  });
811
818
  }
819
+ function createDocumentContentAPIError(adapter, operation, error, status, collectionSlug) {
820
+ const validationError = maybeCreateUniqueValidationError(error, collectionSlug, adapter);
821
+ if (validationError != null) {
822
+ return validationError;
823
+ }
824
+ return createContentAPIError(operation, error, status);
825
+ }
812
826
  function createContentAPIError(operation, error, status) {
813
827
  return new APIError(`Content API ${operation} error: ${error.message}`, status, {
814
828
  code: error.code,
@@ -816,6 +830,27 @@ function createContentAPIError(operation, error, status) {
816
830
  field: error.field
817
831
  });
818
832
  }
833
+ // A unique-constraint violation from the Content API is Payload's `ValidationError` contract:
834
+ // reconstruct it with the offending field path(s) so callers can map the error back to a field
835
+ // (matching what Payload's other database adapters throw). Falls back to a generic APIError when
836
+ // the collection or offending field is unknown.
837
+ function maybeCreateUniqueValidationError(error, collectionSlug, adapter) {
838
+ if (error.constraint !== 'unique_constraint') {
839
+ return null;
840
+ }
841
+ const paths = (error.field ?? '').split(',').map((path)=>path.trim()).filter((path)=>path.length > 0);
842
+ if (paths.length === 0) {
843
+ adapter.payload.logger.error(`Unique constraint violation but no offending field.`);
844
+ return null;
845
+ }
846
+ return new ValidationError({
847
+ collection: collectionSlug,
848
+ errors: paths.map((path)=>({
849
+ message: error.message,
850
+ path
851
+ }))
852
+ });
853
+ }
819
854
  export const contentAPIAdapter = (opts)=>({
820
855
  name: 'content-api',
821
856
  defaultIDType: 'text',
@@ -855,7 +890,7 @@ export const contentAPIAdapter = (opts)=>({
855
890
  deleteMany: deleteMany,
856
891
  deleteOne: deleteOne,
857
892
  deleteVersions: deleteVersions,
858
- documentSchema: buildDocumentSchema(payload.config),
893
+ documentSchema: ()=>getDocumentSchema(payload.config),
859
894
  environmentName: opts.environmentName,
860
895
  find: find,
861
896
  findDistinct: findDistinct,
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Payload expects globals to carry a `globalType` (their slug) alongside the stored data.
3
+ * Globals are stored as `_global-<slug>` collections that have no such column, so the adapter
4
+ * attaches it to every global document it returns.
5
+ */
6
+ export declare function injectGlobalType<T>(doc: T, slug: string): T;
@@ -0,0 +1,11 @@
1
+ import { isRecord } from '../isRecord.js';
2
+ /**
3
+ * Payload expects globals to carry a `globalType` (their slug) alongside the stored data.
4
+ * Globals are stored as `_global-<slug>` collections that have no such column, so the adapter
5
+ * attaches it to every global document it returns.
6
+ */ export function injectGlobalType(doc, slug) {
7
+ return isRecord(doc) ? {
8
+ ...doc,
9
+ globalType: slug
10
+ } : doc;
11
+ }
@@ -0,0 +1 @@
1
+ export declare function stringifyNumericIds(value: unknown): unknown;
@@ -0,0 +1,19 @@
1
+ // The Content API stores every document id as a string, but Payload keeps a
2
+ // collection's custom numeric id as a `number`. Stringify numbers so an id filter
3
+ // matches the stored value.
4
+ // Has two shapes it accepts:
5
+ // - Scalar values (most operators)
6
+ // - Arrays of values (for `in`/`not_in` operators)
7
+ // Non-numeric entries (already-string ids) pass through untouched.
8
+ export function stringifyNumericIds(value) {
9
+ if (typeof value === 'number') {
10
+ return stringifyNumericId(value);
11
+ }
12
+ if (Array.isArray(value)) {
13
+ return value.map((entry)=>stringifyNumericId(entry));
14
+ }
15
+ return value;
16
+ }
17
+ function stringifyNumericId(value) {
18
+ return typeof value === 'number' ? String(value) : value;
19
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A plain, string-keyed object — the shape that can safely be spread and have
3
+ * properties read off it without tripping over arrays, `null`, or exotic objects.
4
+ *
5
+ * Objects carrying symbol-keyed own properties are rejected: symbol keys are how
6
+ * libraries tag a value as something more than data, and a spread copies only the
7
+ * string keys, so treating such a value as a record silently strips the tag.
8
+ */
9
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A plain, string-keyed object — the shape that can safely be spread and have
3
+ * properties read off it without tripping over arrays, `null`, or exotic objects.
4
+ *
5
+ * Objects carrying symbol-keyed own properties are rejected: symbol keys are how
6
+ * libraries tag a value as something more than data, and a spread copies only the
7
+ * string keys, so treating such a value as a record silently strips the tag.
8
+ */ export function isRecord(value) {
9
+ if (value == null) {
10
+ return false;
11
+ }
12
+ if (typeof value !== 'object') {
13
+ return false;
14
+ }
15
+ if (Array.isArray(value)) {
16
+ return false;
17
+ }
18
+ return Object.getOwnPropertySymbols(value).length === 0;
19
+ }
@@ -0,0 +1,3 @@
1
+ import type { Config, SanitizedConfig } from 'payload';
2
+ import type { DocumentSchemaWire } from './buildDocumentSchema.js';
3
+ export declare function getDocumentSchema(config: Config | SanitizedConfig): DocumentSchemaWire;
@@ -0,0 +1,24 @@
1
+ import { buildDocumentSchema } from './buildDocumentSchema.js';
2
+ // The schema built by `getDocumentSchema`, keyed by the config it was built from.
3
+ // Weakly held so a config discarded by HMR takes its schema with it.
4
+ let schemaByConfig = new WeakMap();
5
+ // Retrieves a document schema for the given config, building it if necessary.
6
+ // The last output is memoized, so if this config matches the last config we just return
7
+ // the previous output instead of rebuilding it.
8
+ export function getDocumentSchema(config) {
9
+ const existing = schemaByConfig.get(config);
10
+ if (existing != null) {
11
+ return existing;
12
+ }
13
+ const schema = buildDocumentSchema(config);
14
+ // Clear the cache of any previous config, so only the most recent one is held.
15
+ // This ensures that we never accidentally leak memory by holding on to more
16
+ // and more schemas if the configs are never garbage collected.
17
+ schemaByConfig = new WeakMap([
18
+ [
19
+ config,
20
+ schema
21
+ ]
22
+ ]);
23
+ return schema;
24
+ }
@@ -1,3 +1,4 @@
1
+ import { stringifyNumericIds } from './data/stringifyNumericIds.js';
1
2
  export function convertPayloadWhereToContentAPI(where, options = {}) {
2
3
  // Empty where {} should be { and: [] } not undefined
3
4
  // Content API requires a where clause structure even for "no filter"
@@ -42,7 +43,7 @@ export function convertPayloadWhereToContentAPI(where, options = {}) {
42
43
  conditions.push({
43
44
  operator: 'equals',
44
45
  path: fieldPath,
45
- value: fieldPath === 'id' && typeof value === 'number' ? String(value) : value
46
+ value: fieldPath === 'id' ? stringifyNumericIds(value) : value
46
47
  });
47
48
  continue;
48
49
  }
@@ -69,9 +70,10 @@ export function convertPayloadWhereToContentAPI(where, options = {}) {
69
70
  }
70
71
  // Content API stores all document IDs as strings.
71
72
  // Payload sends numeric values for collections with custom numeric ID fields,
72
- // so we must stringify to match.
73
- if (fieldPath === 'id' && typeof finalValue === 'number') {
74
- finalValue = String(finalValue);
73
+ // so we must stringify to match. This includes the `in`/`not_in` arrays that
74
+ // Payload's relationship-population dataloader always uses ({ id: { in: [...] } }).
75
+ if (fieldPath === 'id') {
76
+ finalValue = stringifyNumericIds(finalValue);
75
77
  }
76
78
  conditions.push({
77
79
  operator: op,
@@ -2,3 +2,5 @@ export { FigmaAvatar } from '../oauth/components/FigmaAvatar/index.js';
2
2
  export { FigmaAvatarCell } from '../oauth/components/FigmaAvatarCell/index.js';
3
3
  export { DefaultLoginButton } from '../oauth/components/LoginButton/index.js';
4
4
  export { LogoutButton } from '../oauth/components/LogoutButton/index.js';
5
+ export { FigmaCloudLimitProvider } from '../plugin/cloud-limits/CloudLimitProvider/index.js';
6
+ export { FigmaCloudLimitSlugTracker } from '../plugin/cloud-limits/SlugTracker/index.js';
@@ -2,3 +2,5 @@ export { FigmaAvatar } from '../oauth/components/FigmaAvatar/index.js';
2
2
  export { FigmaAvatarCell } from '../oauth/components/FigmaAvatarCell/index.js';
3
3
  export { DefaultLoginButton } from '../oauth/components/LoginButton/index.js';
4
4
  export { LogoutButton } from '../oauth/components/LogoutButton/index.js';
5
+ export { FigmaCloudLimitProvider } from '../plugin/cloud-limits/CloudLimitProvider/index.js';
6
+ export { FigmaCloudLimitSlugTracker } from '../plugin/cloud-limits/SlugTracker/index.js';
@@ -0,0 +1,12 @@
1
+ export type WriteAgentConfigResult = {
2
+ agentsMd: 'appended' | 'created' | 'unchanged';
3
+ claudeMd: 'appended' | 'created' | 'unchanged';
4
+ };
5
+ /**
6
+ * Write the root AI-agent pointer files so assistants can find the Payload
7
+ * skill bundled in node_modules. Idempotent: creates when absent, appends the
8
+ * pointer when a file exists without it, and no-ops when already present.
9
+ */
10
+ export declare function writeAgentConfigFiles(args: {
11
+ projectDir: string;
12
+ }): Promise<WriteAgentConfigResult>;
@@ -0,0 +1,52 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ const SKILL_POINTER_PATH = 'node_modules/payload/skills/payload/SKILL.md';
4
+ const AGENTS_BLOCK = `# AI Agent
5
+
6
+ For any Payload-related work, reference the skill at
7
+ \`${SKILL_POINTER_PATH}\`.
8
+ `;
9
+ const CLAUDE_IMPORT = '@AGENTS.md';
10
+ /**
11
+ * Write the root AI-agent pointer files so assistants can find the Payload
12
+ * skill bundled in node_modules. Idempotent: creates when absent, appends the
13
+ * pointer when a file exists without it, and no-ops when already present.
14
+ */ export async function writeAgentConfigFiles(args) {
15
+ const agentsMd = await ensureFileContains({
16
+ content: AGENTS_BLOCK,
17
+ filePath: path.join(args.projectDir, 'AGENTS.md'),
18
+ marker: SKILL_POINTER_PATH
19
+ });
20
+ const claudeMd = await ensureFileContains({
21
+ content: `${CLAUDE_IMPORT}\n`,
22
+ filePath: path.join(args.projectDir, 'CLAUDE.md'),
23
+ marker: CLAUDE_IMPORT
24
+ });
25
+ return {
26
+ agentsMd,
27
+ claudeMd
28
+ };
29
+ }
30
+ async function ensureFileContains(args) {
31
+ const existing = await readFileOrNull(args.filePath);
32
+ if (existing === null) {
33
+ await fs.writeFile(args.filePath, args.content, 'utf8');
34
+ return 'created';
35
+ }
36
+ if (existing.includes(args.marker)) {
37
+ return 'unchanged';
38
+ }
39
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n';
40
+ await fs.writeFile(args.filePath, `${existing}${separator}${args.content}`, 'utf8');
41
+ return 'appended';
42
+ }
43
+ async function readFileOrNull(filePath) {
44
+ try {
45
+ return await fs.readFile(filePath, 'utf8');
46
+ } catch (err) {
47
+ if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
48
+ return null;
49
+ }
50
+ throw err;
51
+ }
52
+ }