Skip to content

refactor(translator): extract framework-agnostic translator-core (keystone — FieldLike + pure predicates)#60

Open
SearheiParkhamchuk wants to merge 4 commits into
mainfrom
feat/translator-core
Open

refactor(translator): extract framework-agnostic translator-core (keystone — FieldLike + pure predicates)#60
SearheiParkhamchuk wants to merge 4 commits into
mainfrom
feat/translator-core

Conversation

@SearheiParkhamchuk

@SearheiParkhamchuk SearheiParkhamchuk commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What

Long-lived branch for extracting a framework-agnostic @repo/translator-core. This first push
lands the keystone that unblocks everything else: src/server/shared/field-traversal/** is now
free of any payload import.

  • Structural FieldLike type family (incl. positive LeafFieldLike with never-guards) replaces
    Payload Field types across the entire FieldWalker contract.
  • Field-classification predicates re-implemented locally (predicates.ts) instead of imported
    from payload/shared.
  • Guard / field.custom accessors widened to structural input.

No behavior change — type re-annotation over unchanged logic.

Why

A /sp-architecture-review showed the original "coupled to Payload only via the Field type"
framing was false at runtime (kernel.ts ran payload/shared predicates; walker callbacks named
Payload types). See docs/plans/2026-06-26-architecture-review.md and
...-translator-core-extraction-design.md (both added here).

Roadmap (this branch accumulates the slices)

  • 1. Re-implement kernel field predicates as pure functions over FieldLike
  • 2. Define FieldLike across the full FieldWalker contract (LeafFieldLike)
  • 3. Widen field.custom / guard accessors to structural input
  • 4. Own Lexical Serialized* types from lexical, not @payloadcms/richtext-lexical
  • 5. Fix cross-layer dep edges (modules→features, server→client) + boundary lint
  • 6. Read-only id-path ContentProjector + branded IdPath (revises provenance phase 2)
  • 7. Create @repo/translator-core, move agnostic surface, enforce no-payload in CI

Verification

  • parity test: local predicates vs payload/shared on a fixture of every field type
  • conformance (tsgo): Payload Field assignable to FieldLike; containers excluded from LeafFieldLike
  • boundary scan: no payload import in field-traversal/**
  • Full suite green (793 tests), tsgo clean, oxlint clean.

Note: the repo-wide pre-push check-types currently fails in apps/cms for unrelated reasons
(@fr-private/payload-plugin-visual-editing not resolvable in a fresh worktree) — the translator
package itself type-checks clean.

Part of #59

🤖 Generated with Claude Code

…e + pure predicates)

Keystone slice of the @repo/translator-core extraction. Makes
src/server/shared/field-traversal/** free of any payload import: a structural
FieldLike type family replaces payload `Field` types across the FieldWalker
contract, and the field-classification predicates are re-implemented locally
instead of imported from `payload/shared`.

No behavior change — type re-annotation over unchanged logic. Guarded by:
- parity test: local predicates vs payload/shared on a fixture of every field type
- conformance (tsgo): payload `Field` assignable to FieldLike; containers excluded
  from LeafFieldLike
- boundary scan: no payload import in field-traversal/**
Full suite green (793 tests), tsgo clean, oxlint clean.

Extraction roadmap (tracked on this branch):
- [x] 1. Re-implement kernel field predicates as pure functions over FieldLike
- [x] 2. Define FieldLike across the full FieldWalker contract (LeafFieldLike)
- [x] 3. Widen field.custom / guard accessors to structural input
- [ ] 4. Own Lexical Serialized* types from `lexical`, not @payloadcms/richtext-lexical
- [ ] 5. Fix cross-layer dep edges (modules->features, server->client) + boundary lint
- [ ] 6. Read-only id-path ContentProjector + branded IdPath (revises provenance phase 2)
- [ ] 7. Create @repo/translator-core, move agnostic surface, enforce no-payload in CI

Design:
  docs/plans/2026-06-26-translator-core-extraction-design.md
  docs/plans/2026-06-26-architecture-review.md
  docs/plans/2026-06-26-translation-provenance-and-lifecycle-design.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WCZ4rvA5GpDBgch29vLBi1
@SearheiParkhamchuk SearheiParkhamchuk self-assigned this Jun 30, 2026
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
turbo-cms-kit-payload Ready Ready Preview, Comment Jun 30, 2026 5:40pm

Request Review

…lized* types)

WHY: shared/lexical is part of the framework-agnostic core surface for the
@repo/translator-core extraction, but types.ts imported the Serialized* Lexical
types from @payloadcms/richtext-lexical — a banned framework dependency for the core.

WHAT:
- Replace the @payloadcms/richtext-lexical import in shared/lexical/types.ts with
  locally-owned structural types (SerializedLexicalNode/SerializedTextNode/
  SerializedRootNode), a structural superset of what the layer reads.
- Add a conformance test: Payload's real Serialized* nodes stay assignable to the
  local types (tsgo-enforced).
- Add a boundary test: shared/lexical imports no payload/@payloadcms.

TESTS: new conformance + boundary tests in shared/lexical; full package suite green
(801 tests), tsgo + oxlint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ndary lint

WHY: prep for @repo/translator-core — remove wrong-direction dependency edges and
guard the already-agnostic zones so payload can't leak back in. No behavior change.

WHAT:
- Relocate shared contracts to the neutral src/types/ leaf: FieldTranslationResult/
  FieldTranslationNotice (kills a client->server-feature import), CollectionSchemaMap,
  RawPayloadComponentExport. Barrels re-export to keep the public surface stable.
- Invert translation-levels: the composition factories (documentLevel/collectionLevel/
  fieldLevel/useDocTranslationApi) move up to a new src/composition/levels/ layer (named
  by role - the composition root beside plugin.ts); modules/translation-levels keeps only
  the abstract mechanism (types + PluginConfigBuilder) and no longer imports features/client.
- Add a zone-scoped oxlint no-restricted-imports boundary (root oxlint.config.ts) for
  field-traversal/ + lexical/ - bans payload/@payloadcms/next/react and relative features/**
  & client/** edges - mirroring the analytics precedent; test files exempt.
- Consolidate the two per-dir no-payload scans into one parametrized
  no-payload-boundary.test.ts; both zones ban payload + @payloadcms in lockstep with the lint.

TESTS: full suite green (801); inversion guarded by plugin.test.ts; boundary lint verified
to fire on an injected payload import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

[translator] Extract framework-agnostic @repo/translator-core

1 participant