Skip to content

fix(engine): preserve runtime extensions via non-enumerable symbol during config cloning#25

Merged
jacksonkasi1 merged 6 commits intomainfrom
dev
Mar 11, 2026
Merged

fix(engine): preserve runtime extensions via non-enumerable symbol during config cloning#25
jacksonkasi1 merged 6 commits intomainfrom
dev

Conversation

@jacksonkasi1
Copy link
Copy Markdown
Owner

@jacksonkasi1 jacksonkasi1 commented Mar 10, 2026

Summary

  • Fixes a bug where engine runtime extensions (computed fields, data transforms, and lifecycle hooks) were lost when a developer or framework adapter cloned the configuration object (e.g., using spread syntax).
  • Changes the hidden extensions property key from an enumerable: false string to a unique symbol (Symbol.for("__tablecraft_ext")).
  • This ensures that the hidden runtime logic successfully survives spread operations and object assign, while continuing to remain completely invisible to JSON.stringify and Object.keys.
  • Adds an optional chaining safety check to resolveInput in engine.ts to prevent a potential TypeError if a falsy config is provided.

Summary by Sourcery

Preserve engine runtime extensions when cloning table configs and make them discoverable via a hidden symbol key while safely resolving configs that may already carry embedded extensions.

Bug Fixes:

  • Ensure runtime extensions on table configurations survive spread/object cloning by storing them under a non-enumerable symbol key.
  • Prevent potential TypeErrors in config resolution when a falsy or pre-wrapped config input is provided.

Enhancements:

  • Embed runtime extensions directly into generated table configs using a shared symbol key to support framework and adapter cloning patterns.

Build:

  • Bump @tablecraft/engine package version from 0.1.6 to 0.1.7.

Summary by CodeRabbit

  • Chores

    • Bumped engine package version to 0.1.7.
  • New Features

    • Table configurations now embed runtime extension state so extension data is preserved when configs are created, serialized, or reused.
  • Bug Fixes

    • Improved input validation to raise a clear error for missing/null table input.
  • Tests

    • Added tests to verify runtime extensions are preserved and rehydrated from serialized configs.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 10, 2026

Reviewer's Guide

Ensures engine runtime extensions are preserved when table configs are cloned by embedding extensions on the config object under a non-enumerable symbol key and teaching the engine to read them back, while hardening resolveInput against falsy configs and bumping the engine package version.

Sequence diagram for resolveInput using embedded runtime extensions

sequenceDiagram
  actor Developer
  participant Builder as TableDefinitionBuilder_T_
  participant Engine as EngineFunctions

  Developer->>Builder: toConfig()
  Builder->>Builder: clone _config to config
  Builder->>Builder: attach TABLECRAFT_EXTENSIONS_KEY with _ext
  Builder-->>Developer: config_with_extensions

  Developer->>Developer: cloned_config = { ...config_with_extensions }

  Developer->>Engine: resolveInput(cloned_config)
  Engine->>Engine: plainConfig = input
  Engine->>Engine: embeddedExt = plainConfig[TABLECRAFT_EXTENSIONS_KEY]
  alt extensions_embedded
    Engine-->>Developer: { config: plainConfig, ext: embeddedExt }
  else no_extensions
    Engine->>Engine: create new RuntimeExtensions_any_
    Engine-->>Developer: { config: plainConfig, ext: new_extensions }
  end
Loading

Class diagram for embedded runtime extensions on TableConfig

classDiagram
  class TableConfig {
  }

  class RuntimeExtensions_any_ {
    +computedExpressions Map
    +transforms Map
    +rawSelects Map
    +rawWheres Array
    +dynamicWheres Array
    +rawJoins Array
    +rawOrderBys Array
    +ctes Map
    +sqlJoinConditions Map
    +countMode any
  }

  class RuntimeExtensions_T_ {
    +computedExpressions Map
    +transforms Map
    +rawSelects Map
    +rawWheres Array
    +dynamicWheres Array
    +rawJoins Array
    +rawOrderBys Array
    +ctes Map
    +sqlJoinConditions Map
    +countMode any
  }

  class TableDefinitionBuilder_T_ {
    -_config TableConfig
    -_ext RuntimeExtensions_T_
    +toConfig() TableConfig
  }

  class EngineFunctions {
    +resolveInput(input ConfigInput) ResolveResult
  }

  class ResolveResult {
    +config TableConfig
    +ext RuntimeExtensions_any_
  }

  class TABLECRAFT_EXTENSIONS_KEY {
    +symbol Symbol
  }

  TableDefinitionBuilder_T_ --> TableConfig : builds
  TableDefinitionBuilder_T_ --> RuntimeExtensions_T_ : holds_ext
  TableDefinitionBuilder_T_ ..> TABLECRAFT_EXTENSIONS_KEY : uses_key_in_toConfig

  TableConfig --> RuntimeExtensions_any_ : property_TABLECRAFT_EXTENSIONS_KEY
  TABLECRAFT_EXTENSIONS_KEY ..> RuntimeExtensions_any_ : indexes_extensions

  EngineFunctions --> ResolveResult : creates
  EngineFunctions --> TableConfig : reads_config
  EngineFunctions --> RuntimeExtensions_any_ : reads_or_creates_ext
Loading

File-Level Changes

Change Details Files
Preserve runtime extensions on cloned table configurations via a hidden symbol-keyed property and read them back in the engine resolver.
  • Introduce a shared TABLECRAFT_EXTENSIONS_KEY unique symbol used to store runtime extensions on table config objects without affecting normal enumeration or JSON serialization.
  • Update TableDefinitionBuilder.toConfig to spread the internal config, attach the runtime extensions under the symbol key, and return this augmented config.
  • Modify resolveInput to treat plain objects as configs that may carry embedded runtime extensions, reading from the symbol property if present and falling back to constructing empty extension maps if absent.
  • Add optional chaining when accessing the embedded extensions to avoid TypeError when resolveInput receives a falsy config input.
packages/engine/src/define.ts
packages/engine/src/engine.ts
Version bump for the engine package and corresponding lockfile update.
  • Increment @tablecraft/engine version from 0.1.6 to 0.1.7 in package.json.
  • Regenerate bun.lock to capture the new engine version and any dependency resolution changes.
packages/engine/package.json
bun.lock

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds persistent runtime extensions support: introduces TABLECRAFT_EXTENSIONS_KEY, extension helpers (emptyExtensions, cloneExtensions), embeds cloned extensions into toConfig() output, and updates resolveInput() to rehydrate embedded extensions; also bumps package version to 0.1.7 and adds a test for rehydration.

Changes

Cohort / File(s) Summary
Version Bump
packages/engine/package.json
Package version updated from 0.1.60.1.7.
Define: extensions API & builder changes
packages/engine/src/define.ts
Added exported TABLECRAFT_EXTENSIONS_KEY, TableConfigWithExtensions type, emptyExtensions() and cloneExtensions(); TableDefinitionBuilder now accepts/returns configs carrying extensions and toConfig() attaches a cloned, non-enumerable extensions object.
Engine: input resolution & rehydration
packages/engine/src/engine.ts
Imported TABLECRAFT_EXTENSIONS_KEY and emptyExtensions; resolveInput() now validates missing input, extracts embedded extensions from builder or plain config via TABLECRAFT_EXTENSIONS_KEY, and falls back to emptyExtensions() when absent.
Tests: extension rehydration
packages/engine/test/columnMeta.test.ts
Added test to verify runtime extensions are preserved in toConfig() output and correctly rehydrated by rebuilding from that config.

Sequence Diagram

sequenceDiagram
    participant User
    participant Builder as TableDefinitionBuilder
    participant Config as TableConfig
    participant Engine as resolveInput()

    User->>Builder: define table (with runtime extensions)
    Builder->>Builder: collect and clone runtime extensions
    Builder->>Config: toConfig() (attach extensions at TABLECRAFT_EXTENSIONS_KEY)
    User->>Engine: pass builder or config
    Engine->>Config: inspect TABLECRAFT_EXTENSIONS_KEY
    alt embedded extensions present
        Engine->>Engine: extract embeddedExt and plainConfig
        Engine->>Engine: use embedded extensions for runtime state
    else no embedded extensions
        Engine->>Engine: use emptyExtensions()
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I hid a tiny key inside a config,

so tables carry secrets when they take a trip.
Builders hum, engines wake,
extensions hop back in place.
Thump-thump — code snug as a crypt!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: introducing a non-enumerable symbol to preserve runtime extensions during config cloning, which is the core objective of this changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • In toConfig, the symbol-backed extensions property is now created as a normal enumerable property; if you intend it to remain hidden from generic property iteration (beyond just JSON and Object.keys), consider using Object.defineProperty with enumerable: false to match the previous behavior more closely.
  • The new resolveInput branch for non-builder inputs can now return { config: undefined, ext: … } when input is falsy; if callers are not expecting a potentially undefined config, it might be safer to either throw early or keep the previous assumption that input must be a valid config.
  • Using Symbol.for("__tablecraft_ext") makes the extension key global across realms; if isolation between different consumers or versions of the engine is desired, consider using a local Symbol("__tablecraft_ext") instead and wiring it through internal imports.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `toConfig`, the symbol-backed extensions property is now created as a normal enumerable property; if you intend it to remain hidden from generic property iteration (beyond just JSON and `Object.keys`), consider using `Object.defineProperty` with `enumerable: false` to match the previous behavior more closely.
- The new `resolveInput` branch for non-builder inputs can now return `{ config: undefined, ext: … }` when `input` is falsy; if callers are not expecting a potentially undefined `config`, it might be safer to either throw early or keep the previous assumption that `input` must be a valid config.
- Using `Symbol.for("__tablecraft_ext")` makes the extension key global across realms; if isolation between different consumers or versions of the engine is desired, consider using a local `Symbol("__tablecraft_ext")` instead and wiring it through internal imports.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/engine/src/define.ts (1)

1056-1063: Expose the augmented return type from toConfig().

Line 1056 still advertises plain TableConfig, but Lines 1057-1061 now attach TABLECRAFT_EXTENSIONS_KEY. That makes the new behavior invisible to consumers and is why packages/engine/src/engine.ts has to re-cast the input. Returning the symbol-bearing type here keeps the API discoverable and removes the unsafe cast downstream.

Type-safe shape for the embedded extensions
+export type TableConfigWithExtensions<T extends Table = Table> =
+	TableConfig & { [TABLECRAFT_EXTENSIONS_KEY]?: RuntimeExtensions<T> };
+
-	toConfig(): TableConfig {
-		const config = { ...this._config } as TableConfig & {
-			[TABLECRAFT_EXTENSIONS_KEY]?: RuntimeExtensions<T>;
-		};
+	toConfig(): TableConfigWithExtensions<T> {
+		const config = { ...this._config } as TableConfigWithExtensions<T>;
 
 		config[TABLECRAFT_EXTENSIONS_KEY] = this._ext;
 
 		return config;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/engine/src/define.ts` around lines 1056 - 1063, The toConfig()
method currently declares a return type of TableConfig but actually returns an
object that includes the TABLECRAFT_EXTENSIONS_KEY with RuntimeExtensions<T>,
causing callers to re-cast; change the declared return type of toConfig() to
TableConfig & { [TABLECRAFT_EXTENSIONS_KEY]?: RuntimeExtensions<T> } so the
symbol-bearing shape is exposed to consumers (update the signature of toConfig()
accordingly and remove downstream unsafe casts such as the re-cast in
engine.ts).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/engine/src/engine.ts`:
- Around line 57-78: The resolveInput function currently allows a falsy input
and returns config: plainConfig which can be undefined; change resolveInput to
validate input at the start (e.g., check for null/undefined) and throw a clear,
descriptive error when input is falsy so callers don't later get TypeError when
accessing config.base or config.name; keep using the same
TABLECRAFT_EXTENSIONS_KEY and embeddedExt logic, but ensure plainConfig is
present before building the returned object (throw a ConfigError or similar from
resolveInput if plainConfig is null/undefined).

---

Nitpick comments:
In `@packages/engine/src/define.ts`:
- Around line 1056-1063: The toConfig() method currently declares a return type
of TableConfig but actually returns an object that includes the
TABLECRAFT_EXTENSIONS_KEY with RuntimeExtensions<T>, causing callers to re-cast;
change the declared return type of toConfig() to TableConfig & {
[TABLECRAFT_EXTENSIONS_KEY]?: RuntimeExtensions<T> } so the symbol-bearing shape
is exposed to consumers (update the signature of toConfig() accordingly and
remove downstream unsafe casts such as the re-cast in engine.ts).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: db8bc184-07a4-4d1e-9ec6-5078ccd16a91

📥 Commits

Reviewing files that changed from the base of the PR and between 490bcbe and c490e40.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • packages/engine/package.json
  • packages/engine/src/define.ts
  • packages/engine/src/engine.ts

Comment thread packages/engine/src/engine.ts Outdated
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 10, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
tablecraft-demo Ignored Ignored Mar 25, 2026 7:43pm

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/engine/src/engine.ts (1)

69-82: Consider reusing emptyExtensions() to reduce duplication.

This inline object duplicates the structure from emptyExtensions() in define.ts, with the addition of countMode: undefined. If emptyExtensions() were exported and updated to include countMode, this would reduce maintenance burden and ensure consistency.

♻️ Suggested approach

In define.ts, export emptyExtensions and add countMode:

// In define.ts
export function emptyExtensions<T extends Table = Table>(): RuntimeExtensions<T> {
  return {
    computedExpressions: new Map(),
    transforms: new Map(),
    rawSelects: new Map(),
    rawWheres: [],
    dynamicWheres: [],
    rawJoins: [],
    rawOrderBys: [],
    ctes: new Map(),
    sqlJoinConditions: new Map(),
    countMode: undefined,
  };
}

Then in engine.ts:

+import { emptyExtensions } from './define';
 // ...
   return {
     config: plainConfig,
-    ext:
-      embeddedExt ??
-      {
-        computedExpressions: new Map(),
-        transforms: new Map(),
-        rawSelects: new Map(),
-        rawWheres: [],
-        dynamicWheres: [],
-        rawJoins: [],
-        rawOrderBys: [],
-        ctes: new Map(),
-        sqlJoinConditions: new Map(),
-        countMode: undefined,
-      },
+    ext: embeddedExt ?? emptyExtensions(),
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/engine/src/engine.ts` around lines 69 - 82, The inline default
extensions object assigned to ext/embeddedExt duplicates the structure from
define.ts; export and update the existing emptyExtensions<T>()
(RuntimeExtensions) in define.ts to include the countMode property (countMode:
undefined) and then replace the inline literal in engine.ts with a call to
emptyExtensions() (e.g., ext: embeddedExt ?? emptyExtensions()). This ensures
the RuntimeExtensions shape (including countMode) is centralized in
emptyExtensions and removes the duplicated object in engine.ts.
packages/engine/src/define.ts (2)

1062-1062: Consider documenting or cloning the shared _ext reference.

The this._ext object is attached by reference, meaning any subsequent mutations to the builder's extensions (e.g., adding computed expressions) will affect configs already returned by toConfig(). If this is intentional for memory efficiency, a doc comment would help. If isolation is preferred, a shallow clone of the extension maps would be safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/engine/src/define.ts` at line 1062, The returned config currently
embeds the builder's extensions by reference via value: this._ext, which means
later mutations to this._ext will mutate previously returned configs from
toConfig(); either add a doc comment on the value field explaining the
shared-by-reference behavior and intentionality, or change the implementation to
return a shallow clone of the extension maps (i.e., clone this._ext and its
top-level maps/objects before assigning to value) so each toConfig() result is
isolated; locate the assignment using this._ext and the toConfig()/value return
and apply the chosen fix.

70-70: Avoid unnecessary as any cast on the symbol declaration.

The as any cast undermines type safety and is likely unnecessary. A unique symbol should be usable directly as a computed property key in TypeScript. If there's a specific type error prompting this cast, it would be better to address the root cause.

🔧 Suggested fix
-export const TABLECRAFT_EXTENSIONS_KEY: unique symbol = Symbol("__tablecraft_ext") as any;
+export const TABLECRAFT_EXTENSIONS_KEY: unique symbol = Symbol("__tablecraft_ext");

If removing the cast causes a TypeScript error, please share the error message so we can address the underlying issue properly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/engine/src/define.ts` at line 70, Remove the unnecessary "as any"
cast on the TABLECRAFT_EXTENSIONS_KEY declaration by changing export const
TABLECRAFT_EXTENSIONS_KEY: unique symbol = Symbol("__tablecraft_ext") as any; to
a plain unique symbol declaration (export const TABLECRAFT_EXTENSIONS_KEY:
unique symbol = Symbol("__tablecraft_ext");) and then update any places that
used it as a computed key to accept a unique symbol (e.g., adjust index
signatures or interfaces that reference [TABLECRAFT_EXTENSIONS_KEY] to use the
correct symbol-typed property) so TypeScript no longer requires the cast; if
removing the cast produces a compiler error, capture and share the exact
TypeScript error so we can adjust the related type (interface/index signature)
instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/engine/src/define.ts`:
- Around line 1061-1066: The property defined on config using
Object.defineProperty with key TABLECRAFT_EXTENSIONS_KEY is set enumerable:
false which causes the symbol to be omitted by spread/Object.assign; change the
descriptor to enumerable: true (keep configurable: true and writable: true) when
defining TABLECRAFT_EXTENSIONS_KEY on the config object so the symbol survives
spread/assign while remaining invisible to JSON.stringify/Object.keys; update
the Object.defineProperty call where TABLECRAFT_EXTENSIONS_KEY is used in
define.ts accordingly.

---

Nitpick comments:
In `@packages/engine/src/define.ts`:
- Line 1062: The returned config currently embeds the builder's extensions by
reference via value: this._ext, which means later mutations to this._ext will
mutate previously returned configs from toConfig(); either add a doc comment on
the value field explaining the shared-by-reference behavior and intentionality,
or change the implementation to return a shallow clone of the extension maps
(i.e., clone this._ext and its top-level maps/objects before assigning to value)
so each toConfig() result is isolated; locate the assignment using this._ext and
the toConfig()/value return and apply the chosen fix.
- Line 70: Remove the unnecessary "as any" cast on the TABLECRAFT_EXTENSIONS_KEY
declaration by changing export const TABLECRAFT_EXTENSIONS_KEY: unique symbol =
Symbol("__tablecraft_ext") as any; to a plain unique symbol declaration (export
const TABLECRAFT_EXTENSIONS_KEY: unique symbol = Symbol("__tablecraft_ext");)
and then update any places that used it as a computed key to accept a unique
symbol (e.g., adjust index signatures or interfaces that reference
[TABLECRAFT_EXTENSIONS_KEY] to use the correct symbol-typed property) so
TypeScript no longer requires the cast; if removing the cast produces a compiler
error, capture and share the exact TypeScript error so we can adjust the related
type (interface/index signature) instead.

In `@packages/engine/src/engine.ts`:
- Around line 69-82: The inline default extensions object assigned to
ext/embeddedExt duplicates the structure from define.ts; export and update the
existing emptyExtensions<T>() (RuntimeExtensions) in define.ts to include the
countMode property (countMode: undefined) and then replace the inline literal in
engine.ts with a call to emptyExtensions() (e.g., ext: embeddedExt ??
emptyExtensions()). This ensures the RuntimeExtensions shape (including
countMode) is centralized in emptyExtensions and removes the duplicated object
in engine.ts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70b87b85-e233-44c5-bd09-5778bd4a3303

📥 Commits

Reviewing files that changed from the base of the PR and between c490e40 and 114e784.

📒 Files selected for processing (2)
  • packages/engine/src/define.ts
  • packages/engine/src/engine.ts

Comment thread packages/engine/src/define.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/engine/src/define.ts`:
- Line 70: Replace the module-local symbol with a globally registered symbol so
extensions are shared across duplicated package instances: change the
TABLECRAFT_EXTENSIONS_KEY declaration (symbol name TABLECRAFT_EXTENSIONS_KEY) to
use Symbol.for("__tablecraft_ext") instead of Symbol("__tablecraft_ext"); update
any code that imports/compares this symbol (e.g., where engine.ts probes the
extensions key) to rely on the global symbol so different module copies resolve
to the same key.

In `@packages/engine/src/engine.ts`:
- Around line 57-60: The code returns extensions from the live builder field
b._ext, which bypasses the immutable snapshot created by
TableDefinitionBuilder.toConfig(); fix by calling toConfig() first, grab the
config snapshot (e.g. const cfg = b.toConfig()), and return the extensions from
that snapshot (use the snapshot's extension property from cfg) instead of b._ext
so the engine uses the cloned, immutable extensions produced by toConfig();
update the return to use cfg (and its extension field) with
TableDefinitionBuilder.toConfig() and stop referencing
TableDefinitionBuilder._ext directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 03a635f0-d108-439b-a882-e80ef26455b5

📥 Commits

Reviewing files that changed from the base of the PR and between 114e784 and 8cbe758.

📒 Files selected for processing (2)
  • packages/engine/src/define.ts
  • packages/engine/src/engine.ts

Comment thread packages/engine/src/define.ts Outdated
Comment thread packages/engine/src/engine.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/engine/src/define.ts`:
- Around line 1059-1085: The config emitted by toConfig() stores runtime
extensions under TABLECRAFT_EXTENSIONS_KEY but defineTable(table, cfg) currently
calls emptyExtensions() when rebuilding a TableDefinitionBuilder; change
defineTable to check cfg[TABLECRAFT_EXTENSIONS_KEY] and, if present, use that
extensions object (or a shallow clone of it) instead of emptyExtensions() when
constructing the TableDefinitionBuilder so computedExpressions, transforms,
hooks, etc. are preserved; update the TableDefinitionBuilder constructor call
(and any related typing) to accept the rehydrated RuntimeExtensions<T> and
ensure Maps/arrays from the stored value are used (or cloned) to avoid future
mutation leaks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a226086e-273c-4264-9d3e-5e2f782ab571

📥 Commits

Reviewing files that changed from the base of the PR and between 8cbe758 and a0a27a0.

📒 Files selected for processing (2)
  • packages/engine/src/define.ts
  • packages/engine/src/engine.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/engine/src/engine.ts

Comment thread packages/engine/src/define.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/engine/src/define.ts (1)

70-74: ⚠️ Potential issue | 🔴 Critical

Remove the stale second extensions-key/type declaration block.

TABLECRAFT_EXTENSIONS_KEY and TableConfigWithExtensions are declared twice here, which is a TypeScript compile blocker. The later block also switches back to Symbol() instead of Symbol.for(), so even after deduping it would regress the shared-key behavior. Keep the Symbol.for("__tablecraft_ext") declaration as the single source of truth and delete the later duplicate block.

Suggested fix
-export const TABLECRAFT_EXTENSIONS_KEY = Symbol(
-	"tablecraft.runtimeExtensions",
-);
-
-export type TableConfigWithExtensions<T extends Table = Table> = TableConfig & {
-	[TABLECRAFT_EXTENSIONS_KEY]?: RuntimeExtensions<T>;
-};
#!/bin/bash
set -euo pipefail

sed -n '68,130p' packages/engine/src/define.ts

Expected result: exactly one TABLECRAFT_EXTENSIONS_KEY declaration and one TableConfigWithExtensions declaration remain, and the surviving key uses Symbol.for("__tablecraft_ext").

Also applies to: 122-128

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/engine/src/define.ts` around lines 70 - 74, Delete the duplicate
declarations of TABLECRAFT_EXTENSIONS_KEY and TableConfigWithExtensions and keep
only the original declaration that uses Symbol.for("__tablecraft_ext");
specifically, remove the later block that re-declares TABLECRAFT_EXTENSIONS_KEY
using Symbol() and the duplicate TableConfigWithExtensions type so there is
exactly one TABLECRAFT_EXTENSIONS_KEY (Symbol.for("__tablecraft_ext")) and one
TableConfigWithExtensions in the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/engine/src/define.ts`:
- Around line 70-74: Delete the duplicate declarations of
TABLECRAFT_EXTENSIONS_KEY and TableConfigWithExtensions and keep only the
original declaration that uses Symbol.for("__tablecraft_ext"); specifically,
remove the later block that re-declares TABLECRAFT_EXTENSIONS_KEY using Symbol()
and the duplicate TableConfigWithExtensions type so there is exactly one
TABLECRAFT_EXTENSIONS_KEY (Symbol.for("__tablecraft_ext")) and one
TableConfigWithExtensions in the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e605019-ce3b-480b-be73-5bfc6ce2f709

📥 Commits

Reviewing files that changed from the base of the PR and between a0a27a0 and 753d323.

📒 Files selected for processing (2)
  • packages/engine/src/define.ts
  • packages/engine/test/columnMeta.test.ts

@jacksonkasi1 jacksonkasi1 merged commit 85bdbc8 into main Mar 11, 2026
6 checks passed
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