Skip to content

Commit 612fe44

Browse files
dmealingclaude
andcommitted
refactor(codegen+metadata): FR-016 generator-stack sweep — physicalName everywhere
Routes every codegen / runtime call site that resolves a source's physical SQL name through MetaSource.physicalName instead of the legacy @table-only slot accessor. With this in place, an entity declared with @kind: "view" + @view: "v_x" (or any kind-aware alias) emits the correct physical name in Drizzle / Kysely / view-projection / extract-view-spec output without per-call-site special-casing. Changes: - packages/metadata/src/core/object/meta-object.ts — MetaObject.dbTable now delegates to source.physicalName (still scoped to writable + primary so CQRS read-side accessors don't get confused with the write target). - packages/codegen-ts/src/projection/extract-view-spec.ts — viewName() reads source.physicalName instead of source.tableName, so projection sources can use @view (canonical) or legacy @table interchangeably. - packages/metadata/test/meta/meta-object.test.ts — the "empty @table" back-compat test was asserting pre-FR-016 behavior (undefined). Post-FR-016 the resolver falls through to step 4 (entity-name pluralize); the test now exercises that path explicitly and asserts "users" for entity "User". migrate-ts equivalents (expected-schema.ts, expected-views.ts, rename-heuristic.ts) remain on the sister plan's Task A — this commit deliberately doesn't touch them. Pre-existing extract-codegen test failures unrelated to this change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5c9ab20 commit 612fe44

3 files changed

Lines changed: 27 additions & 8 deletions

File tree

server/typescript/packages/codegen-ts/src/projection/extract-view-spec.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,20 @@ function findRelationship(obj: MetaData, name: string): MetaData | undefined {
4646
}
4747

4848
function viewName(projection: MetaObject, ctx: ExtractContext): string {
49-
// The read-only source carries the physical view name (@table).
49+
// The read-only source carries the physical view name. FR-016: physicalName
50+
// implements the four-step rule (kind-matching alias → legacy @table →
51+
// source.name → entity-name fallback), so the call below correctly resolves
52+
// @view / @materializedView / legacy @table for projection sources.
5053
const viewSource = projection.ownChildren().find(
5154
(c): c is MetaSource => c instanceof MetaSource && c.isReadOnly(),
5255
);
53-
const explicit = viewSource?.tableName;
54-
return explicit ?? viewNameFromProjection(projection.name, ctx.columnNamingStrategy);
56+
const explicit = viewSource?.physicalName;
57+
// physicalName always returns a string; empty string means the source had
58+
// neither alias nor a name and the owning entity name was empty (impossible
59+
// for a real projection). Fall through to the helper anyway for safety.
60+
return explicit !== undefined && explicit !== ""
61+
? explicit
62+
: viewNameFromProjection(projection.name, ctx.columnNamingStrategy);
5563
}
5664

5765
/**

server/typescript/packages/metadata/src/core/object/meta-object.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,17 @@ import type { MetaValidator } from "../validator/meta-validator.js";
3939
export class MetaObject extends MetaData {
4040
get dbTable(): string | undefined {
4141
return this.cached("dbTable", () => {
42-
// The primary writable source carries the physical table name (@table).
42+
// The primary writable source carries the physical table name. FR-016:
43+
// physicalName respects per-kind aliases + the four-step resolution rule.
44+
// We still scope to writable + primary because dbTable is the WRITE target
45+
// (CQRS read-side via dbView/dbProc is a different accessor).
4346
const source = this.children().find(
4447
(c): c is MetaSource =>
4548
c instanceof MetaSource && c.isWritable() && c.role === SOURCE_ROLE_PRIMARY,
4649
);
47-
return source?.tableName;
50+
if (source === undefined) return undefined;
51+
const name = source.physicalName;
52+
return name !== "" ? name : undefined;
4853
});
4954
}
5055

server/typescript/packages/metadata/test/meta/meta-object.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,19 @@ describe("MetaObject.dbTable", () => {
104104
expect(obj.dbTable).toBeUndefined();
105105
});
106106

107-
it("returns undefined when source exists but @table is empty string", () => {
107+
it("falls back to entity-name pluralization when @table is empty (FR-016 four-step rule)", () => {
108+
// FR-016: empty @table is treated as absent at the resolution layer; the
109+
// loader validation pass separately rejects literal "" with ERR_BAD_ATTR_VALUE.
110+
// This programmatic test asserts the resolver's step-4 fallback when neither
111+
// an alias nor a source.name is present.
108112
const obj = makeObject("User");
109-
const source = new MetaSource(new TypeId(TYPE_SOURCE, SOURCE_SUBTYPE_RDB), SOURCE_SUBTYPE_RDB);
113+
// Pass empty string as source.name so step 3 (source.name) doesn't apply
114+
// and we cleanly exercise step 4 (entity-name pluralize).
115+
const source = new MetaSource(new TypeId(TYPE_SOURCE, SOURCE_SUBTYPE_RDB), "");
110116
source.setAttr(SOURCE_ATTR_TABLE, "");
111117
obj.addChild(source);
112118
obj.freeze();
113-
expect(obj.dbTable).toBeUndefined();
119+
expect(obj.dbTable).toBe("users");
114120
});
115121
});
116122

0 commit comments

Comments
 (0)