@happyvertical/smrt-core 0.36.8 → 0.37.0

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.
Files changed (70) hide show
  1. package/dist/child-accessors.d.ts +1 -1
  2. package/dist/child-accessors.d.ts.map +1 -1
  3. package/dist/child-accessors.js +1 -1
  4. package/dist/child-accessors.js.map +1 -1
  5. package/dist/class.d.ts.map +1 -1
  6. package/dist/class.js +3 -1
  7. package/dist/class.js.map +1 -1
  8. package/dist/collection-cache.d.ts.map +1 -1
  9. package/dist/collection-cache.js +5 -3
  10. package/dist/collection-cache.js.map +1 -1
  11. package/dist/collection.d.ts +39 -16
  12. package/dist/collection.d.ts.map +1 -1
  13. package/dist/collection.js +40 -19
  14. package/dist/collection.js.map +1 -1
  15. package/dist/decorators/compatibility.d.ts +1 -1
  16. package/dist/decorators/compatibility.d.ts.map +1 -1
  17. package/dist/decorators/compatibility.js.map +1 -1
  18. package/dist/decorators/index.d.ts +4 -4
  19. package/dist/decorators/index.d.ts.map +1 -1
  20. package/dist/decorators/index.js.map +1 -1
  21. package/dist/hierarchical.d.ts.map +1 -1
  22. package/dist/hierarchical.js.map +1 -1
  23. package/dist/junction.d.ts.map +1 -1
  24. package/dist/junction.js.map +1 -1
  25. package/dist/manifest/static-manifest.d.ts.map +1 -1
  26. package/dist/manifest/static-manifest.js +39 -20
  27. package/dist/manifest/static-manifest.js.map +1 -1
  28. package/dist/manifest/store.js +2 -2
  29. package/dist/manifest/store.js.map +1 -1
  30. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  31. package/dist/manifest/test-manifest-stub.js +2301 -629
  32. package/dist/manifest/test-manifest-stub.js.map +1 -1
  33. package/dist/manifest.json +39 -20
  34. package/dist/object.d.ts +64 -17
  35. package/dist/object.d.ts.map +1 -1
  36. package/dist/object.js +76 -30
  37. package/dist/object.js.map +1 -1
  38. package/dist/registry/class-registration.d.ts +3 -3
  39. package/dist/registry/class-registration.d.ts.map +1 -1
  40. package/dist/registry/class-registration.js +39 -42
  41. package/dist/registry/class-registration.js.map +1 -1
  42. package/dist/registry/inheritance-resolver.d.ts +17 -3
  43. package/dist/registry/inheritance-resolver.d.ts.map +1 -1
  44. package/dist/registry/inheritance-resolver.js +1 -1
  45. package/dist/registry/inheritance-resolver.js.map +1 -1
  46. package/dist/registry/manifest-field-merge.d.ts +17 -3
  47. package/dist/registry/manifest-field-merge.d.ts.map +1 -1
  48. package/dist/registry/manifest-field-merge.js +8 -6
  49. package/dist/registry/manifest-field-merge.js.map +1 -1
  50. package/dist/registry/schema-builder.d.ts +1 -1
  51. package/dist/registry/schema-builder.d.ts.map +1 -1
  52. package/dist/registry/schema-builder.js.map +1 -1
  53. package/dist/registry/shared-state.d.ts +3 -3
  54. package/dist/registry/shared-state.d.ts.map +1 -1
  55. package/dist/registry/shared-state.js.map +1 -1
  56. package/dist/registry/types.d.ts +78 -19
  57. package/dist/registry/types.d.ts.map +1 -1
  58. package/dist/registry/validator.d.ts +2 -1
  59. package/dist/registry/validator.d.ts.map +1 -1
  60. package/dist/registry/validator.js +38 -39
  61. package/dist/registry/validator.js.map +1 -1
  62. package/dist/registry.d.ts +84 -57
  63. package/dist/registry.d.ts.map +1 -1
  64. package/dist/registry.js +31 -25
  65. package/dist/registry.js.map +1 -1
  66. package/dist/smrt-knowledge.json +5 -4
  67. package/dist/system-fields.d.ts +1 -1
  68. package/dist/system-fields.d.ts.map +1 -1
  69. package/dist/system-fields.js.map +1 -1
  70. package/package.json +4 -4
@@ -1,4 +1,17 @@
1
1
  import { SmrtObject } from '../object';
2
+ import { MethodDefinition } from '../scanner/types.js';
3
+ import { RegisteredField } from './types';
4
+ /**
5
+ * Runtime field shape merged across an inheritance chain.
6
+ *
7
+ * This is the canonical {@link RegisteredField} shape (a `FieldDefinition`
8
+ * superset that carries the root-level `__tenancy` marker mirrored from the
9
+ * `@tenantId` decorator alongside the canonical `_meta.__tenancy`, a runtime
10
+ * `value`, and an open index signature for forward-compatible keys). Aliasing
11
+ * it keeps merged fields assignable back into the
12
+ * `Map<string, RegisteredField>` on each registered class.
13
+ */
14
+ type InheritedFieldDefinition = RegisteredField;
2
15
  /**
3
16
  * Build inheritance chain by walking prototype chain
4
17
  *
@@ -7,7 +20,8 @@ import { SmrtObject } from '../object';
7
20
  */
8
21
  export declare function buildInheritanceChain(ctor: typeof SmrtObject): string[];
9
22
  export declare function getInheritanceChain(className: string): string[];
10
- export declare function getAllFields(className: string): Promise<Map<string, any>>;
11
- export declare function mergeFieldConfigs(parentField: any, childField: any, fieldName: string): any;
12
- export declare function getAllMethods(className: string): Promise<Map<string, any>>;
23
+ export declare function getAllFields(className: string): Promise<Map<string, RegisteredField>>;
24
+ export declare function mergeFieldConfigs(parentField: InheritedFieldDefinition, childField: InheritedFieldDefinition, fieldName: string): InheritedFieldDefinition;
25
+ export declare function getAllMethods(className: string): Promise<Map<string, MethodDefinition>>;
26
+ export {};
13
27
  //# sourceMappingURL=inheritance-resolver.d.ts.map
@@ -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,WAAW,CAAC;AAY5C;;;;;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,GAAG,CAAC,CAAC,CAkG3B;AA4BD,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,GAAG,EAChB,UAAU,EAAE,GAAG,EACf,SAAS,EAAE,MAAM,GAChB,GAAG,CA6EL;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAiD3B"}
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,WAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQ5D,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,SAAS,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"}
@@ -142,7 +142,7 @@ To fix:
142
142
  }
143
143
  }
144
144
  registered.inheritedFields = new Map(allFields);
145
- return prependSmrtSystemFields(allFields);
145
+ return prependSmrtSystemFields(new Map(allFields));
146
146
  }
147
147
  function normalizeFrameworkInheritedField(ancestorName, fieldName, field, childClassName) {
148
148
  const simpleAncestorName = ancestorName.includes(":") ? ancestorName.split(":").pop() : ancestorName;
@@ -1 +1 @@
1
- {"version":3,"file":"inheritance-resolver.js","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 { prependSmrtSystemFields } from '../system-fields';\nimport { findClass, findClassStrict } from './name-resolver';\nimport {\n getInheritanceCache,\n getInheritanceConfig,\n verboseLog,\n} from './shared-state';\n\nconst logger = createLogger({ level: 'info' });\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: any = 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<any>(); // Cycle detection using object identity\n let current: any = 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, any>> {\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, any>();\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(allFields);\n}\n\nfunction normalizeFrameworkInheritedField(\n ancestorName: string,\n fieldName: string,\n field: any,\n childClassName: string,\n): any {\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: any,\n childField: any,\n fieldName: string,\n): any {\n // Start with parent field as base\n const merged = { ...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 const parentValidator = parentField._meta.validate;\n const childValidator = childField._meta.validate;\n merged._meta.validate = async (value: any) => {\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, any>> {\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, any>();\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inheritance-resolver.js","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"],"names":[],"mappings":"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"}
@@ -1,4 +1,18 @@
1
- export declare function createFieldFromManifest(fieldDef: any): any;
2
- export declare function mergeManifestField(existingField: any, fieldDef: any): any;
3
- export declare function manifestFieldDiffers(existingField: any, fieldDef: any): boolean;
1
+ import { FieldDefinition } from '../scanner/types.js';
2
+ import { RegisteredField } from './types';
3
+ /**
4
+ * Manifest-sourced field input handled by the merge helpers.
5
+ *
6
+ * Manifest fields are plain {@link FieldDefinition}s. Already-registered
7
+ * runtime fields are the canonical {@link RegisteredField} superset, which
8
+ * extends `FieldDefinition`, so a `FieldDefinition` parameter accepts both
9
+ * shapes as the incoming `fieldDef`. The helpers return the canonical
10
+ * {@link RegisteredField} so merged fields stay assignable back into the
11
+ * `Map<string, RegisteredField>` they came from.
12
+ */
13
+ type ManifestFieldInput = FieldDefinition;
14
+ export declare function createFieldFromManifest(fieldDef: ManifestFieldInput): RegisteredField;
15
+ export declare function mergeManifestField(existingField: RegisteredField, fieldDef: ManifestFieldInput): RegisteredField;
16
+ export declare function manifestFieldDiffers(existingField: RegisteredField, fieldDef: ManifestFieldInput): boolean;
17
+ export {};
4
18
  //# sourceMappingURL=manifest-field-merge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-field-merge.d.ts","sourceRoot":"","sources":["../../src/registry/manifest-field-merge.ts"],"names":[],"mappings":"AAoDA,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,GAAG,GAAG,GAAG,CAsB1D;AAED,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,GAAG,CA8BzE;AAED,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,GAAG,GACZ,OAAO,CAQT"}
1
+ {"version":3,"file":"manifest-field-merge.d.ts","sourceRoot":"","sources":["../../src/registry/manifest-field-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;GASG;AACH,KAAK,kBAAkB,GAAG,eAAe,CAAC;AAuD1C,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,kBAAkB,GAC3B,eAAe,CA0BjB;AAED,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,eAAe,EAC9B,QAAQ,EAAE,kBAAkB,GAC3B,eAAe,CAiCjB;AAED,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,eAAe,EAC9B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAQT"}
@@ -6,7 +6,7 @@ function readExistingFieldMeta(existingField, key) {
6
6
  return existingField[key];
7
7
  }
8
8
  if (hasOwnDefinedValue(existingField?._meta, key)) {
9
- return existingField._meta[key];
9
+ return existingField._meta?.[key];
10
10
  }
11
11
  return void 0;
12
12
  }
@@ -15,7 +15,7 @@ function readManifestFieldMeta(fieldDef, key) {
15
15
  return fieldDef[key];
16
16
  }
17
17
  if (hasOwnDefinedValue(fieldDef?._meta, key)) {
18
- return fieldDef._meta[key];
18
+ return fieldDef._meta?.[key];
19
19
  }
20
20
  return void 0;
21
21
  }
@@ -32,10 +32,11 @@ function assignDefinedMeta(target, source) {
32
32
  function createFieldFromManifest(fieldDef) {
33
33
  const meta = {};
34
34
  assignDefinedMeta(meta, fieldDef?._meta);
35
+ const metaRecord = meta;
35
36
  for (const key of ["required", "default", "description"]) {
36
37
  const value = readManifestFieldMeta(fieldDef, key);
37
38
  if (value !== void 0) {
38
- meta[key] = value;
39
+ metaRecord[key] = value;
39
40
  }
40
41
  }
41
42
  const out = {
@@ -53,15 +54,16 @@ function mergeManifestField(existingField, fieldDef) {
53
54
  ...existingField._meta || {}
54
55
  };
55
56
  assignDefinedMeta(nextMeta, fieldDef?._meta);
57
+ const nextMetaRecord = nextMeta;
56
58
  for (const key of ["required", "default", "description"]) {
57
59
  const manifestValue = readManifestFieldMeta(fieldDef, key);
58
60
  if (manifestValue !== void 0) {
59
- nextMeta[key] = manifestValue;
61
+ nextMetaRecord[key] = manifestValue;
60
62
  continue;
61
63
  }
62
64
  const existingValue = readExistingFieldMeta(existingField, key);
63
- if (existingValue !== void 0 && nextMeta[key] === void 0) {
64
- nextMeta[key] = existingValue;
65
+ if (existingValue !== void 0 && nextMetaRecord[key] === void 0) {
66
+ nextMetaRecord[key] = existingValue;
65
67
  }
66
68
  }
67
69
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-field-merge.js","sources":["../../src/registry/manifest-field-merge.ts"],"sourcesContent":["function hasOwnDefinedValue(\n source: Record<string, any> | undefined,\n key: string,\n) {\n return !!source && Object.hasOwn(source, key) && source[key] !== undefined;\n}\n\nfunction readExistingFieldMeta(\n existingField: any,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(existingField, key)) {\n return existingField[key];\n }\n\n if (hasOwnDefinedValue(existingField?._meta, key)) {\n return existingField._meta[key];\n }\n\n return undefined;\n}\n\nfunction readManifestFieldMeta(\n fieldDef: any,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(fieldDef, key)) {\n return fieldDef[key];\n }\n\n if (hasOwnDefinedValue(fieldDef?._meta, key)) {\n return fieldDef._meta[key];\n }\n\n return undefined;\n}\n\nfunction assignDefinedMeta(\n target: Record<string, any>,\n source: Record<string, any> | undefined,\n) {\n if (!source) {\n return;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) {\n target[key] = value;\n }\n }\n}\n\nexport function createFieldFromManifest(fieldDef: any): any {\n const meta: Record<string, any> = {};\n\n assignDefinedMeta(meta, fieldDef?._meta);\n\n for (const key of ['required', 'default', 'description'] as const) {\n const value = readManifestFieldMeta(fieldDef, key);\n if (value !== undefined) {\n meta[key] = value;\n }\n }\n\n // Preserve top-level `related` for relationship-graph lookups\n // (foreignKey / crossPackageRef / oneToMany / manyToMany all rely on it).\n const out: Record<string, any> = {\n type: fieldDef.type,\n _meta: meta,\n };\n if (fieldDef.related !== undefined) {\n out.related = fieldDef.related;\n }\n return out;\n}\n\nexport function mergeManifestField(existingField: any, fieldDef: any): any {\n const hasTenancyMarker =\n existingField.__tenancy?.isTenantIdField ||\n existingField._meta?.__tenancy?.isTenantIdField;\n\n const nextMeta: Record<string, any> = {\n ...(existingField._meta || {}),\n };\n\n assignDefinedMeta(nextMeta, fieldDef?._meta);\n\n for (const key of ['required', 'default', 'description'] as const) {\n const manifestValue = readManifestFieldMeta(fieldDef, key);\n if (manifestValue !== undefined) {\n nextMeta[key] = manifestValue;\n continue;\n }\n\n const existingValue = readExistingFieldMeta(existingField, key);\n if (existingValue !== undefined && nextMeta[key] === undefined) {\n nextMeta[key] = existingValue;\n }\n }\n\n return {\n ...existingField,\n type:\n !hasTenancyMarker && fieldDef.type ? fieldDef.type : existingField.type,\n _meta: nextMeta,\n };\n}\n\nexport function manifestFieldDiffers(\n existingField: any,\n fieldDef: any,\n): boolean {\n const mergedField = mergeManifestField(existingField, fieldDef);\n\n return (\n mergedField.type !== existingField.type ||\n JSON.stringify(mergedField._meta || {}) !==\n JSON.stringify(existingField._meta || {})\n );\n}\n"],"names":[],"mappings":"AAAA,SAAS,mBACP,QACA,KACA;AACA,SAAO,CAAC,CAAC,UAAU,OAAO,OAAO,QAAQ,GAAG,KAAK,OAAO,GAAG,MAAM;AACnE;AAEA,SAAS,sBACP,eACA,KACA;AACA,MAAI,mBAAmB,eAAe,GAAG,GAAG;AAC1C,WAAO,cAAc,GAAG;AAAA,EAC1B;AAEA,MAAI,mBAAmB,eAAe,OAAO,GAAG,GAAG;AACjD,WAAO,cAAc,MAAM,GAAG;AAAA,EAChC;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,UACA,KACA;AACA,MAAI,mBAAmB,UAAU,GAAG,GAAG;AACrC,WAAO,SAAS,GAAG;AAAA,EACrB;AAEA,MAAI,mBAAmB,UAAU,OAAO,GAAG,GAAG;AAC5C,WAAO,SAAS,MAAM,GAAG;AAAA,EAC3B;AAEA,SAAO;AACT;AAEA,SAAS,kBACP,QACA,QACA;AACA,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,QAAW;AACvB,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEO,SAAS,wBAAwB,UAAoB;AAC1D,QAAM,OAA4B,CAAA;AAElC,oBAAkB,MAAM,UAAU,KAAK;AAEvC,aAAW,OAAO,CAAC,YAAY,WAAW,aAAa,GAAY;AACjE,UAAM,QAAQ,sBAAsB,UAAU,GAAG;AACjD,QAAI,UAAU,QAAW;AACvB,WAAK,GAAG,IAAI;AAAA,IACd;AAAA,EACF;AAIA,QAAM,MAA2B;AAAA,IAC/B,MAAM,SAAS;AAAA,IACf,OAAO;AAAA,EAAA;AAET,MAAI,SAAS,YAAY,QAAW;AAClC,QAAI,UAAU,SAAS;AAAA,EACzB;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,eAAoB,UAAoB;AACzE,QAAM,mBACJ,cAAc,WAAW,mBACzB,cAAc,OAAO,WAAW;AAElC,QAAM,WAAgC;AAAA,IACpC,GAAI,cAAc,SAAS,CAAA;AAAA,EAAC;AAG9B,oBAAkB,UAAU,UAAU,KAAK;AAE3C,aAAW,OAAO,CAAC,YAAY,WAAW,aAAa,GAAY;AACjE,UAAM,gBAAgB,sBAAsB,UAAU,GAAG;AACzD,QAAI,kBAAkB,QAAW;AAC/B,eAAS,GAAG,IAAI;AAChB;AAAA,IACF;AAEA,UAAM,gBAAgB,sBAAsB,eAAe,GAAG;AAC9D,QAAI,kBAAkB,UAAa,SAAS,GAAG,MAAM,QAAW;AAC9D,eAAS,GAAG,IAAI;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MACE,CAAC,oBAAoB,SAAS,OAAO,SAAS,OAAO,cAAc;AAAA,IACrE,OAAO;AAAA,EAAA;AAEX;AAEO,SAAS,qBACd,eACA,UACS;AACT,QAAM,cAAc,mBAAmB,eAAe,QAAQ;AAE9D,SACE,YAAY,SAAS,cAAc,QACnC,KAAK,UAAU,YAAY,SAAS,CAAA,CAAE,MACpC,KAAK,UAAU,cAAc,SAAS,EAAE;AAE9C;"}
1
+ {"version":3,"file":"manifest-field-merge.js","sources":["../../src/registry/manifest-field-merge.ts"],"sourcesContent":["import type { FieldDefinition, FieldMeta } from '../scanner/types.js';\nimport type { RegisteredField } from './types';\n\n/**\n * Manifest-sourced field input handled by the merge helpers.\n *\n * Manifest fields are plain {@link FieldDefinition}s. Already-registered\n * runtime fields are the canonical {@link RegisteredField} superset, which\n * extends `FieldDefinition`, so a `FieldDefinition` parameter accepts both\n * shapes as the incoming `fieldDef`. The helpers return the canonical\n * {@link RegisteredField} so merged fields stay assignable back into the\n * `Map<string, RegisteredField>` they came from.\n */\ntype ManifestFieldInput = FieldDefinition;\n\nfunction hasOwnDefinedValue(source: object | undefined, key: string): boolean {\n return (\n !!source &&\n Object.hasOwn(source, key) &&\n (source as Record<string, unknown>)[key] !== undefined\n );\n}\n\nfunction readExistingFieldMeta(\n existingField: RegisteredField,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(existingField, key)) {\n return existingField[key];\n }\n\n if (hasOwnDefinedValue(existingField?._meta, key)) {\n return existingField._meta?.[key];\n }\n\n return undefined;\n}\n\nfunction readManifestFieldMeta(\n fieldDef: ManifestFieldInput,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(fieldDef, key)) {\n return fieldDef[key];\n }\n\n if (hasOwnDefinedValue(fieldDef?._meta, key)) {\n return fieldDef._meta?.[key];\n }\n\n return undefined;\n}\n\nfunction assignDefinedMeta(\n target: Record<string, unknown>,\n source: Record<string, unknown> | undefined,\n) {\n if (!source) {\n return;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) {\n target[key] = value;\n }\n }\n}\n\nexport function createFieldFromManifest(\n fieldDef: ManifestFieldInput,\n): RegisteredField {\n const meta: FieldMeta = {};\n\n assignDefinedMeta(meta, fieldDef?._meta);\n\n // Write through the open `FieldMeta` index signature: assigning the same\n // loop value to a union of named keys (`default` is `unknown`, the others\n // narrower) collapses the write target to `undefined`, so route by string.\n const metaRecord = meta as Record<string, unknown>;\n for (const key of ['required', 'default', 'description'] as const) {\n const value = readManifestFieldMeta(fieldDef, key);\n if (value !== undefined) {\n metaRecord[key] = value;\n }\n }\n\n // Preserve top-level `related` for relationship-graph lookups\n // (foreignKey / crossPackageRef / oneToMany / manyToMany all rely on it).\n const out: RegisteredField = {\n type: fieldDef.type,\n _meta: meta,\n };\n if (fieldDef.related !== undefined) {\n out.related = fieldDef.related;\n }\n return out;\n}\n\nexport function mergeManifestField(\n existingField: RegisteredField,\n fieldDef: ManifestFieldInput,\n): RegisteredField {\n const hasTenancyMarker =\n existingField.__tenancy?.isTenantIdField ||\n existingField._meta?.__tenancy?.isTenantIdField;\n\n const nextMeta: FieldMeta = {\n ...(existingField._meta || {}),\n };\n\n assignDefinedMeta(nextMeta, fieldDef?._meta);\n\n // See createFieldFromManifest: route writes through the index signature so a\n // shared loop value isn't narrowed to the `undefined`-only union write target.\n const nextMetaRecord = nextMeta as Record<string, unknown>;\n for (const key of ['required', 'default', 'description'] as const) {\n const manifestValue = readManifestFieldMeta(fieldDef, key);\n if (manifestValue !== undefined) {\n nextMetaRecord[key] = manifestValue;\n continue;\n }\n\n const existingValue = readExistingFieldMeta(existingField, key);\n if (existingValue !== undefined && nextMetaRecord[key] === undefined) {\n nextMetaRecord[key] = existingValue;\n }\n }\n\n return {\n ...existingField,\n type:\n !hasTenancyMarker && fieldDef.type ? fieldDef.type : existingField.type,\n _meta: nextMeta,\n };\n}\n\nexport function manifestFieldDiffers(\n existingField: RegisteredField,\n fieldDef: ManifestFieldInput,\n): boolean {\n const mergedField = mergeManifestField(existingField, fieldDef);\n\n return (\n mergedField.type !== existingField.type ||\n JSON.stringify(mergedField._meta || {}) !==\n JSON.stringify(existingField._meta || {})\n );\n}\n"],"names":[],"mappings":"AAeA,SAAS,mBAAmB,QAA4B,KAAsB;AAC5E,SACE,CAAC,CAAC,UACF,OAAO,OAAO,QAAQ,GAAG,KACxB,OAAmC,GAAG,MAAM;AAEjD;AAEA,SAAS,sBACP,eACA,KACA;AACA,MAAI,mBAAmB,eAAe,GAAG,GAAG;AAC1C,WAAO,cAAc,GAAG;AAAA,EAC1B;AAEA,MAAI,mBAAmB,eAAe,OAAO,GAAG,GAAG;AACjD,WAAO,cAAc,QAAQ,GAAG;AAAA,EAClC;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,UACA,KACA;AACA,MAAI,mBAAmB,UAAU,GAAG,GAAG;AACrC,WAAO,SAAS,GAAG;AAAA,EACrB;AAEA,MAAI,mBAAmB,UAAU,OAAO,GAAG,GAAG;AAC5C,WAAO,SAAS,QAAQ,GAAG;AAAA,EAC7B;AAEA,SAAO;AACT;AAEA,SAAS,kBACP,QACA,QACA;AACA,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,QAAW;AACvB,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACF;AAEO,SAAS,wBACd,UACiB;AACjB,QAAM,OAAkB,CAAA;AAExB,oBAAkB,MAAM,UAAU,KAAK;AAKvC,QAAM,aAAa;AACnB,aAAW,OAAO,CAAC,YAAY,WAAW,aAAa,GAAY;AACjE,UAAM,QAAQ,sBAAsB,UAAU,GAAG;AACjD,QAAI,UAAU,QAAW;AACvB,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AAIA,QAAM,MAAuB;AAAA,IAC3B,MAAM,SAAS;AAAA,IACf,OAAO;AAAA,EAAA;AAET,MAAI,SAAS,YAAY,QAAW;AAClC,QAAI,UAAU,SAAS;AAAA,EACzB;AACA,SAAO;AACT;AAEO,SAAS,mBACd,eACA,UACiB;AACjB,QAAM,mBACJ,cAAc,WAAW,mBACzB,cAAc,OAAO,WAAW;AAElC,QAAM,WAAsB;AAAA,IAC1B,GAAI,cAAc,SAAS,CAAA;AAAA,EAAC;AAG9B,oBAAkB,UAAU,UAAU,KAAK;AAI3C,QAAM,iBAAiB;AACvB,aAAW,OAAO,CAAC,YAAY,WAAW,aAAa,GAAY;AACjE,UAAM,gBAAgB,sBAAsB,UAAU,GAAG;AACzD,QAAI,kBAAkB,QAAW;AAC/B,qBAAe,GAAG,IAAI;AACtB;AAAA,IACF;AAEA,UAAM,gBAAgB,sBAAsB,eAAe,GAAG;AAC9D,QAAI,kBAAkB,UAAa,eAAe,GAAG,MAAM,QAAW;AACpE,qBAAe,GAAG,IAAI;AAAA,IACxB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MACE,CAAC,oBAAoB,SAAS,OAAO,SAAS,OAAO,cAAc;AAAA,IACrE,OAAO;AAAA,EAAA;AAEX;AAEO,SAAS,qBACd,eACA,UACS;AACT,QAAM,cAAc,mBAAmB,eAAe,QAAQ;AAE9D,SACE,YAAY,SAAS,cAAc,QACnC,KAAK,UAAU,YAAY,SAAS,CAAA,CAAE,MACpC,KAAK,UAAU,cAAc,SAAS,EAAE;AAE9C;"}
@@ -93,7 +93,7 @@ export declare function generateDDLFromColumns(tableName: string, columns: Recor
93
93
  * @param type - Column SQL type
94
94
  * @returns Formatted SQL default value
95
95
  */
96
- export declare function formatDefaultValue(value: any, type: string): string;
96
+ export declare function formatDefaultValue(value: unknown, type: string): string;
97
97
  /**
98
98
  * Convert a Map of field definitions to column definitions
99
99
  *
@@ -1 +1 @@
1
- {"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../../src/registry/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAK3D,OAAO,KAAK,EACV,gBAAgB,EAEhB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAuI5B;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,IAAI,MAAM,CACrC,MAAM,EACN;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CACvD,CAwMA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAgK7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACzC,KAAK,UAAQ,GACZ,MAAM,CAqDR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CA+ElC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,GACjC,WAAW,CAqBb"}
1
+ {"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../../src/registry/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAK3D,OAAO,KAAK,EACV,gBAAgB,EAEhB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AA6I5B;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,IAAI,MAAM,CACrC,MAAM,EACN;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CACvD,CAwMA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAgK7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACzC,KAAK,UAAQ,GACZ,MAAM,CAqDR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAgFlC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,GACjC,WAAW,CAqBb"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema-builder.js","sources":["../../src/registry/schema-builder.ts"],"sourcesContent":["/**\n * Schema building logic for the SMRT ObjectRegistry.\n *\n * Extracted from registry.ts as part of issue #1006.\n */\n\nimport { ObjectRegistry } from '../registry';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport {\n formatDefaultValue as formatDefaultValueShared,\n quoteIdentifier,\n} from '../schema/sql-identifiers.js';\nimport type {\n ColumnDefinition,\n IndexDefinition,\n SchemaDefinition,\n SQLDataType,\n} from '../schema/types.js';\nimport { classnameToTablename, toSnakeCase } from '../utils';\nimport {\n type CollectionRegistrationLookup,\n isCollectionRegistration,\n} from './collection-resolution';\nimport { findClass } from './name-resolver';\nimport { getClasses, getCollectionTableNames } from './shared-state';\n\ntype ForeignKeyAction = NonNullable<\n NonNullable<ColumnDefinition['foreignKey']>['onDelete']\n>;\n\nconst collectionRegistrationLookup: CollectionRegistrationLookup = {\n findClass,\n findClassInPackage: (packageName, className) =>\n ObjectRegistry.getClassInPackage(packageName, className),\n getInheritanceChain: (className) =>\n ObjectRegistry.getInheritanceChain(className),\n};\n\nfunction applyDecoratorSqlTypeOverrides(\n className: string,\n columns: Record<string, ColumnDefinition>,\n): Record<string, ColumnDefinition> {\n const decorators = ObjectRegistry.getFieldDecorators(className);\n if (!decorators.size) {\n return columns;\n }\n\n for (const [fieldName, options] of decorators) {\n if (!options?.sqlType) {\n continue;\n }\n\n const columnName = toSnakeCase(fieldName);\n const existing = columns[columnName];\n if (!existing) {\n continue;\n }\n\n const referenceKind = getReferenceKind(options as FieldDefinition);\n columns[columnName] = {\n ...existing,\n type: String(options.sqlType).toUpperCase() as SQLDataType,\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n\n return columns;\n}\n\nfunction mergeRuntimeFieldColumns(\n className: string,\n schemaColumns: Record<string, ColumnDefinition> | undefined,\n fields: Map<string, FieldDefinition>,\n): Record<string, ColumnDefinition> {\n const columnsToUse = { ...(schemaColumns || {}) };\n\n if (fields.size > 0) {\n const fieldColumns = fieldsToColumns(fields);\n for (const [columnName, columnDef] of Object.entries(fieldColumns)) {\n if (!columnsToUse[columnName]) {\n columnsToUse[columnName] = columnDef;\n }\n }\n\n for (const [fieldName, fieldDef] of fields) {\n const columnName = toSnakeCase(fieldName);\n const existing = columnsToUse[columnName];\n if (!existing) {\n continue;\n }\n\n const sqlType = fieldDef._meta?.sqlType || (fieldDef as any).sqlType;\n const referenceKind = getReferenceKind(fieldDef);\n columnsToUse[columnName] = {\n ...existing,\n ...(sqlType\n ? { type: String(sqlType).toUpperCase() as SQLDataType }\n : {}),\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n }\n\n applyDecoratorSqlTypeOverrides(className, columnsToUse);\n return columnsToUse;\n}\n\nfunction getReferenceKind(\n fieldDef: FieldDefinition,\n): ColumnDefinition['referenceKind'] | undefined {\n if (\n (fieldDef as any).__tenancy?.isTenantIdField ||\n fieldDef._meta?.__tenancy?.isTenantIdField\n ) {\n return 'tenantId';\n }\n\n if (fieldDef.type === 'foreignKey') {\n return 'foreignKey';\n }\n\n if (fieldDef.type === 'crossPackageRef') {\n return 'crossPackageRef';\n }\n\n return undefined;\n}\n\nfunction shouldEmitDefault(fieldDef: FieldDefinition, sqlType: SQLDataType) {\n return !(\n getReferenceKind(fieldDef) === 'tenantId' &&\n sqlType === 'UUID' &&\n fieldDef.default === ''\n );\n}\n\nfunction createBaseColumns(registered: {\n config?: { idType?: 'uuid' | 'text' };\n}): Record<string, ColumnDefinition> {\n return {\n id: {\n type: registered.config?.idType === 'text' ? 'TEXT' : 'UUID',\n primaryKey: true,\n referenceKind: 'id',\n },\n slug: { type: 'TEXT', notNull: true },\n context: { type: 'TEXT' },\n created_at: { type: 'TIMESTAMP' },\n updated_at: { type: 'TIMESTAMP' },\n };\n}\n\n/**\n * Get cached schema definition for a registered class\n *\n * @param name - Name of the registered class\n * @returns Schema definition or undefined if not found\n * @example\n * ```typescript\n * const schema = getSchema('Product');\n * console.log(schema.tableName); // 'products'\n * console.log(schema.ddl); // 'CREATE TABLE...'\n * ```\n */\nexport function getSchema(name: string): SchemaDefinition | undefined {\n // Issue #951: Use findClass for multi-strategy lookup\n const registered = findClass(name);\n return registered?.schema;\n}\n\n/**\n * Get SQL DDL statement for a registered class\n *\n * @param name - Name of the registered class\n * @returns SQL DDL statement or undefined if not found\n * @example\n * ```typescript\n * const ddl = ObjectRegistry.getSchemaDDL('Product');\n * await db.query(ddl);\n * ```\n */\nexport function getSchemaDDL(name: string): string | undefined {\n return getSchema(name)?.ddl;\n}\n\n/**\n * Get table name for a registered class\n *\n * @param name - Name of the registered class\n * @returns Table name or undefined if not found\n * @example\n * ```typescript\n * const tableName = getTableName('Product');\n * console.log(tableName); // 'products'\n * ```\n */\nexport function getTableName(name: string): string | undefined {\n // Check if this is a collection class - collections have their own tableName mapping\n const collectionTableName = getCollectionTableNames().get(name);\n if (collectionTableName) {\n return collectionTableName;\n }\n\n // For STI classes, return the STI base class's table name.\n // R5-canon: `getSTIBase` returns the qualified name; resolve `name`\n // (which may be simple) to its registration's qualified form for the\n // comparison.\n const stiBase = ObjectRegistry.getSTIBase(name);\n const registered = ObjectRegistry.getClass(name);\n const qualifiedName = registered?.qualifiedName ?? registered?.name ?? name;\n if (stiBase && stiBase !== qualifiedName) {\n return getSchema(stiBase)?.tableName;\n }\n return getSchema(name)?.tableName;\n}\n\n/**\n * Get all pre-generated schemas for explicit adapter bootstrap paths.\n *\n * Returns schemas in SDK SchemaProvider format for all registered classes.\n * Tooling and test helpers can pass these to `getDatabase()` when they want\n * to bootstrap schema before runtime. Core runtime no longer does this\n * implicitly.\n *\n * @returns Record of table names to schema definitions\n * @example\n * ```typescript\n * const schemas = ObjectRegistry.getAllSchemas();\n * const db = await getDatabase({ type: 'json', url: './data', schemas });\n * ```\n */\nexport function getAllSchemas(): Record<\n string,\n { tableName: string; ddl: string; indexes?: string[] }\n> {\n // Step 1: Collect all schemas grouped by tableName\n // For STI, multiple classes may share the same table\n const tableSchemas: Record<\n string,\n {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n indexes: Array<{ name: string; columns: string[]; unique?: boolean }>;\n ddl: string;\n isSTI: boolean;\n }\n > = {};\n\n for (const [_className, registered] of getClasses()) {\n // Skip collection classes - they don't have their own tables\n // Their schemas incorrectly contain collection properties (loaded, options, etc.)\n if (\n isCollectionRegistration(\n _className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n // Issue #951: Use simple name for STI comparisons (map key may be qualified)\n const simpleName = registered.name || _className;\n\n // Issue #703: Handle STI subclasses with null tableName from external manifests\n // When loaded from external manifests, tableName may be null, causing\n // registerFromManifest() to derive a tableName from class name.\n // For STI subclasses, we need to use the STI base's tableName instead.\n if (!registered.schema?.tableName && registered.extends) {\n // R5-canon: use the qualified key for STI lookups so colliding\n // simple names don't resolve to the wrong package's class.\n // `getTableStrategy` / `getSTIBase` both accept qualified names\n // via `findClass`'s multi-strategy lookup.\n const qualifiedName = (registered as any).qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n // Ensure we have a schema object to modify\n if (!registered.schema) {\n registered.schema = {\n tableName: '',\n ddl: '',\n columns: {},\n indexes: [],\n triggers: [],\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n // Set tableName from STI base so the following block processes this class\n registered.schema.tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n if (registered.schema?.tableName) {\n // For STI subclasses, use the STI base class's tableName\n // This ensures all STI subclass columns are merged into the parent table\n // (Issue #693: STI subclasses with separate tableName still serialize to parent table)\n let tableName = registered.schema.tableName;\n // R5-canon: same qualified-key strategy as the block above.\n const qualifiedName = (registered as any).qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n // This is an STI subclass - use the base class's tableName\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n\n // Start with manifest columns, then backfill any columns that only exist\n // in runtime field metadata (for example tenantScoped injections). We do\n // not replace existing manifest column metadata here, because the manifest\n // is authoritative for foreign keys, defaults, and other schema details.\n // Explicit decorator sqlType overrides are patched onto the merged column.\n const columnsToUse = mergeRuntimeFieldColumns(\n simpleName,\n registered.schema.columns,\n registered.fields,\n );\n\n if (!tableSchemas[tableName]) {\n // First class for this table - initialize with base columns\n // These are required for all tables but are skipped by fieldsToColumns()\n const baseColumns = createBaseColumns(registered);\n\n // For STI tables, add discriminator and data columns\n // (Issue #690: db:diff needs these columns to detect schema changes)\n const isSTI = tableStrategy === 'sti';\n if (isSTI) {\n baseColumns._meta_type = {\n type: 'TEXT',\n notNull: true,\n defaultValue: '',\n };\n baseColumns._meta_data = { type: 'JSON' };\n }\n\n tableSchemas[tableName] = {\n tableName,\n columns: { ...baseColumns, ...columnsToUse },\n indexes: [],\n ddl: registered.schema.ddl || '',\n isSTI,\n };\n } else {\n // Additional class sharing this table (STI scenario)\n // Merge columns from this class into the existing schema\n for (const [colName, colDef] of Object.entries(columnsToUse)) {\n if (!tableSchemas[tableName].columns[colName]) {\n // New column from this subtype - add it\n tableSchemas[tableName].columns[colName] = colDef;\n }\n }\n }\n\n // Merge indexes (avoid duplicates by name)\n if (registered.schema.indexes && registered.schema.indexes.length > 0) {\n const existingNames = new Set(\n tableSchemas[tableName].indexes.map((idx) =>\n typeof idx === 'string' ? idx : idx.name,\n ),\n );\n for (const idx of registered.schema.indexes) {\n const indexName = typeof idx === 'string' ? idx : idx.name;\n if (!existingNames.has(indexName)) {\n if (typeof idx === 'string') {\n // Legacy string format - skip, can't merge properly\n } else {\n tableSchemas[tableName].indexes.push(idx);\n existingNames.add(idx.name);\n }\n }\n }\n }\n }\n }\n\n // Step 2: Convert to output format, regenerating DDL for merged schemas\n const schemas: Record<\n string,\n { tableName: string; ddl: string; indexes?: string[] }\n > = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n // Generate DDL from merged columns (or use original DDL if columns are empty)\n let ddl: string;\n if (Object.keys(tableSchema.columns).length === 0 && tableSchema.ddl) {\n // No columns merged - use original DDL to avoid generating invalid SQL\n ddl = tableSchema.ddl;\n } else if (Object.keys(tableSchema.columns).length === 0) {\n // Skip schemas with no columns and no original DDL\n continue;\n } else {\n ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n }\n\n // Convert index definitions to SQL strings for SDK compatibility\n let indexSQL: string[] | undefined;\n if (tableSchema.indexes.length > 0) {\n indexSQL = tableSchema.indexes.map((idx) => {\n const indexType = idx.unique ? 'UNIQUE INDEX' : 'INDEX';\n const columnList = idx.columns\n .map((col) => quoteIdentifier(col))\n .join(', ');\n return `CREATE ${indexType} IF NOT EXISTS ${quoteIdentifier(\n idx.name,\n )} ON ${quoteIdentifier(tableName)} (${columnList});`;\n });\n }\n\n schemas[tableName] = {\n tableName,\n ddl,\n indexes: indexSQL,\n };\n }\n\n return schemas;\n}\n\n/**\n * Get all registered schemas as SchemaDefinition objects\n *\n * Similar to getAllSchemas(), but returns SchemaDefinition format suitable\n * for use with SchemaComparer (migrations/differ.ts).\n *\n * Key difference: Indexes are kept as IndexDefinition objects instead of\n * being converted to SQL strings.\n *\n * @returns Map of tableName to SchemaDefinition\n */\nexport function getAllSchemasAsDefinitions(): Record<string, SchemaDefinition> {\n // Step 1: Collect all schemas grouped by tableName\n // For STI, multiple classes may share the same table\n const tableSchemas: Record<\n string,\n {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n indexes: IndexDefinition[];\n isSTI: boolean;\n }\n > = {};\n\n for (const [_className, registered] of getClasses()) {\n // Skip collection classes - they don't have their own tables\n if (\n isCollectionRegistration(\n _className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n // Issue #951: Use simple name for STI comparisons (map key may be qualified)\n const simpleName = registered.name || _className;\n\n // Handle STI subclasses with null tableName from external manifests\n if (!registered.schema?.tableName && registered.extends) {\n // R5-canon: use the qualified key for STI lookups so colliding\n // simple names don't resolve to the wrong package's class.\n // `getTableStrategy` / `getSTIBase` both accept qualified names\n // via `findClass`'s multi-strategy lookup.\n const qualifiedName = (registered as any).qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n if (!registered.schema) {\n registered.schema = {\n tableName: '',\n ddl: '',\n columns: {},\n indexes: [],\n triggers: [],\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n registered.schema.tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n if (registered.schema?.tableName) {\n // For STI subclasses, use the STI base class's tableName.\n // R5-canon: qualified-key lookup so a colliding simple name in\n // another package can't yield the wrong tableStrategy / STI base\n // and move this class's columns under that other package's table.\n let tableName = registered.schema.tableName;\n const qualifiedName = (registered as any).qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n\n // Manifest schema remains authoritative for existing columns. Runtime\n // field metadata can backfill missing columns and apply explicit sqlType\n // overrides without erasing richer manifest metadata.\n const columnsToUse = mergeRuntimeFieldColumns(\n simpleName,\n registered.schema.columns,\n registered.fields,\n );\n\n if (!tableSchemas[tableName]) {\n // First class for this table - initialize with base columns\n const baseColumns = createBaseColumns(registered);\n\n const isSTI = tableStrategy === 'sti';\n if (isSTI) {\n baseColumns._meta_type = {\n type: 'TEXT',\n notNull: true,\n defaultValue: '',\n };\n baseColumns._meta_data = { type: 'JSON' };\n }\n\n tableSchemas[tableName] = {\n tableName,\n columns: { ...baseColumns, ...columnsToUse },\n indexes: [],\n isSTI,\n };\n } else {\n // Additional class sharing this table (STI scenario) - merge columns\n for (const [colName, colDef] of Object.entries(columnsToUse)) {\n if (!tableSchemas[tableName].columns[colName]) {\n tableSchemas[tableName].columns[colName] = colDef;\n }\n }\n }\n\n // Merge indexes (avoid duplicates by name)\n if (registered.schema.indexes && registered.schema.indexes.length > 0) {\n const existingNames = new Set(\n tableSchemas[tableName].indexes.map((idx) => idx.name),\n );\n for (const idx of registered.schema.indexes) {\n if (!existingNames.has(idx.name)) {\n tableSchemas[tableName].indexes.push(idx);\n existingNames.add(idx.name);\n }\n }\n }\n }\n }\n\n // Step 2: Convert to SchemaDefinition format\n const schemas: Record<string, SchemaDefinition> = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n if (Object.keys(tableSchema.columns).length === 0) {\n continue;\n }\n\n // Generate DDL from columns\n const ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n\n schemas[tableName] = {\n tableName,\n ddl,\n columns: tableSchema.columns,\n indexes: tableSchema.indexes, // Keep as IndexDefinition[]\n triggers: [],\n foreignKeys: [],\n version: '',\n dependencies: [],\n };\n }\n\n return schemas;\n}\n\n/**\n * Generate DDL CREATE TABLE statement from columns\n *\n * Used internally by getAllSchemas() to regenerate DDL after merging\n * columns from multiple STI subtypes that share the same table.\n *\n * @param tableName - Name of the table\n * @param columns - Column definitions\n * @returns DDL CREATE TABLE statement\n */\nexport function generateDDLFromColumns(\n tableName: string,\n columns: Record<string, ColumnDefinition>,\n isSTI = false,\n): string {\n let sql = `CREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\\n`;\n\n const columnLines: string[] = [];\n for (const [columnName, columnDef] of Object.entries(columns)) {\n const parts: string[] = [];\n\n // Column name and type\n parts.push(` ${quoteIdentifier(columnName)} ${columnDef.type}`);\n\n // Primary key\n if (columnDef.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n\n // Not null constraint\n if (columnDef.notNull) {\n parts.push('NOT NULL');\n }\n\n // Unique constraint (skip if primary key already implies uniqueness)\n if (columnDef.unique && !columnDef.primaryKey) {\n parts.push('UNIQUE');\n }\n\n // Default value\n if (columnDef.defaultValue !== undefined) {\n const defaultSQL = formatDefaultValue(\n columnDef.defaultValue,\n columnDef.type,\n );\n parts.push(`DEFAULT ${defaultSQL}`);\n }\n\n columnLines.push(parts.join(' '));\n }\n\n sql += columnLines.join(',\\n');\n\n // Add UNIQUE constraint for UPSERT operations\n // For STI tables: UNIQUE(slug, context, _meta_type) - different types can have same slug+context\n // For non-STI tables: UNIQUE(slug, context)\n if (columns.slug && columns.context) {\n if (isSTI && columns._meta_type) {\n sql += ',\\n UNIQUE(slug, context, _meta_type)';\n } else {\n sql += ',\\n UNIQUE(slug, context)';\n }\n }\n\n sql += '\\n);';\n\n return sql;\n}\n\n/**\n * Format default value for SQL DDL.\n *\n * Thin wrapper over the shared, injection-safe formatter\n * (`schema/sql-identifiers.ts`) so the registry schema-builder uses the same\n * rules as the DDL strategies and schema generator: an allowlist of SQL\n * keyword/function defaults (not \"contains `(`\"), type-driven literal quoting,\n * and no folding of a literal string `\"null\"` into the SQL NULL keyword.\n *\n * @param value - Default value\n * @param type - Column SQL type\n * @returns Formatted SQL default value\n */\nexport function formatDefaultValue(value: any, type: string): string {\n return formatDefaultValueShared(value, type);\n}\n\n/**\n * Convert a Map of field definitions to column definitions\n *\n * Used by getAllSchemas() to generate columns from fields when a class\n * has no pre-generated schema (e.g., STI subclasses registered from manifest).\n *\n * @param fields - Map of field name to field definition\n * @returns Record of column name to column definition\n * @private\n */\nexport function fieldsToColumns(\n fields: Map<string, FieldDefinition>,\n): Record<string, ColumnDefinition> {\n const columns: Record<string, ColumnDefinition> = {};\n\n for (const [fieldName, fieldDef] of fields) {\n // Skip id, timestamps - they're on the base table\n if (\n fieldName === 'id' ||\n fieldName === 'created_at' ||\n fieldName === 'updated_at' ||\n fieldName === 'slug' ||\n fieldName === 'context'\n ) {\n continue;\n }\n\n // Skip transient fields (non-persisted)\n if (fieldDef.transient || fieldDef._meta?.transient) {\n continue;\n }\n\n // Skip relationship fields that don't create columns\n // oneToMany and manyToMany are relationship metadata, not actual database columns\n if (fieldDef.type === 'oneToMany' || fieldDef.type === 'manyToMany') {\n continue;\n }\n\n // Skip meta fields - they're stored in _meta_data JSONB column\n if (fieldDef.type === 'meta') {\n continue;\n }\n\n // Map field type to SQL type\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef.type === 'crossPackageRef' &&\n (fieldDef._meta?.idType === 'text' || (fieldDef as any).idType === 'text')\n ? 'TEXT'\n : mapFieldTypeToSQL(fieldDef.type));\n const normalizedSqlType = String(sqlType).toUpperCase() as SQLDataType;\n const referenceKind = getReferenceKind(fieldDef);\n\n const column: ColumnDefinition = {\n type: normalizedSqlType,\n referenceKind,\n notNull: fieldDef._meta?.nullable ? false : fieldDef.required || false,\n unique: fieldDef._meta?.unique || false,\n description: fieldDef.description,\n };\n\n // Handle default values\n if (\n fieldDef.default !== undefined &&\n shouldEmitDefault(fieldDef, normalizedSqlType)\n ) {\n column.defaultValue = fieldDef.default;\n }\n\n // Handle foreign keys\n if (fieldDef.type === 'foreignKey' && fieldDef.related) {\n const [table, columnName = 'id'] = fieldDef.related.split('.');\n const fieldMeta = fieldDef._meta as\n | {\n onDelete?: ForeignKeyAction;\n onUpdate?: ForeignKeyAction;\n }\n | undefined;\n column.foreignKey = {\n table: classnameToTablename(table),\n column: columnName,\n onDelete: fieldMeta?.onDelete ?? 'CASCADE',\n onUpdate: fieldMeta?.onUpdate ?? 'CASCADE',\n };\n }\n\n // Use snake_case for column names\n columns[toSnakeCase(fieldName)] = column;\n }\n\n return columns;\n}\n\n/**\n * Map field type to SQL data type\n * @private\n */\nexport function mapFieldTypeToSQL(\n fieldType: FieldDefinition['type'],\n): SQLDataType {\n switch (fieldType) {\n case 'text':\n return 'TEXT';\n case 'integer':\n return 'INTEGER';\n case 'decimal':\n return 'REAL';\n case 'boolean':\n return 'BOOLEAN';\n case 'datetime':\n return 'TIMESTAMP';\n case 'json':\n return 'JSON';\n case 'foreignKey':\n return 'UUID';\n case 'crossPackageRef':\n return 'UUID';\n default:\n return 'TEXT';\n }\n}\n"],"names":["formatDefaultValueShared"],"mappings":"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"}
1
+ {"version":3,"file":"schema-builder.js","sources":["../../src/registry/schema-builder.ts"],"sourcesContent":["/**\n * Schema building logic for the SMRT ObjectRegistry.\n *\n * Extracted from registry.ts as part of issue #1006.\n */\n\nimport { ObjectRegistry } from '../registry';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport {\n formatDefaultValue as formatDefaultValueShared,\n quoteIdentifier,\n} from '../schema/sql-identifiers.js';\nimport type {\n ColumnDefinition,\n IndexDefinition,\n SchemaDefinition,\n SQLDataType,\n} from '../schema/types.js';\nimport { classnameToTablename, toSnakeCase } from '../utils';\nimport {\n type CollectionRegistrationLookup,\n isCollectionRegistration,\n} from './collection-resolution';\nimport { findClass } from './name-resolver';\nimport { getClasses, getCollectionTableNames } from './shared-state';\n\ntype ForeignKeyAction = NonNullable<\n NonNullable<ColumnDefinition['foreignKey']>['onDelete']\n>;\n\nconst collectionRegistrationLookup: CollectionRegistrationLookup = {\n findClass,\n findClassInPackage: (packageName, className) =>\n ObjectRegistry.getClassInPackage(packageName, className),\n getInheritanceChain: (className) =>\n ObjectRegistry.getInheritanceChain(className),\n};\n\nfunction applyDecoratorSqlTypeOverrides(\n className: string,\n columns: Record<string, ColumnDefinition>,\n): Record<string, ColumnDefinition> {\n const decorators = ObjectRegistry.getFieldDecorators(className);\n if (!decorators.size) {\n return columns;\n }\n\n for (const [fieldName, options] of decorators) {\n if (!options?.sqlType) {\n continue;\n }\n\n const columnName = toSnakeCase(fieldName);\n const existing = columns[columnName];\n if (!existing) {\n continue;\n }\n\n const referenceKind = getReferenceKind(options as FieldDefinition);\n columns[columnName] = {\n ...existing,\n type: String(options.sqlType).toUpperCase() as SQLDataType,\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n\n return columns;\n}\n\nfunction mergeRuntimeFieldColumns(\n className: string,\n schemaColumns: Record<string, ColumnDefinition> | undefined,\n fields: Map<string, FieldDefinition>,\n): Record<string, ColumnDefinition> {\n const columnsToUse = { ...(schemaColumns || {}) };\n\n if (fields.size > 0) {\n const fieldColumns = fieldsToColumns(fields);\n for (const [columnName, columnDef] of Object.entries(fieldColumns)) {\n if (!columnsToUse[columnName]) {\n columnsToUse[columnName] = columnDef;\n }\n }\n\n for (const [fieldName, fieldDef] of fields) {\n const columnName = toSnakeCase(fieldName);\n const existing = columnsToUse[columnName];\n if (!existing) {\n continue;\n }\n\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef as unknown as { sqlType?: string }).sqlType;\n const referenceKind = getReferenceKind(fieldDef);\n columnsToUse[columnName] = {\n ...existing,\n ...(sqlType\n ? { type: String(sqlType).toUpperCase() as SQLDataType }\n : {}),\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n }\n\n applyDecoratorSqlTypeOverrides(className, columnsToUse);\n return columnsToUse;\n}\n\nfunction getReferenceKind(\n fieldDef: FieldDefinition,\n): ColumnDefinition['referenceKind'] | undefined {\n if (\n (\n fieldDef as unknown as {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField ||\n fieldDef._meta?.__tenancy?.isTenantIdField\n ) {\n return 'tenantId';\n }\n\n if (fieldDef.type === 'foreignKey') {\n return 'foreignKey';\n }\n\n if (fieldDef.type === 'crossPackageRef') {\n return 'crossPackageRef';\n }\n\n return undefined;\n}\n\nfunction shouldEmitDefault(fieldDef: FieldDefinition, sqlType: SQLDataType) {\n return !(\n getReferenceKind(fieldDef) === 'tenantId' &&\n sqlType === 'UUID' &&\n fieldDef.default === ''\n );\n}\n\nfunction createBaseColumns(registered: {\n config?: { idType?: 'uuid' | 'text' };\n}): Record<string, ColumnDefinition> {\n return {\n id: {\n type: registered.config?.idType === 'text' ? 'TEXT' : 'UUID',\n primaryKey: true,\n referenceKind: 'id',\n },\n slug: { type: 'TEXT', notNull: true },\n context: { type: 'TEXT' },\n created_at: { type: 'TIMESTAMP' },\n updated_at: { type: 'TIMESTAMP' },\n };\n}\n\n/**\n * Get cached schema definition for a registered class\n *\n * @param name - Name of the registered class\n * @returns Schema definition or undefined if not found\n * @example\n * ```typescript\n * const schema = getSchema('Product');\n * console.log(schema.tableName); // 'products'\n * console.log(schema.ddl); // 'CREATE TABLE...'\n * ```\n */\nexport function getSchema(name: string): SchemaDefinition | undefined {\n // Issue #951: Use findClass for multi-strategy lookup\n const registered = findClass(name);\n return registered?.schema;\n}\n\n/**\n * Get SQL DDL statement for a registered class\n *\n * @param name - Name of the registered class\n * @returns SQL DDL statement or undefined if not found\n * @example\n * ```typescript\n * const ddl = ObjectRegistry.getSchemaDDL('Product');\n * await db.query(ddl);\n * ```\n */\nexport function getSchemaDDL(name: string): string | undefined {\n return getSchema(name)?.ddl;\n}\n\n/**\n * Get table name for a registered class\n *\n * @param name - Name of the registered class\n * @returns Table name or undefined if not found\n * @example\n * ```typescript\n * const tableName = getTableName('Product');\n * console.log(tableName); // 'products'\n * ```\n */\nexport function getTableName(name: string): string | undefined {\n // Check if this is a collection class - collections have their own tableName mapping\n const collectionTableName = getCollectionTableNames().get(name);\n if (collectionTableName) {\n return collectionTableName;\n }\n\n // For STI classes, return the STI base class's table name.\n // R5-canon: `getSTIBase` returns the qualified name; resolve `name`\n // (which may be simple) to its registration's qualified form for the\n // comparison.\n const stiBase = ObjectRegistry.getSTIBase(name);\n const registered = ObjectRegistry.getClass(name);\n const qualifiedName = registered?.qualifiedName ?? registered?.name ?? name;\n if (stiBase && stiBase !== qualifiedName) {\n return getSchema(stiBase)?.tableName;\n }\n return getSchema(name)?.tableName;\n}\n\n/**\n * Get all pre-generated schemas for explicit adapter bootstrap paths.\n *\n * Returns schemas in SDK SchemaProvider format for all registered classes.\n * Tooling and test helpers can pass these to `getDatabase()` when they want\n * to bootstrap schema before runtime. Core runtime no longer does this\n * implicitly.\n *\n * @returns Record of table names to schema definitions\n * @example\n * ```typescript\n * const schemas = ObjectRegistry.getAllSchemas();\n * const db = await getDatabase({ type: 'json', url: './data', schemas });\n * ```\n */\nexport function getAllSchemas(): Record<\n string,\n { tableName: string; ddl: string; indexes?: string[] }\n> {\n // Step 1: Collect all schemas grouped by tableName\n // For STI, multiple classes may share the same table\n const tableSchemas: Record<\n string,\n {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n indexes: Array<{ name: string; columns: string[]; unique?: boolean }>;\n ddl: string;\n isSTI: boolean;\n }\n > = {};\n\n for (const [_className, registered] of getClasses()) {\n // Skip collection classes - they don't have their own tables\n // Their schemas incorrectly contain collection properties (loaded, options, etc.)\n if (\n isCollectionRegistration(\n _className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n // Issue #951: Use simple name for STI comparisons (map key may be qualified)\n const simpleName = registered.name || _className;\n\n // Issue #703: Handle STI subclasses with null tableName from external manifests\n // When loaded from external manifests, tableName may be null, causing\n // registerFromManifest() to derive a tableName from class name.\n // For STI subclasses, we need to use the STI base's tableName instead.\n if (!registered.schema?.tableName && registered.extends) {\n // R5-canon: use the qualified key for STI lookups so colliding\n // simple names don't resolve to the wrong package's class.\n // `getTableStrategy` / `getSTIBase` both accept qualified names\n // via `findClass`'s multi-strategy lookup.\n const qualifiedName = registered.qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n // Ensure we have a schema object to modify\n if (!registered.schema) {\n registered.schema = {\n tableName: '',\n ddl: '',\n columns: {},\n indexes: [],\n triggers: [],\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n // Set tableName from STI base so the following block processes this class\n registered.schema.tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n if (registered.schema?.tableName) {\n // For STI subclasses, use the STI base class's tableName\n // This ensures all STI subclass columns are merged into the parent table\n // (Issue #693: STI subclasses with separate tableName still serialize to parent table)\n let tableName = registered.schema.tableName;\n // R5-canon: same qualified-key strategy as the block above.\n const qualifiedName = registered.qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n // This is an STI subclass - use the base class's tableName\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n\n // Start with manifest columns, then backfill any columns that only exist\n // in runtime field metadata (for example tenantScoped injections). We do\n // not replace existing manifest column metadata here, because the manifest\n // is authoritative for foreign keys, defaults, and other schema details.\n // Explicit decorator sqlType overrides are patched onto the merged column.\n const columnsToUse = mergeRuntimeFieldColumns(\n simpleName,\n registered.schema.columns,\n registered.fields,\n );\n\n if (!tableSchemas[tableName]) {\n // First class for this table - initialize with base columns\n // These are required for all tables but are skipped by fieldsToColumns()\n const baseColumns = createBaseColumns(registered);\n\n // For STI tables, add discriminator and data columns\n // (Issue #690: db:diff needs these columns to detect schema changes)\n const isSTI = tableStrategy === 'sti';\n if (isSTI) {\n baseColumns._meta_type = {\n type: 'TEXT',\n notNull: true,\n defaultValue: '',\n };\n baseColumns._meta_data = { type: 'JSON' };\n }\n\n tableSchemas[tableName] = {\n tableName,\n columns: { ...baseColumns, ...columnsToUse },\n indexes: [],\n ddl: registered.schema.ddl || '',\n isSTI,\n };\n } else {\n // Additional class sharing this table (STI scenario)\n // Merge columns from this class into the existing schema\n for (const [colName, colDef] of Object.entries(columnsToUse)) {\n if (!tableSchemas[tableName].columns[colName]) {\n // New column from this subtype - add it\n tableSchemas[tableName].columns[colName] = colDef;\n }\n }\n }\n\n // Merge indexes (avoid duplicates by name)\n if (registered.schema.indexes && registered.schema.indexes.length > 0) {\n const existingNames = new Set(\n tableSchemas[tableName].indexes.map((idx) =>\n typeof idx === 'string' ? idx : idx.name,\n ),\n );\n for (const idx of registered.schema.indexes) {\n const indexName = typeof idx === 'string' ? idx : idx.name;\n if (!existingNames.has(indexName)) {\n if (typeof idx === 'string') {\n // Legacy string format - skip, can't merge properly\n } else {\n tableSchemas[tableName].indexes.push(idx);\n existingNames.add(idx.name);\n }\n }\n }\n }\n }\n }\n\n // Step 2: Convert to output format, regenerating DDL for merged schemas\n const schemas: Record<\n string,\n { tableName: string; ddl: string; indexes?: string[] }\n > = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n // Generate DDL from merged columns (or use original DDL if columns are empty)\n let ddl: string;\n if (Object.keys(tableSchema.columns).length === 0 && tableSchema.ddl) {\n // No columns merged - use original DDL to avoid generating invalid SQL\n ddl = tableSchema.ddl;\n } else if (Object.keys(tableSchema.columns).length === 0) {\n // Skip schemas with no columns and no original DDL\n continue;\n } else {\n ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n }\n\n // Convert index definitions to SQL strings for SDK compatibility\n let indexSQL: string[] | undefined;\n if (tableSchema.indexes.length > 0) {\n indexSQL = tableSchema.indexes.map((idx) => {\n const indexType = idx.unique ? 'UNIQUE INDEX' : 'INDEX';\n const columnList = idx.columns\n .map((col) => quoteIdentifier(col))\n .join(', ');\n return `CREATE ${indexType} IF NOT EXISTS ${quoteIdentifier(\n idx.name,\n )} ON ${quoteIdentifier(tableName)} (${columnList});`;\n });\n }\n\n schemas[tableName] = {\n tableName,\n ddl,\n indexes: indexSQL,\n };\n }\n\n return schemas;\n}\n\n/**\n * Get all registered schemas as SchemaDefinition objects\n *\n * Similar to getAllSchemas(), but returns SchemaDefinition format suitable\n * for use with SchemaComparer (migrations/differ.ts).\n *\n * Key difference: Indexes are kept as IndexDefinition objects instead of\n * being converted to SQL strings.\n *\n * @returns Map of tableName to SchemaDefinition\n */\nexport function getAllSchemasAsDefinitions(): Record<string, SchemaDefinition> {\n // Step 1: Collect all schemas grouped by tableName\n // For STI, multiple classes may share the same table\n const tableSchemas: Record<\n string,\n {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n indexes: IndexDefinition[];\n isSTI: boolean;\n }\n > = {};\n\n for (const [_className, registered] of getClasses()) {\n // Skip collection classes - they don't have their own tables\n if (\n isCollectionRegistration(\n _className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n // Issue #951: Use simple name for STI comparisons (map key may be qualified)\n const simpleName = registered.name || _className;\n\n // Handle STI subclasses with null tableName from external manifests\n if (!registered.schema?.tableName && registered.extends) {\n // R5-canon: use the qualified key for STI lookups so colliding\n // simple names don't resolve to the wrong package's class.\n // `getTableStrategy` / `getSTIBase` both accept qualified names\n // via `findClass`'s multi-strategy lookup.\n const qualifiedName = registered.qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n if (!registered.schema) {\n registered.schema = {\n tableName: '',\n ddl: '',\n columns: {},\n indexes: [],\n triggers: [],\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n registered.schema.tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n if (registered.schema?.tableName) {\n // For STI subclasses, use the STI base class's tableName.\n // R5-canon: qualified-key lookup so a colliding simple name in\n // another package can't yield the wrong tableStrategy / STI base\n // and move this class's columns under that other package's table.\n let tableName = registered.schema.tableName;\n const qualifiedName = registered.qualifiedName ?? simpleName;\n const lookupKey = qualifiedName;\n const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);\n if (tableStrategy === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n\n // Manifest schema remains authoritative for existing columns. Runtime\n // field metadata can backfill missing columns and apply explicit sqlType\n // overrides without erasing richer manifest metadata.\n const columnsToUse = mergeRuntimeFieldColumns(\n simpleName,\n registered.schema.columns,\n registered.fields,\n );\n\n if (!tableSchemas[tableName]) {\n // First class for this table - initialize with base columns\n const baseColumns = createBaseColumns(registered);\n\n const isSTI = tableStrategy === 'sti';\n if (isSTI) {\n baseColumns._meta_type = {\n type: 'TEXT',\n notNull: true,\n defaultValue: '',\n };\n baseColumns._meta_data = { type: 'JSON' };\n }\n\n tableSchemas[tableName] = {\n tableName,\n columns: { ...baseColumns, ...columnsToUse },\n indexes: [],\n isSTI,\n };\n } else {\n // Additional class sharing this table (STI scenario) - merge columns\n for (const [colName, colDef] of Object.entries(columnsToUse)) {\n if (!tableSchemas[tableName].columns[colName]) {\n tableSchemas[tableName].columns[colName] = colDef;\n }\n }\n }\n\n // Merge indexes (avoid duplicates by name)\n if (registered.schema.indexes && registered.schema.indexes.length > 0) {\n const existingNames = new Set(\n tableSchemas[tableName].indexes.map((idx) => idx.name),\n );\n for (const idx of registered.schema.indexes) {\n if (!existingNames.has(idx.name)) {\n tableSchemas[tableName].indexes.push(idx);\n existingNames.add(idx.name);\n }\n }\n }\n }\n }\n\n // Step 2: Convert to SchemaDefinition format\n const schemas: Record<string, SchemaDefinition> = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n if (Object.keys(tableSchema.columns).length === 0) {\n continue;\n }\n\n // Generate DDL from columns\n const ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n\n schemas[tableName] = {\n tableName,\n ddl,\n columns: tableSchema.columns,\n indexes: tableSchema.indexes, // Keep as IndexDefinition[]\n triggers: [],\n foreignKeys: [],\n version: '',\n dependencies: [],\n };\n }\n\n return schemas;\n}\n\n/**\n * Generate DDL CREATE TABLE statement from columns\n *\n * Used internally by getAllSchemas() to regenerate DDL after merging\n * columns from multiple STI subtypes that share the same table.\n *\n * @param tableName - Name of the table\n * @param columns - Column definitions\n * @returns DDL CREATE TABLE statement\n */\nexport function generateDDLFromColumns(\n tableName: string,\n columns: Record<string, ColumnDefinition>,\n isSTI = false,\n): string {\n let sql = `CREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\\n`;\n\n const columnLines: string[] = [];\n for (const [columnName, columnDef] of Object.entries(columns)) {\n const parts: string[] = [];\n\n // Column name and type\n parts.push(` ${quoteIdentifier(columnName)} ${columnDef.type}`);\n\n // Primary key\n if (columnDef.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n\n // Not null constraint\n if (columnDef.notNull) {\n parts.push('NOT NULL');\n }\n\n // Unique constraint (skip if primary key already implies uniqueness)\n if (columnDef.unique && !columnDef.primaryKey) {\n parts.push('UNIQUE');\n }\n\n // Default value\n if (columnDef.defaultValue !== undefined) {\n const defaultSQL = formatDefaultValue(\n columnDef.defaultValue,\n columnDef.type,\n );\n parts.push(`DEFAULT ${defaultSQL}`);\n }\n\n columnLines.push(parts.join(' '));\n }\n\n sql += columnLines.join(',\\n');\n\n // Add UNIQUE constraint for UPSERT operations\n // For STI tables: UNIQUE(slug, context, _meta_type) - different types can have same slug+context\n // For non-STI tables: UNIQUE(slug, context)\n if (columns.slug && columns.context) {\n if (isSTI && columns._meta_type) {\n sql += ',\\n UNIQUE(slug, context, _meta_type)';\n } else {\n sql += ',\\n UNIQUE(slug, context)';\n }\n }\n\n sql += '\\n);';\n\n return sql;\n}\n\n/**\n * Format default value for SQL DDL.\n *\n * Thin wrapper over the shared, injection-safe formatter\n * (`schema/sql-identifiers.ts`) so the registry schema-builder uses the same\n * rules as the DDL strategies and schema generator: an allowlist of SQL\n * keyword/function defaults (not \"contains `(`\"), type-driven literal quoting,\n * and no folding of a literal string `\"null\"` into the SQL NULL keyword.\n *\n * @param value - Default value\n * @param type - Column SQL type\n * @returns Formatted SQL default value\n */\nexport function formatDefaultValue(value: unknown, type: string): string {\n return formatDefaultValueShared(value, type);\n}\n\n/**\n * Convert a Map of field definitions to column definitions\n *\n * Used by getAllSchemas() to generate columns from fields when a class\n * has no pre-generated schema (e.g., STI subclasses registered from manifest).\n *\n * @param fields - Map of field name to field definition\n * @returns Record of column name to column definition\n * @private\n */\nexport function fieldsToColumns(\n fields: Map<string, FieldDefinition>,\n): Record<string, ColumnDefinition> {\n const columns: Record<string, ColumnDefinition> = {};\n\n for (const [fieldName, fieldDef] of fields) {\n // Skip id, timestamps - they're on the base table\n if (\n fieldName === 'id' ||\n fieldName === 'created_at' ||\n fieldName === 'updated_at' ||\n fieldName === 'slug' ||\n fieldName === 'context'\n ) {\n continue;\n }\n\n // Skip transient fields (non-persisted)\n if (fieldDef.transient || fieldDef._meta?.transient) {\n continue;\n }\n\n // Skip relationship fields that don't create columns\n // oneToMany and manyToMany are relationship metadata, not actual database columns\n if (fieldDef.type === 'oneToMany' || fieldDef.type === 'manyToMany') {\n continue;\n }\n\n // Skip meta fields - they're stored in _meta_data JSONB column\n if (fieldDef.type === 'meta') {\n continue;\n }\n\n // Map field type to SQL type\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef.type === 'crossPackageRef' &&\n (fieldDef._meta?.idType === 'text' ||\n (fieldDef as unknown as { idType?: string }).idType === 'text')\n ? 'TEXT'\n : mapFieldTypeToSQL(fieldDef.type));\n const normalizedSqlType = String(sqlType).toUpperCase() as SQLDataType;\n const referenceKind = getReferenceKind(fieldDef);\n\n const column: ColumnDefinition = {\n type: normalizedSqlType,\n referenceKind,\n notNull: fieldDef._meta?.nullable ? false : fieldDef.required || false,\n unique: fieldDef._meta?.unique || false,\n description: fieldDef.description,\n };\n\n // Handle default values\n if (\n fieldDef.default !== undefined &&\n shouldEmitDefault(fieldDef, normalizedSqlType)\n ) {\n column.defaultValue = fieldDef.default;\n }\n\n // Handle foreign keys\n if (fieldDef.type === 'foreignKey' && fieldDef.related) {\n const [table, columnName = 'id'] = fieldDef.related.split('.');\n const fieldMeta = fieldDef._meta as\n | {\n onDelete?: ForeignKeyAction;\n onUpdate?: ForeignKeyAction;\n }\n | undefined;\n column.foreignKey = {\n table: classnameToTablename(table),\n column: columnName,\n onDelete: fieldMeta?.onDelete ?? 'CASCADE',\n onUpdate: fieldMeta?.onUpdate ?? 'CASCADE',\n };\n }\n\n // Use snake_case for column names\n columns[toSnakeCase(fieldName)] = column;\n }\n\n return columns;\n}\n\n/**\n * Map field type to SQL data type\n * @private\n */\nexport function mapFieldTypeToSQL(\n fieldType: FieldDefinition['type'],\n): SQLDataType {\n switch (fieldType) {\n case 'text':\n return 'TEXT';\n case 'integer':\n return 'INTEGER';\n case 'decimal':\n return 'REAL';\n case 'boolean':\n return 'BOOLEAN';\n case 'datetime':\n return 'TIMESTAMP';\n case 'json':\n return 'JSON';\n case 'foreignKey':\n return 'UUID';\n case 'crossPackageRef':\n return 'UUID';\n default:\n return 'TEXT';\n }\n}\n"],"names":["formatDefaultValueShared"],"mappings":"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"}
@@ -2,13 +2,13 @@ import { SmrtCollection } from '../collection';
2
2
  import { LRUCache } from '../utils/lru-cache';
3
3
  import { RegisteredClass, SmrtObjectConstructor } from './types';
4
4
  declare global {
5
- var __smrtRegistryClasses: Map<string, any> | undefined;
5
+ var __smrtRegistryClasses: Map<string, RegisteredClass> | undefined;
6
6
  var __smrtRegistryCollections: Map<string, typeof SmrtCollection> | undefined;
7
7
  var __smrtRegistryCollectionCache: LRUCache<string, SmrtCollection<any>> | undefined;
8
8
  var __smrtRegistryDbInstanceIds: WeakMap<object, number> | undefined;
9
9
  var __smrtRegistryNextDbId: number | undefined;
10
10
  var __smrtRegistryInheritanceChainCache: LRUCache<string, string[]> | undefined;
11
- var __smrtRegistryFieldDecorators: Map<string, Map<string, any>> | undefined;
11
+ var __smrtRegistryFieldDecorators: Map<string, Map<string, Record<string, unknown>>> | undefined;
12
12
  var __smrtRegistryStiSiblingsLoaded: Set<string> | undefined;
13
13
  var __smrtRegistryCollectionTableNames: Map<string, string> | undefined;
14
14
  var __smrtRegistrySchemasInitialized: WeakSet<object> | undefined;
@@ -53,7 +53,7 @@ export declare function getCollectionCache(): LRUCache<string, SmrtCollection<an
53
53
  export declare function getDbInstanceIds(): WeakMap<object, number>;
54
54
  export declare function getNextDbId(): number;
55
55
  export declare function setNextDbId(value: number): void;
56
- export declare function getFieldDecorators(): Map<string, Map<string, any>>;
56
+ export declare function getFieldDecorators(): Map<string, Map<string, Record<string, unknown>>>;
57
57
  export declare function getStiSiblingsLoaded(): Set<string>;
58
58
  export declare function getSchemasInitialized(): WeakSet<object>;
59
59
  export declare function getConstructorIndex(): WeakMap<SmrtObjectConstructor, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"shared-state.d.ts","sourceRoot":"","sources":["../../src/registry/shared-state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared-state.d.ts","sourceRoot":"","sources":["../../src/registry/shared-state.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared-state.js","sources":["../../src/registry/shared-state.ts"],"sourcesContent":["/**\n * Shared state accessors for the SMRT ObjectRegistry.\n *\n * All registry sub-modules share state via globalThis to ensure\n * cross-module consistency in monorepo environments.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n * @see https://github.com/happyvertical/smrt/issues/543\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtCollection } from '../collection';\nimport {\n getSmrtModuleConfig,\n type SmrtGlobalConfig,\n} from '../config/global-config.js';\nimport { LRUCache } from '../utils/lru-cache';\nimport type { RegisteredClass, SmrtObjectConstructor } from './types';\n\n// Re-export the globalThis augmentation so it's visible everywhere\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtRegistryClasses: Map<string, any> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollections: Map<string, typeof SmrtCollection> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollectionCache:\n | LRUCache<string, SmrtCollection<any>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDbInstanceIds: WeakMap<object, number> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryNextDbId: number | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryInheritanceChainCache:\n | LRUCache<string, string[]>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryFieldDecorators: Map<string, Map<string, any>> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryStiSiblingsLoaded: Set<string> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollectionTableNames: Map<string, string> | undefined;\n // Release B (#1133) removed __smrtRegistryClassNameMap — case-insensitive\n // lookups now iterate the `classes` Map directly. The globalThis slot is\n // left undeclared so any leftover code that reaches for it gets a clean\n // TS error instead of silently stale data.\n // eslint-disable-next-line no-var\n var __smrtRegistrySchemasInitialized: WeakSet<object> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryConstructorIndex:\n | WeakMap<SmrtObjectConstructor, string>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDiscoveryAttemptCache: Map<string, boolean> | undefined;\n}\n\n/**\n * Cached verbose flag evaluated once at module load time.\n * Checks SMRT_VERBOSE=true or DEBUG containing 'smrt'.\n * Avoids repeated env var parsing on every log statement.\n * @see https://github.com/happyvertical/smrt/issues/782\n */\nexport const VERBOSE_ENABLED =\n process.env.SMRT_VERBOSE === 'true' ||\n (process.env.DEBUG?.includes('smrt') ?? false);\n\n// verboseLog() is gated by VERBOSE_ENABLED, so the level must allow debug when\n// it's set (a fixed 'info' would filter the trace out and break SMRT_VERBOSE).\nconst logger = createLogger({ level: VERBOSE_ENABLED ? 'debug' : 'info' });\n\n/**\n * Log a message only when verbose mode is enabled.\n * Used throughout registry modules to reduce noise during normal operation.\n * @param args - Arguments to pass to console.log\n */\nexport function verboseLog(...args: unknown[]): void {\n if (VERBOSE_ENABLED) {\n const [message, ...rest] = args;\n logger.debug(\n typeof message === 'string' ? message : String(message),\n rest.length > 0 ? { args: rest } : undefined,\n );\n }\n}\n\n/**\n * Get inheritance config synchronously.\n * Uses the config package's sync accessor which returns already-loaded config.\n */\nexport function getInheritanceConfig() {\n const config = getSmrtModuleConfig<SmrtGlobalConfig>('smrt', {});\n return {\n cacheSize: config.inheritance?.cacheSize ?? 200,\n onMissingAncestor:\n config.inheritance?.onMissingAncestor ?? ('warn' as 'warn' | 'error'),\n };\n}\n\n/**\n * Extract the source file path from the call stack.\n *\n * Used to identify where a class was defined for collision detection.\n * This allows the same class to be re-registered when modules are re-evaluated\n * (e.g., during vitest test file collection with isolation enabled).\n *\n * @returns The file path where the @smrt() decorator was called, or undefined\n */\nexport function getSourceFileFromStack(): string | undefined {\n const error = new Error();\n const stack = error.stack || '';\n const stackLines = stack.split('\\n');\n\n // Look for the first file path that's NOT from smrt-core internal files\n // Stack trace format: \" at FunctionName (file:///path/to/file.ts:line:col)\"\n // or \" at file:///path/to/file.ts:line:col\"\n for (const line of stackLines) {\n // Match file paths in stack trace (include all JS/TS file extensions)\n const fileMatch = line.match(\n /(?:file:\\/\\/)?([^)\\s]+\\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?::\\d+:\\d+)?/,\n );\n if (fileMatch) {\n const rawPath = fileMatch[1];\n // Normalize the path:\n // - remove any leading file:// or file:/// prefix\n // - convert Windows backslashes to POSIX-style forward slashes\n const normalizedPath = rawPath\n .replace(/^file:\\/+/, '')\n .replace(/\\\\/g, '/');\n const lowerPath = normalizedPath.toLowerCase();\n\n // Skip smrt-core internal files using specific path patterns\n // to avoid false positives with user files containing \"registry\" in name\n if (\n // Installed package form: node_modules/@happyvertical/smrt-core/...\n lowerPath.includes('/node_modules/@happyvertical/smrt-core/') ||\n // Monorepo/workspace form: packages/core/src/...\n (lowerPath.includes('/packages/core/src/') &&\n !lowerPath.includes('__tests__')) ||\n // Specific manifest loader internals\n lowerPath.includes('/manifest/manifest-loader')\n ) {\n continue;\n }\n return normalizedPath;\n }\n }\n return undefined;\n}\n\n// ── Shared state accessor functions ──────────────────────────────────────\n\nexport function getClasses(): Map<string, RegisteredClass> {\n if (!globalThis.__smrtRegistryClasses) {\n globalThis.__smrtRegistryClasses = new Map<string, RegisteredClass>();\n }\n return globalThis.__smrtRegistryClasses;\n}\n\nexport function getCollections(): Map<string, typeof SmrtCollection> {\n if (!globalThis.__smrtRegistryCollections) {\n globalThis.__smrtRegistryCollections = new Map<\n string,\n typeof SmrtCollection\n >();\n }\n return globalThis.__smrtRegistryCollections;\n}\n\nexport function getCollectionTableNames(): Map<string, string> {\n if (!globalThis.__smrtRegistryCollectionTableNames) {\n globalThis.__smrtRegistryCollectionTableNames = new Map<string, string>();\n }\n return globalThis.__smrtRegistryCollectionTableNames;\n}\n\nexport function getCollectionCache(): LRUCache<string, SmrtCollection<any>> {\n if (!globalThis.__smrtRegistryCollectionCache) {\n globalThis.__smrtRegistryCollectionCache = new LRUCache<\n string,\n SmrtCollection<any>\n >(100);\n }\n return globalThis.__smrtRegistryCollectionCache;\n}\n\nexport function getDbInstanceIds(): WeakMap<object, number> {\n if (!globalThis.__smrtRegistryDbInstanceIds) {\n globalThis.__smrtRegistryDbInstanceIds = new WeakMap<object, number>();\n }\n return globalThis.__smrtRegistryDbInstanceIds;\n}\n\nexport function getNextDbId(): number {\n if (globalThis.__smrtRegistryNextDbId === undefined) {\n globalThis.__smrtRegistryNextDbId = 1;\n }\n return globalThis.__smrtRegistryNextDbId;\n}\n\nexport function setNextDbId(value: number): void {\n globalThis.__smrtRegistryNextDbId = value;\n}\n\nexport function getFieldDecorators(): Map<string, Map<string, any>> {\n if (!globalThis.__smrtRegistryFieldDecorators) {\n globalThis.__smrtRegistryFieldDecorators = new Map<\n string,\n Map<string, any>\n >();\n }\n return globalThis.__smrtRegistryFieldDecorators;\n}\n\nexport function getStiSiblingsLoaded(): Set<string> {\n if (!globalThis.__smrtRegistryStiSiblingsLoaded) {\n globalThis.__smrtRegistryStiSiblingsLoaded = new Set<string>();\n }\n return globalThis.__smrtRegistryStiSiblingsLoaded;\n}\n\n// getClassNameMap removed in Release B (#1133). The eagerly-maintained\n// lowercase-simple-name → qualified-key[] index was replaced by on-demand\n// iteration over the `classes` Map in name-resolver.ts. Any remaining\n// consumer should switch to `findClassesByName`, `getCanonicalClassName`,\n// or `findClass`.\n\nexport function getSchemasInitialized(): WeakSet<object> {\n if (!globalThis.__smrtRegistrySchemasInitialized) {\n globalThis.__smrtRegistrySchemasInitialized = new WeakSet<object>();\n }\n return globalThis.__smrtRegistrySchemasInitialized;\n}\n\nexport function getConstructorIndex(): WeakMap<SmrtObjectConstructor, string> {\n if (!globalThis.__smrtRegistryConstructorIndex) {\n globalThis.__smrtRegistryConstructorIndex = new WeakMap<\n SmrtObjectConstructor,\n string\n >();\n }\n return globalThis.__smrtRegistryConstructorIndex;\n}\n\nexport function getInheritanceCache(): LRUCache<string, string[]> {\n if (!globalThis.__smrtRegistryInheritanceChainCache) {\n const config = getInheritanceConfig();\n globalThis.__smrtRegistryInheritanceChainCache = new LRUCache<\n string,\n string[]\n >(config.cacheSize);\n }\n return globalThis.__smrtRegistryInheritanceChainCache;\n}\n\nexport function getDiscoveryAttemptCache(): Map<string, boolean> {\n if (!globalThis.__smrtRegistryDiscoveryAttemptCache) {\n globalThis.__smrtRegistryDiscoveryAttemptCache = new Map<string, boolean>();\n }\n return globalThis.__smrtRegistryDiscoveryAttemptCache;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared-state.js","sources":["../../src/registry/shared-state.ts"],"sourcesContent":["/**\n * Shared state accessors for the SMRT ObjectRegistry.\n *\n * All registry sub-modules share state via globalThis to ensure\n * cross-module consistency in monorepo environments.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n * @see https://github.com/happyvertical/smrt/issues/543\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtCollection } from '../collection';\nimport {\n getSmrtModuleConfig,\n type SmrtGlobalConfig,\n} from '../config/global-config.js';\nimport type { SmrtObject } from '../object';\nimport { LRUCache } from '../utils/lru-cache';\nimport type { RegisteredClass, SmrtObjectConstructor } from './types';\n\n// Re-export the globalThis augmentation so it's visible everywhere\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtRegistryClasses: Map<string, RegisteredClass> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollections: Map<string, typeof SmrtCollection> | undefined;\n // eslint-disable-next-line no-var\n // Heterogeneous cache: stores collections of many different item types keyed\n // by class name. `SmrtCollection` is invariant in its item type (it has\n // `create(options: SmrtCreateInput<ModelType>)`), so this cannot be narrowed\n // to `SmrtCollection<SmrtObject>` — the type argument stays `any` (read sites\n // cast back to the concrete collection type).\n var __smrtRegistryCollectionCache:\n // biome-ignore lint/suspicious/noExplicitAny: heterogeneous cache keyed by class name; `SmrtCollection` is invariant in `ModelType` so the type arg cannot narrow to `SmrtObject` (read sites cast back). S4 #1579.\n | LRUCache<string, SmrtCollection<any>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDbInstanceIds: WeakMap<object, number> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryNextDbId: number | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryInheritanceChainCache:\n | LRUCache<string, string[]>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryFieldDecorators:\n | Map<string, Map<string, Record<string, unknown>>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryStiSiblingsLoaded: Set<string> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollectionTableNames: Map<string, string> | undefined;\n // Release B (#1133) removed __smrtRegistryClassNameMap — case-insensitive\n // lookups now iterate the `classes` Map directly. The globalThis slot is\n // left undeclared so any leftover code that reaches for it gets a clean\n // TS error instead of silently stale data.\n // eslint-disable-next-line no-var\n var __smrtRegistrySchemasInitialized: WeakSet<object> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryConstructorIndex:\n | WeakMap<SmrtObjectConstructor, string>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDiscoveryAttemptCache: Map<string, boolean> | undefined;\n}\n\n/**\n * Cached verbose flag evaluated once at module load time.\n * Checks SMRT_VERBOSE=true or DEBUG containing 'smrt'.\n * Avoids repeated env var parsing on every log statement.\n * @see https://github.com/happyvertical/smrt/issues/782\n */\nexport const VERBOSE_ENABLED =\n process.env.SMRT_VERBOSE === 'true' ||\n (process.env.DEBUG?.includes('smrt') ?? false);\n\n// verboseLog() is gated by VERBOSE_ENABLED, so the level must allow debug when\n// it's set (a fixed 'info' would filter the trace out and break SMRT_VERBOSE).\nconst logger = createLogger({ level: VERBOSE_ENABLED ? 'debug' : 'info' });\n\n/**\n * Log a message only when verbose mode is enabled.\n * Used throughout registry modules to reduce noise during normal operation.\n * @param args - Arguments to pass to console.log\n */\nexport function verboseLog(...args: unknown[]): void {\n if (VERBOSE_ENABLED) {\n const [message, ...rest] = args;\n logger.debug(\n typeof message === 'string' ? message : String(message),\n rest.length > 0 ? { args: rest } : undefined,\n );\n }\n}\n\n/**\n * Get inheritance config synchronously.\n * Uses the config package's sync accessor which returns already-loaded config.\n */\nexport function getInheritanceConfig() {\n const config = getSmrtModuleConfig<SmrtGlobalConfig>('smrt', {});\n return {\n cacheSize: config.inheritance?.cacheSize ?? 200,\n onMissingAncestor:\n config.inheritance?.onMissingAncestor ?? ('warn' as 'warn' | 'error'),\n };\n}\n\n/**\n * Extract the source file path from the call stack.\n *\n * Used to identify where a class was defined for collision detection.\n * This allows the same class to be re-registered when modules are re-evaluated\n * (e.g., during vitest test file collection with isolation enabled).\n *\n * @returns The file path where the @smrt() decorator was called, or undefined\n */\nexport function getSourceFileFromStack(): string | undefined {\n const error = new Error();\n const stack = error.stack || '';\n const stackLines = stack.split('\\n');\n\n // Look for the first file path that's NOT from smrt-core internal files\n // Stack trace format: \" at FunctionName (file:///path/to/file.ts:line:col)\"\n // or \" at file:///path/to/file.ts:line:col\"\n for (const line of stackLines) {\n // Match file paths in stack trace (include all JS/TS file extensions)\n const fileMatch = line.match(\n /(?:file:\\/\\/)?([^)\\s]+\\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?::\\d+:\\d+)?/,\n );\n if (fileMatch) {\n const rawPath = fileMatch[1];\n // Normalize the path:\n // - remove any leading file:// or file:/// prefix\n // - convert Windows backslashes to POSIX-style forward slashes\n const normalizedPath = rawPath\n .replace(/^file:\\/+/, '')\n .replace(/\\\\/g, '/');\n const lowerPath = normalizedPath.toLowerCase();\n\n // Skip smrt-core internal files using specific path patterns\n // to avoid false positives with user files containing \"registry\" in name\n if (\n // Installed package form: node_modules/@happyvertical/smrt-core/...\n lowerPath.includes('/node_modules/@happyvertical/smrt-core/') ||\n // Monorepo/workspace form: packages/core/src/...\n (lowerPath.includes('/packages/core/src/') &&\n !lowerPath.includes('__tests__')) ||\n // Specific manifest loader internals\n lowerPath.includes('/manifest/manifest-loader')\n ) {\n continue;\n }\n return normalizedPath;\n }\n }\n return undefined;\n}\n\n// ── Shared state accessor functions ──────────────────────────────────────\n\nexport function getClasses(): Map<string, RegisteredClass> {\n if (!globalThis.__smrtRegistryClasses) {\n globalThis.__smrtRegistryClasses = new Map<string, RegisteredClass>();\n }\n return globalThis.__smrtRegistryClasses;\n}\n\nexport function getCollections(): Map<string, typeof SmrtCollection> {\n if (!globalThis.__smrtRegistryCollections) {\n globalThis.__smrtRegistryCollections = new Map<\n string,\n typeof SmrtCollection\n >();\n }\n return globalThis.__smrtRegistryCollections;\n}\n\nexport function getCollectionTableNames(): Map<string, string> {\n if (!globalThis.__smrtRegistryCollectionTableNames) {\n globalThis.__smrtRegistryCollectionTableNames = new Map<string, string>();\n }\n return globalThis.__smrtRegistryCollectionTableNames;\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: heterogeneous cache; `SmrtCollection` is invariant in `ModelType` so the type arg cannot narrow to `SmrtObject` (read sites cast back). S4 #1579.\nexport function getCollectionCache(): LRUCache<string, SmrtCollection<any>> {\n if (!globalThis.__smrtRegistryCollectionCache) {\n globalThis.__smrtRegistryCollectionCache = new LRUCache<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: same heterogeneous-cache invariance as the return type above. S4 #1579.\n SmrtCollection<any>\n >(100);\n }\n return globalThis.__smrtRegistryCollectionCache;\n}\n\nexport function getDbInstanceIds(): WeakMap<object, number> {\n if (!globalThis.__smrtRegistryDbInstanceIds) {\n globalThis.__smrtRegistryDbInstanceIds = new WeakMap<object, number>();\n }\n return globalThis.__smrtRegistryDbInstanceIds;\n}\n\nexport function getNextDbId(): number {\n if (globalThis.__smrtRegistryNextDbId === undefined) {\n globalThis.__smrtRegistryNextDbId = 1;\n }\n return globalThis.__smrtRegistryNextDbId;\n}\n\nexport function setNextDbId(value: number): void {\n globalThis.__smrtRegistryNextDbId = value;\n}\n\nexport function getFieldDecorators(): Map<\n string,\n Map<string, Record<string, unknown>>\n> {\n if (!globalThis.__smrtRegistryFieldDecorators) {\n globalThis.__smrtRegistryFieldDecorators = new Map<\n string,\n Map<string, Record<string, unknown>>\n >();\n }\n return globalThis.__smrtRegistryFieldDecorators;\n}\n\nexport function getStiSiblingsLoaded(): Set<string> {\n if (!globalThis.__smrtRegistryStiSiblingsLoaded) {\n globalThis.__smrtRegistryStiSiblingsLoaded = new Set<string>();\n }\n return globalThis.__smrtRegistryStiSiblingsLoaded;\n}\n\n// getClassNameMap removed in Release B (#1133). The eagerly-maintained\n// lowercase-simple-name → qualified-key[] index was replaced by on-demand\n// iteration over the `classes` Map in name-resolver.ts. Any remaining\n// consumer should switch to `findClassesByName`, `getCanonicalClassName`,\n// or `findClass`.\n\nexport function getSchemasInitialized(): WeakSet<object> {\n if (!globalThis.__smrtRegistrySchemasInitialized) {\n globalThis.__smrtRegistrySchemasInitialized = new WeakSet<object>();\n }\n return globalThis.__smrtRegistrySchemasInitialized;\n}\n\nexport function getConstructorIndex(): WeakMap<SmrtObjectConstructor, string> {\n if (!globalThis.__smrtRegistryConstructorIndex) {\n globalThis.__smrtRegistryConstructorIndex = new WeakMap<\n SmrtObjectConstructor,\n string\n >();\n }\n return globalThis.__smrtRegistryConstructorIndex;\n}\n\nexport function getInheritanceCache(): LRUCache<string, string[]> {\n if (!globalThis.__smrtRegistryInheritanceChainCache) {\n const config = getInheritanceConfig();\n globalThis.__smrtRegistryInheritanceChainCache = new LRUCache<\n string,\n string[]\n >(config.cacheSize);\n }\n return globalThis.__smrtRegistryInheritanceChainCache;\n}\n\nexport function getDiscoveryAttemptCache(): Map<string, boolean> {\n if (!globalThis.__smrtRegistryDiscoveryAttemptCache) {\n globalThis.__smrtRegistryDiscoveryAttemptCache = new Map<string, boolean>();\n }\n return globalThis.__smrtRegistryDiscoveryAttemptCache;\n}\n"],"names":[],"mappings":"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"}