Skip to content

feat(relation): add Relation decorator for declarative table links - #8

Merged
Upd4ting merged 3 commits into
mainfrom
feat/relation-decorator
Jul 8, 2026
Merged

feat(relation): add Relation decorator for declarative table links#8
Upd4ting merged 3 commits into
mainfrom
feat/relation-decorator

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Jul 8, 2026

Copy link
Copy Markdown
Member

🔗 Linked issue

N/A

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Adds a Relation property decorator that declares a link from a field to another table as declarative metadata only — database implementations do not enforce it (no foreign key constraint, no referential validation). Introspection tooling consumes it to expose the links between tables, e.g. to render schema diagrams or navigate related records.

  • New src/relation.ts with Relation decorator, RelationOptions (to thunk for forward references, optional toField, many flag) and RelationStaticMetadata.
  • Exported from src/index.ts and exposed as a ./relation subpath in package.json.
  • Unit tests covering metadata storage, multiple relations, forward references, and per-class metadata isolation.
  • Documentation added to docs/2.table-definitions.md.

Test plan: pnpm run test — 96 tests pass, including the 5 new Relation tests.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

https://claude.ai/code/session_01HnTc4Zvaayya2HqKGEVwTP

Greptile Summary

This PR adds a Relation property decorator that attaches declarative link metadata from a field to a target table class. The implementation follows the existing MakePropertyDecorator / getMetadata pattern used throughout the codebase for Field, Index, and other decorators.

  • New src/relation.ts exports RelationOptions, RelationStaticMetadata, and the Relation decorator, which stores relation options keyed by property name on the class's RelationStaticMetadata instance.
  • The decorator is wired into src/index.ts and given its own ./relation subpath export in package.json, consistent with the existing ./model, ./schema, etc. subpath pattern.
  • Five unit tests validate metadata storage, option round-tripping, multiple relations per class, forward references via the thunk, and per-class metadata isolation.

Confidence Score: 5/5

This PR is safe to merge — it adds purely additive, declarative metadata with no enforcement logic and no changes to existing behavior.

The new Relation decorator is a thin wrapper over the existing MakePropertyDecorator / getMetadata infrastructure, storing options in a new metadata class. No existing code paths are modified, and the implementation is structurally identical to Field, Index, and other decorators in the codebase. Tests cover the key scenarios — storage, options round-tripping, forward references, and class isolation.

No files require special attention.

Important Files Changed

Filename Overview
src/relation.ts Introduces RelationOptions interface, RelationStaticMetadata class, and the Relation property decorator — all following the established MakePropertyDecorator / getMetadata pattern exactly.
src/tests/relation.test.ts Five new unit tests cover: basic metadata storage, toField/many options, multiple relations per class, forward references via thunk, and metadata isolation between sibling classes.
src/index.ts Adds export * from "./relation" in the correct alphabetical position alongside existing exports.
package.json Adds ./relation subpath export (types + default) and typesVersions entry, consistent with existing ./model, ./schema pattern.
docs/2.table-definitions.md Documents the Relation decorator with a usage example and options table; the many flag description was updated to 'many targets per source record' to avoid ORM cardinality ambiguity.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Dev as Developer
    participant Decorator as @Relation decorator
    participant MakePD as MakePropertyDecorator
    participant GetMeta as getMetadata()
    participant Meta as RelationStaticMetadata
    participant Tool as Introspection Tooling

    Dev->>Decorator: "@Relation({ to: () => Post, toField: "id", many: false })"
    Decorator->>MakePD: wraps callback
    MakePD->>GetMeta: getMetadata(target.constructor, RelationStaticMetadata)
    GetMeta-->>Meta: create or reuse instance (stored via Reflect)
    MakePD->>Meta: "metadata.relations["fieldName"] = options"
    Note over Meta: relations: { postId: { to, toField, many } }

    Tool->>GetMeta: getMetadata(CommentClass, RelationStaticMetadata)
    GetMeta-->>Meta: return stored instance
    Tool->>Meta: read relations Record
    Tool-->>Dev: schema diagram / navigation links
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Dev as Developer
    participant Decorator as @Relation decorator
    participant MakePD as MakePropertyDecorator
    participant GetMeta as getMetadata()
    participant Meta as RelationStaticMetadata
    participant Tool as Introspection Tooling

    Dev->>Decorator: "@Relation({ to: () => Post, toField: "id", many: false })"
    Decorator->>MakePD: wraps callback
    MakePD->>GetMeta: getMetadata(target.constructor, RelationStaticMetadata)
    GetMeta-->>Meta: create or reuse instance (stored via Reflect)
    MakePD->>Meta: "metadata.relations["fieldName"] = options"
    Note over Meta: relations: { postId: { to, toField, many } }

    Tool->>GetMeta: getMetadata(CommentClass, RelationStaticMetadata)
    GetMeta-->>Meta: return stored instance
    Tool->>Meta: read relations Record
    Tool-->>Dev: schema diagram / navigation links
Loading

Reviews (2): Last reviewed commit: "address greptile review feedback (greplo..." | Re-trigger Greptile

Upd4ting added 2 commits July 8, 2026 16:50
Adds a property decorator that records relation metadata (target table
thunk, optional target field, one-to-many flag) without enforcing any
database constraint. Consumed by introspection tooling to expose links
between tables.

Claude-Session: https://claude.ai/code/session_01HnTc4Zvaayya2HqKGEVwTP
Comment thread docs/2.table-definitions.md Outdated
@Upd4ting

Upd4ting commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

@greptile review

@Upd4ting
Upd4ting merged commit 144a855 into main Jul 8, 2026
3 checks passed
@Upd4ting
Upd4ting deleted the feat/relation-decorator branch July 8, 2026 15:04
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