Skip to content

🐞 fix(resolver): Fixing circular-reference handling to avoid call stack errors#340

Open
Charles Hudson (phobetron) wants to merge 1 commit into
mainfrom
NT-3567_fix-circular-reference-resolver-issue
Open

🐞 fix(resolver): Fixing circular-reference handling to avoid call stack errors#340
Charles Hudson (phobetron) wants to merge 1 commit into
mainfrom
NT-3567_fix-circular-reference-resolver-issue

Conversation

@phobetron

@phobetron Charles Hudson (phobetron) commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes circular-reference handling in optimized Contentful entry resolution by making generic Contentful entry fields pass-through data during schema validation.

Resolved Contentful graphs can contain repeated or circular references, especially through rich-text data.target links. Previously, EntryFields used z.json(), which recursively walked arbitrary consumer fields. When resolveOptimizedEntry validated a selected optimization path against an entry graph containing cycles, Zod could overflow the stack and cause personalization resolution to fail.

Changes

  • Changed EntryFields from recursive JSON validation to pass-through field validation with z.any().
  • Updated schema documentation to clarify that consumer-defined Contentful fields are intentionally not strongly validated by SDK schemas.
  • Added schema tests proving:
    • arbitrary/circular consumer fields are accepted without recursive traversal;
    • Contentful sys metadata is still validated;
    • SDK-owned optimization fields such as nt_config and nt_experiences are still validated.
  • Added resolver regression coverage for circular Contentful-like graphs:
    • unrelated rich-text linked-entry cycles under the baseline graph;
    • rich-text linked-entry cycles inside the selected variant entry.

Rationale

The SDK only needs to validate and inspect SDK-owned fields and Contentful structural metadata. Consumer-authored entry fields can contain resolved Contentful entries, rich-text documents, assets, and repeated references. Treating those fields as JSON-only data is too strict for real resolved CDA graphs and can trigger recursive validation failures.

This change keeps validation where it matters while avoiding traversal of arbitrary consumer content graphs.

Validation

  • pnpm exec eslint ... on touched files
  • pnpm exec prettier --check ... on touched files
  • pnpm --filter @contentful/optimization-api-schemas typecheck
  • pnpm --filter @contentful/optimization-api-schemas test:unit
  • pnpm --filter @contentful/optimization-api-client typecheck
  • pnpm --filter @contentful/optimization-core typecheck
  • pnpm --filter @contentful/optimization-core exec rstest run src/resolvers/OptimizedEntryResolver.test.ts
  • pnpm --filter @contentful/optimization-core test:unit
  • pnpm --filter @contentful/optimization-api-schemas size:check
  • pnpm --filter @contentful/optimization-api-client size:check
  • pnpm --filter @contentful/optimization-core size:check

[NT-3567]

…ck errors

## Summary

Fixes circular-reference handling in optimized Contentful entry resolution by making generic Contentful entry fields pass-through data during schema validation.

Resolved Contentful graphs can contain repeated or circular references, especially through rich-text `data.target` links. Previously, `EntryFields` used `z.json()`, which recursively walked arbitrary consumer fields. When `resolveOptimizedEntry` validated a selected optimization path against an entry graph containing cycles, Zod could overflow the stack and cause personalization resolution to fail.

## Changes

- Changed `EntryFields` from recursive JSON validation to pass-through field validation with `z.any()`.
- Updated schema documentation to clarify that consumer-defined Contentful fields are intentionally not strongly validated by SDK schemas.
- Added schema tests proving:
  - arbitrary/circular consumer fields are accepted without recursive traversal;
  - Contentful `sys` metadata is still validated;
  - SDK-owned optimization fields such as `nt_config` and `nt_experiences` are still validated.
- Added resolver regression coverage for circular Contentful-like graphs:
  - unrelated rich-text linked-entry cycles under the baseline graph;
  - rich-text linked-entry cycles inside the selected variant entry.

## Rationale

The SDK only needs to validate and inspect SDK-owned fields and Contentful structural metadata. Consumer-authored entry fields can contain resolved Contentful entries, rich-text documents, assets, and repeated references. Treating those fields as JSON-only data is too strict for real resolved CDA graphs and can trigger recursive validation failures.

This change keeps validation where it matters while avoiding traversal of arbitrary consumer content graphs.

## Validation

- `pnpm exec eslint ...` on touched files
- `pnpm exec prettier --check ...` on touched files
- `pnpm --filter @contentful/optimization-api-schemas typecheck`
- `pnpm --filter @contentful/optimization-api-schemas test:unit`
- `pnpm --filter @contentful/optimization-api-client typecheck`
- `pnpm --filter @contentful/optimization-core typecheck`
- `pnpm --filter @contentful/optimization-core exec rstest run src/resolvers/OptimizedEntryResolver.test.ts`
- `pnpm --filter @contentful/optimization-core test:unit`
- `pnpm --filter @contentful/optimization-api-schemas size:check`
- `pnpm --filter @contentful/optimization-api-client size:check`
- `pnpm --filter @contentful/optimization-core size:check`

[[NT-3567](https://contentful.atlassian.net/browse/NT-3567)]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant