@happyvertical/smrt-core 0.57.9 → 0.57.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"inheritance-resolver.d.ts","sourceRoot":"","sources":["../../src/registry/inheritance-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQ5D,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,YAAS,CAAC;AAIhE;;;;;;;;;GASG;AACH,KAAK,wBAAwB,GAAG,eAAe,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,UAAU,GAAG,MAAM,EAAE,CAyBvE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAgG/D;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAkGvC;AA4BD,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,wBAAwB,EACrC,UAAU,EAAE,wBAAwB,EACpC,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAgF1B;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAiDxC"}
1
+ {"version":3,"file":"inheritance-resolver.d.ts","sourceRoot":"","sources":["../../src/registry/inheritance-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQ5D,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,YAAS,CAAC;AAIhE;;;;;;;;;GASG;AACH,KAAK,wBAAwB,GAAG,eAAe,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,UAAU,GAAG,MAAM,EAAE,CAyBvE;AAoCD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAgG/D;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAkGvC;AA4BD,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,wBAAwB,EACrC,UAAU,EAAE,wBAAwB,EACpC,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAgF1B;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAiDxC"}
@@ -30,6 +30,24 @@ function buildInheritanceChain(ctor) {
30
30
  }
31
31
  return chain;
32
32
  }
33
+ /**
34
+ * {@link findClassStrict} for a registered class's parent. An ambiguous
35
+ * simple `extends` is rethrown naming the subclass and its package, so the
36
+ * error points at the class whose manifest needs a qualified parent (#4145).
37
+ */
38
+ function findParentClassStrict(child, parentName) {
39
+ try {
40
+ return findClassStrict(parentName, child.packageName);
41
+ } catch (error) {
42
+ if (!(error instanceof ConfigurationError) || error.code !== "CONFIG_AMBIGUOUS_CLASS") throw error;
43
+ const subclass = child.qualifiedName ?? child.name;
44
+ throw new ConfigurationError(`${error.message} Subclass ${subclass} declares \`extends ${parentName}\` without a package; regenerate ${child.packageName ?? "its"} manifest so the parent is qualified from its import (extendsQualified).`, "CONFIG_AMBIGUOUS_CLASS", {
45
+ ...error.details ?? {},
46
+ subclass,
47
+ subclassPackage: child.packageName
48
+ }, error);
49
+ }
50
+ }
33
51
  function getInheritanceChain(className) {
34
52
  const cache = getInheritanceCache();
35
53
  const cached = cache.get(className);
@@ -56,7 +74,7 @@ function getInheritanceChain(className) {
56
74
  chain.unshift(current.qualifiedName ?? current.name);
57
75
  if (!current.extends) break;
58
76
  if (current.extends === "SmrtObject" || current.extends === "SmrtClass" || current.extends === "SmrtCollection") break;
59
- const parent = findClassStrict(current.extends, current.packageName);
77
+ const parent = findParentClassStrict(current, current.extends);
60
78
  if (!parent) verboseLog(`[ObjectRegistry] Parent class '${current.extends}' not found while building inheritance chain. Ensure the parent package is installed and loadAllManifests() was called at startup.`);
61
79
  current = parent;
62
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"inheritance-resolver.js","names":[],"sources":["../../src/registry/inheritance-resolver.ts"],"sourcesContent":["/**\n * Inheritance resolution module for the SMRT ObjectRegistry.\n *\n * Extracted from registry.ts as part of issue #1006.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { ConfigurationError } from '../errors';\nimport type { SmrtObject } from '../object';\nimport { ObjectRegistry } from '../registry';\nimport type { MethodDefinition } from '../scanner/types.js';\nimport { prependSmrtSystemFields } from '../system-fields';\nimport { findClass, findClassStrict } from './name-resolver';\nimport {\n getInheritanceCache,\n getInheritanceConfig,\n verboseLog,\n} from './shared-state';\nimport type { RegisteredClass, RegisteredField } from './types';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Runtime field shape merged across an inheritance chain.\n *\n * This is the canonical {@link RegisteredField} shape (a `FieldDefinition`\n * superset that carries the root-level `__tenancy` marker mirrored from the\n * `@tenantId` decorator alongside the canonical `_meta.__tenancy`, a runtime\n * `value`, and an open index signature for forward-compatible keys). Aliasing\n * it keeps merged fields assignable back into the\n * `Map<string, RegisteredField>` on each registered class.\n */\ntype InheritedFieldDefinition = RegisteredField;\n\n/**\n * Build inheritance chain by walking prototype chain\n *\n * Walks from child → parent → ... → SmrtObject, building array from base to child.\n * Stops at SmrtObject (the framework base class).\n */\nexport function buildInheritanceChain(ctor: typeof SmrtObject): string[] {\n const chain: string[] = [];\n const visited = new Set<Function>();\n let current: Function | null = ctor;\n\n // Walk up the prototype chain\n while (current?.name) {\n if (current.name === 'SmrtObject') {\n break;\n }\n\n if (visited.has(current)) {\n throw ConfigurationError.circularInheritance(\n current.name,\n Array.from(chain),\n );\n }\n\n visited.add(current);\n chain.unshift(current.name);\n\n current = Object.getPrototypeOf(current);\n }\n\n return chain;\n}\n\nexport function getInheritanceChain(className: string): string[] {\n const cache = getInheritanceCache();\n\n // Check cache first\n const cached = cache.get(className);\n if (cached) {\n return cached;\n }\n\n // Get registered class\n const registered = findClass(className);\n if (!registered) {\n return [];\n }\n\n // Check if already computed and stored\n if (registered.inheritanceChain) {\n cache.set(className, registered.inheritanceChain);\n return registered.inheritanceChain;\n }\n\n // Build chain from `extends` field (works for manifest-loaded classes)\n // This is critical because manifest-loaded classes have stub constructors\n // that only extend SmrtObject, not their actual parent class.\n // The `extends` field IS stored correctly during registerFromManifest().\n const chain: string[] = [];\n const visited = new Set<RegisteredClass>(); // Cycle detection using object identity\n let current: RegisteredClass | undefined = registered;\n let circularDetected = false;\n while (current) {\n // Cycle detection: use object identity to handle cases where distinct\n // classes share the same name (e.g., histrio:Performer vs smrt-video:Performer)\n if (visited.has(current)) {\n // Self-referential (chain length 1) is expected when a child package\n // extends a parent with the same className and only one is installed.\n // Longer cycles indicate misconfigured manifests.\n if (chain.length > 1) {\n logger.warn(\n `[ObjectRegistry] Circular inheritance detected in chain: ${chain.join(' -> ')} -> ${current.name}. ` +\n `Check that '${current.extends ?? current.name}' resolves to the correct package class.`,\n );\n circularDetected = true;\n }\n break;\n }\n visited.add(current);\n\n // R5-canon: emit qualified names when available, matching the\n // public `ObjectRegistry.getInheritanceChain` contract. Falls back\n // to simple name for classes registered without a package context.\n chain.unshift(current.qualifiedName ?? current.name); // [ancestor, ..., descendant]\n if (!current.extends) break;\n\n // Skip framework base classes that are never registered\n if (\n current.extends === 'SmrtObject' ||\n current.extends === 'SmrtClass' ||\n current.extends === 'SmrtCollection'\n ) {\n break;\n }\n\n // Try to find the parent class\n // Issue #1004/#1005: Use findClassStrict with package context for\n // unambiguous resolution. If extends is already qualified (from\n // qualifyExtendsName), this is an O(1) direct lookup.\n // If ambiguous, let the ConfigurationError propagate — callers\n // should fix their manifests rather than silently get wrong results.\n const parent = findClassStrict(current.extends, current.packageName);\n\n // Issue #1007: Parent not found — the class is not registered.\n // After loadAllManifests() completes at startup, all classes are\n // pre-registered so this path is only hit when a package is not\n // installed or the manifest is stale. We gracefully end the chain\n // here instead of mutating registry state during a read operation.\n if (!parent) {\n verboseLog(\n `[ObjectRegistry] Parent class '${current.extends}' not found ` +\n `while building inheritance chain. Ensure the parent package ` +\n `is installed and loadAllManifests() was called at startup.`,\n );\n }\n\n current = parent;\n }\n\n // Only cache complete, non-circular chains.\n // Caching a broken chain from a circular detection would cause all future\n // lookups to return incorrect inheritance data (e.g., [\"VideoShot\",\"VideoShot\"]\n // instead of [\"Content\",\"VideoShot\"]), breaking STI table resolution.\n if (!circularDetected) {\n registered.inheritanceChain = chain;\n cache.set(className, chain);\n }\n\n return chain;\n}\n\nexport async function getAllFields(\n className: string,\n): Promise<Map<string, RegisteredField>> {\n let registered = findClass(className);\n if (!registered) {\n const loaded = await ObjectRegistry.tryLoadFromExternalPackage(className);\n if (!loaded) {\n return new Map();\n }\n\n registered = findClass(className);\n }\n\n if (!registered) {\n return new Map();\n }\n\n // Ensure manifest is loaded (handles external packages)\n await ObjectRegistry.ensureManifestLoaded(className);\n\n // Check if class has inheritedFields cache (set during manifest loading)\n if (registered.inheritedFields) {\n return prependSmrtSystemFields(new Map(registered.inheritedFields));\n }\n\n // Get config for error handling behavior\n const { onMissingAncestor } = getInheritanceConfig();\n\n // For local classes (not from manifests), merge fields from inheritance chain\n const allFields = new Map<string, InheritedFieldDefinition>();\n const chain = getInheritanceChain(className);\n\n // Walk chain from base to child (parent fields first)\n for (const ancestorName of chain) {\n // Skip framework base classes\n if (\n ancestorName === 'SmrtObject' ||\n ancestorName === 'SmrtClass' ||\n ancestorName === 'SmrtCollection'\n ) {\n continue;\n }\n\n try {\n await ObjectRegistry.ensureManifestLoaded(ancestorName);\n } catch {\n // Local classes and pure test fixtures may not have manifests. Continue\n // with whatever runtime metadata is already available.\n }\n\n const ancestor = findClass(ancestorName);\n if (!ancestor) {\n // Handle missing ancestors according to config\n const message = `Missing ancestor class \"${ancestorName}\" in inheritance chain for \"${className}\"`;\n\n if (onMissingAncestor === 'error') {\n throw new Error(\n `${message}\\n\\n` +\n `This usually means:\\n` +\n ` 1. The parent class is not registered with @smrt()\\n` +\n ` 2. The parent class file is not imported\\n` +\n ` 3. The manifest does not include the parent class\\n\\n` +\n `To fix:\\n` +\n ` - Ensure all parent classes use @smrt() decorator\\n` +\n ` - Import all parent class files before child classes\\n` +\n ` - Rebuild to regenerate manifest\\n` +\n ` - Or set smrt.inheritance.onMissingAncestor='warn' in config`,\n );\n } else if (onMissingAncestor === 'warn') {\n logger.warn(`[ObjectRegistry] ${message}`);\n }\n\n continue;\n }\n\n // Merge fields from this ancestor\n for (const [fieldName, field] of ancestor.fields) {\n const normalizedField = normalizeFrameworkInheritedField(\n ancestorName,\n fieldName,\n field,\n className,\n );\n const existingField = allFields.get(fieldName);\n if (!existingField) {\n // New field from parent\n allFields.set(fieldName, normalizedField);\n } else {\n // Field exists - merge configs\n allFields.set(\n fieldName,\n mergeFieldConfigs(existingField, normalizedField, fieldName),\n );\n }\n }\n }\n\n registered.inheritedFields = new Map(allFields);\n\n return prependSmrtSystemFields(new Map<string, RegisteredField>(allFields));\n}\n\nfunction normalizeFrameworkInheritedField(\n ancestorName: string,\n fieldName: string,\n field: InheritedFieldDefinition,\n childClassName: string,\n): InheritedFieldDefinition {\n const simpleAncestorName = ancestorName.includes(':')\n ? ancestorName.split(':').pop()\n : ancestorName;\n\n if (simpleAncestorName === 'SmrtHierarchical' && fieldName === 'parentId') {\n return {\n ...field,\n type: 'foreignKey',\n related: childClassName,\n required: false,\n _meta: {\n ...(field._meta || {}),\n nullable: true,\n },\n };\n }\n\n return field;\n}\n\nexport function mergeFieldConfigs(\n parentField: InheritedFieldDefinition,\n childField: InheritedFieldDefinition,\n fieldName: string,\n): InheritedFieldDefinition {\n // Start with parent field as base\n const merged: InheritedFieldDefinition = { ...parentField };\n\n // Type: Child wins (warn if different)\n if (childField.type && childField.type !== parentField.type) {\n // Don't warn for tenantId field - expected type conflict between decorator and AST\n // The @tenantId decorator registers as 'foreignKey' but AST sees 'text' from type annotation\n // __tenancy can be at root level (from @tenantId decorator) or under _meta (from @smrt({ tenantScoped: true }))\n const isTenantField =\n parentField.__tenancy?.isTenantIdField ||\n parentField._meta?.__tenancy?.isTenantIdField;\n if (!(isTenantField && fieldName === 'tenantId')) {\n logger.warn(\n `Field type mismatch: \"${fieldName}\" is ${parentField.type} in parent but ${childField.type} in child. Using child type.`,\n );\n }\n merged.type = childField.type;\n }\n\n // _meta: Merge with child precedence\n if (childField._meta || parentField._meta) {\n merged._meta = {\n ...(parentField._meta || {}),\n ...(childField._meta || {}),\n };\n\n // Special handling for numeric constraints (take strictest)\n if (\n parentField._meta?.min !== undefined &&\n childField._meta?.min !== undefined\n ) {\n // Take the larger min (strictest lower bound)\n merged._meta.min = Math.max(parentField._meta.min, childField._meta.min);\n }\n if (\n parentField._meta?.max !== undefined &&\n childField._meta?.max !== undefined\n ) {\n // Take the smaller max (strictest upper bound)\n merged._meta.max = Math.min(parentField._meta.max, childField._meta.max);\n }\n\n // Validators: Combine (both must pass)\n if (parentField._meta?.validate && childField._meta?.validate) {\n // `_meta.validate` is carried through the open `FieldMeta` index\n // signature as `unknown`; both sides are field-validator callbacks.\n type FieldValidator = (value: unknown) => boolean | Promise<boolean>;\n const parentValidator = parentField._meta.validate as FieldValidator;\n const childValidator = childField._meta.validate as FieldValidator;\n merged._meta.validate = async (value: unknown) => {\n const parentResult = await parentValidator(value);\n const childResult = await childValidator(value);\n return parentResult && childResult;\n };\n }\n\n // Unique: Take OR (unique if either says unique)\n if (parentField._meta?.unique || childField._meta?.unique) {\n merged._meta.unique = true;\n }\n }\n\n // __tenancy: Preserve from parent (decorator takes precedence)\n // This ensures @tenantId decorator metadata survives type conflicts (Issue #841)\n // The decorator registers __tenancy metadata, but AST scanner may override the type.\n // We need to preserve __tenancy so the interceptor can identify tenant fields.\n if (parentField.__tenancy || childField.__tenancy) {\n merged.__tenancy = {\n ...(childField.__tenancy || {}),\n ...(parentField.__tenancy || {}), // Parent (decorator) wins\n };\n }\n\n // Value: Child wins\n if (childField.value !== undefined) {\n merged.value = childField.value;\n }\n\n return merged;\n}\n\nexport async function getAllMethods(\n className: string,\n): Promise<Map<string, MethodDefinition>> {\n const registered = findClass(className);\n if (!registered) {\n return new Map();\n }\n\n // Check cache first\n if (registered.inheritedMethods) {\n return new Map(registered.inheritedMethods);\n }\n\n // Build merged methods from inheritance chain\n const allMethods = new Map<string, MethodDefinition>();\n const chain = getInheritanceChain(className);\n\n // Walk chain from base to child (parent methods first)\n for (const ancestorName of chain) {\n // Skip framework base classes (check BEFORE looking up in registry)\n if (\n ancestorName === 'SmrtObject' ||\n ancestorName === 'SmrtClass' ||\n ancestorName === 'SmrtCollection'\n ) {\n continue;\n }\n\n // Load manifest for ancestor class (handles external packages)\n // This ensures inherited methods from external packages are available\n try {\n await ObjectRegistry.ensureManifestLoaded(ancestorName);\n } catch (error) {\n // Manifest loading failed - this is expected for classes not in manifest\n // Continue to next ancestor\n }\n\n const ancestor = findClass(ancestorName);\n if (!ancestor) continue;\n\n // Merge parent methods into result\n for (const [methodName, method] of ancestor.methods) {\n // Child methods override parent methods (no merging)\n allMethods.set(methodName, method);\n }\n }\n\n // Cache the merged result\n registered.inheritedMethods = allMethods;\n\n return new Map(allMethods);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"inheritance-resolver.js","names":[],"sources":["../../src/registry/inheritance-resolver.ts"],"sourcesContent":["/**\n * Inheritance resolution module for the SMRT ObjectRegistry.\n *\n * Extracted from registry.ts as part of issue #1006.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { ConfigurationError } from '../errors';\nimport type { SmrtObject } from '../object';\nimport { ObjectRegistry } from '../registry';\nimport type { MethodDefinition } from '../scanner/types.js';\nimport { prependSmrtSystemFields } from '../system-fields';\nimport { findClass, findClassStrict } from './name-resolver';\nimport {\n getInheritanceCache,\n getInheritanceConfig,\n verboseLog,\n} from './shared-state';\nimport type { RegisteredClass, RegisteredField } from './types';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Runtime field shape merged across an inheritance chain.\n *\n * This is the canonical {@link RegisteredField} shape (a `FieldDefinition`\n * superset that carries the root-level `__tenancy` marker mirrored from the\n * `@tenantId` decorator alongside the canonical `_meta.__tenancy`, a runtime\n * `value`, and an open index signature for forward-compatible keys). Aliasing\n * it keeps merged fields assignable back into the\n * `Map<string, RegisteredField>` on each registered class.\n */\ntype InheritedFieldDefinition = RegisteredField;\n\n/**\n * Build inheritance chain by walking prototype chain\n *\n * Walks from child → parent → ... → SmrtObject, building array from base to child.\n * Stops at SmrtObject (the framework base class).\n */\nexport function buildInheritanceChain(ctor: typeof SmrtObject): string[] {\n const chain: string[] = [];\n const visited = new Set<Function>();\n let current: Function | null = ctor;\n\n // Walk up the prototype chain\n while (current?.name) {\n if (current.name === 'SmrtObject') {\n break;\n }\n\n if (visited.has(current)) {\n throw ConfigurationError.circularInheritance(\n current.name,\n Array.from(chain),\n );\n }\n\n visited.add(current);\n chain.unshift(current.name);\n\n current = Object.getPrototypeOf(current);\n }\n\n return chain;\n}\n\n/**\n * {@link findClassStrict} for a registered class's parent. An ambiguous\n * simple `extends` is rethrown naming the subclass and its package, so the\n * error points at the class whose manifest needs a qualified parent (#4145).\n */\nfunction findParentClassStrict(\n child: RegisteredClass,\n parentName: string,\n): RegisteredClass | undefined {\n try {\n return findClassStrict(parentName, child.packageName);\n } catch (error) {\n if (\n !(error instanceof ConfigurationError) ||\n error.code !== 'CONFIG_AMBIGUOUS_CLASS'\n ) {\n throw error;\n }\n const subclass = child.qualifiedName ?? child.name;\n throw new ConfigurationError(\n `${error.message} Subclass ${subclass} declares \\`extends ${parentName}\\` ` +\n `without a package; regenerate ${child.packageName ?? 'its'} manifest ` +\n `so the parent is qualified from its import (extendsQualified).`,\n 'CONFIG_AMBIGUOUS_CLASS',\n {\n ...(error.details ?? {}),\n subclass,\n subclassPackage: child.packageName,\n },\n error,\n );\n }\n}\n\nexport function getInheritanceChain(className: string): string[] {\n const cache = getInheritanceCache();\n\n // Check cache first\n const cached = cache.get(className);\n if (cached) {\n return cached;\n }\n\n // Get registered class\n const registered = findClass(className);\n if (!registered) {\n return [];\n }\n\n // Check if already computed and stored\n if (registered.inheritanceChain) {\n cache.set(className, registered.inheritanceChain);\n return registered.inheritanceChain;\n }\n\n // Build chain from `extends` field (works for manifest-loaded classes)\n // This is critical because manifest-loaded classes have stub constructors\n // that only extend SmrtObject, not their actual parent class.\n // The `extends` field IS stored correctly during registerFromManifest().\n const chain: string[] = [];\n const visited = new Set<RegisteredClass>(); // Cycle detection using object identity\n let current: RegisteredClass | undefined = registered;\n let circularDetected = false;\n while (current) {\n // Cycle detection: use object identity to handle cases where distinct\n // classes share the same name (e.g., histrio:Performer vs smrt-video:Performer)\n if (visited.has(current)) {\n // Self-referential (chain length 1) is expected when a child package\n // extends a parent with the same className and only one is installed.\n // Longer cycles indicate misconfigured manifests.\n if (chain.length > 1) {\n logger.warn(\n `[ObjectRegistry] Circular inheritance detected in chain: ${chain.join(' -> ')} -> ${current.name}. ` +\n `Check that '${current.extends ?? current.name}' resolves to the correct package class.`,\n );\n circularDetected = true;\n }\n break;\n }\n visited.add(current);\n\n // R5-canon: emit qualified names when available, matching the\n // public `ObjectRegistry.getInheritanceChain` contract. Falls back\n // to simple name for classes registered without a package context.\n chain.unshift(current.qualifiedName ?? current.name); // [ancestor, ..., descendant]\n if (!current.extends) break;\n\n // Skip framework base classes that are never registered\n if (\n current.extends === 'SmrtObject' ||\n current.extends === 'SmrtClass' ||\n current.extends === 'SmrtCollection'\n ) {\n break;\n }\n\n // Try to find the parent class\n // Issue #1004/#1005: Use findClassStrict with package context for\n // unambiguous resolution. If extends is already qualified (from\n // qualifyExtendsName), this is an O(1) direct lookup.\n // If ambiguous, let the ConfigurationError propagate — callers\n // should fix their manifests rather than silently get wrong results.\n const parent = findParentClassStrict(current, current.extends);\n\n // Issue #1007: Parent not found — the class is not registered.\n // After loadAllManifests() completes at startup, all classes are\n // pre-registered so this path is only hit when a package is not\n // installed or the manifest is stale. We gracefully end the chain\n // here instead of mutating registry state during a read operation.\n if (!parent) {\n verboseLog(\n `[ObjectRegistry] Parent class '${current.extends}' not found ` +\n `while building inheritance chain. Ensure the parent package ` +\n `is installed and loadAllManifests() was called at startup.`,\n );\n }\n\n current = parent;\n }\n\n // Only cache complete, non-circular chains.\n // Caching a broken chain from a circular detection would cause all future\n // lookups to return incorrect inheritance data (e.g., [\"VideoShot\",\"VideoShot\"]\n // instead of [\"Content\",\"VideoShot\"]), breaking STI table resolution.\n if (!circularDetected) {\n registered.inheritanceChain = chain;\n cache.set(className, chain);\n }\n\n return chain;\n}\n\nexport async function getAllFields(\n className: string,\n): Promise<Map<string, RegisteredField>> {\n let registered = findClass(className);\n if (!registered) {\n const loaded = await ObjectRegistry.tryLoadFromExternalPackage(className);\n if (!loaded) {\n return new Map();\n }\n\n registered = findClass(className);\n }\n\n if (!registered) {\n return new Map();\n }\n\n // Ensure manifest is loaded (handles external packages)\n await ObjectRegistry.ensureManifestLoaded(className);\n\n // Check if class has inheritedFields cache (set during manifest loading)\n if (registered.inheritedFields) {\n return prependSmrtSystemFields(new Map(registered.inheritedFields));\n }\n\n // Get config for error handling behavior\n const { onMissingAncestor } = getInheritanceConfig();\n\n // For local classes (not from manifests), merge fields from inheritance chain\n const allFields = new Map<string, InheritedFieldDefinition>();\n const chain = getInheritanceChain(className);\n\n // Walk chain from base to child (parent fields first)\n for (const ancestorName of chain) {\n // Skip framework base classes\n if (\n ancestorName === 'SmrtObject' ||\n ancestorName === 'SmrtClass' ||\n ancestorName === 'SmrtCollection'\n ) {\n continue;\n }\n\n try {\n await ObjectRegistry.ensureManifestLoaded(ancestorName);\n } catch {\n // Local classes and pure test fixtures may not have manifests. Continue\n // with whatever runtime metadata is already available.\n }\n\n const ancestor = findClass(ancestorName);\n if (!ancestor) {\n // Handle missing ancestors according to config\n const message = `Missing ancestor class \"${ancestorName}\" in inheritance chain for \"${className}\"`;\n\n if (onMissingAncestor === 'error') {\n throw new Error(\n `${message}\\n\\n` +\n `This usually means:\\n` +\n ` 1. The parent class is not registered with @smrt()\\n` +\n ` 2. The parent class file is not imported\\n` +\n ` 3. The manifest does not include the parent class\\n\\n` +\n `To fix:\\n` +\n ` - Ensure all parent classes use @smrt() decorator\\n` +\n ` - Import all parent class files before child classes\\n` +\n ` - Rebuild to regenerate manifest\\n` +\n ` - Or set smrt.inheritance.onMissingAncestor='warn' in config`,\n );\n } else if (onMissingAncestor === 'warn') {\n logger.warn(`[ObjectRegistry] ${message}`);\n }\n\n continue;\n }\n\n // Merge fields from this ancestor\n for (const [fieldName, field] of ancestor.fields) {\n const normalizedField = normalizeFrameworkInheritedField(\n ancestorName,\n fieldName,\n field,\n className,\n );\n const existingField = allFields.get(fieldName);\n if (!existingField) {\n // New field from parent\n allFields.set(fieldName, normalizedField);\n } else {\n // Field exists - merge configs\n allFields.set(\n fieldName,\n mergeFieldConfigs(existingField, normalizedField, fieldName),\n );\n }\n }\n }\n\n registered.inheritedFields = new Map(allFields);\n\n return prependSmrtSystemFields(new Map<string, RegisteredField>(allFields));\n}\n\nfunction normalizeFrameworkInheritedField(\n ancestorName: string,\n fieldName: string,\n field: InheritedFieldDefinition,\n childClassName: string,\n): InheritedFieldDefinition {\n const simpleAncestorName = ancestorName.includes(':')\n ? ancestorName.split(':').pop()\n : ancestorName;\n\n if (simpleAncestorName === 'SmrtHierarchical' && fieldName === 'parentId') {\n return {\n ...field,\n type: 'foreignKey',\n related: childClassName,\n required: false,\n _meta: {\n ...(field._meta || {}),\n nullable: true,\n },\n };\n }\n\n return field;\n}\n\nexport function mergeFieldConfigs(\n parentField: InheritedFieldDefinition,\n childField: InheritedFieldDefinition,\n fieldName: string,\n): InheritedFieldDefinition {\n // Start with parent field as base\n const merged: InheritedFieldDefinition = { ...parentField };\n\n // Type: Child wins (warn if different)\n if (childField.type && childField.type !== parentField.type) {\n // Don't warn for tenantId field - expected type conflict between decorator and AST\n // The @tenantId decorator registers as 'foreignKey' but AST sees 'text' from type annotation\n // __tenancy can be at root level (from @tenantId decorator) or under _meta (from @smrt({ tenantScoped: true }))\n const isTenantField =\n parentField.__tenancy?.isTenantIdField ||\n parentField._meta?.__tenancy?.isTenantIdField;\n if (!(isTenantField && fieldName === 'tenantId')) {\n logger.warn(\n `Field type mismatch: \"${fieldName}\" is ${parentField.type} in parent but ${childField.type} in child. Using child type.`,\n );\n }\n merged.type = childField.type;\n }\n\n // _meta: Merge with child precedence\n if (childField._meta || parentField._meta) {\n merged._meta = {\n ...(parentField._meta || {}),\n ...(childField._meta || {}),\n };\n\n // Special handling for numeric constraints (take strictest)\n if (\n parentField._meta?.min !== undefined &&\n childField._meta?.min !== undefined\n ) {\n // Take the larger min (strictest lower bound)\n merged._meta.min = Math.max(parentField._meta.min, childField._meta.min);\n }\n if (\n parentField._meta?.max !== undefined &&\n childField._meta?.max !== undefined\n ) {\n // Take the smaller max (strictest upper bound)\n merged._meta.max = Math.min(parentField._meta.max, childField._meta.max);\n }\n\n // Validators: Combine (both must pass)\n if (parentField._meta?.validate && childField._meta?.validate) {\n // `_meta.validate` is carried through the open `FieldMeta` index\n // signature as `unknown`; both sides are field-validator callbacks.\n type FieldValidator = (value: unknown) => boolean | Promise<boolean>;\n const parentValidator = parentField._meta.validate as FieldValidator;\n const childValidator = childField._meta.validate as FieldValidator;\n merged._meta.validate = async (value: unknown) => {\n const parentResult = await parentValidator(value);\n const childResult = await childValidator(value);\n return parentResult && childResult;\n };\n }\n\n // Unique: Take OR (unique if either says unique)\n if (parentField._meta?.unique || childField._meta?.unique) {\n merged._meta.unique = true;\n }\n }\n\n // __tenancy: Preserve from parent (decorator takes precedence)\n // This ensures @tenantId decorator metadata survives type conflicts (Issue #841)\n // The decorator registers __tenancy metadata, but AST scanner may override the type.\n // We need to preserve __tenancy so the interceptor can identify tenant fields.\n if (parentField.__tenancy || childField.__tenancy) {\n merged.__tenancy = {\n ...(childField.__tenancy || {}),\n ...(parentField.__tenancy || {}), // Parent (decorator) wins\n };\n }\n\n // Value: Child wins\n if (childField.value !== undefined) {\n merged.value = childField.value;\n }\n\n return merged;\n}\n\nexport async function getAllMethods(\n className: string,\n): Promise<Map<string, MethodDefinition>> {\n const registered = findClass(className);\n if (!registered) {\n return new Map();\n }\n\n // Check cache first\n if (registered.inheritedMethods) {\n return new Map(registered.inheritedMethods);\n }\n\n // Build merged methods from inheritance chain\n const allMethods = new Map<string, MethodDefinition>();\n const chain = getInheritanceChain(className);\n\n // Walk chain from base to child (parent methods first)\n for (const ancestorName of chain) {\n // Skip framework base classes (check BEFORE looking up in registry)\n if (\n ancestorName === 'SmrtObject' ||\n ancestorName === 'SmrtClass' ||\n ancestorName === 'SmrtCollection'\n ) {\n continue;\n }\n\n // Load manifest for ancestor class (handles external packages)\n // This ensures inherited methods from external packages are available\n try {\n await ObjectRegistry.ensureManifestLoaded(ancestorName);\n } catch (error) {\n // Manifest loading failed - this is expected for classes not in manifest\n // Continue to next ancestor\n }\n\n const ancestor = findClass(ancestorName);\n if (!ancestor) continue;\n\n // Merge parent methods into result\n for (const [methodName, method] of ancestor.methods) {\n // Child methods override parent methods (no merging)\n allMethods.set(methodName, method);\n }\n }\n\n // Cache the merged result\n registered.inheritedMethods = allMethods;\n\n return new Map(allMethods);\n}\n"],"mappings":"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"}
@@ -47,6 +47,31 @@ export declare class ManifestGenerator {
47
47
  packageName?: string;
48
48
  packageJson?: PackageJsonLike;
49
49
  }): void;
50
+ /**
51
+ * Drop an `extendsQualified` naming ANOTHER package unless that package's
52
+ * manifest declares the class under exactly that qualified name (#4145).
53
+ *
54
+ * The scanner qualifies a parent from the import it is bound to, but a
55
+ * module can re-export another package's class, and an alias-like specifier
56
+ * (`@app/models`) can look like a package. Neither may become a parent the
57
+ * registry cannot find, so an unconfirmed qualification falls back to the
58
+ * simple `extends` and today's resolution. Same-package qualifications are
59
+ * left alone: they name this manifest's own classes.
60
+ */
61
+ verifyImportedParents(manifest: SmartObjectManifest): void;
62
+ /**
63
+ * The name to resolve an object's parent by: the import-derived qualified
64
+ * name when the scanner traced one (#4145), else the simple `extends`.
65
+ */
66
+ private parentRef;
67
+ /**
68
+ * Resolve a parent reference from {@link parentRef}. A qualified reference
69
+ * matches only that package's class (this manifest's, else the dependency's
70
+ * manifest), never a same-named class of another package; a simple one
71
+ * keeps the established local-then-dependency lookup. `child` is skipped so
72
+ * a local class named like its imported parent never resolves to itself.
73
+ */
74
+ private resolveParentDefinition;
50
75
  /**
51
76
  * Stamp `importPath` (via {@link determineImportPath}) on every object
52
77
  * that does not already have one. Safe to call unconditionally: objects
@@ -169,6 +194,11 @@ export declare class ManifestGenerator {
169
194
  private refreshManifestSchemaDDL;
170
195
  private computeManifestSchemaVersion;
171
196
  private findDescendantsInManifest;
197
+ /**
198
+ * Whether `obj`'s import-qualified parent rules out `baseClassName` (a
199
+ * qualified name of another class with the same simple name).
200
+ */
201
+ private qualifiedParentExcludes;
172
202
  private applySqlTypeOverrides;
173
203
  private getReferenceKind;
174
204
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-generator.d.ts","sourceRoot":"","sources":["../../src/scanner/manifest-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manifest-generator.d.ts","sourceRoot":"","sources":["../../src/scanner/manifest-generator.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { ConfigurationError } from "../errors.js";
2
2
  import { defaultConflictColumns, resolveOwnershipTenantColumn, resolveTenantColumn } from "../schema/conflict-target.js";
3
3
  import { resolveForeignKeyDeleteAction, resolveForeignKeyUpdateAction } from "../schema/foreign-key-policy.js";
4
4
  import { classnameToTablename, toSnakeCase } from "../utils/naming.js";
5
- import { assertScopedPackageName, createQualifiedName, isQualifiedNameAliasFormat, readPreviousQualifiedNames } from "../utils/qualified-names.js";
5
+ import { assertScopedPackageName, createQualifiedName, isQualifiedName, isQualifiedNameAliasFormat, parseQualifiedName, readPreviousQualifiedNames } from "../utils/qualified-names.js";
6
6
  import { isSmrtCollectionExtendsName } from "../registry/collection-resolution.js";
7
7
  import "./types.js";
8
8
  import { loadExternalManifestSync, lookupInManifest } from "../manifest/manifest-loader.js";
@@ -227,6 +227,7 @@ var ManifestGenerator = class {
227
227
  if (!Object.hasOwn(objectDef, "abstractDeclaredApi")) objectDef.abstractDeclaredApi = objectDef.decoratorConfig.api ?? null;
228
228
  }
229
229
  this.assertQualifiedNameAliases(manifest);
230
+ this.verifyImportedParents(manifest);
230
231
  this.normalizeReportTenantScope(manifest);
231
232
  this.injectTenantScopedFields(manifest);
232
233
  this.applyReportSurfaceDefaults(manifest);
@@ -289,6 +290,49 @@ var ManifestGenerator = class {
289
290
  this.resolveImportPaths(manifest, options?.packageName, options?.packageJson);
290
291
  }
291
292
  /**
293
+ * Drop an `extendsQualified` naming ANOTHER package unless that package's
294
+ * manifest declares the class under exactly that qualified name (#4145).
295
+ *
296
+ * The scanner qualifies a parent from the import it is bound to, but a
297
+ * module can re-export another package's class, and an alias-like specifier
298
+ * (`@app/models`) can look like a package. Neither may become a parent the
299
+ * registry cannot find, so an unconfirmed qualification falls back to the
300
+ * simple `extends` and today's resolution. Same-package qualifications are
301
+ * left alone: they name this manifest's own classes.
302
+ */
303
+ verifyImportedParents(manifest) {
304
+ for (const obj of Object.values(manifest.objects)) {
305
+ const parent = obj.extendsQualified;
306
+ if (!parent || !isQualifiedName(parent)) continue;
307
+ const { packageName } = parseQualifiedName(parent);
308
+ if (packageName === (obj.packageName ?? manifest.packageName)) continue;
309
+ const parentManifest = loadExternalManifestSync(packageName, { warn: false });
310
+ if (parentManifest && lookupInManifest(parentManifest, parent)) continue;
311
+ logger.debug(`[manifest-generator] ${obj.className}: ${packageName} does not declare ${parent}; keeping the unqualified parent "${obj.extends}"`);
312
+ delete obj.extendsQualified;
313
+ }
314
+ }
315
+ /**
316
+ * The name to resolve an object's parent by: the import-derived qualified
317
+ * name when the scanner traced one (#4145), else the simple `extends`.
318
+ */
319
+ parentRef(obj) {
320
+ return obj.extendsQualified || obj.extends;
321
+ }
322
+ /**
323
+ * Resolve a parent reference from {@link parentRef}. A qualified reference
324
+ * matches only that package's class (this manifest's, else the dependency's
325
+ * manifest), never a same-named class of another package; a simple one
326
+ * keeps the established local-then-dependency lookup. `child` is skipped so
327
+ * a local class named like its imported parent never resolves to itself.
328
+ */
329
+ resolveParentDefinition(ref, child, manifest, objectsByName) {
330
+ let parent = isQualifiedName(ref) ? manifest.objects[ref] : objectsByName.get(ref);
331
+ if (parent === child) parent = void 0;
332
+ if (!parent && manifest.smrtDependencies?.length) parent = this.loadParentFromExternalPackage(ref, manifest.smrtDependencies, objectsByName);
333
+ return parent === child ? void 0 : parent;
334
+ }
335
+ /**
292
336
  * Stamp `importPath` (via {@link determineImportPath}) on every object
293
337
  * that does not already have one. Safe to call unconditionally: objects
294
338
  * that went through `generateManifest()`'s own inline assignment already
@@ -886,13 +930,20 @@ var ManifestGenerator = class {
886
930
  const classNameLower = this.simpleClassName(obj.className).toLowerCase();
887
931
  const extendsLower = obj.extends ? this.simpleClassName(obj.extends).toLowerCase() : void 0;
888
932
  if (classNameLower === baseClassLower && extendsLower === baseClassLower) continue;
889
- if (extendsLower === baseClassLower) {
933
+ if (extendsLower === baseClassLower && !this.qualifiedParentExcludes(obj, baseClassName)) {
890
934
  descendants.push(name);
891
935
  descendants.push(...this.findDescendantsInManifest(name, manifest, visited));
892
936
  }
893
937
  }
894
938
  return descendants;
895
939
  }
940
+ /**
941
+ * Whether `obj`'s import-qualified parent rules out `baseClassName` (a
942
+ * qualified name of another class with the same simple name).
943
+ */
944
+ qualifiedParentExcludes(obj, baseClassName) {
945
+ return !!obj.extendsQualified && isQualifiedName(baseClassName) && obj.extendsQualified !== baseClassName;
946
+ }
896
947
  applySqlTypeOverrides(obj) {
897
948
  if (!obj.schema?.columns) return;
898
949
  for (const [fieldName, field] of Object.entries(obj.fields || {})) {
@@ -951,16 +1002,15 @@ var ManifestGenerator = class {
951
1002
  objectsByName.set(objDef.className, objDef);
952
1003
  objectsByName.set(objDef.className.toLowerCase(), objDef);
953
1004
  }
954
- let currentClass = obj.extends;
1005
+ let currentClass = this.parentRef(obj);
955
1006
  const visited = /* @__PURE__ */ new Set();
956
1007
  while (currentClass) {
957
1008
  if (visited.has(currentClass)) break;
958
1009
  visited.add(currentClass);
959
- let parentObj = objectsByName.get(currentClass);
960
- if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) parentObj = this.loadParentFromExternalPackage(currentClass, manifest.smrtDependencies, objectsByName);
1010
+ const parentObj = this.resolveParentDefinition(currentClass, obj, manifest, objectsByName);
961
1011
  if (!parentObj) break;
962
1012
  if (parentObj.decoratorConfig?.tableStrategy === "sti") return true;
963
- currentClass = parentObj.extends;
1013
+ currentClass = this.parentRef(parentObj);
964
1014
  }
965
1015
  return false;
966
1016
  }
@@ -990,13 +1040,12 @@ var ManifestGenerator = class {
990
1040
  objectsByName.set(objDef.className.toLowerCase(), objDef);
991
1041
  }
992
1042
  let stiBaseInfo;
993
- let currentClass = obj.extends;
1043
+ let currentClass = this.parentRef(obj);
994
1044
  const visited = /* @__PURE__ */ new Set();
995
1045
  while (currentClass) {
996
1046
  if (visited.has(currentClass)) break;
997
1047
  visited.add(currentClass);
998
- let parentObj = objectsByName.get(currentClass);
999
- if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) parentObj = this.loadParentFromExternalPackage(currentClass, manifest.smrtDependencies, objectsByName);
1048
+ const parentObj = this.resolveParentDefinition(currentClass, obj, manifest, objectsByName);
1000
1049
  if (!parentObj) break;
1001
1050
  if (parentObj.decoratorConfig?.tableStrategy === "sti") {
1002
1051
  const tableName = parentObj.decoratorConfig?.tableName || parentObj.schema?.tableName || this.classNameToTableName(parentObj.className);
@@ -1005,7 +1054,7 @@ var ManifestGenerator = class {
1005
1054
  tableName
1006
1055
  };
1007
1056
  }
1008
- currentClass = parentObj.extends;
1057
+ currentClass = this.parentRef(parentObj);
1009
1058
  }
1010
1059
  return stiBaseInfo;
1011
1060
  }
@@ -1060,7 +1109,8 @@ var ManifestGenerator = class {
1060
1109
  const usesSTI = this.isSTIClass(obj, objectsByName, manifest);
1061
1110
  logger.debug(`[manifest-generator] Merging inherited fields for ${obj.className} from ${obj.extends}`);
1062
1111
  const inheritanceChain = [];
1063
- let currentClass = obj.extends;
1112
+ const ancestorDefs = [];
1113
+ let currentClass = this.parentRef(obj);
1064
1114
  const visited = /* @__PURE__ */ new Set();
1065
1115
  while (currentClass) {
1066
1116
  if (visited.has(currentClass)) {
@@ -1068,19 +1118,19 @@ var ManifestGenerator = class {
1068
1118
  break;
1069
1119
  }
1070
1120
  visited.add(currentClass);
1071
- inheritanceChain.unshift(currentClass);
1072
- let parentObj = objectsByName.get(currentClass);
1073
- if (parentObj === obj) parentObj = void 0;
1074
- if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) parentObj = this.loadParentFromExternalPackage(currentClass, manifest.smrtDependencies, objectsByName);
1121
+ const parentObj = this.resolveParentDefinition(currentClass, obj, manifest, objectsByName);
1122
+ const ancestorName = this.simpleClassName(currentClass);
1123
+ inheritanceChain.unshift(ancestorName);
1124
+ ancestorDefs.unshift(parentObj);
1075
1125
  if (!parentObj) break;
1076
- currentClass = parentObj.extends;
1126
+ currentClass = this.parentRef(parentObj);
1077
1127
  }
1078
1128
  logger.debug(`[manifest-generator] Inheritance chain for ${obj.className}: ${inheritanceChain.join(" -> ")}`);
1079
1129
  const mergedFields = {};
1080
1130
  const mergedFieldOwners = /* @__PURE__ */ new Map();
1081
1131
  const mergedMethods = {};
1082
- for (const ancestorName of inheritanceChain) {
1083
- const ancestor = objectsByName.get(ancestorName);
1132
+ for (const [index, ancestorName] of inheritanceChain.entries()) {
1133
+ const ancestor = ancestorDefs[index];
1084
1134
  if (!ancestor) continue;
1085
1135
  const ancestorIsFrameworkBase = FRAMEWORK_ABSTRACT_BASE_NAMES.has(ancestorName);
1086
1136
  if (usesSTI || ancestorIsFrameworkBase) for (const [fieldName, fieldDef] of Object.entries(ancestor.fields)) {
@@ -1365,17 +1415,15 @@ var ManifestGenerator = class {
1365
1415
  */
1366
1416
  isSTIClass(obj, objectsByName, manifest) {
1367
1417
  if (obj.decoratorConfig?.tableStrategy === "sti") return true;
1368
- let currentClass = obj.extends;
1418
+ let currentClass = this.parentRef(obj);
1369
1419
  const visited = /* @__PURE__ */ new Set();
1370
1420
  while (currentClass) {
1371
1421
  if (visited.has(currentClass)) break;
1372
1422
  visited.add(currentClass);
1373
- let parentDef = objectsByName.get(currentClass);
1374
- if (parentDef === obj) parentDef = void 0;
1375
- if (!parentDef && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) parentDef = this.loadParentFromExternalPackage(currentClass, manifest.smrtDependencies, objectsByName);
1423
+ const parentDef = this.resolveParentDefinition(currentClass, obj, manifest, objectsByName);
1376
1424
  if (!parentDef) break;
1377
1425
  if (parentDef.decoratorConfig?.tableStrategy === "sti") return true;
1378
- currentClass = parentDef.extends;
1426
+ currentClass = this.parentRef(parentDef);
1379
1427
  }
1380
1428
  return false;
1381
1429
  }
@@ -1393,17 +1441,15 @@ var ManifestGenerator = class {
1393
1441
  findSTIBase(obj, objectsByName, manifest) {
1394
1442
  let stiBase;
1395
1443
  if (obj.decoratorConfig?.tableStrategy === "sti") stiBase = obj;
1396
- let currentClass = obj.extends;
1444
+ let currentClass = this.parentRef(obj);
1397
1445
  const visited = /* @__PURE__ */ new Set();
1398
1446
  while (currentClass) {
1399
1447
  if (visited.has(currentClass)) break;
1400
1448
  visited.add(currentClass);
1401
- let parentDef = objectsByName.get(currentClass);
1402
- if (parentDef === obj) parentDef = void 0;
1403
- if (!parentDef && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) parentDef = this.loadParentFromExternalPackage(currentClass, manifest.smrtDependencies, objectsByName);
1449
+ const parentDef = this.resolveParentDefinition(currentClass, obj, manifest, objectsByName);
1404
1450
  if (!parentDef) break;
1405
1451
  if (parentDef.decoratorConfig?.tableStrategy === "sti") stiBase = parentDef;
1406
- currentClass = parentDef.extends;
1452
+ currentClass = this.parentRef(parentDef);
1407
1453
  }
1408
1454
  return stiBase;
1409
1455
  }
@@ -1425,8 +1471,8 @@ var ManifestGenerator = class {
1425
1471
  if (!externalManifest) continue;
1426
1472
  const parentObj = lookupInManifest(externalManifest, parentClassName);
1427
1473
  if (parentObj) {
1428
- objectsByName.set(parentObj.className, parentObj);
1429
- objectsByName.set(parentObj.className.toLowerCase(), parentObj);
1474
+ if (!objectsByName.has(parentObj.className)) objectsByName.set(parentObj.className, parentObj);
1475
+ if (!objectsByName.has(parentObj.className.toLowerCase())) objectsByName.set(parentObj.className.toLowerCase(), parentObj);
1430
1476
  return parentObj;
1431
1477
  }
1432
1478
  }