@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.
- package/dist/api/check-project-subdomain-availability.d.ts +12 -0
- package/dist/api/check-project-subdomain-availability.js +32 -0
- package/dist/api/control-plane-error.d.ts +6 -0
- package/dist/api/control-plane-error.js +8 -0
- package/dist/api/control-plane-fetch.d.ts +7 -0
- package/dist/api/control-plane-fetch.js +37 -0
- package/dist/api/control-plane.d.ts +1 -9
- package/dist/api/control-plane.js +2 -48
- package/dist/api/create-project.d.ts +18 -0
- package/dist/api/create-project.js +37 -0
- package/dist/api/list-project-workspaces.d.ts +8 -0
- package/dist/api/list-project-workspaces.js +28 -0
- package/dist/api/project-resource-type.d.ts +1 -0
- package/dist/api/project-resource-type.js +1 -0
- package/dist/auth/headless-oauth-flow.d.ts +23 -0
- package/dist/auth/headless-oauth-flow.js +69 -0
- package/dist/auth/oauth-flow.d.ts +2 -0
- package/dist/auth/oauth-flow.js +13 -8
- package/dist/auth/token-store.d.ts +4 -1
- package/dist/auth/token-store.js +14 -0
- package/dist/auth/types.d.ts +12 -0
- package/dist/cli.d.ts +39 -0
- package/dist/cli.js +71 -36
- package/dist/commands/bootstrap.js +2 -1
- package/dist/commands/deploy.d.ts +12 -0
- package/dist/commands/deploy.js +61 -23
- package/dist/commands/detect-project-resource-type.d.ts +3 -0
- package/dist/commands/detect-project-resource-type.js +28 -0
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.js +163 -78
- package/dist/commands/login.d.ts +11 -1
- package/dist/commands/login.js +66 -4
- package/dist/commands/prompt-to-create-project/format-project-subdomain.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/format-project-subdomain.js +3 -0
- package/dist/commands/prompt-to-create-project/format-unavailable-subdomain-reason.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/format-unavailable-subdomain-reason.js +7 -0
- package/dist/commands/prompt-to-create-project/get-default-project-name.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/get-default-project-name.js +13 -0
- package/dist/commands/prompt-to-create-project/prompt-for-available-subdomain.d.ts +7 -0
- package/dist/commands/prompt-to-create-project/prompt-for-available-subdomain.js +32 -0
- package/dist/commands/prompt-to-create-project/resolve-project-region.d.ts +8 -0
- package/dist/commands/prompt-to-create-project/resolve-project-region.js +27 -0
- package/dist/commands/prompt-to-create-project/resolve-project-subdomain.d.ts +9 -0
- package/dist/commands/prompt-to-create-project/resolve-project-subdomain.js +54 -0
- package/dist/commands/prompt-to-create-project/resolve-text-input.d.ts +9 -0
- package/dist/commands/prompt-to-create-project/resolve-text-input.js +23 -0
- package/dist/commands/prompt-to-create-project/resolve-workspace-plan-key.d.ts +12 -0
- package/dist/commands/prompt-to-create-project/resolve-workspace-plan-key.js +36 -0
- package/dist/commands/prompt-to-create-project/validate-subdomain.d.ts +1 -0
- package/dist/commands/prompt-to-create-project/validate-subdomain.js +9 -0
- package/dist/commands/prompt-to-create-project.d.ts +24 -0
- package/dist/commands/prompt-to-create-project.js +69 -0
- package/dist/commands/setup-agent.d.ts +12 -0
- package/dist/commands/setup-agent.js +123 -0
- package/dist/commands/write-project-configuration.d.ts +12 -0
- package/dist/commands/write-project-configuration.js +16 -0
- package/dist/db-content-api/index.d.ts +1 -1
- package/dist/db-content-api/index.js +90 -55
- package/dist/db-content-api/utilities/data/injectGlobalType.d.ts +6 -0
- package/dist/db-content-api/utilities/data/injectGlobalType.js +11 -0
- package/dist/db-content-api/utilities/data/stringifyNumericIds.d.ts +1 -0
- package/dist/db-content-api/utilities/data/stringifyNumericIds.js +19 -0
- package/dist/db-content-api/utilities/isRecord.d.ts +9 -0
- package/dist/db-content-api/utilities/isRecord.js +19 -0
- package/dist/db-content-api/utilities/schema/getDocumentSchema.d.ts +3 -0
- package/dist/db-content-api/utilities/schema/getDocumentSchema.js +24 -0
- package/dist/db-content-api/utilities/where.js +6 -4
- package/dist/exports/client.d.ts +2 -0
- package/dist/exports/client.js +2 -0
- package/dist/lib/write-agent-config.d.ts +12 -0
- package/dist/lib/write-agent-config.js +52 -0
- package/dist/plugin/build-config.js +32 -3
- package/dist/plugin/cloud-limits/CloudLimitModal/index.d.ts +9 -0
- package/dist/plugin/cloud-limits/CloudLimitModal/index.js +78 -0
- package/dist/plugin/cloud-limits/CloudLimitModal/index.scss +51 -0
- package/dist/plugin/cloud-limits/CloudLimitProvider/index.d.ts +7 -0
- package/dist/plugin/cloud-limits/CloudLimitProvider/index.js +169 -0
- package/dist/plugin/cloud-limits/SlugRegistryContext/index.d.ts +2 -0
- package/dist/plugin/cloud-limits/SlugRegistryContext/index.js +3 -0
- package/dist/plugin/cloud-limits/SlugTracker/index.d.ts +9 -0
- package/dist/plugin/cloud-limits/SlugTracker/index.js +23 -0
- package/dist/plugin/cloud-limits/icons/ExternalLinkIcon.d.ts +2 -0
- package/dist/plugin/cloud-limits/icons/ExternalLinkIcon.js +30 -0
- package/dist/plugin/cloud-limits/shared.d.ts +10 -0
- package/dist/plugin/cloud-limits/shared.js +27 -0
- package/dist/skills/deploy-to-figma/SKILL.md +234 -0
- package/dist/skills/deploy-to-figma/references/cli-reference.md +146 -0
- package/dist/translations/ar.d.ts +12 -0
- package/dist/translations/ar.js +12 -0
- package/dist/translations/az.d.ts +12 -0
- package/dist/translations/az.js +12 -0
- package/dist/translations/bg.d.ts +12 -0
- package/dist/translations/bg.js +12 -0
- package/dist/translations/bnBd.d.ts +12 -0
- package/dist/translations/bnBd.js +12 -0
- package/dist/translations/bnIn.d.ts +12 -0
- package/dist/translations/bnIn.js +12 -0
- package/dist/translations/ca.d.ts +12 -0
- package/dist/translations/ca.js +12 -0
- package/dist/translations/cs.d.ts +12 -0
- package/dist/translations/cs.js +12 -0
- package/dist/translations/da.d.ts +12 -0
- package/dist/translations/da.js +12 -0
- package/dist/translations/de.d.ts +12 -0
- package/dist/translations/de.js +12 -0
- package/dist/translations/en.d.ts +12 -0
- package/dist/translations/en.js +12 -0
- package/dist/translations/es.d.ts +12 -0
- package/dist/translations/es.js +12 -0
- package/dist/translations/et.d.ts +12 -0
- package/dist/translations/et.js +12 -0
- package/dist/translations/fa.d.ts +12 -0
- package/dist/translations/fa.js +12 -0
- package/dist/translations/fr.d.ts +12 -0
- package/dist/translations/fr.js +12 -0
- package/dist/translations/he.d.ts +12 -0
- package/dist/translations/he.js +12 -0
- package/dist/translations/hr.d.ts +12 -0
- package/dist/translations/hr.js +12 -0
- package/dist/translations/hu.d.ts +12 -0
- package/dist/translations/hu.js +12 -0
- package/dist/translations/hy.d.ts +12 -0
- package/dist/translations/hy.js +12 -0
- package/dist/translations/id.d.ts +12 -0
- package/dist/translations/id.js +12 -0
- package/dist/translations/index.d.ts +533 -0
- package/dist/translations/index.js +90 -0
- package/dist/translations/is.d.ts +12 -0
- package/dist/translations/is.js +12 -0
- package/dist/translations/it.d.ts +12 -0
- package/dist/translations/it.js +12 -0
- package/dist/translations/ja.d.ts +12 -0
- package/dist/translations/ja.js +12 -0
- package/dist/translations/ko.d.ts +12 -0
- package/dist/translations/ko.js +12 -0
- package/dist/translations/lt.d.ts +12 -0
- package/dist/translations/lt.js +12 -0
- package/dist/translations/lv.d.ts +12 -0
- package/dist/translations/lv.js +12 -0
- package/dist/translations/my.d.ts +12 -0
- package/dist/translations/my.js +12 -0
- package/dist/translations/nb.d.ts +12 -0
- package/dist/translations/nb.js +12 -0
- package/dist/translations/nl.d.ts +12 -0
- package/dist/translations/nl.js +12 -0
- package/dist/translations/pl.d.ts +12 -0
- package/dist/translations/pl.js +12 -0
- package/dist/translations/pt.d.ts +12 -0
- package/dist/translations/pt.js +12 -0
- package/dist/translations/ro.d.ts +12 -0
- package/dist/translations/ro.js +12 -0
- package/dist/translations/rs.d.ts +12 -0
- package/dist/translations/rs.js +12 -0
- package/dist/translations/rsLatin.d.ts +12 -0
- package/dist/translations/rsLatin.js +12 -0
- package/dist/translations/ru.d.ts +12 -0
- package/dist/translations/ru.js +12 -0
- package/dist/translations/sk.d.ts +12 -0
- package/dist/translations/sk.js +12 -0
- package/dist/translations/sl.d.ts +12 -0
- package/dist/translations/sl.js +12 -0
- package/dist/translations/sv.d.ts +12 -0
- package/dist/translations/sv.js +12 -0
- package/dist/translations/ta.d.ts +12 -0
- package/dist/translations/ta.js +12 -0
- package/dist/translations/th.d.ts +12 -0
- package/dist/translations/th.js +12 -0
- package/dist/translations/tr.d.ts +12 -0
- package/dist/translations/tr.js +12 -0
- package/dist/translations/uk.d.ts +12 -0
- package/dist/translations/uk.js +12 -0
- package/dist/translations/vi.d.ts +12 -0
- package/dist/translations/vi.js +12 -0
- package/dist/translations/zh.d.ts +12 -0
- package/dist/translations/zh.js +12 -0
- package/dist/translations/zhTw.d.ts +12 -0
- package/dist/translations/zhTw.js +12 -0
- package/dist/types.d.ts +11 -0
- package/dist/utils/adapters/nextjs.d.ts +3 -3
- package/dist/utils/adapters/nextjs.js +9 -7
- package/dist/utils/adapters/nitro.d.ts +3 -3
- package/dist/utils/adapters/nitro.js +73 -77
- package/dist/utils/adapters/vite.d.ts +1 -1
- package/dist/utils/adapters/vite.js +8 -7
- package/dist/utils/assert-never.d.ts +1 -0
- package/dist/utils/assert-never.js +3 -0
- package/dist/utils/asset-collection.d.ts +2 -2
- package/dist/utils/asset-collection.js +10 -15
- package/dist/utils/build-detection.d.ts +6 -1
- package/dist/utils/build-detection.js +68 -89
- package/dist/utils/build-lambda-zip.d.ts +5 -6
- package/dist/utils/build-lambda-zip.js +31 -48
- package/dist/utils/deploy-adapter.d.ts +4 -4
- package/dist/utils/deploy-adapter.js +12 -8
- package/dist/utils/deploy-output/inspectNextjsOutput/getNextRelativeAppDir.d.ts +1 -0
- package/dist/utils/deploy-output/inspectNextjsOutput/getNextRelativeAppDir.js +15 -0
- package/dist/utils/deploy-output/inspectNextjsOutput.d.ts +12 -0
- package/dist/utils/deploy-output/inspectNextjsOutput.js +28 -0
- package/dist/utils/deploy-output/inspectNitroOutput.d.ts +9 -0
- package/dist/utils/deploy-output/inspectNitroOutput.js +13 -0
- package/dist/utils/deploy-output/isFile.d.ts +1 -0
- package/dist/utils/deploy-output/isFile.js +4 -0
- package/dist/utils/deploy-output/readJson.d.ts +1 -0
- package/dist/utils/deploy-output/readJson.js +9 -0
- package/dist/utils/deploy-output/resolveNitroOutputLayout.d.ts +8 -0
- package/dist/utils/deploy-output/resolveNitroOutputLayout.js +16 -0
- package/dist/utils/deploy-output/resolveOutputPath.d.ts +2 -0
- package/dist/utils/deploy-output/resolveOutputPath.js +9 -0
- package/dist/utils/deploy-output/resolveWithin.d.ts +1 -0
- package/dist/utils/deploy-output/resolveWithin.js +12 -0
- package/dist/utils/lambda-config.js +4 -14
- package/dist/utils/messages.js +24 -3
- package/dist/utils/payload-generate.d.ts +10 -0
- package/dist/utils/payload-generate.js +59 -0
- package/dist/utils/pnpm-builds.d.ts +14 -0
- package/dist/utils/pnpm-builds.js +125 -0
- package/package.json +6 -5
- package/dist/lib/download-skill.d.ts +0 -12
- 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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
305
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
682
|
-
const sortClause = addFallbackSort(sort || '-updatedAt', this.payload,
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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,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'
|
|
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
|
-
|
|
74
|
-
|
|
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,
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -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';
|
package/dist/exports/client.js
CHANGED
|
@@ -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
|
+
}
|