@happyvertical/smrt-core 0.57.8 → 0.57.10
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.
- package/AGENTS.md +7 -1
- package/agents/build-knowledge.md +19 -3
- package/dist/knowledge.js +1 -0
- package/dist/knowledge.js.map +1 -1
- package/dist/manifest/browser-capability.generated.js +8 -1
- package/dist/manifest/browser-capability.generated.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +18 -2
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/inheritance-resolver.d.ts.map +1 -1
- package/dist/registry/inheritance-resolver.js +19 -1
- package/dist/registry/inheritance-resolver.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +30 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +76 -30
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +1 -1
- package/dist/schema/generator.js.map +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/vite-plugin/worker-registration.d.ts +5 -3
- package/dist/vite-plugin/worker-registration.d.ts.map +1 -1
- package/dist/vite-plugin/worker-registration.js +6 -3
- package/dist/vite-plugin/worker-registration.js.map +1 -1
- package/dist/vite-preset/index.d.ts +4 -3
- package/dist/vite-preset/index.d.ts.map +1 -1
- package/dist/vite-preset/index.js +21 -5
- package/dist/vite-preset/index.js.map +1 -1
- package/package.json +10 -10
|
@@ -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;
|
|
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 =
|
|
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":"AAAA;;GAEG;
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
}
|