@valkyrianlabs/payload-markdown-docs 0.2.1 → 0.3.1

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.
@@ -55,6 +55,7 @@ const resolveSyncSource = async ({ manifest, options, payload })=>{
55
55
  allowedSourceIds: [
56
56
  sourceId
57
57
  ],
58
+ auth: docsSet.auth,
58
59
  docsSet,
59
60
  routeBase: docsSet.routeBase,
60
61
  sourceRoot: docsSet.sourceRoot
@@ -62,9 +63,9 @@ const resolveSyncSource = async ({ manifest, options, payload })=>{
62
63
  };
63
64
  }
64
65
  const sourceConfig = findSourceConfig(sourceId, options.sources);
65
- if (options.sources && options.sources.length > 0 && !sourceConfig) {
66
+ if ((options.docsSetsEnabled || options.sources && options.sources.length > 0) && !sourceConfig) {
66
67
  return {
67
- response: errorResponse('source_not_allowed', `Manifest source.id "${sourceId}" is not configured for this endpoint.`, 400)
68
+ response: errorResponse('source_not_allowed', options.docsSetsEnabled ? `No docs set exists for manifest source.id "${sourceId}". Create a docs set in Payload Admin before syncing this source.` : `Manifest source.id "${sourceId}" is not configured for this endpoint.`, 400)
68
69
  };
69
70
  }
70
71
  if (sourceConfig?.root && manifest.source.root && sourceConfig.root !== manifest.source.root) {
@@ -191,20 +192,73 @@ const getBearerToken = (headers)=>{
191
192
  }
192
193
  return token;
193
194
  };
194
- const assertReplayProtectionAvailable = (options)=>options.noncesEnabled ? undefined : errorResponse('replay_protection_unavailable', 'Sync endpoint requires nonce replay protection.', 500);
195
- const authenticateEd25519Request = async ({ now, options, rawBody, req })=>{
196
- if (!options.auth || options.auth.mode !== 'ed25519') {
195
+ const hasEd25519AuthHeaders = (headers)=>getRequiredHeader(headers, 'x-vl-md-docs-key-id') !== undefined || getRequiredHeader(headers, 'x-vl-md-docs-signature') !== undefined || getRequiredHeader(headers, 'x-vl-md-docs-timestamp') !== undefined || getRequiredHeader(headers, 'x-vl-md-docs-nonce') !== undefined;
196
+ const getGlobalEd25519AuthConfig = (auth)=>{
197
+ if (!auth || auth.mode === 'disabled' || auth.mode === 'github-oidc') {
198
+ return undefined;
199
+ }
200
+ if (auth.mode === 'ed25519') {
201
+ return auth;
202
+ }
203
+ return auth.ed25519 ? {
204
+ ...auth.ed25519,
205
+ mode: 'ed25519'
206
+ } : undefined;
207
+ };
208
+ const getEd25519AuthConfig = ({ auth, sourceAuth })=>{
209
+ const globalAuth = getGlobalEd25519AuthConfig(auth);
210
+ if (sourceAuth?.ed25519?.keys.length) {
197
211
  return {
198
- response: errorResponse('auth_disabled', 'Signed sync authentication is not configured for this endpoint.', 401)
212
+ ...globalAuth,
213
+ ...sourceAuth.ed25519,
214
+ keys: sourceAuth.ed25519.keys,
215
+ mode: 'ed25519'
199
216
  };
200
217
  }
218
+ return globalAuth;
219
+ };
220
+ const getGlobalGitHubOidcAuthConfig = (auth)=>{
221
+ if (!auth || auth.mode === 'disabled' || auth.mode === 'ed25519') {
222
+ return undefined;
223
+ }
224
+ if (auth.mode === 'github-oidc') {
225
+ return auth;
226
+ }
227
+ return auth.githubOidc ? {
228
+ ...auth.githubOidc,
229
+ mode: 'github-oidc'
230
+ } : undefined;
231
+ };
232
+ const getGitHubOidcAuthConfig = ({ auth, sourceAuth })=>{
233
+ const globalAuth = getGlobalGitHubOidcAuthConfig(auth);
234
+ const sourceOidc = sourceAuth?.githubOidc;
235
+ if (!sourceOidc) {
236
+ return globalAuth;
237
+ }
238
+ if (sourceOidc.enabled === false) {
239
+ return globalAuth;
240
+ }
241
+ const { enabled: _enabled, ...sourceOptions } = sourceOidc;
242
+ const audience = sourceOptions.audience ?? globalAuth?.audience;
243
+ if (!audience) {
244
+ return globalAuth;
245
+ }
246
+ return {
247
+ ...globalAuth,
248
+ ...sourceOptions,
249
+ audience,
250
+ mode: 'github-oidc'
251
+ };
252
+ };
253
+ const assertReplayProtectionAvailable = (options)=>options.noncesEnabled ? undefined : errorResponse('replay_protection_unavailable', 'Sync endpoint requires nonce replay protection.', 500);
254
+ const authenticateEd25519Request = async ({ auth, now, options, rawBody, req })=>{
201
255
  const headersResult = extractSyncRequestHeaders(req.headers);
202
256
  if (!headersResult.ok) {
203
257
  return {
204
258
  response: errorResponse('missing_header', `Missing required sync header: ${headersResult.header}.`, 401)
205
259
  };
206
260
  }
207
- const keyConfig = options.auth.keys.find((key)=>key.id === headersResult.headers.keyId);
261
+ const keyConfig = auth.keys.find((key)=>key.id === headersResult.headers.keyId);
208
262
  if (!keyConfig) {
209
263
  return {
210
264
  response: errorResponse('unknown_key', 'Unknown sync request key id.', 401)
@@ -220,7 +274,7 @@ const authenticateEd25519Request = async ({ now, options, rawBody, req })=>{
220
274
  };
221
275
  }
222
276
  const timestampValidation = validateTimestampSkew({
223
- maxSkewSeconds: options.auth.maxSkewSeconds ?? options.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS,
277
+ maxSkewSeconds: auth.maxSkewSeconds ?? options.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS,
224
278
  now,
225
279
  timestamp: headersResult.headers.timestamp
226
280
  });
@@ -267,7 +321,7 @@ const authenticateEd25519Request = async ({ now, options, rawBody, req })=>{
267
321
  response: errorResponse('invalid_signature', 'Invalid sync request signature.', 401)
268
322
  };
269
323
  }
270
- const nonceTtlSeconds = options.auth.nonceTtlSeconds ?? options.nonceTtlSeconds ?? DEFAULT_NONCE_TTL_SECONDS;
324
+ const nonceTtlSeconds = auth.nonceTtlSeconds ?? options.nonceTtlSeconds ?? DEFAULT_NONCE_TTL_SECONDS;
271
325
  return {
272
326
  identity: {
273
327
  bodyHash: bodyHash.computedHash,
@@ -277,12 +331,7 @@ const authenticateEd25519Request = async ({ now, options, rawBody, req })=>{
277
331
  }
278
332
  };
279
333
  };
280
- const authenticateGitHubOidcRequest = async ({ now, options, rawBody, req })=>{
281
- if (!options.auth || options.auth.mode !== 'github-oidc') {
282
- return {
283
- response: errorResponse('auth_disabled', 'GitHub OIDC sync authentication is not configured for this endpoint.', 401)
284
- };
285
- }
334
+ const authenticateGitHubOidcRequest = async ({ auth, now, options, rawBody, req })=>{
286
335
  const token = getBearerToken(req.headers);
287
336
  if (token === undefined) {
288
337
  return {
@@ -310,7 +359,7 @@ const authenticateGitHubOidcRequest = async ({ now, options, rawBody, req })=>{
310
359
  };
311
360
  }
312
361
  const verified = await verifyGitHubOidcToken({
313
- config: options.auth,
362
+ config: auth,
314
363
  fetchJson: options.oidcFetchJson,
315
364
  now,
316
365
  token
@@ -351,21 +400,51 @@ const authenticateGitHubOidcRequest = async ({ now, options, rawBody, req })=>{
351
400
  }
352
401
  };
353
402
  };
354
- const authenticateSyncRequest = async ({ now, options, rawBody, req })=>{
355
- if (!options.auth || options.auth.mode === 'disabled') {
403
+ const authenticateSyncRequest = async ({ now, options, rawBody, req, sourceAuth })=>{
404
+ const ed25519Auth = getEd25519AuthConfig({
405
+ auth: options.auth,
406
+ sourceAuth
407
+ });
408
+ const githubOidcAuth = getGitHubOidcAuthConfig({
409
+ auth: options.auth,
410
+ sourceAuth
411
+ });
412
+ if (!ed25519Auth && !githubOidcAuth) {
356
413
  return {
357
414
  response: errorResponse('auth_disabled', 'Sync authentication is not configured for this endpoint.', 401)
358
415
  };
359
416
  }
360
- if (options.auth.mode === 'github-oidc') {
417
+ const bearerToken = getBearerToken(req.headers);
418
+ if (bearerToken !== undefined) {
419
+ if (!githubOidcAuth) {
420
+ return {
421
+ response: errorResponse('auth_disabled', 'GitHub OIDC sync authentication is not configured for this endpoint.', 401)
422
+ };
423
+ }
361
424
  return authenticateGitHubOidcRequest({
425
+ auth: githubOidcAuth,
362
426
  now,
363
427
  options,
364
428
  rawBody,
365
429
  req
366
430
  });
367
431
  }
368
- return authenticateEd25519Request({
432
+ if (hasEd25519AuthHeaders(req.headers) || !githubOidcAuth) {
433
+ if (!ed25519Auth) {
434
+ return {
435
+ response: errorResponse('auth_disabled', 'Signed sync authentication is not configured for this endpoint.', 401)
436
+ };
437
+ }
438
+ return authenticateEd25519Request({
439
+ auth: ed25519Auth,
440
+ now,
441
+ options,
442
+ rawBody,
443
+ req
444
+ });
445
+ }
446
+ return authenticateGitHubOidcRequest({
447
+ auth: githubOidcAuth,
369
448
  now,
370
449
  options,
371
450
  rawBody,
@@ -385,15 +464,6 @@ const createSyncEndpointHandler = (options)=>async (req)=>{
385
464
  if (Buffer.byteLength(rawBody, 'utf8') > maxBodyBytes) {
386
465
  return errorResponse('invalid_body', 'Sync request body is too large.', 413);
387
466
  }
388
- const authentication = await authenticateSyncRequest({
389
- now: startedAt,
390
- options,
391
- rawBody,
392
- req
393
- });
394
- if (authentication.response) {
395
- return authentication.response;
396
- }
397
467
  const manifest = parseManifestBody(rawBody);
398
468
  if (!manifest) {
399
469
  return errorResponse('invalid_body', 'Sync request body must be a JSON manifest.', 400);
@@ -406,6 +476,16 @@ const createSyncEndpointHandler = (options)=>async (req)=>{
406
476
  if (sourceResolution.response) {
407
477
  return sourceResolution.response;
408
478
  }
479
+ const authentication = await authenticateSyncRequest({
480
+ now: startedAt,
481
+ options,
482
+ rawBody,
483
+ req,
484
+ sourceAuth: sourceResolution.source.auth
485
+ });
486
+ if (authentication.response) {
487
+ return authentication.response;
488
+ }
409
489
  const validation = validateDocsManifest(manifest, {
410
490
  allowedSourceIds: sourceResolution.source.allowedSourceIds,
411
491
  maxTotalBytes: maxBodyBytes,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoints/sync.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport type {\n ApplyDocsSyncPayloadOperations,\n DocsPublishMode,\n DocsSetPayloadOperations,\n ExistingDocsPayloadOperations,\n ExistingPayloadDocsRecord,\n ResolvedDocsSet,\n RouteCollisionPayloadOperations,\n SyncRunsPayloadOperations,\n} from '../payload/index.js'\nimport type {\n FetchJson,\n NoncePayloadOperations,\n} from '../security/index.js'\nimport type {\n DocsDeleteBehavior,\n DocsManifest,\n DocsValidationIssue,\n PlannedDocChange,\n ValidatedDocsManifest,\n} from '../sync/index.js'\nimport type { PayloadMarkdownDocsAuthConfig } from '../types.js'\n\nimport {\n DEFAULT_DOCS_ROUTE_BASE,\n DEFAULT_MAX_BODY_BYTES,\n DEFAULT_MAX_SKEW_SECONDS,\n DEFAULT_NONCE_TTL_SECONDS,\n} from '../constants.js'\nimport {\n applyDocsSync,\n assertApplyDeleteBehaviorSupported,\n createSyncRunAudit,\n findConfiguredPagesRouteCollisions,\n findDocsSetBySourceId,\n findDocsSyncConflicts,\n findDuplicateDesiredRouteCollisions,\n findExistingDocsRouteCollisions,\n findExistingPayloadDocsRecords,\n getRecordId,\n toExistingDocsRecord,\n updateDocsSetAfterSync,\n updateSyncRunAudit,\n} from '../payload/index.js'\nimport {\n assertNonceNotReplayed,\n buildCanonicalSigningString,\n extractSyncRequestHeaders,\n getCanonicalPathFromRequestUrl,\n storeAcceptedNonce,\n validateTimestampSkew,\n verifyBodySha256,\n verifyEd25519Signature,\n verifyGitHubOidcToken,\n} from '../security/index.js'\nimport {\n planDocsSync,\n validateDocsManifest,\n} from '../sync/index.js'\n\nexport type DocsSyncEndpointErrorCode =\n | 'audit_unavailable'\n | 'auth_disabled'\n | 'body_hash_mismatch'\n | 'delete_behavior_not_implemented'\n | 'draft_behavior_not_available'\n | 'dry_run_required_not_implemented'\n | 'hard_delete_disabled'\n | 'invalid_body'\n | 'invalid_manifest'\n | 'invalid_method'\n | 'invalid_signature'\n | 'invalid_timestamp'\n | 'manual_edit_conflict'\n | 'missing_header'\n | 'nonce_replay'\n | 'oidc_environment_not_allowed'\n | 'oidc_expired'\n | 'oidc_invalid_audience'\n | 'oidc_invalid_issuer'\n | 'oidc_invalid_token'\n | 'oidc_jwks_unavailable'\n | 'oidc_missing_claim'\n | 'oidc_missing_jti'\n | 'oidc_not_yet_valid'\n | 'oidc_owner_not_allowed'\n | 'oidc_pull_request_not_allowed'\n | 'oidc_ref_not_allowed'\n | 'oidc_replay'\n | 'oidc_repository_not_allowed'\n | 'oidc_workflow_not_allowed'\n | 'publish_disabled'\n | 'publish_not_available'\n | 'replay_protection_unavailable'\n | 'route_collision'\n | 'source_not_allowed'\n | 'sync_apply_failed'\n | 'sync_mode_not_implemented'\n | 'sync_writes_disabled'\n | 'unknown_key'\n\nexport type CreateSyncEndpointOptions = {\n allowHardDelete?: boolean\n allowPublish?: boolean\n allowWrites?: boolean\n auth?: PayloadMarkdownDocsAuthConfig\n defaultPublishMode?: DocsPublishMode\n deleteBehavior?: DocsDeleteBehavior\n docsCollectionSlug: string\n docsEnabled: boolean\n docsEnableDrafts: boolean\n docsSetsCollectionSlug: string\n docsSetsEnabled: boolean\n endpointPath: string\n getNow?: () => Date\n markdownFieldName: string\n maxBodyBytes?: number\n maxSkewSeconds?: number\n noncesCollectionSlug: string\n noncesEnabled: boolean\n nonceTtlSeconds?: number\n oidcFetchJson?: FetchJson\n requireDryRunBeforeApply?: boolean\n routeBase?: string\n routing?: {\n pages?: {\n allowBridgePages: boolean\n bridgeField: string\n collection: string\n enabled: boolean\n routeField: string\n }\n }\n sources?: {\n id: string\n root?: string\n routeBase: string\n }[]\n syncRunsCollectionSlug: string\n syncRunsEnabled: boolean\n}\n\ntype SyncErrorResponse = {\n conflicts?: {\n reason: string\n route?: string\n sourcePath: string\n }[]\n error: {\n code: DocsSyncEndpointErrorCode\n message: string\n }\n ok: false\n routeCollisions?: {\n reason: string\n route: string\n }[]\n}\n\ntype SerializedChange = {\n current?: {\n archived?: boolean\n route: string\n sourceHash?: string\n title?: string\n }\n desired?: {\n route: string\n sha256: string\n title: string\n }\n reason: string\n sourcePath: string\n}\n\ntype SyncSuccessResponse = {\n changes: {\n archive: SerializedChange[]\n create: SerializedChange[]\n delete: SerializedChange[]\n draft: SerializedChange[]\n unchanged: SerializedChange[]\n update: SerializedChange[]\n }\n deleteBehavior: DocsDeleteBehavior\n dryRun: boolean\n effectivePublishMode: DocsPublishMode\n ok: true\n publishRequested: boolean\n summary: {\n archive: number\n create: number\n delete: number\n draft: number\n unchanged: number\n update: number\n warnings: number\n }\n syncRunId?: string\n warnings: DocsValidationIssue[]\n}\n\nconst jsonResponse = (\n body: SyncErrorResponse | SyncSuccessResponse,\n status = 200,\n): Response =>\n Response.json(body, {\n status,\n })\n\nconst errorResponse = (\n code: DocsSyncEndpointErrorCode,\n message: string,\n status = 400,\n extras: Omit<SyncErrorResponse, 'error' | 'ok'> = {},\n): Response =>\n jsonResponse(\n {\n ...extras,\n error: {\n code,\n message,\n },\n ok: false,\n },\n status,\n )\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst parseManifestBody = (rawBody: string): DocsManifest | undefined => {\n try {\n const parsed = JSON.parse(rawBody) as unknown\n\n return isRecord(parsed) ? (parsed as DocsManifest) : undefined\n } catch {\n return undefined\n }\n}\n\nconst findSourceConfig = (\n sourceId: string,\n sources: CreateSyncEndpointOptions['sources'],\n) => sources?.find((source) => source.id === sourceId)\n\nconst getAllowedSourceIds = (\n sources: CreateSyncEndpointOptions['sources'],\n): string[] | undefined => {\n if (!sources || sources.length === 0) {\n return undefined\n }\n\n return sources.map((source) => source.id)\n}\n\ntype ResolvedSyncSource = {\n allowedSourceIds?: string[]\n docsSet?: ResolvedDocsSet\n routeBase: string\n sourceRoot?: string\n}\n\nconst resolveSyncSource = async ({\n manifest,\n options,\n payload,\n}: {\n manifest: DocsManifest\n options: CreateSyncEndpointOptions\n payload: DocsSetPayloadOperations\n}): Promise<\n | {\n response: Response\n source?: never\n }\n | {\n response?: never\n source: ResolvedSyncSource\n }\n> => {\n const sourceId = manifest.source?.id\n\n if (!sourceId) {\n return {\n source: {\n allowedSourceIds: getAllowedSourceIds(options.sources),\n routeBase: options.routeBase ?? DEFAULT_DOCS_ROUTE_BASE,\n },\n }\n }\n\n const docsSet =\n options.docsSetsEnabled\n ? await findDocsSetBySourceId({\n collectionSlug: options.docsSetsCollectionSlug,\n payload,\n sourceId,\n })\n : undefined\n\n if (docsSet) {\n if (\n docsSet.sourceRoot &&\n manifest.source.root &&\n docsSet.sourceRoot !== manifest.source.root\n ) {\n return {\n response: errorResponse(\n 'source_not_allowed',\n `Manifest source.root \"${manifest.source.root}\" is not allowed for docs set source \"${sourceId}\".`,\n 400,\n ),\n }\n }\n\n return {\n source: {\n allowedSourceIds: [sourceId],\n docsSet,\n routeBase: docsSet.routeBase,\n sourceRoot: docsSet.sourceRoot,\n },\n }\n }\n\n const sourceConfig = findSourceConfig(sourceId, options.sources)\n\n if (options.sources && options.sources.length > 0 && !sourceConfig) {\n return {\n response: errorResponse(\n 'source_not_allowed',\n `Manifest source.id \"${sourceId}\" is not configured for this endpoint.`,\n 400,\n ),\n }\n }\n\n if (\n sourceConfig?.root &&\n manifest.source.root &&\n sourceConfig.root !== manifest.source.root\n ) {\n return {\n response: errorResponse(\n 'source_not_allowed',\n `Manifest source.root \"${manifest.source.root}\" is not allowed for source \"${sourceId}\".`,\n 400,\n ),\n }\n }\n\n return {\n source: {\n allowedSourceIds: getAllowedSourceIds(options.sources),\n routeBase: sourceConfig?.routeBase ?? options.routeBase ?? DEFAULT_DOCS_ROUTE_BASE,\n sourceRoot: sourceConfig?.root,\n },\n }\n}\n\nconst summarizePlan = (plan: ReturnType<typeof planDocsSync>) => ({\n archive: plan.archive.length,\n create: plan.create.length,\n delete: plan.delete.length,\n draft: plan.draft.length,\n unchanged: plan.unchanged.length,\n update: plan.update.length,\n warnings: plan.warnings.length,\n})\n\nconst serializeChange = (change: PlannedDocChange): SerializedChange => ({\n current: change.current\n ? {\n archived: change.current.archived,\n route: change.current.route,\n sourceHash: change.current.sourceHash,\n title: change.current.title,\n }\n : undefined,\n desired: change.desired\n ? {\n route: change.desired.route,\n sha256: change.desired.sha256,\n title: change.desired.title,\n }\n : undefined,\n reason: change.reason,\n sourcePath: change.sourcePath,\n})\n\nconst serializeChanges = (plan: ReturnType<typeof planDocsSync>) => ({\n archive: plan.archive.map(serializeChange),\n create: plan.create.map(serializeChange),\n delete: plan.delete.map(serializeChange),\n draft: plan.draft.map(serializeChange),\n unchanged: plan.unchanged.map(serializeChange),\n update: plan.update.map(serializeChange),\n})\n\nconst getTotalManifestBytes = (manifest: ValidatedDocsManifest): number =>\n manifest.files.reduce(\n (total, file) => total + Buffer.byteLength(file.content, 'utf8'),\n 0,\n )\n\nconst getPlannedConflictChanges = ({\n existing,\n plan,\n}: {\n existing: ExistingPayloadDocsRecord[]\n plan: ReturnType<typeof planDocsSync>\n}): PlannedDocChange[] => {\n const existingBySourcePath = new Map(\n existing.map((record) => [record.sourcePath, record]),\n )\n const archivedUnchanged = plan.unchanged.filter((change) => {\n const current = existingBySourcePath.get(change.sourcePath)\n\n return current?.archived === true\n })\n\n return [\n ...plan.update,\n ...plan.archive,\n ...plan.draft,\n ...plan.delete,\n ...archivedUnchanged,\n ]\n}\n\nconst getDefaultPublishMode = (\n options: CreateSyncEndpointOptions,\n): DocsPublishMode =>\n options.defaultPublishMode ?? (options.docsEnableDrafts ? 'draft' : 'preserve')\n\nconst getLifecyclePolicyError = ({\n deleteBehavior,\n manifest,\n options,\n publishMode,\n}: {\n deleteBehavior: DocsDeleteBehavior\n manifest: ValidatedDocsManifest\n options: CreateSyncEndpointOptions\n publishMode: DocsPublishMode\n}): Response | undefined => {\n if (manifest.publish && options.allowPublish !== true) {\n return errorResponse(\n 'publish_disabled',\n 'Publishing is disabled by server configuration.',\n 403,\n )\n }\n\n if (\n (manifest.publish || publishMode === 'published') &&\n !options.docsEnableDrafts\n ) {\n return errorResponse(\n 'publish_not_available',\n 'Publishing requires a draft-enabled dedicated docs collection.',\n 400,\n )\n }\n\n if (publishMode === 'published' && options.allowPublish !== true) {\n return errorResponse(\n 'publish_disabled',\n 'Publishing is disabled by server configuration.',\n 403,\n )\n }\n\n if (publishMode === 'draft' && !options.docsEnableDrafts) {\n return errorResponse(\n 'draft_behavior_not_available',\n 'Draft mode requires a draft-enabled dedicated docs collection.',\n 400,\n )\n }\n\n if (deleteBehavior === 'draft' && !options.docsEnableDrafts) {\n return errorResponse(\n 'draft_behavior_not_available',\n 'Draft delete behavior requires a draft-enabled dedicated docs collection.',\n 400,\n )\n }\n\n if (deleteBehavior === 'delete' && options.allowHardDelete !== true) {\n return errorResponse(\n 'hard_delete_disabled',\n 'Hard delete is disabled by server configuration.',\n 403,\n )\n }\n\n return undefined\n}\n\nconst getRouteCollisionIssues = async ({\n docsSet,\n manifest,\n options,\n payload,\n routeBase,\n}: {\n docsSet?: ResolvedDocsSet\n manifest: ValidatedDocsManifest\n options: CreateSyncEndpointOptions\n payload: RouteCollisionPayloadOperations\n routeBase: string\n}) => {\n const desiredRoutes = manifest.files.map((file) => file.route)\n const duplicateDesiredRouteCollisions =\n findDuplicateDesiredRouteCollisions(desiredRoutes)\n const existingDocsRouteCollisions = options.docsEnabled\n ? await findExistingDocsRouteCollisions({\n collectionSlug: options.docsCollectionSlug,\n docsSetId: docsSet?.id,\n payload,\n routes: desiredRoutes,\n sourceId: manifest.source.id,\n })\n : []\n const pageRouteCollisions =\n options.routing?.pages?.enabled === true\n ? await findConfiguredPagesRouteCollisions({\n allowBridgePages: options.routing.pages.allowBridgePages,\n bridgeField: options.routing.pages.bridgeField,\n collectionSlug: options.routing.pages.collection,\n docsSetRouteBase: routeBase,\n payload,\n routeField: options.routing.pages.routeField,\n })\n : []\n\n return [\n ...duplicateDesiredRouteCollisions,\n ...existingDocsRouteCollisions,\n ...pageRouteCollisions,\n ]\n}\n\ntype AuthenticatedSyncRequest = {\n actor?: string\n bodyHash: string\n branch?: string\n commit?: string\n expiresAt: Date\n keyId: string\n nonce: string\n repository?: string\n}\n\nconst getRequiredHeader = (\n headers: Headers,\n name: string,\n): string | undefined => {\n const value = headers.get(name)\n\n return value && value.trim() !== '' ? value.trim() : undefined\n}\n\nconst getBearerToken = (headers: Headers): string | undefined => {\n const authorization = getRequiredHeader(headers, 'authorization')\n\n if (!authorization) {\n return undefined\n }\n\n const [scheme, token] = authorization.split(/\\s+/, 2)\n\n if (scheme?.toLowerCase() !== 'bearer' || !token) {\n return ''\n }\n\n return token\n}\n\nconst assertReplayProtectionAvailable = (\n options: CreateSyncEndpointOptions,\n): Response | undefined =>\n options.noncesEnabled\n ? undefined\n : errorResponse(\n 'replay_protection_unavailable',\n 'Sync endpoint requires nonce replay protection.',\n 500,\n )\n\nconst authenticateEd25519Request = async ({\n now,\n options,\n rawBody,\n req,\n}: {\n now: Date\n options: CreateSyncEndpointOptions\n rawBody: string\n req: PayloadRequest\n}): Promise<\n | {\n identity: AuthenticatedSyncRequest\n response?: never\n }\n | {\n identity?: never\n response: Response\n }\n> => {\n if (!options.auth || options.auth.mode !== 'ed25519') {\n return {\n response: errorResponse(\n 'auth_disabled',\n 'Signed sync authentication is not configured for this endpoint.',\n 401,\n ),\n }\n }\n\n const headersResult = extractSyncRequestHeaders(req.headers)\n\n if (!headersResult.ok) {\n return {\n response: errorResponse(\n 'missing_header',\n `Missing required sync header: ${headersResult.header}.`,\n 401,\n ),\n }\n }\n\n const keyConfig = options.auth.keys.find(\n (key) => key.id === headersResult.headers.keyId,\n )\n\n if (!keyConfig) {\n return {\n response: errorResponse('unknown_key', 'Unknown sync request key id.', 401),\n }\n }\n\n const bodyHash = verifyBodySha256({\n body: rawBody,\n expectedHash: headersResult.headers.bodySha256,\n })\n\n if (!bodyHash.ok) {\n return {\n response: errorResponse(\n 'body_hash_mismatch',\n 'Sync request body hash does not match the signed header.',\n 401,\n ),\n }\n }\n\n const timestampValidation = validateTimestampSkew({\n maxSkewSeconds:\n options.auth.maxSkewSeconds ??\n options.maxSkewSeconds ??\n DEFAULT_MAX_SKEW_SECONDS,\n now,\n timestamp: headersResult.headers.timestamp,\n })\n\n if (!timestampValidation.ok) {\n return {\n response: errorResponse(\n 'invalid_timestamp',\n timestampValidation.message,\n 401,\n ),\n }\n }\n\n const replayUnavailable = assertReplayProtectionAvailable(options)\n\n if (replayUnavailable) {\n return {\n response: replayUnavailable,\n }\n }\n\n const nonceAvailable = await assertNonceNotReplayed({\n collectionSlug: options.noncesCollectionSlug,\n keyId: headersResult.headers.keyId,\n nonce: headersResult.headers.nonce,\n now,\n payload: req.payload as unknown as NoncePayloadOperations,\n })\n\n if (!nonceAvailable) {\n return {\n response: errorResponse(\n 'nonce_replay',\n 'Sync request nonce has already been used.',\n 409,\n ),\n }\n }\n\n const canonicalPath = getCanonicalPathFromRequestUrl({\n endpointPath: options.endpointPath,\n url: req.url,\n })\n const canonicalString = buildCanonicalSigningString({\n bodySha256: bodyHash.computedHash,\n method: 'POST',\n nonce: headersResult.headers.nonce,\n path: canonicalPath,\n timestamp: headersResult.headers.timestamp,\n })\n\n if (\n !verifyEd25519Signature({\n canonicalString,\n publicKey: keyConfig.publicKey,\n signature: headersResult.headers.signature,\n })\n ) {\n return {\n response: errorResponse(\n 'invalid_signature',\n 'Invalid sync request signature.',\n 401,\n ),\n }\n }\n\n const nonceTtlSeconds =\n options.auth.nonceTtlSeconds ??\n options.nonceTtlSeconds ??\n DEFAULT_NONCE_TTL_SECONDS\n\n return {\n identity: {\n bodyHash: bodyHash.computedHash,\n expiresAt: new Date(now.getTime() + nonceTtlSeconds * 1000),\n keyId: headersResult.headers.keyId,\n nonce: headersResult.headers.nonce,\n },\n }\n}\n\nconst authenticateGitHubOidcRequest = async ({\n now,\n options,\n rawBody,\n req,\n}: {\n now: Date\n options: CreateSyncEndpointOptions\n rawBody: string\n req: PayloadRequest\n}): Promise<\n | {\n identity: AuthenticatedSyncRequest\n response?: never\n }\n | {\n identity?: never\n response: Response\n }\n> => {\n if (!options.auth || options.auth.mode !== 'github-oidc') {\n return {\n response: errorResponse(\n 'auth_disabled',\n 'GitHub OIDC sync authentication is not configured for this endpoint.',\n 401,\n ),\n }\n }\n\n const token = getBearerToken(req.headers)\n\n if (token === undefined) {\n return {\n response: errorResponse(\n 'missing_header',\n 'Missing required sync header: Authorization.',\n 401,\n ),\n }\n }\n\n if (token === '') {\n return {\n response: errorResponse(\n 'oidc_invalid_token',\n 'Authorization must be a Bearer GitHub OIDC token.',\n 401,\n ),\n }\n }\n\n const expectedHash = getRequiredHeader(\n req.headers,\n 'x-vl-md-docs-body-sha256',\n )\n\n if (!expectedHash) {\n return {\n response: errorResponse(\n 'missing_header',\n 'Missing required sync header: X-VL-MD-DOCS-Body-SHA256.',\n 401,\n ),\n }\n }\n\n const bodyHash = verifyBodySha256({\n body: rawBody,\n expectedHash,\n })\n\n if (!bodyHash.ok) {\n return {\n response: errorResponse(\n 'body_hash_mismatch',\n 'Sync request body hash does not match the OIDC header.',\n 401,\n ),\n }\n }\n\n const verified = await verifyGitHubOidcToken({\n config: options.auth,\n fetchJson: options.oidcFetchJson,\n now,\n token,\n })\n\n if (!verified.ok) {\n return {\n response: errorResponse(\n verified.code,\n verified.message,\n verified.code === 'oidc_jwks_unavailable' ? 503 : 401,\n ),\n }\n }\n\n const replayUnavailable = assertReplayProtectionAvailable(options)\n\n if (replayUnavailable) {\n return {\n response: replayUnavailable,\n }\n }\n\n const nonceAvailable = await assertNonceNotReplayed({\n collectionSlug: options.noncesCollectionSlug,\n keyId: verified.token.keyId,\n nonce: verified.token.claims.jti,\n now,\n payload: req.payload as unknown as NoncePayloadOperations,\n })\n\n if (!nonceAvailable) {\n return {\n response: errorResponse(\n 'oidc_replay',\n 'GitHub OIDC token jti has already been used.',\n 409,\n ),\n }\n }\n\n return {\n identity: {\n actor: verified.token.claims.actor,\n bodyHash: bodyHash.computedHash,\n branch: verified.token.claims.ref,\n commit: verified.token.claims.sha,\n expiresAt: verified.token.expiresAt,\n keyId: verified.token.keyId,\n nonce: verified.token.claims.jti,\n repository: verified.token.claims.repository,\n },\n }\n}\n\nconst authenticateSyncRequest = async ({\n now,\n options,\n rawBody,\n req,\n}: {\n now: Date\n options: CreateSyncEndpointOptions\n rawBody: string\n req: PayloadRequest\n}): Promise<\n | {\n identity: AuthenticatedSyncRequest\n response?: never\n }\n | {\n identity?: never\n response: Response\n }\n> => {\n if (!options.auth || options.auth.mode === 'disabled') {\n return {\n response: errorResponse(\n 'auth_disabled',\n 'Sync authentication is not configured for this endpoint.',\n 401,\n ),\n }\n }\n\n if (options.auth.mode === 'github-oidc') {\n return authenticateGitHubOidcRequest({\n now,\n options,\n rawBody,\n req,\n })\n }\n\n return authenticateEd25519Request({\n now,\n options,\n rawBody,\n req,\n })\n}\n\nconst createSyncEndpointHandler =\n (options: CreateSyncEndpointOptions) =>\n async (req: PayloadRequest): Promise<Response> => {\n const startedAt = options.getNow?.() ?? new Date()\n\n if (req.method && req.method.toUpperCase() !== 'POST') {\n return errorResponse('invalid_method', 'Sync endpoint only accepts POST.', 405)\n }\n\n if (typeof req.text !== 'function') {\n return errorResponse(\n 'invalid_body',\n 'Sync endpoint requires access to the request body text.',\n 400,\n )\n }\n\n const rawBody = await req.text()\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES\n\n if (Buffer.byteLength(rawBody, 'utf8') > maxBodyBytes) {\n return errorResponse('invalid_body', 'Sync request body is too large.', 413)\n }\n\n const authentication = await authenticateSyncRequest({\n now: startedAt,\n options,\n rawBody,\n req,\n })\n\n if (authentication.response) {\n return authentication.response\n }\n\n const manifest = parseManifestBody(rawBody)\n\n if (!manifest) {\n return errorResponse('invalid_body', 'Sync request body must be a JSON manifest.', 400)\n }\n\n const sourceResolution = await resolveSyncSource({\n manifest,\n options,\n payload: req.payload as unknown as DocsSetPayloadOperations,\n })\n\n if (sourceResolution.response) {\n return sourceResolution.response\n }\n\n const validation = validateDocsManifest(manifest, {\n allowedSourceIds: sourceResolution.source.allowedSourceIds,\n maxTotalBytes: maxBodyBytes,\n routeBase: sourceResolution.source.routeBase,\n })\n\n if (!validation.ok) {\n return jsonResponse(\n {\n error: {\n code: 'invalid_manifest',\n message: 'Sync manifest is invalid.',\n },\n ok: false,\n },\n 400,\n )\n }\n\n const effectiveDeleteBehavior = options.deleteBehavior ?? 'archive'\n const effectivePublishMode: DocsPublishMode = validation.data.publish\n ? 'published'\n : getDefaultPublishMode(options)\n const lifecyclePolicyError = getLifecyclePolicyError({\n deleteBehavior: effectiveDeleteBehavior,\n manifest: validation.data,\n options,\n publishMode: effectivePublishMode,\n })\n\n if (lifecyclePolicyError) {\n return lifecyclePolicyError\n }\n\n const routeCollisions = await getRouteCollisionIssues({\n docsSet: sourceResolution.source.docsSet,\n manifest: validation.data,\n options,\n payload: req.payload as unknown as RouteCollisionPayloadOperations,\n routeBase: sourceResolution.source.routeBase,\n })\n\n if (routeCollisions.length > 0) {\n return errorResponse(\n 'route_collision',\n 'One or more docs routes collide with an existing route reservation.',\n 409,\n {\n routeCollisions,\n },\n )\n }\n\n const isSyncMode = validation.data.mode === 'sync'\n\n if (isSyncMode && options.allowWrites !== true) {\n return errorResponse(\n 'sync_writes_disabled',\n 'Sync writes are disabled by server configuration.',\n 403,\n )\n }\n\n if (isSyncMode && options.requireDryRunBeforeApply === true) {\n return errorResponse(\n 'dry_run_required_not_implemented',\n 'Required dry-run proof before apply is not implemented yet.',\n 400,\n )\n }\n\n if (\n isSyncMode &&\n !assertApplyDeleteBehaviorSupported(effectiveDeleteBehavior, {\n allowHardDelete: options.allowHardDelete,\n docsEnableDrafts: options.docsEnableDrafts,\n })\n ) {\n return errorResponse(\n 'delete_behavior_not_implemented',\n 'Configured delete behavior cannot be applied.',\n 400,\n )\n }\n\n if (isSyncMode && !options.syncRunsEnabled) {\n return errorResponse(\n 'audit_unavailable',\n 'Applied sync requires the sync-run audit collection.',\n 500,\n )\n }\n\n const existingPayloadDocs = options.docsEnabled\n ? await findExistingPayloadDocsRecords({\n collectionSlug: options.docsCollectionSlug,\n docsSetId: sourceResolution.source.docsSet?.id,\n markdownFieldName: options.markdownFieldName,\n payload: req.payload as unknown as ExistingDocsPayloadOperations,\n sourceId: validation.data.source.id,\n })\n : []\n const existingDocs = existingPayloadDocs.map(toExistingDocsRecord)\n const plan = planDocsSync({\n deleteBehavior: effectiveDeleteBehavior,\n desired: validation.data,\n existing: existingDocs,\n })\n const summary = summarizePlan(plan)\n const warnings = [...validation.warnings, ...plan.warnings]\n if (isSyncMode) {\n const existingBySourcePath = new Map(\n existingPayloadDocs.map((record) => [record.sourcePath, record]),\n )\n const conflicts = findDocsSyncConflicts({\n existingBySourcePath,\n plannedChanges: getPlannedConflictChanges({\n existing: existingPayloadDocs,\n plan,\n }),\n })\n\n if (conflicts.length > 0) {\n return errorResponse(\n 'manual_edit_conflict',\n 'One or more docs were modified outside the docs sync workflow.',\n 409,\n {\n conflicts,\n },\n )\n }\n }\n\n await storeAcceptedNonce({\n bodyHash: authentication.identity.bodyHash,\n collectionSlug: options.noncesCollectionSlug,\n expiresAt: authentication.identity.expiresAt,\n keyId: authentication.identity.keyId,\n nonce: authentication.identity.nonce,\n payload: req.payload as unknown as NoncePayloadOperations,\n sourceId: validation.data.source.id,\n usedAt: startedAt,\n })\n\n let syncRunId: number | string | undefined\n\n if (options.syncRunsEnabled) {\n const syncRun = await createSyncRunAudit({\n actor: authentication.identity.actor,\n bodyHash: authentication.identity.bodyHash,\n branch: authentication.identity.branch ?? validation.data.source.branch,\n collectionSlug: options.syncRunsCollectionSlug,\n commit: authentication.identity.commit ?? validation.data.source.commit,\n completedAt: isSyncMode ? startedAt : options.getNow?.() ?? new Date(),\n deleteBehavior: effectiveDeleteBehavior,\n effectivePublishMode,\n errors: [],\n fileCount: validation.data.files.length,\n keyId: authentication.identity.keyId,\n mode: isSyncMode ? 'sync' : 'dry-run',\n payload: req.payload as unknown as SyncRunsPayloadOperations,\n publishRequested: validation.data.publish,\n repository:\n authentication.identity.repository ?? validation.data.source.repository,\n sourceId: validation.data.source.id,\n startedAt,\n status: isSyncMode ? 'pending' : 'success',\n summary,\n totalBytes: getTotalManifestBytes(validation.data),\n warnings,\n })\n\n syncRunId = getRecordId(syncRun)\n }\n\n if (isSyncMode) {\n if (!syncRunId) {\n return errorResponse(\n 'audit_unavailable',\n 'Applied sync could not create a sync-run audit record.',\n 500,\n )\n }\n\n try {\n const applyResult = await applyDocsSync({\n collectionSlug: options.docsCollectionSlug,\n deleteBehavior: effectiveDeleteBehavior,\n docsEnableDrafts: options.docsEnableDrafts,\n docsSetId: sourceResolution.source.docsSet?.id,\n existing: existingPayloadDocs,\n manifest: validation.data,\n markdownFieldName: options.markdownFieldName,\n now: options.getNow?.() ?? new Date(),\n payload: req.payload as unknown as ApplyDocsSyncPayloadOperations,\n plan,\n publishMode: effectivePublishMode,\n syncRunId,\n })\n\n if (!applyResult.ok) {\n return errorResponse(\n 'manual_edit_conflict',\n 'One or more docs were modified outside the docs sync workflow.',\n 409,\n {\n conflicts: applyResult.conflicts,\n },\n )\n }\n\n await updateSyncRunAudit({\n collectionSlug: options.syncRunsCollectionSlug,\n completedAt: options.getNow?.() ?? new Date(),\n payload: req.payload as unknown as SyncRunsPayloadOperations,\n status: 'success',\n summary,\n syncRunId,\n warnings,\n })\n\n if (sourceResolution.source.docsSet) {\n await updateDocsSetAfterSync({\n aiExport: validation.data.aiExport,\n collectionSlug: options.docsSetsCollectionSlug,\n docsCount: validation.data.files.length,\n docsSetId: sourceResolution.source.docsSet.id,\n now: options.getNow?.() ?? new Date(),\n payload: req.payload as unknown as DocsSetPayloadOperations,\n syncRunId,\n })\n }\n } catch (error) {\n await updateSyncRunAudit({\n collectionSlug: options.syncRunsCollectionSlug,\n completedAt: options.getNow?.() ?? new Date(),\n errors: [\n {\n code: 'invalid_manifest',\n message: error instanceof Error ? error.message : 'Sync apply failed.',\n },\n ],\n payload: req.payload as unknown as SyncRunsPayloadOperations,\n status: 'failed',\n summary,\n syncRunId,\n warnings,\n })\n\n return errorResponse('sync_apply_failed', 'Sync apply failed.', 500)\n }\n }\n\n return jsonResponse({\n changes: serializeChanges(plan),\n deleteBehavior: effectiveDeleteBehavior,\n dryRun: !isSyncMode,\n effectivePublishMode,\n ok: true,\n publishRequested: validation.data.publish,\n summary,\n syncRunId: syncRunId === undefined ? undefined : String(syncRunId),\n warnings,\n })\n }\n\nexport const createSyncEndpoint = (options: CreateSyncEndpointOptions): Endpoint => ({\n handler: createSyncEndpointHandler(options),\n method: 'post',\n path: options.endpointPath,\n})\n"],"names":["DEFAULT_DOCS_ROUTE_BASE","DEFAULT_MAX_BODY_BYTES","DEFAULT_MAX_SKEW_SECONDS","DEFAULT_NONCE_TTL_SECONDS","applyDocsSync","assertApplyDeleteBehaviorSupported","createSyncRunAudit","findConfiguredPagesRouteCollisions","findDocsSetBySourceId","findDocsSyncConflicts","findDuplicateDesiredRouteCollisions","findExistingDocsRouteCollisions","findExistingPayloadDocsRecords","getRecordId","toExistingDocsRecord","updateDocsSetAfterSync","updateSyncRunAudit","assertNonceNotReplayed","buildCanonicalSigningString","extractSyncRequestHeaders","getCanonicalPathFromRequestUrl","storeAcceptedNonce","validateTimestampSkew","verifyBodySha256","verifyEd25519Signature","verifyGitHubOidcToken","planDocsSync","validateDocsManifest","jsonResponse","body","status","Response","json","errorResponse","code","message","extras","error","ok","isRecord","value","Array","isArray","parseManifestBody","rawBody","parsed","JSON","parse","undefined","findSourceConfig","sourceId","sources","find","source","id","getAllowedSourceIds","length","map","resolveSyncSource","manifest","options","payload","allowedSourceIds","routeBase","docsSet","docsSetsEnabled","collectionSlug","docsSetsCollectionSlug","sourceRoot","root","response","sourceConfig","summarizePlan","plan","archive","create","delete","draft","unchanged","update","warnings","serializeChange","change","current","archived","route","sourceHash","title","desired","sha256","reason","sourcePath","serializeChanges","getTotalManifestBytes","files","reduce","total","file","Buffer","byteLength","content","getPlannedConflictChanges","existing","existingBySourcePath","Map","record","archivedUnchanged","filter","get","getDefaultPublishMode","defaultPublishMode","docsEnableDrafts","getLifecyclePolicyError","deleteBehavior","publishMode","publish","allowPublish","allowHardDelete","getRouteCollisionIssues","desiredRoutes","duplicateDesiredRouteCollisions","existingDocsRouteCollisions","docsEnabled","docsCollectionSlug","docsSetId","routes","pageRouteCollisions","routing","pages","enabled","allowBridgePages","bridgeField","collection","docsSetRouteBase","routeField","getRequiredHeader","headers","name","trim","getBearerToken","authorization","scheme","token","split","toLowerCase","assertReplayProtectionAvailable","noncesEnabled","authenticateEd25519Request","now","req","auth","mode","headersResult","header","keyConfig","keys","key","keyId","bodyHash","expectedHash","bodySha256","timestampValidation","maxSkewSeconds","timestamp","replayUnavailable","nonceAvailable","noncesCollectionSlug","nonce","canonicalPath","endpointPath","url","canonicalString","computedHash","method","path","publicKey","signature","nonceTtlSeconds","identity","expiresAt","Date","getTime","authenticateGitHubOidcRequest","verified","config","fetchJson","oidcFetchJson","claims","jti","actor","branch","ref","commit","sha","repository","authenticateSyncRequest","createSyncEndpointHandler","startedAt","getNow","toUpperCase","text","maxBodyBytes","authentication","sourceResolution","validation","maxTotalBytes","effectiveDeleteBehavior","effectivePublishMode","data","lifecyclePolicyError","routeCollisions","isSyncMode","allowWrites","requireDryRunBeforeApply","syncRunsEnabled","existingPayloadDocs","markdownFieldName","existingDocs","summary","conflicts","plannedChanges","usedAt","syncRunId","syncRun","syncRunsCollectionSlug","completedAt","errors","fileCount","publishRequested","totalBytes","applyResult","aiExport","docsCount","Error","changes","dryRun","String","createSyncEndpoint","handler"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoints/sync.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport type {\n ApplyDocsSyncPayloadOperations,\n DocsPublishMode,\n DocsSetPayloadOperations,\n ExistingDocsPayloadOperations,\n ExistingPayloadDocsRecord,\n ResolvedDocsSet,\n RouteCollisionPayloadOperations,\n SyncRunsPayloadOperations,\n} from '../payload/index.js'\nimport type {\n FetchJson,\n NoncePayloadOperations,\n} from '../security/index.js'\nimport type {\n DocsDeleteBehavior,\n DocsManifest,\n DocsValidationIssue,\n PlannedDocChange,\n ValidatedDocsManifest,\n} from '../sync/index.js'\nimport type {\n PayloadMarkdownDocsAuthConfig,\n PayloadMarkdownDocsDocsSetAuthConfig,\n PayloadMarkdownDocsEd25519AuthConfig,\n PayloadMarkdownDocsGitHubOidcAuthConfig,\n} from '../types.js'\n\nimport {\n DEFAULT_DOCS_ROUTE_BASE,\n DEFAULT_MAX_BODY_BYTES,\n DEFAULT_MAX_SKEW_SECONDS,\n DEFAULT_NONCE_TTL_SECONDS,\n} from '../constants.js'\nimport {\n applyDocsSync,\n assertApplyDeleteBehaviorSupported,\n createSyncRunAudit,\n findConfiguredPagesRouteCollisions,\n findDocsSetBySourceId,\n findDocsSyncConflicts,\n findDuplicateDesiredRouteCollisions,\n findExistingDocsRouteCollisions,\n findExistingPayloadDocsRecords,\n getRecordId,\n toExistingDocsRecord,\n updateDocsSetAfterSync,\n updateSyncRunAudit,\n} from '../payload/index.js'\nimport {\n assertNonceNotReplayed,\n buildCanonicalSigningString,\n extractSyncRequestHeaders,\n getCanonicalPathFromRequestUrl,\n storeAcceptedNonce,\n validateTimestampSkew,\n verifyBodySha256,\n verifyEd25519Signature,\n verifyGitHubOidcToken,\n} from '../security/index.js'\nimport {\n planDocsSync,\n validateDocsManifest,\n} from '../sync/index.js'\n\nexport type DocsSyncEndpointErrorCode =\n | 'audit_unavailable'\n | 'auth_disabled'\n | 'body_hash_mismatch'\n | 'delete_behavior_not_implemented'\n | 'draft_behavior_not_available'\n | 'dry_run_required_not_implemented'\n | 'hard_delete_disabled'\n | 'invalid_body'\n | 'invalid_manifest'\n | 'invalid_method'\n | 'invalid_signature'\n | 'invalid_timestamp'\n | 'manual_edit_conflict'\n | 'missing_header'\n | 'nonce_replay'\n | 'oidc_environment_not_allowed'\n | 'oidc_expired'\n | 'oidc_invalid_audience'\n | 'oidc_invalid_issuer'\n | 'oidc_invalid_token'\n | 'oidc_jwks_unavailable'\n | 'oidc_missing_claim'\n | 'oidc_missing_jti'\n | 'oidc_not_yet_valid'\n | 'oidc_owner_not_allowed'\n | 'oidc_pull_request_not_allowed'\n | 'oidc_ref_not_allowed'\n | 'oidc_replay'\n | 'oidc_repository_not_allowed'\n | 'oidc_workflow_not_allowed'\n | 'publish_disabled'\n | 'publish_not_available'\n | 'replay_protection_unavailable'\n | 'route_collision'\n | 'source_not_allowed'\n | 'sync_apply_failed'\n | 'sync_mode_not_implemented'\n | 'sync_writes_disabled'\n | 'unknown_key'\n\nexport type CreateSyncEndpointOptions = {\n allowHardDelete?: boolean\n allowPublish?: boolean\n allowWrites?: boolean\n auth?: PayloadMarkdownDocsAuthConfig\n defaultPublishMode?: DocsPublishMode\n deleteBehavior?: DocsDeleteBehavior\n docsCollectionSlug: string\n docsEnabled: boolean\n docsEnableDrafts: boolean\n docsSetsCollectionSlug: string\n docsSetsEnabled: boolean\n endpointPath: string\n getNow?: () => Date\n markdownFieldName: string\n maxBodyBytes?: number\n maxSkewSeconds?: number\n noncesCollectionSlug: string\n noncesEnabled: boolean\n nonceTtlSeconds?: number\n oidcFetchJson?: FetchJson\n requireDryRunBeforeApply?: boolean\n routeBase?: string\n routing?: {\n pages?: {\n allowBridgePages: boolean\n bridgeField: string\n collection: string\n enabled: boolean\n routeField: string\n }\n }\n sources?: {\n id: string\n root?: string\n routeBase: string\n }[]\n syncRunsCollectionSlug: string\n syncRunsEnabled: boolean\n}\n\ntype SyncErrorResponse = {\n conflicts?: {\n reason: string\n route?: string\n sourcePath: string\n }[]\n error: {\n code: DocsSyncEndpointErrorCode\n message: string\n }\n ok: false\n routeCollisions?: {\n reason: string\n route: string\n }[]\n}\n\ntype SerializedChange = {\n current?: {\n archived?: boolean\n route: string\n sourceHash?: string\n title?: string\n }\n desired?: {\n route: string\n sha256: string\n title: string\n }\n reason: string\n sourcePath: string\n}\n\ntype SyncSuccessResponse = {\n changes: {\n archive: SerializedChange[]\n create: SerializedChange[]\n delete: SerializedChange[]\n draft: SerializedChange[]\n unchanged: SerializedChange[]\n update: SerializedChange[]\n }\n deleteBehavior: DocsDeleteBehavior\n dryRun: boolean\n effectivePublishMode: DocsPublishMode\n ok: true\n publishRequested: boolean\n summary: {\n archive: number\n create: number\n delete: number\n draft: number\n unchanged: number\n update: number\n warnings: number\n }\n syncRunId?: string\n warnings: DocsValidationIssue[]\n}\n\nconst jsonResponse = (\n body: SyncErrorResponse | SyncSuccessResponse,\n status = 200,\n): Response =>\n Response.json(body, {\n status,\n })\n\nconst errorResponse = (\n code: DocsSyncEndpointErrorCode,\n message: string,\n status = 400,\n extras: Omit<SyncErrorResponse, 'error' | 'ok'> = {},\n): Response =>\n jsonResponse(\n {\n ...extras,\n error: {\n code,\n message,\n },\n ok: false,\n },\n status,\n )\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst parseManifestBody = (rawBody: string): DocsManifest | undefined => {\n try {\n const parsed = JSON.parse(rawBody) as unknown\n\n return isRecord(parsed) ? (parsed as DocsManifest) : undefined\n } catch {\n return undefined\n }\n}\n\nconst findSourceConfig = (\n sourceId: string,\n sources: CreateSyncEndpointOptions['sources'],\n) => sources?.find((source) => source.id === sourceId)\n\nconst getAllowedSourceIds = (\n sources: CreateSyncEndpointOptions['sources'],\n): string[] | undefined => {\n if (!sources || sources.length === 0) {\n return undefined\n }\n\n return sources.map((source) => source.id)\n}\n\ntype ResolvedSyncSource = {\n allowedSourceIds?: string[]\n auth?: PayloadMarkdownDocsDocsSetAuthConfig\n docsSet?: ResolvedDocsSet\n routeBase: string\n sourceRoot?: string\n}\n\nconst resolveSyncSource = async ({\n manifest,\n options,\n payload,\n}: {\n manifest: DocsManifest\n options: CreateSyncEndpointOptions\n payload: DocsSetPayloadOperations\n}): Promise<\n | {\n response: Response\n source?: never\n }\n | {\n response?: never\n source: ResolvedSyncSource\n }\n> => {\n const sourceId = manifest.source?.id\n\n if (!sourceId) {\n return {\n source: {\n allowedSourceIds: getAllowedSourceIds(options.sources),\n routeBase: options.routeBase ?? DEFAULT_DOCS_ROUTE_BASE,\n },\n }\n }\n\n const docsSet =\n options.docsSetsEnabled\n ? await findDocsSetBySourceId({\n collectionSlug: options.docsSetsCollectionSlug,\n payload,\n sourceId,\n })\n : undefined\n\n if (docsSet) {\n if (\n docsSet.sourceRoot &&\n manifest.source.root &&\n docsSet.sourceRoot !== manifest.source.root\n ) {\n return {\n response: errorResponse(\n 'source_not_allowed',\n `Manifest source.root \"${manifest.source.root}\" is not allowed for docs set source \"${sourceId}\".`,\n 400,\n ),\n }\n }\n\n return {\n source: {\n allowedSourceIds: [sourceId],\n auth: docsSet.auth,\n docsSet,\n routeBase: docsSet.routeBase,\n sourceRoot: docsSet.sourceRoot,\n },\n }\n }\n\n const sourceConfig = findSourceConfig(sourceId, options.sources)\n\n if (\n (options.docsSetsEnabled || (options.sources && options.sources.length > 0)) &&\n !sourceConfig\n ) {\n return {\n response: errorResponse(\n 'source_not_allowed',\n options.docsSetsEnabled\n ? `No docs set exists for manifest source.id \"${sourceId}\". Create a docs set in Payload Admin before syncing this source.`\n : `Manifest source.id \"${sourceId}\" is not configured for this endpoint.`,\n 400,\n ),\n }\n }\n\n if (\n sourceConfig?.root &&\n manifest.source.root &&\n sourceConfig.root !== manifest.source.root\n ) {\n return {\n response: errorResponse(\n 'source_not_allowed',\n `Manifest source.root \"${manifest.source.root}\" is not allowed for source \"${sourceId}\".`,\n 400,\n ),\n }\n }\n\n return {\n source: {\n allowedSourceIds: getAllowedSourceIds(options.sources),\n routeBase: sourceConfig?.routeBase ?? options.routeBase ?? DEFAULT_DOCS_ROUTE_BASE,\n sourceRoot: sourceConfig?.root,\n },\n }\n}\n\nconst summarizePlan = (plan: ReturnType<typeof planDocsSync>) => ({\n archive: plan.archive.length,\n create: plan.create.length,\n delete: plan.delete.length,\n draft: plan.draft.length,\n unchanged: plan.unchanged.length,\n update: plan.update.length,\n warnings: plan.warnings.length,\n})\n\nconst serializeChange = (change: PlannedDocChange): SerializedChange => ({\n current: change.current\n ? {\n archived: change.current.archived,\n route: change.current.route,\n sourceHash: change.current.sourceHash,\n title: change.current.title,\n }\n : undefined,\n desired: change.desired\n ? {\n route: change.desired.route,\n sha256: change.desired.sha256,\n title: change.desired.title,\n }\n : undefined,\n reason: change.reason,\n sourcePath: change.sourcePath,\n})\n\nconst serializeChanges = (plan: ReturnType<typeof planDocsSync>) => ({\n archive: plan.archive.map(serializeChange),\n create: plan.create.map(serializeChange),\n delete: plan.delete.map(serializeChange),\n draft: plan.draft.map(serializeChange),\n unchanged: plan.unchanged.map(serializeChange),\n update: plan.update.map(serializeChange),\n})\n\nconst getTotalManifestBytes = (manifest: ValidatedDocsManifest): number =>\n manifest.files.reduce(\n (total, file) => total + Buffer.byteLength(file.content, 'utf8'),\n 0,\n )\n\nconst getPlannedConflictChanges = ({\n existing,\n plan,\n}: {\n existing: ExistingPayloadDocsRecord[]\n plan: ReturnType<typeof planDocsSync>\n}): PlannedDocChange[] => {\n const existingBySourcePath = new Map(\n existing.map((record) => [record.sourcePath, record]),\n )\n const archivedUnchanged = plan.unchanged.filter((change) => {\n const current = existingBySourcePath.get(change.sourcePath)\n\n return current?.archived === true\n })\n\n return [\n ...plan.update,\n ...plan.archive,\n ...plan.draft,\n ...plan.delete,\n ...archivedUnchanged,\n ]\n}\n\nconst getDefaultPublishMode = (\n options: CreateSyncEndpointOptions,\n): DocsPublishMode =>\n options.defaultPublishMode ?? (options.docsEnableDrafts ? 'draft' : 'preserve')\n\nconst getLifecyclePolicyError = ({\n deleteBehavior,\n manifest,\n options,\n publishMode,\n}: {\n deleteBehavior: DocsDeleteBehavior\n manifest: ValidatedDocsManifest\n options: CreateSyncEndpointOptions\n publishMode: DocsPublishMode\n}): Response | undefined => {\n if (manifest.publish && options.allowPublish !== true) {\n return errorResponse(\n 'publish_disabled',\n 'Publishing is disabled by server configuration.',\n 403,\n )\n }\n\n if (\n (manifest.publish || publishMode === 'published') &&\n !options.docsEnableDrafts\n ) {\n return errorResponse(\n 'publish_not_available',\n 'Publishing requires a draft-enabled dedicated docs collection.',\n 400,\n )\n }\n\n if (publishMode === 'published' && options.allowPublish !== true) {\n return errorResponse(\n 'publish_disabled',\n 'Publishing is disabled by server configuration.',\n 403,\n )\n }\n\n if (publishMode === 'draft' && !options.docsEnableDrafts) {\n return errorResponse(\n 'draft_behavior_not_available',\n 'Draft mode requires a draft-enabled dedicated docs collection.',\n 400,\n )\n }\n\n if (deleteBehavior === 'draft' && !options.docsEnableDrafts) {\n return errorResponse(\n 'draft_behavior_not_available',\n 'Draft delete behavior requires a draft-enabled dedicated docs collection.',\n 400,\n )\n }\n\n if (deleteBehavior === 'delete' && options.allowHardDelete !== true) {\n return errorResponse(\n 'hard_delete_disabled',\n 'Hard delete is disabled by server configuration.',\n 403,\n )\n }\n\n return undefined\n}\n\nconst getRouteCollisionIssues = async ({\n docsSet,\n manifest,\n options,\n payload,\n routeBase,\n}: {\n docsSet?: ResolvedDocsSet\n manifest: ValidatedDocsManifest\n options: CreateSyncEndpointOptions\n payload: RouteCollisionPayloadOperations\n routeBase: string\n}) => {\n const desiredRoutes = manifest.files.map((file) => file.route)\n const duplicateDesiredRouteCollisions =\n findDuplicateDesiredRouteCollisions(desiredRoutes)\n const existingDocsRouteCollisions = options.docsEnabled\n ? await findExistingDocsRouteCollisions({\n collectionSlug: options.docsCollectionSlug,\n docsSetId: docsSet?.id,\n payload,\n routes: desiredRoutes,\n sourceId: manifest.source.id,\n })\n : []\n const pageRouteCollisions =\n options.routing?.pages?.enabled === true\n ? await findConfiguredPagesRouteCollisions({\n allowBridgePages: options.routing.pages.allowBridgePages,\n bridgeField: options.routing.pages.bridgeField,\n collectionSlug: options.routing.pages.collection,\n docsSetRouteBase: routeBase,\n payload,\n routeField: options.routing.pages.routeField,\n })\n : []\n\n return [\n ...duplicateDesiredRouteCollisions,\n ...existingDocsRouteCollisions,\n ...pageRouteCollisions,\n ]\n}\n\ntype AuthenticatedSyncRequest = {\n actor?: string\n bodyHash: string\n branch?: string\n commit?: string\n expiresAt: Date\n keyId: string\n nonce: string\n repository?: string\n}\n\nconst getRequiredHeader = (\n headers: Headers,\n name: string,\n): string | undefined => {\n const value = headers.get(name)\n\n return value && value.trim() !== '' ? value.trim() : undefined\n}\n\nconst getBearerToken = (headers: Headers): string | undefined => {\n const authorization = getRequiredHeader(headers, 'authorization')\n\n if (!authorization) {\n return undefined\n }\n\n const [scheme, token] = authorization.split(/\\s+/, 2)\n\n if (scheme?.toLowerCase() !== 'bearer' || !token) {\n return ''\n }\n\n return token\n}\n\nconst hasEd25519AuthHeaders = (headers: Headers): boolean =>\n getRequiredHeader(headers, 'x-vl-md-docs-key-id') !== undefined ||\n getRequiredHeader(headers, 'x-vl-md-docs-signature') !== undefined ||\n getRequiredHeader(headers, 'x-vl-md-docs-timestamp') !== undefined ||\n getRequiredHeader(headers, 'x-vl-md-docs-nonce') !== undefined\n\nconst getGlobalEd25519AuthConfig = (\n auth: PayloadMarkdownDocsAuthConfig | undefined,\n): PayloadMarkdownDocsEd25519AuthConfig | undefined => {\n if (!auth || auth.mode === 'disabled' || auth.mode === 'github-oidc') {\n return undefined\n }\n\n if (auth.mode === 'ed25519') {\n return auth\n }\n\n return auth.ed25519\n ? {\n ...auth.ed25519,\n mode: 'ed25519',\n }\n : undefined\n}\n\nconst getEd25519AuthConfig = ({\n auth,\n sourceAuth,\n}: {\n auth: PayloadMarkdownDocsAuthConfig | undefined\n sourceAuth?: PayloadMarkdownDocsDocsSetAuthConfig\n}): PayloadMarkdownDocsEd25519AuthConfig | undefined => {\n const globalAuth = getGlobalEd25519AuthConfig(auth)\n\n if (sourceAuth?.ed25519?.keys.length) {\n return {\n ...globalAuth,\n ...sourceAuth.ed25519,\n keys: sourceAuth.ed25519.keys,\n mode: 'ed25519',\n }\n }\n\n return globalAuth\n}\n\nconst getGlobalGitHubOidcAuthConfig = (\n auth: PayloadMarkdownDocsAuthConfig | undefined,\n): PayloadMarkdownDocsGitHubOidcAuthConfig | undefined => {\n if (!auth || auth.mode === 'disabled' || auth.mode === 'ed25519') {\n return undefined\n }\n\n if (auth.mode === 'github-oidc') {\n return auth\n }\n\n return auth.githubOidc\n ? {\n ...auth.githubOidc,\n mode: 'github-oidc',\n }\n : undefined\n}\n\nconst getGitHubOidcAuthConfig = ({\n auth,\n sourceAuth,\n}: {\n auth: PayloadMarkdownDocsAuthConfig | undefined\n sourceAuth?: PayloadMarkdownDocsDocsSetAuthConfig\n}): PayloadMarkdownDocsGitHubOidcAuthConfig | undefined => {\n const globalAuth = getGlobalGitHubOidcAuthConfig(auth)\n const sourceOidc = sourceAuth?.githubOidc\n\n if (!sourceOidc) {\n return globalAuth\n }\n\n if (sourceOidc.enabled === false) {\n return globalAuth\n }\n\n const { enabled: _enabled, ...sourceOptions } = sourceOidc\n const audience = sourceOptions.audience ?? globalAuth?.audience\n\n if (!audience) {\n return globalAuth\n }\n\n return {\n ...globalAuth,\n ...sourceOptions,\n audience,\n mode: 'github-oidc',\n }\n}\n\nconst assertReplayProtectionAvailable = (\n options: CreateSyncEndpointOptions,\n): Response | undefined =>\n options.noncesEnabled\n ? undefined\n : errorResponse(\n 'replay_protection_unavailable',\n 'Sync endpoint requires nonce replay protection.',\n 500,\n )\n\nconst authenticateEd25519Request = async ({\n auth,\n now,\n options,\n rawBody,\n req,\n}: {\n auth: PayloadMarkdownDocsEd25519AuthConfig\n now: Date\n options: CreateSyncEndpointOptions\n rawBody: string\n req: PayloadRequest\n}): Promise<\n | {\n identity: AuthenticatedSyncRequest\n response?: never\n }\n | {\n identity?: never\n response: Response\n }\n> => {\n const headersResult = extractSyncRequestHeaders(req.headers)\n\n if (!headersResult.ok) {\n return {\n response: errorResponse(\n 'missing_header',\n `Missing required sync header: ${headersResult.header}.`,\n 401,\n ),\n }\n }\n\n const keyConfig = auth.keys.find(\n (key) => key.id === headersResult.headers.keyId,\n )\n\n if (!keyConfig) {\n return {\n response: errorResponse('unknown_key', 'Unknown sync request key id.', 401),\n }\n }\n\n const bodyHash = verifyBodySha256({\n body: rawBody,\n expectedHash: headersResult.headers.bodySha256,\n })\n\n if (!bodyHash.ok) {\n return {\n response: errorResponse(\n 'body_hash_mismatch',\n 'Sync request body hash does not match the signed header.',\n 401,\n ),\n }\n }\n\n const timestampValidation = validateTimestampSkew({\n maxSkewSeconds:\n auth.maxSkewSeconds ??\n options.maxSkewSeconds ??\n DEFAULT_MAX_SKEW_SECONDS,\n now,\n timestamp: headersResult.headers.timestamp,\n })\n\n if (!timestampValidation.ok) {\n return {\n response: errorResponse(\n 'invalid_timestamp',\n timestampValidation.message,\n 401,\n ),\n }\n }\n\n const replayUnavailable = assertReplayProtectionAvailable(options)\n\n if (replayUnavailable) {\n return {\n response: replayUnavailable,\n }\n }\n\n const nonceAvailable = await assertNonceNotReplayed({\n collectionSlug: options.noncesCollectionSlug,\n keyId: headersResult.headers.keyId,\n nonce: headersResult.headers.nonce,\n now,\n payload: req.payload as unknown as NoncePayloadOperations,\n })\n\n if (!nonceAvailable) {\n return {\n response: errorResponse(\n 'nonce_replay',\n 'Sync request nonce has already been used.',\n 409,\n ),\n }\n }\n\n const canonicalPath = getCanonicalPathFromRequestUrl({\n endpointPath: options.endpointPath,\n url: req.url,\n })\n const canonicalString = buildCanonicalSigningString({\n bodySha256: bodyHash.computedHash,\n method: 'POST',\n nonce: headersResult.headers.nonce,\n path: canonicalPath,\n timestamp: headersResult.headers.timestamp,\n })\n\n if (\n !verifyEd25519Signature({\n canonicalString,\n publicKey: keyConfig.publicKey,\n signature: headersResult.headers.signature,\n })\n ) {\n return {\n response: errorResponse(\n 'invalid_signature',\n 'Invalid sync request signature.',\n 401,\n ),\n }\n }\n\n const nonceTtlSeconds =\n auth.nonceTtlSeconds ??\n options.nonceTtlSeconds ??\n DEFAULT_NONCE_TTL_SECONDS\n\n return {\n identity: {\n bodyHash: bodyHash.computedHash,\n expiresAt: new Date(now.getTime() + nonceTtlSeconds * 1000),\n keyId: headersResult.headers.keyId,\n nonce: headersResult.headers.nonce,\n },\n }\n}\n\nconst authenticateGitHubOidcRequest = async ({\n auth,\n now,\n options,\n rawBody,\n req,\n}: {\n auth: PayloadMarkdownDocsGitHubOidcAuthConfig\n now: Date\n options: CreateSyncEndpointOptions\n rawBody: string\n req: PayloadRequest\n}): Promise<\n | {\n identity: AuthenticatedSyncRequest\n response?: never\n }\n | {\n identity?: never\n response: Response\n }\n> => {\n const token = getBearerToken(req.headers)\n\n if (token === undefined) {\n return {\n response: errorResponse(\n 'missing_header',\n 'Missing required sync header: Authorization.',\n 401,\n ),\n }\n }\n\n if (token === '') {\n return {\n response: errorResponse(\n 'oidc_invalid_token',\n 'Authorization must be a Bearer GitHub OIDC token.',\n 401,\n ),\n }\n }\n\n const expectedHash = getRequiredHeader(\n req.headers,\n 'x-vl-md-docs-body-sha256',\n )\n\n if (!expectedHash) {\n return {\n response: errorResponse(\n 'missing_header',\n 'Missing required sync header: X-VL-MD-DOCS-Body-SHA256.',\n 401,\n ),\n }\n }\n\n const bodyHash = verifyBodySha256({\n body: rawBody,\n expectedHash,\n })\n\n if (!bodyHash.ok) {\n return {\n response: errorResponse(\n 'body_hash_mismatch',\n 'Sync request body hash does not match the OIDC header.',\n 401,\n ),\n }\n }\n\n const verified = await verifyGitHubOidcToken({\n config: auth,\n fetchJson: options.oidcFetchJson,\n now,\n token,\n })\n\n if (!verified.ok) {\n return {\n response: errorResponse(\n verified.code,\n verified.message,\n verified.code === 'oidc_jwks_unavailable' ? 503 : 401,\n ),\n }\n }\n\n const replayUnavailable = assertReplayProtectionAvailable(options)\n\n if (replayUnavailable) {\n return {\n response: replayUnavailable,\n }\n }\n\n const nonceAvailable = await assertNonceNotReplayed({\n collectionSlug: options.noncesCollectionSlug,\n keyId: verified.token.keyId,\n nonce: verified.token.claims.jti,\n now,\n payload: req.payload as unknown as NoncePayloadOperations,\n })\n\n if (!nonceAvailable) {\n return {\n response: errorResponse(\n 'oidc_replay',\n 'GitHub OIDC token jti has already been used.',\n 409,\n ),\n }\n }\n\n return {\n identity: {\n actor: verified.token.claims.actor,\n bodyHash: bodyHash.computedHash,\n branch: verified.token.claims.ref,\n commit: verified.token.claims.sha,\n expiresAt: verified.token.expiresAt,\n keyId: verified.token.keyId,\n nonce: verified.token.claims.jti,\n repository: verified.token.claims.repository,\n },\n }\n}\n\nconst authenticateSyncRequest = async ({\n now,\n options,\n rawBody,\n req,\n sourceAuth,\n}: {\n now: Date\n options: CreateSyncEndpointOptions\n rawBody: string\n req: PayloadRequest\n sourceAuth?: PayloadMarkdownDocsDocsSetAuthConfig\n}): Promise<\n | {\n identity: AuthenticatedSyncRequest\n response?: never\n }\n | {\n identity?: never\n response: Response\n }\n> => {\n const ed25519Auth = getEd25519AuthConfig({\n auth: options.auth,\n sourceAuth,\n })\n const githubOidcAuth = getGitHubOidcAuthConfig({\n auth: options.auth,\n sourceAuth,\n })\n\n if (!ed25519Auth && !githubOidcAuth) {\n return {\n response: errorResponse(\n 'auth_disabled',\n 'Sync authentication is not configured for this endpoint.',\n 401,\n ),\n }\n }\n\n const bearerToken = getBearerToken(req.headers)\n\n if (bearerToken !== undefined) {\n if (!githubOidcAuth) {\n return {\n response: errorResponse(\n 'auth_disabled',\n 'GitHub OIDC sync authentication is not configured for this endpoint.',\n 401,\n ),\n }\n }\n\n return authenticateGitHubOidcRequest({\n auth: githubOidcAuth,\n now,\n options,\n rawBody,\n req,\n })\n }\n\n if (hasEd25519AuthHeaders(req.headers) || !githubOidcAuth) {\n if (!ed25519Auth) {\n return {\n response: errorResponse(\n 'auth_disabled',\n 'Signed sync authentication is not configured for this endpoint.',\n 401,\n ),\n }\n }\n\n return authenticateEd25519Request({\n auth: ed25519Auth,\n now,\n options,\n rawBody,\n req,\n })\n }\n\n return authenticateGitHubOidcRequest({\n auth: githubOidcAuth,\n now,\n options,\n rawBody,\n req,\n })\n}\n\nconst createSyncEndpointHandler =\n (options: CreateSyncEndpointOptions) =>\n async (req: PayloadRequest): Promise<Response> => {\n const startedAt = options.getNow?.() ?? new Date()\n\n if (req.method && req.method.toUpperCase() !== 'POST') {\n return errorResponse('invalid_method', 'Sync endpoint only accepts POST.', 405)\n }\n\n if (typeof req.text !== 'function') {\n return errorResponse(\n 'invalid_body',\n 'Sync endpoint requires access to the request body text.',\n 400,\n )\n }\n\n const rawBody = await req.text()\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES\n\n if (Buffer.byteLength(rawBody, 'utf8') > maxBodyBytes) {\n return errorResponse('invalid_body', 'Sync request body is too large.', 413)\n }\n\n const manifest = parseManifestBody(rawBody)\n\n if (!manifest) {\n return errorResponse('invalid_body', 'Sync request body must be a JSON manifest.', 400)\n }\n\n const sourceResolution = await resolveSyncSource({\n manifest,\n options,\n payload: req.payload as unknown as DocsSetPayloadOperations,\n })\n\n if (sourceResolution.response) {\n return sourceResolution.response\n }\n\n const authentication = await authenticateSyncRequest({\n now: startedAt,\n options,\n rawBody,\n req,\n sourceAuth: sourceResolution.source.auth,\n })\n\n if (authentication.response) {\n return authentication.response\n }\n\n const validation = validateDocsManifest(manifest, {\n allowedSourceIds: sourceResolution.source.allowedSourceIds,\n maxTotalBytes: maxBodyBytes,\n routeBase: sourceResolution.source.routeBase,\n })\n\n if (!validation.ok) {\n return jsonResponse(\n {\n error: {\n code: 'invalid_manifest',\n message: 'Sync manifest is invalid.',\n },\n ok: false,\n },\n 400,\n )\n }\n\n const effectiveDeleteBehavior = options.deleteBehavior ?? 'archive'\n const effectivePublishMode: DocsPublishMode = validation.data.publish\n ? 'published'\n : getDefaultPublishMode(options)\n const lifecyclePolicyError = getLifecyclePolicyError({\n deleteBehavior: effectiveDeleteBehavior,\n manifest: validation.data,\n options,\n publishMode: effectivePublishMode,\n })\n\n if (lifecyclePolicyError) {\n return lifecyclePolicyError\n }\n\n const routeCollisions = await getRouteCollisionIssues({\n docsSet: sourceResolution.source.docsSet,\n manifest: validation.data,\n options,\n payload: req.payload as unknown as RouteCollisionPayloadOperations,\n routeBase: sourceResolution.source.routeBase,\n })\n\n if (routeCollisions.length > 0) {\n return errorResponse(\n 'route_collision',\n 'One or more docs routes collide with an existing route reservation.',\n 409,\n {\n routeCollisions,\n },\n )\n }\n\n const isSyncMode = validation.data.mode === 'sync'\n\n if (isSyncMode && options.allowWrites !== true) {\n return errorResponse(\n 'sync_writes_disabled',\n 'Sync writes are disabled by server configuration.',\n 403,\n )\n }\n\n if (isSyncMode && options.requireDryRunBeforeApply === true) {\n return errorResponse(\n 'dry_run_required_not_implemented',\n 'Required dry-run proof before apply is not implemented yet.',\n 400,\n )\n }\n\n if (\n isSyncMode &&\n !assertApplyDeleteBehaviorSupported(effectiveDeleteBehavior, {\n allowHardDelete: options.allowHardDelete,\n docsEnableDrafts: options.docsEnableDrafts,\n })\n ) {\n return errorResponse(\n 'delete_behavior_not_implemented',\n 'Configured delete behavior cannot be applied.',\n 400,\n )\n }\n\n if (isSyncMode && !options.syncRunsEnabled) {\n return errorResponse(\n 'audit_unavailable',\n 'Applied sync requires the sync-run audit collection.',\n 500,\n )\n }\n\n const existingPayloadDocs = options.docsEnabled\n ? await findExistingPayloadDocsRecords({\n collectionSlug: options.docsCollectionSlug,\n docsSetId: sourceResolution.source.docsSet?.id,\n markdownFieldName: options.markdownFieldName,\n payload: req.payload as unknown as ExistingDocsPayloadOperations,\n sourceId: validation.data.source.id,\n })\n : []\n const existingDocs = existingPayloadDocs.map(toExistingDocsRecord)\n const plan = planDocsSync({\n deleteBehavior: effectiveDeleteBehavior,\n desired: validation.data,\n existing: existingDocs,\n })\n const summary = summarizePlan(plan)\n const warnings = [...validation.warnings, ...plan.warnings]\n if (isSyncMode) {\n const existingBySourcePath = new Map(\n existingPayloadDocs.map((record) => [record.sourcePath, record]),\n )\n const conflicts = findDocsSyncConflicts({\n existingBySourcePath,\n plannedChanges: getPlannedConflictChanges({\n existing: existingPayloadDocs,\n plan,\n }),\n })\n\n if (conflicts.length > 0) {\n return errorResponse(\n 'manual_edit_conflict',\n 'One or more docs were modified outside the docs sync workflow.',\n 409,\n {\n conflicts,\n },\n )\n }\n }\n\n await storeAcceptedNonce({\n bodyHash: authentication.identity.bodyHash,\n collectionSlug: options.noncesCollectionSlug,\n expiresAt: authentication.identity.expiresAt,\n keyId: authentication.identity.keyId,\n nonce: authentication.identity.nonce,\n payload: req.payload as unknown as NoncePayloadOperations,\n sourceId: validation.data.source.id,\n usedAt: startedAt,\n })\n\n let syncRunId: number | string | undefined\n\n if (options.syncRunsEnabled) {\n const syncRun = await createSyncRunAudit({\n actor: authentication.identity.actor,\n bodyHash: authentication.identity.bodyHash,\n branch: authentication.identity.branch ?? validation.data.source.branch,\n collectionSlug: options.syncRunsCollectionSlug,\n commit: authentication.identity.commit ?? validation.data.source.commit,\n completedAt: isSyncMode ? startedAt : options.getNow?.() ?? new Date(),\n deleteBehavior: effectiveDeleteBehavior,\n effectivePublishMode,\n errors: [],\n fileCount: validation.data.files.length,\n keyId: authentication.identity.keyId,\n mode: isSyncMode ? 'sync' : 'dry-run',\n payload: req.payload as unknown as SyncRunsPayloadOperations,\n publishRequested: validation.data.publish,\n repository:\n authentication.identity.repository ?? validation.data.source.repository,\n sourceId: validation.data.source.id,\n startedAt,\n status: isSyncMode ? 'pending' : 'success',\n summary,\n totalBytes: getTotalManifestBytes(validation.data),\n warnings,\n })\n\n syncRunId = getRecordId(syncRun)\n }\n\n if (isSyncMode) {\n if (!syncRunId) {\n return errorResponse(\n 'audit_unavailable',\n 'Applied sync could not create a sync-run audit record.',\n 500,\n )\n }\n\n try {\n const applyResult = await applyDocsSync({\n collectionSlug: options.docsCollectionSlug,\n deleteBehavior: effectiveDeleteBehavior,\n docsEnableDrafts: options.docsEnableDrafts,\n docsSetId: sourceResolution.source.docsSet?.id,\n existing: existingPayloadDocs,\n manifest: validation.data,\n markdownFieldName: options.markdownFieldName,\n now: options.getNow?.() ?? new Date(),\n payload: req.payload as unknown as ApplyDocsSyncPayloadOperations,\n plan,\n publishMode: effectivePublishMode,\n syncRunId,\n })\n\n if (!applyResult.ok) {\n return errorResponse(\n 'manual_edit_conflict',\n 'One or more docs were modified outside the docs sync workflow.',\n 409,\n {\n conflicts: applyResult.conflicts,\n },\n )\n }\n\n await updateSyncRunAudit({\n collectionSlug: options.syncRunsCollectionSlug,\n completedAt: options.getNow?.() ?? new Date(),\n payload: req.payload as unknown as SyncRunsPayloadOperations,\n status: 'success',\n summary,\n syncRunId,\n warnings,\n })\n\n if (sourceResolution.source.docsSet) {\n await updateDocsSetAfterSync({\n aiExport: validation.data.aiExport,\n collectionSlug: options.docsSetsCollectionSlug,\n docsCount: validation.data.files.length,\n docsSetId: sourceResolution.source.docsSet.id,\n now: options.getNow?.() ?? new Date(),\n payload: req.payload as unknown as DocsSetPayloadOperations,\n syncRunId,\n })\n }\n } catch (error) {\n await updateSyncRunAudit({\n collectionSlug: options.syncRunsCollectionSlug,\n completedAt: options.getNow?.() ?? new Date(),\n errors: [\n {\n code: 'invalid_manifest',\n message: error instanceof Error ? error.message : 'Sync apply failed.',\n },\n ],\n payload: req.payload as unknown as SyncRunsPayloadOperations,\n status: 'failed',\n summary,\n syncRunId,\n warnings,\n })\n\n return errorResponse('sync_apply_failed', 'Sync apply failed.', 500)\n }\n }\n\n return jsonResponse({\n changes: serializeChanges(plan),\n deleteBehavior: effectiveDeleteBehavior,\n dryRun: !isSyncMode,\n effectivePublishMode,\n ok: true,\n publishRequested: validation.data.publish,\n summary,\n syncRunId: syncRunId === undefined ? undefined : String(syncRunId),\n warnings,\n })\n }\n\nexport const createSyncEndpoint = (options: CreateSyncEndpointOptions): Endpoint => ({\n handler: createSyncEndpointHandler(options),\n method: 'post',\n path: options.endpointPath,\n})\n"],"names":["DEFAULT_DOCS_ROUTE_BASE","DEFAULT_MAX_BODY_BYTES","DEFAULT_MAX_SKEW_SECONDS","DEFAULT_NONCE_TTL_SECONDS","applyDocsSync","assertApplyDeleteBehaviorSupported","createSyncRunAudit","findConfiguredPagesRouteCollisions","findDocsSetBySourceId","findDocsSyncConflicts","findDuplicateDesiredRouteCollisions","findExistingDocsRouteCollisions","findExistingPayloadDocsRecords","getRecordId","toExistingDocsRecord","updateDocsSetAfterSync","updateSyncRunAudit","assertNonceNotReplayed","buildCanonicalSigningString","extractSyncRequestHeaders","getCanonicalPathFromRequestUrl","storeAcceptedNonce","validateTimestampSkew","verifyBodySha256","verifyEd25519Signature","verifyGitHubOidcToken","planDocsSync","validateDocsManifest","jsonResponse","body","status","Response","json","errorResponse","code","message","extras","error","ok","isRecord","value","Array","isArray","parseManifestBody","rawBody","parsed","JSON","parse","undefined","findSourceConfig","sourceId","sources","find","source","id","getAllowedSourceIds","length","map","resolveSyncSource","manifest","options","payload","allowedSourceIds","routeBase","docsSet","docsSetsEnabled","collectionSlug","docsSetsCollectionSlug","sourceRoot","root","response","auth","sourceConfig","summarizePlan","plan","archive","create","delete","draft","unchanged","update","warnings","serializeChange","change","current","archived","route","sourceHash","title","desired","sha256","reason","sourcePath","serializeChanges","getTotalManifestBytes","files","reduce","total","file","Buffer","byteLength","content","getPlannedConflictChanges","existing","existingBySourcePath","Map","record","archivedUnchanged","filter","get","getDefaultPublishMode","defaultPublishMode","docsEnableDrafts","getLifecyclePolicyError","deleteBehavior","publishMode","publish","allowPublish","allowHardDelete","getRouteCollisionIssues","desiredRoutes","duplicateDesiredRouteCollisions","existingDocsRouteCollisions","docsEnabled","docsCollectionSlug","docsSetId","routes","pageRouteCollisions","routing","pages","enabled","allowBridgePages","bridgeField","collection","docsSetRouteBase","routeField","getRequiredHeader","headers","name","trim","getBearerToken","authorization","scheme","token","split","toLowerCase","hasEd25519AuthHeaders","getGlobalEd25519AuthConfig","mode","ed25519","getEd25519AuthConfig","sourceAuth","globalAuth","keys","getGlobalGitHubOidcAuthConfig","githubOidc","getGitHubOidcAuthConfig","sourceOidc","_enabled","sourceOptions","audience","assertReplayProtectionAvailable","noncesEnabled","authenticateEd25519Request","now","req","headersResult","header","keyConfig","key","keyId","bodyHash","expectedHash","bodySha256","timestampValidation","maxSkewSeconds","timestamp","replayUnavailable","nonceAvailable","noncesCollectionSlug","nonce","canonicalPath","endpointPath","url","canonicalString","computedHash","method","path","publicKey","signature","nonceTtlSeconds","identity","expiresAt","Date","getTime","authenticateGitHubOidcRequest","verified","config","fetchJson","oidcFetchJson","claims","jti","actor","branch","ref","commit","sha","repository","authenticateSyncRequest","ed25519Auth","githubOidcAuth","bearerToken","createSyncEndpointHandler","startedAt","getNow","toUpperCase","text","maxBodyBytes","sourceResolution","authentication","validation","maxTotalBytes","effectiveDeleteBehavior","effectivePublishMode","data","lifecyclePolicyError","routeCollisions","isSyncMode","allowWrites","requireDryRunBeforeApply","syncRunsEnabled","existingPayloadDocs","markdownFieldName","existingDocs","summary","conflicts","plannedChanges","usedAt","syncRunId","syncRun","syncRunsCollectionSlug","completedAt","errors","fileCount","publishRequested","totalBytes","applyResult","aiExport","docsCount","Error","changes","dryRun","String","createSyncEndpoint","handler"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { PayloadMarkdownDocsDocsSetAuthConfig } from '../types.js';
1
2
  export type DocsSetPayloadOperations = {
2
3
  find: (args: {
3
4
  collection: string;
@@ -17,6 +18,7 @@ export type DocsSetPayloadOperations = {
17
18
  };
18
19
  export type PayloadRecordId = number | string;
19
20
  export type ResolvedDocsSet = {
21
+ auth?: PayloadMarkdownDocsDocsSetAuthConfig;
20
22
  id: PayloadRecordId;
21
23
  routeBase: string;
22
24
  sourceId: string;
@@ -24,6 +24,78 @@ const getRecordId = (doc)=>{
24
24
  }
25
25
  return undefined;
26
26
  };
27
+ const getString = (value)=>typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
28
+ const getNumber = (value)=>typeof value === 'number' && Number.isFinite(value) ? value : undefined;
29
+ const getStringArray = (value)=>{
30
+ if (!Array.isArray(value)) {
31
+ return undefined;
32
+ }
33
+ const values = value.flatMap((item)=>{
34
+ if (typeof item === 'string' && item.trim() !== '') {
35
+ return [
36
+ item.trim()
37
+ ];
38
+ }
39
+ if (isRecord(item)) {
40
+ const nestedValue = getString(item.value);
41
+ return nestedValue ? [
42
+ nestedValue
43
+ ] : [];
44
+ }
45
+ return [];
46
+ });
47
+ return values.length > 0 ? values : undefined;
48
+ };
49
+ const getRecord = (value)=>isRecord(value) ? value : undefined;
50
+ const toResolvedDocsSetAuth = (value)=>{
51
+ const auth = getRecord(value);
52
+ if (!auth) {
53
+ return undefined;
54
+ }
55
+ const ed25519 = getRecord(auth.ed25519);
56
+ const keys = Array.isArray(ed25519?.keys) ? ed25519.keys.flatMap((key)=>{
57
+ if (!isRecord(key)) {
58
+ return [];
59
+ }
60
+ const id = getString(key.keyId) ?? getString(key.id);
61
+ const publicKey = getString(key.publicKey);
62
+ return id && publicKey ? [
63
+ {
64
+ id,
65
+ publicKey
66
+ }
67
+ ] : [];
68
+ }) : [];
69
+ const githubOidc = getRecord(auth.githubOidc);
70
+ const resolvedGithubOidc = githubOidc && githubOidc.enabled !== false ? {
71
+ allowedEnvironments: getStringArray(githubOidc.allowedEnvironments),
72
+ allowedRefs: getStringArray(githubOidc.allowedRefs),
73
+ allowedRepositories: getStringArray(githubOidc.allowedRepositories),
74
+ allowedRepositoryOwners: getStringArray(githubOidc.allowedRepositoryOwners),
75
+ allowedWorkflowRefs: getStringArray(githubOidc.allowedWorkflowRefs),
76
+ allowedWorkflows: getStringArray(githubOidc.allowedWorkflows),
77
+ allowPullRequests: typeof githubOidc.allowPullRequests === 'boolean' ? githubOidc.allowPullRequests : undefined,
78
+ audience: getString(githubOidc.audience),
79
+ enabled: githubOidc.enabled === true,
80
+ issuer: getString(githubOidc.issuer),
81
+ jwksUrl: getString(githubOidc.jwksUrl),
82
+ maxSkewSeconds: getNumber(githubOidc.maxSkewSeconds)
83
+ } : undefined;
84
+ const hasGithubOidcPolicy = Boolean(resolvedGithubOidc && (resolvedGithubOidc.enabled || resolvedGithubOidc.audience || resolvedGithubOidc.allowedEnvironments || resolvedGithubOidc.allowedRefs || resolvedGithubOidc.allowedRepositories || resolvedGithubOidc.allowedRepositoryOwners || resolvedGithubOidc.allowedWorkflowRefs || resolvedGithubOidc.allowedWorkflows || resolvedGithubOidc.allowPullRequests !== undefined || resolvedGithubOidc.issuer || resolvedGithubOidc.jwksUrl || resolvedGithubOidc.maxSkewSeconds !== undefined));
85
+ const resolvedAuth = {
86
+ ...keys.length > 0 ? {
87
+ ed25519: {
88
+ keys,
89
+ maxSkewSeconds: getNumber(ed25519?.maxSkewSeconds),
90
+ nonceTtlSeconds: getNumber(ed25519?.nonceTtlSeconds)
91
+ }
92
+ } : {},
93
+ ...hasGithubOidcPolicy && resolvedGithubOidc ? {
94
+ githubOidc: resolvedGithubOidc
95
+ } : {}
96
+ };
97
+ return resolvedAuth.ed25519 || resolvedAuth.githubOidc ? resolvedAuth : undefined;
98
+ };
27
99
  const toResolvedDocsSet = (doc)=>{
28
100
  if (!isRecord(doc)) {
29
101
  return undefined;
@@ -34,6 +106,7 @@ const toResolvedDocsSet = (doc)=>{
34
106
  }
35
107
  return {
36
108
  id,
109
+ auth: toResolvedDocsSetAuth(doc.auth),
37
110
  routeBase: doc.routeBase,
38
111
  sourceId: doc.sourceId,
39
112
  sourceRoot: typeof doc.sourceRoot === 'string' ? doc.sourceRoot : undefined