@happyvertical/smrt-core 0.38.2 → 0.38.3

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 (128) hide show
  1. package/AGENTS.md +11 -2
  2. package/dist/change-feed.d.ts +19 -10
  3. package/dist/change-feed.d.ts.map +1 -1
  4. package/dist/change-feed.js +43 -13
  5. package/dist/change-feed.js.map +1 -1
  6. package/dist/change-signals.d.ts +83 -0
  7. package/dist/change-signals.d.ts.map +1 -0
  8. package/dist/change-signals.js +272 -0
  9. package/dist/change-signals.js.map +1 -0
  10. package/dist/collection-cache.d.ts +21 -0
  11. package/dist/collection-cache.d.ts.map +1 -1
  12. package/dist/collection-cache.js +0 -0
  13. package/dist/collection-cache.js.map +1 -1
  14. package/dist/config.d.ts +1 -1
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js.map +1 -1
  17. package/dist/decorators/index.d.ts +8 -0
  18. package/dist/decorators/index.d.ts.map +1 -1
  19. package/dist/decorators/index.js.map +1 -1
  20. package/dist/errors.js +2 -1
  21. package/dist/errors.js.map +1 -1
  22. package/dist/generators/changes-route.d.ts +15 -27
  23. package/dist/generators/changes-route.d.ts.map +1 -1
  24. package/dist/generators/changes-route.js +17 -7
  25. package/dist/generators/changes-route.js.map +1 -1
  26. package/dist/generators/cli.d.ts +3 -0
  27. package/dist/generators/cli.d.ts.map +1 -1
  28. package/dist/generators/cli.js +7 -4
  29. package/dist/generators/cli.js.map +1 -1
  30. package/dist/generators/conditional-get.d.ts +17 -6
  31. package/dist/generators/conditional-get.d.ts.map +1 -1
  32. package/dist/generators/conditional-get.js +19 -13
  33. package/dist/generators/conditional-get.js.map +1 -1
  34. package/dist/generators/events-route.d.ts +72 -0
  35. package/dist/generators/events-route.d.ts.map +1 -0
  36. package/dist/generators/events-route.js +234 -0
  37. package/dist/generators/events-route.js.map +1 -0
  38. package/dist/generators/index.d.ts +1 -0
  39. package/dist/generators/index.d.ts.map +1 -1
  40. package/dist/generators/index.js +2 -1
  41. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  42. package/dist/generators/mcp-runtime-template.js +11 -5
  43. package/dist/generators/mcp-runtime-template.js.map +1 -1
  44. package/dist/generators/mcp.d.ts +3 -0
  45. package/dist/generators/mcp.d.ts.map +1 -1
  46. package/dist/generators/mcp.js +18 -9
  47. package/dist/generators/mcp.js.map +1 -1
  48. package/dist/generators/rest.d.ts +7 -1
  49. package/dist/generators/rest.d.ts.map +1 -1
  50. package/dist/generators/rest.js +77 -13
  51. package/dist/generators/rest.js.map +1 -1
  52. package/dist/generators.js +2 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3 -1
  56. package/dist/manifest/generator.js.map +1 -1
  57. package/dist/manifest/manifest-loader.js.map +1 -1
  58. package/dist/manifest/static-manifest.d.ts.map +1 -1
  59. package/dist/manifest/static-manifest.js +7 -3
  60. package/dist/manifest/static-manifest.js.map +1 -1
  61. package/dist/manifest/store.js +1 -1
  62. package/dist/manifest/store.js.map +1 -1
  63. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  64. package/dist/manifest/test-manifest-stub.js +1050 -124
  65. package/dist/manifest/test-manifest-stub.js.map +1 -1
  66. package/dist/manifest.json +9 -3
  67. package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts +1 -0
  68. package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts.map +1 -1
  69. package/dist/mcp-advisor/types.d.ts +3 -0
  70. package/dist/mcp-advisor/types.d.ts.map +1 -1
  71. package/dist/migrations/tracker.js +5 -4
  72. package/dist/migrations/tracker.js.map +1 -1
  73. package/dist/migrations/types.d.ts +1 -1
  74. package/dist/migrations/types.d.ts.map +1 -1
  75. package/dist/object.d.ts +17 -8
  76. package/dist/object.d.ts.map +1 -1
  77. package/dist/object.js +42 -26
  78. package/dist/object.js.map +1 -1
  79. package/dist/registry/class-registration.d.ts.map +1 -1
  80. package/dist/registry/class-registration.js +12 -0
  81. package/dist/registry/class-registration.js.map +1 -1
  82. package/dist/registry/shared-state.d.ts +2 -1
  83. package/dist/registry/shared-state.d.ts.map +1 -1
  84. package/dist/registry/shared-state.js.map +1 -1
  85. package/dist/registry/types.d.ts.map +1 -1
  86. package/dist/registry.d.ts.map +1 -1
  87. package/dist/registry.js +1 -1
  88. package/dist/registry.js.map +1 -1
  89. package/dist/runtime/client.d.ts.map +1 -1
  90. package/dist/runtime/client.js.map +1 -1
  91. package/dist/scanner/manifest-generator.js.map +1 -1
  92. package/dist/scanner/types.d.ts +7 -0
  93. package/dist/scanner/types.d.ts.map +1 -1
  94. package/dist/schema/ddl/base-strategy.js +1 -1
  95. package/dist/schema/ddl/base-strategy.js.map +1 -1
  96. package/dist/scripts/migrate-datetime-to-timestamp.ts +1 -1
  97. package/dist/signals/sanitizer.d.ts.map +1 -1
  98. package/dist/signals/sanitizer.js.map +1 -1
  99. package/dist/smrt-knowledge.json +8 -8
  100. package/dist/testing/index.d.ts +1 -0
  101. package/dist/testing/index.d.ts.map +1 -1
  102. package/dist/testing/index.js +2 -1
  103. package/dist/testing/notifications-stub.d.ts +24 -0
  104. package/dist/testing/notifications-stub.d.ts.map +1 -0
  105. package/dist/testing/notifications-stub.js +62 -0
  106. package/dist/testing/notifications-stub.js.map +1 -0
  107. package/dist/testing.js +2 -1
  108. package/dist/tools/tool-generator.js +1 -1
  109. package/dist/tools/tool-generator.js.map +1 -1
  110. package/dist/vite-plugin/changes-route.d.ts +14 -0
  111. package/dist/vite-plugin/changes-route.d.ts.map +1 -1
  112. package/dist/vite-plugin/changes-route.js +2 -2
  113. package/dist/vite-plugin/changes-route.js.map +1 -1
  114. package/dist/vite-plugin/events-route.d.ts +9 -0
  115. package/dist/vite-plugin/events-route.d.ts.map +1 -0
  116. package/dist/vite-plugin/events-route.js +155 -0
  117. package/dist/vite-plugin/events-route.js.map +1 -0
  118. package/dist/vite-plugin/index.d.ts +8 -0
  119. package/dist/vite-plugin/index.d.ts.map +1 -1
  120. package/dist/vite-plugin/index.js +8 -2
  121. package/dist/vite-plugin/index.js.map +1 -1
  122. package/dist/vite-plugin/sveltekit-generator.d.ts +8 -0
  123. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  124. package/dist/vite-plugin/sveltekit-generator.js +193 -31
  125. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  126. package/dist/vite-plugin/web-collections.js +1 -1
  127. package/dist/vite-plugin/web-collections.js.map +1 -1
  128. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"class-registration.d.ts","sourceRoot":"","sources":["../../src/registry/class-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAKV,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAqC7B,OAAO,KAAK,EACV,eAAe,EAEf,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAqapB,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,UAAU,EACvB,MAAM,GAAE,iBAAsB,GAC7B,IAAI,CAgnBN;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAMlB,qBAAqB,EAAE,WAAW,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,GAC3E,IAAI,CAeN;AA0LD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EACzB,gBAAgB,CAAC,EAAE;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACA,IAAI,CA2CN;AA0ID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,qBAAqB,EAChC,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAmQN"}
1
+ {"version":3,"file":"class-registration.d.ts","sourceRoot":"","sources":["../../src/registry/class-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAKV,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAqC7B,OAAO,KAAK,EACV,eAAe,EAEf,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAqapB,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,UAAU,EACvB,MAAM,GAAE,iBAAsB,GAC7B,IAAI,CAipBN;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAMlB,qBAAqB,EAAE,WAAW,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,GAC3E,IAAI,CAeN;AA6LD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EACzB,gBAAgB,CAAC,EAAE;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACA,IAAI,CA2CN;AA0ID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,qBAAqB,EAChC,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAmQN"}
@@ -334,6 +334,9 @@ function register(ctor, config = {}) {
334
334
  if (Object.keys(options).length > 0) field._meta = options;
335
335
  if (fieldDef.transient !== void 0) field.transient = fieldDef.transient;
336
336
  if (fieldDef.required !== void 0) field.required = fieldDef.required;
337
+ if (fieldDef.sensitive !== void 0) field.sensitive = fieldDef.sensitive;
338
+ if (fieldDef.readonly !== void 0) field.readonly = fieldDef.readonly;
339
+ if (fieldDef.readPermission !== void 0) field.readPermission = fieldDef.readPermission;
337
340
  if (fieldDef.related !== void 0) field.related = fieldDef.related;
338
341
  else if (options.related !== void 0) {
339
342
  field.related = options.related;
@@ -365,6 +368,12 @@ function register(ctor, config = {}) {
365
368
  if (mergedMeta.type) delete mergedMeta.type;
366
369
  if (opts.transient !== void 0) mergedField.transient = opts.transient;
367
370
  else if (existingField.transient !== void 0) mergedField.transient = existingField.transient;
371
+ if (opts.sensitive !== void 0) mergedField.sensitive = opts.sensitive;
372
+ else if (existingField.sensitive !== void 0) mergedField.sensitive = existingField.sensitive;
373
+ if (opts.readonly !== void 0) mergedField.readonly = opts.readonly;
374
+ else if (existingField.readonly !== void 0) mergedField.readonly = existingField.readonly;
375
+ if (typeof opts.readPermission === "string") mergedField.readPermission = opts.readPermission;
376
+ else if (typeof existingField.readPermission === "string") mergedField.readPermission = existingField.readPermission;
368
377
  if (opts.nullable === true) {
369
378
  mergedField.required = false;
370
379
  mergedMeta.required = false;
@@ -383,6 +392,9 @@ function register(ctor, config = {}) {
383
392
  _meta: newMeta
384
393
  };
385
394
  if (opts.transient !== void 0) newField.transient = opts.transient;
395
+ if (opts.sensitive !== void 0) newField.sensitive = opts.sensitive;
396
+ if (opts.readonly !== void 0) newField.readonly = opts.readonly;
397
+ if (typeof opts.readPermission === "string") newField.readPermission = opts.readPermission;
386
398
  if (opts.nullable === true) {
387
399
  newField.required = false;
388
400
  newMeta.required = false;
@@ -1 +1 @@
1
- {"version":3,"file":"class-registration.js","names":[],"sources":["../../src/registry/class-registration.ts"],"sourcesContent":["/**\n * Class registration module for the SMRT ObjectRegistry.\n *\n * Handles registering classes, collections, and manifest entries.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n */\n\nimport { ConfigurationError } from '../errors';\nimport {\n discoverManifestSync,\n discoverSTISiblingsSync,\n getPackageName,\n lookupInManifest,\n} from '../manifest/manifest-loader.js';\nimport { cloneManifestSchemaColumns } from '../manifest/store.js';\nimport { SmrtObject } from '../object';\nimport type {\n FieldDefinition,\n FieldMeta,\n MethodDefinition,\n QualifiedClassName,\n SmartObjectDefinition,\n SmrtVisibility,\n} from '../scanner/types.js';\nimport type {\n ColumnDefinition,\n IndexDefinition,\n SchemaDefinition,\n} from '../schema/types.js';\nimport { tableNameFromClass, toSnakeCase } from '../utils';\nimport {\n createQualifiedName,\n isQualifiedName,\n} from '../utils/qualified-names.js';\nimport {\n type CollisionInputs,\n decideCollisionPolicy,\n type MatchKind,\n} from './collision-policy.js';\nimport { buildInheritanceChain } from './inheritance-resolver';\nimport {\n createFieldFromManifest,\n mergeManifestField,\n} from './manifest-field-merge.js';\nimport {\n findClass,\n getCanonicalClassName,\n hasClassCaseInsensitive,\n qualifyExtendsName,\n} from './name-resolver';\nimport {\n getClasses,\n getCollections,\n getConstructorIndex,\n getFieldDecorators,\n getInheritanceCache,\n getSourceFileFromStack,\n getStiSiblingsLoaded,\n verboseLog,\n} from './shared-state';\nimport type {\n RegisteredClass,\n RegisteredField,\n SmartObjectConfig,\n ValidatorFunction,\n} from './types.js';\nimport { compileValidators } from './validator';\n\n/**\n * Compute the pluralized endpoint/collection name from a class name, using\n * the SAME simple inflection rules as the scanner's manifest adapter\n * (`packages/scanner/src/manifest-adapter.ts`). Used only as a fallback when\n * a registered class has no manifest-provided `collection` (e.g. inline test\n * classes registered purely via the decorator path). Keep in sync with the\n * scanner; the manifest value is always preferred when present. (smrt#1311.)\n *\n * Currency -> currencies (y -> ies)\n * CompanyResearch -> companyresearches (ch -> ches)\n * SourceCrawl -> sourcecrawls (+ s)\n * EmploymentPerson -> employmentpersons (naive + s, NOT \"people\")\n */\nfunction pluralizeCollection(className: string): string {\n const lower = className.toLowerCase();\n if (lower.endsWith('y')) return `${lower.slice(0, -1)}ies`;\n if (lower.endsWith('s') || lower.endsWith('x') || lower.endsWith('z')) {\n return `${lower}es`;\n }\n if (lower.endsWith('ch') || lower.endsWith('sh')) return `${lower}es`;\n return `${lower}s`;\n}\n\n/**\n * Shared bundled-context detector. Source files in these output\n * directories come from a bundler (Vite library mode, webpack, Next.js,\n * Nuxt, svelte-kit) that can duplicate module code across chunks.\n */\nfunction isBundledOutputPath(sourceFile: string | undefined): boolean {\n if (!sourceFile) return false;\n return (\n sourceFile.includes('.svelte-kit/output/') ||\n sourceFile.includes('/dist/') ||\n sourceFile.includes('/build/') ||\n sourceFile.includes('.next/') ||\n sourceFile.includes('.nuxt/')\n );\n}\n\n/**\n * Build `CollisionInputs` for the decorator-origin path (`register()`).\n * Manifest-origin inputs are built separately inside `registerFromManifest`.\n */\nfunction buildDecoratorCollisionInputs(args: {\n ctor: typeof SmrtObject;\n name: string;\n newPackageName: string | undefined;\n newSourceFile: string | undefined;\n newInBundledContext: boolean;\n existing: RegisteredClass;\n existingKey: string;\n matchKind: MatchKind;\n}): CollisionInputs {\n const {\n ctor,\n name,\n newPackageName,\n newSourceFile,\n newInBundledContext,\n existing,\n existingKey,\n matchKind,\n } = args;\n\n let newExtendsExisting = false;\n let existingExtendsNew = false;\n try {\n newExtendsExisting = ctor.prototype instanceof existing.constructor;\n existingExtendsNew = existing.constructor.prototype instanceof ctor;\n } catch {\n // `instanceof` can throw for non-function constructors. Keep both false.\n }\n\n const bothSourceFilesKnown = !!(newSourceFile && existing.sourceFilePath);\n const sameSourceFile =\n bothSourceFilesKnown && newSourceFile === existing.sourceFilePath;\n const existingIsPackageQualified = !!existing.packageName?.startsWith('@');\n const samePackage =\n !!newPackageName &&\n !!existing.packageName &&\n newPackageName === existing.packageName;\n const hasNewQualifiedKey = !!newPackageName;\n const existingKeyIsQualified = isQualifiedName(existingKey);\n\n return {\n origin: 'decorator',\n matchKind,\n sameConstructor: existing.constructor === ctor,\n sameSourceFile,\n bothSourceFilesKnown,\n existingIsManifestStub:\n (existing.constructor as { _isManifestStub?: boolean })\n ._isManifestStub === true,\n newInBundledContext,\n newExtendsExisting,\n existingExtendsNew,\n samePackage,\n existingIsPackageQualified,\n sameName: name === existing.name,\n hasNewQualifiedKey,\n existingKeyIsQualified,\n existingHasAnyPackage: !!existing.packageName,\n // Manifest-only fields; filled with safe defaults for decorator origin.\n hasManifestContent: false,\n registrationKeyDiffersFromExistingKey: false,\n existingHasNoPackage: !existing.packageName,\n };\n}\n\n/**\n * Execute the collision policy for `register()`. Returns `true` if the\n * policy resolved the collision (caller should return from `register`);\n * `false` means no policy match (the decision table is exhaustive so\n * that never actually happens — the default row throws).\n *\n * All state mutation (`upsertExistingEntry`, verboseLog) stays in this\n * caller-side helper; the policy function itself is pure.\n */\nfunction applyRegisterCollisionPolicy(args: {\n ctor: typeof SmrtObject;\n name: string;\n newPackageName: string | undefined;\n newSourceFile: string | undefined;\n newInBundledContext: boolean;\n existing: RegisteredClass;\n existingKey: string;\n matchKind: MatchKind;\n upsertExistingEntry: (\n existingKey: string,\n existing: RegisteredClass,\n ) => string;\n}): boolean {\n const inputs = buildDecoratorCollisionInputs(args);\n const decision = decideCollisionPolicy(inputs);\n\n switch (decision.policy) {\n case 'accept':\n case 'replace': {\n // Both map to the same operation for the decorator path:\n // update the existing slot's constructor + metadata. `replace`\n // only differs in verboseLog flavor so a reviewer reading the log\n // can tell which scenario fired.\n const newKey = args.upsertExistingEntry(args.existingKey, args.existing);\n if (\n decision.scenario === 'sti-child-wins' ||\n decision.scenario === 'manifest-stub-replacement'\n ) {\n verboseLog(\n `[registry] ${decision.scenario}: '${args.name}' → key='${newKey}' (${decision.reason})`,\n );\n }\n return true;\n }\n case 'skip':\n verboseLog(\n `[registry] ${decision.scenario}: skipping '${args.name}' (${decision.reason})`,\n );\n return true;\n case 'throw': {\n const matchSuffix =\n args.matchKind === 'case-insensitive'\n ? ` (case-insensitive match with \"${args.existingKey}\")`\n : '';\n throw new Error(\n `SMRT Class Name Collision: \"${args.name}\"${matchSuffix}\\n\\n` +\n `A class with this name is already registered, but with a different constructor.\\n` +\n `This usually happens when:\\n` +\n ` 1. Multiple test files define classes with the same name\\n` +\n ` 2. Different packages export classes with the same name\\n\\n` +\n `The collision will cause the wrong field definitions to be used,\\n` +\n `leading to properties not being initialized correctly.\\n\\n` +\n `To fix:\\n` +\n ` - Use unique class names (e.g., ${args.name}_UniqueId)\\n` +\n ` - Or use @smrt({ name: 'unique_name' }) to override the registration name`,\n );\n }\n case 'merge-manifest':\n case 'coexist-qualified':\n // These policies are only reachable from the manifest path; if we\n // somehow get here from `register()`, something is miswired.\n throw new Error(\n `decideCollisionPolicy returned '${decision.policy}' for decorator origin (scenario: ${decision.scenario}). This is a bug.`,\n );\n }\n}\n\n/**\n * Build `CollisionInputs` for the manifest-origin path\n * (`registerFromManifest`). Manifest entries have string-based inheritance\n * (`objectDef.extends`) rather than prototype-based, and no real\n * constructor to compare — `sameConstructor` is always false here.\n */\nfunction buildManifestCollisionInputs(args: {\n name: string;\n objectDef: SmartObjectDefinition;\n packageName: string | undefined;\n existing: RegisteredClass;\n existingKey: string;\n registrationKey: string;\n matchKind: MatchKind;\n}): CollisionInputs {\n const {\n name,\n objectDef,\n packageName,\n existing,\n existingKey,\n registrationKey,\n matchKind,\n } = args;\n const newClassName = objectDef.className || name;\n const newExtends = objectDef.extends;\n\n // When an incoming manifest's `extends` points at the existing entry,\n // OR the existing entry's `extends` points at the incoming class, we\n // have an STI inheritance relationship. Each side checks:\n // - the simple name of the other\n // - the key under which the other is registered\n // - a case-insensitive fallback\n //\n // The `existingKey` comparison is load-bearing for cross-package STI:\n // `existing.extends` is stored post-`qualifyExtendsName()`, so it may\n // be the fully-qualified parent key like `@pkg:Parent`. Without this\n // comparison, `existingExtendsNew` falsely reads as false and the\n // arriving parent manifest would be registered as a duplicate instead\n // of skipped. See the deep-review report on #1140 for the fix rationale.\n const newExtendsExisting = !!(\n newExtends &&\n (newExtends === existing.name ||\n newExtends === existingKey ||\n newExtends.toLowerCase() === existing.name.toLowerCase())\n );\n const existingExtendsNew = !!(\n existing.extends &&\n (existing.extends === newClassName ||\n existing.extends === name ||\n existing.extends === registrationKey ||\n existing.extends.toLowerCase() === newClassName.toLowerCase())\n );\n\n const newSourceFile = objectDef.filePath;\n const bothSourceFilesKnown = !!(newSourceFile && existing.sourceFilePath);\n const sameSourceFile =\n bothSourceFilesKnown && newSourceFile === existing.sourceFilePath;\n const existingIsPackageQualified = !!existing.packageName?.startsWith('@');\n const samePackage =\n !!packageName &&\n !!existing.packageName &&\n packageName === existing.packageName;\n\n return {\n origin: 'manifest',\n matchKind,\n sameConstructor: false,\n sameSourceFile,\n bothSourceFilesKnown,\n existingIsManifestStub:\n (existing.constructor as { _isManifestStub?: boolean })\n ._isManifestStub === true,\n newInBundledContext: false,\n newExtendsExisting,\n existingExtendsNew,\n samePackage,\n existingIsPackageQualified,\n sameName: newClassName === existing.name,\n hasNewQualifiedKey: isQualifiedName(registrationKey),\n existingKeyIsQualified: isQualifiedName(existingKey),\n existingHasAnyPackage: !!existing.packageName,\n hasManifestContent: !!(\n objectDef.fields ||\n objectDef.methods ||\n objectDef.schema ||\n objectDef.decoratorConfig\n ),\n registrationKeyDiffersFromExistingKey: registrationKey !== existingKey,\n existingHasNoPackage: !existing.packageName,\n };\n // Note: manifest-origin sets hasNewQualifiedKey from the final\n // registrationKey (not packageName) because registerFromManifest accepts\n // an objectDef.qualifiedName fallback when packageName is omitted —\n // deriving from packageName alone would miss that case and misroute the\n // coexistence rows. See PR #1140 review (Copilot P1 at line 287).\n}\n\n/**\n * Execute the collision policy for `registerFromManifest()`. Returns\n * `'return'` when the policy fully resolves the collision (caller should\n * early-return from `registerFromManifest`) or `'continue'` when the new\n * registration should proceed to the fresh-entry code below the collision\n * block (either a child-wins delete-then-register or a different-package\n * coexist-qualified scenario).\n *\n * Implements the `merge-manifest` contract end-to-end: fold fields via\n * `mergeManifestIntoExistingRegistration`, then conditionally alias the\n * existing entry under `registrationKey` when it differs from the\n * canonical key — both steps preserve `issue-951:144-200`.\n */\nfunction applyManifestCollisionPolicy(args: {\n name: string;\n objectDef: SmartObjectDefinition;\n packageName: string | undefined;\n existing: RegisteredClass;\n existingKey: string;\n registrationKey: string;\n matchKind: MatchKind;\n}): 'return' | 'continue' {\n const inputs = buildManifestCollisionInputs(args);\n const decision = decideCollisionPolicy(inputs);\n\n switch (decision.policy) {\n case 'skip':\n verboseLog(\n `[registry] ${decision.scenario}: skipping manifest '${args.name}' (${decision.reason})`,\n );\n return 'return';\n case 'replace':\n // STI child-wins: delete existing so the new entry can register fresh.\n getClasses().delete(args.existingKey);\n verboseLog(\n `[registry] ${decision.scenario}: '${args.name}' replaces '${args.existingKey}' (${decision.reason})`,\n );\n return 'continue';\n case 'merge-manifest': {\n mergeManifestIntoExistingRegistration(\n args.existing,\n args.objectDef,\n args.packageName,\n );\n if (args.registrationKey !== args.existingKey) {\n const classes = getClasses();\n classes.set(args.registrationKey, args.existing);\n getConstructorIndex().set(\n args.existing.constructor,\n args.registrationKey,\n );\n }\n return 'return';\n }\n case 'coexist-qualified':\n // Both registrations live under their qualified keys; caller falls\n // through to register the new one fresh.\n return 'continue';\n case 'accept':\n // Manifest origin never returns 'accept' (that's a decorator-path\n // policy). Treat defensively as return-no-op.\n return 'return';\n case 'throw':\n // Manifest collisions default to skip, never throw — this branch is\n // unreachable from the manifest origin. Guard against future drift.\n throw new Error(\n `decideCollisionPolicy returned 'throw' for manifest origin (scenario: ${decision.scenario}). This is a bug.`,\n );\n }\n}\n\nfunction resolveTableName(\n ctor: typeof SmrtObject,\n name: string,\n config: SmartObjectConfig,\n): string {\n const manifestEntry = config._manifest\n ? lookupInManifest(config._manifest, name)\n : discoverManifestSync(name);\n\n return (\n manifestEntry?.schema?.tableName ||\n manifestEntry?.decoratorConfig?.tableName ||\n config.tableName ||\n tableNameFromClass(ctor)\n );\n}\n\nfunction setSmrtTableName(ctor: typeof SmrtObject, tableName: string): void {\n const existing = Object.getOwnPropertyDescriptor(ctor, 'SMRT_TABLE_NAME');\n if (existing?.value === tableName) {\n return;\n }\n\n Object.defineProperty(ctor, 'SMRT_TABLE_NAME', {\n value: tableName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n}\n\n/**\n * Attach the registry's qualified name to the constructor as a static\n * property. Mirrors the `SMRT_TABLE_NAME` pattern.\n *\n * Survives:\n * - Minification (the property is set on the constructor itself).\n * - HMR / module duplication / federated-module boundaries where the\n * `constructorIndex` WeakMap holds an older constructor identity than\n * the one a caller has in hand.\n *\n * Acts as a belt-and-suspenders fallback for runtime code that has a\n * constructor reference and wants its registry identity without going\n * through a Map lookup (e.g. `SmrtHierarchical._hierarchyCollection`).\n */\nfunction setSmrtQualifiedName(\n ctor: typeof SmrtObject,\n qualifiedName: string | undefined,\n): void {\n if (!qualifiedName) return;\n const existing = Object.getOwnPropertyDescriptor(ctor, '_smrtQualifiedName');\n if (existing?.value === qualifiedName) {\n return;\n }\n\n Object.defineProperty(ctor, '_smrtQualifiedName', {\n value: qualifiedName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n}\n\nexport function register(\n ctor: typeof SmrtObject,\n config: SmartObjectConfig = {},\n): void {\n const name = config.name || ctor.name;\n const explicitPackageName = config.packageName;\n\n function upsertExistingEntry(\n existingKey: string,\n existing: RegisteredClass,\n ): string {\n const nextPackageName = explicitPackageName || existing.packageName;\n const nextKey = nextPackageName\n ? createQualifiedName(nextPackageName, name)\n : name;\n // Capture pre-mutation identity so descendants whose `extends` still\n // references the old name/qualifiedName are caught by the post-mutation\n // invalidation sweep (#1139 Gap 2).\n const previousIdentity = {\n name: existing.name,\n qualifiedName: existing.qualifiedName as string | undefined,\n };\n\n existing.name = name;\n existing.packageName = nextPackageName;\n existing.qualifiedName = nextPackageName\n ? (createQualifiedName(nextPackageName, name) as QualifiedClassName)\n : undefined;\n const nextTableName = resolveTableName(ctor, name, {\n ...existing.config,\n ...config,\n });\n existing.config = {\n ...existing.config,\n ...config,\n tableName: nextTableName,\n };\n if (!existing.schema) {\n existing.schema = {\n ddl: '',\n indexes: [],\n triggers: [],\n tableName: nextTableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n existing.schema.tableName = nextTableName;\n existing.constructor = ctor;\n setSmrtTableName(ctor, nextTableName);\n setSmrtQualifiedName(ctor, existing.qualifiedName);\n\n if (existingKey !== nextKey) {\n const classes = getClasses();\n const existingAtNextKey = classes.get(nextKey);\n\n if (existingAtNextKey && existingAtNextKey !== existing) {\n throw new ConfigurationError(\n `Class registry collision while promoting \"${existingKey}\" to \"${nextKey}\". ` +\n 'A different class is already registered under the target key.',\n 'CONFIG_REGISTRY_PROMOTION_COLLISION',\n {\n existingKey,\n nextKey,\n className: name,\n },\n );\n }\n\n classes.delete(existingKey);\n classes.set(nextKey, existing);\n }\n\n getConstructorIndex().set(ctor, nextKey);\n\n // Release C follow-up #1139 Gap 2: we just mutated `existing.name`,\n // `existing.packageName`, `existing.qualifiedName`, `existing.config`,\n // `existing.constructor`, and `existing.schema.tableName`. Any\n // descendant whose cached `inheritedFields` was computed against the\n // prior identity now carries stale data. Invalidate `existing` and\n // every descendant whose `extends` matches the pre- or post-mutation\n // identity so the next field read re-merges from the current state.\n invalidateInheritanceEntries(existing, previousIdentity);\n\n return nextKey;\n }\n\n // Release C (#1134): collision resolution for both exact-match and\n // case-insensitive paths now routes through decideCollisionPolicy, which\n // consolidates the 11-branch if/else tree from pre-C.\n const newSourceFile = getSourceFileFromStack();\n const newPackageName =\n explicitPackageName || getPackageName(ctor, true) || undefined;\n const newInBundledContext = isBundledOutputPath(newSourceFile);\n\n // 1. Exact-match check (existingKey === name)\n if (getClasses().has(name)) {\n const existing = getClasses().get(name);\n if (!existing) {\n throw new Error(\n `Registry inconsistency: ${name} exists in classes Map but get() returned undefined`,\n );\n }\n\n const handled = applyRegisterCollisionPolicy({\n ctor,\n name,\n newPackageName,\n newSourceFile,\n newInBundledContext,\n existing,\n existingKey: name,\n matchKind: 'exact-key',\n upsertExistingEntry,\n });\n if (handled) return;\n }\n\n // 2. Case-insensitive check for manifest stubs (Issue #531, #847) and\n // other same-name-different-case collisions.\n const lowerName = name.toLowerCase();\n for (const [existingKey, existing] of getClasses().entries()) {\n const keyMatches = existingKey.toLowerCase() === lowerName;\n const nameMatches = existing.name?.toLowerCase() === lowerName;\n if (!(keyMatches || nameMatches) || existingKey === name) continue;\n\n const handled = applyRegisterCollisionPolicy({\n ctor,\n name,\n newPackageName,\n newSourceFile,\n newInBundledContext,\n existing,\n existingKey,\n matchKind: 'case-insensitive',\n upsertExistingEntry,\n });\n if (handled) return;\n }\n\n // CRITICAL: Capture package name NOW, while stack trace still shows external package\n // This is called from the @smrt() decorator during import, so the stack trace\n // includes the external package file path. Later calls won't have this context.\n // Skip registry check to avoid circular dependency - class isn't registered yet!\n // This solves issue #159 where external package manifests couldn't be loaded.\n const packageNameFromStack =\n explicitPackageName || getPackageName(ctor, true) || undefined;\n\n // Capture source file path for collision detection during module re-evaluation\n // (Issue #555: Test isolation - class name collision during vitest collection)\n const sourceFilePath = getSourceFileFromStack();\n\n // Get field definitions from manifest\n // Priority order (Issue #270 Phase 1 - synchronous manifest loading):\n // 1. Explicitly provided manifest (_manifest parameter)\n // 2. Test manifests (for test classes)\n // 3. Static manifests (for core framework classes)\n // 4. Cached external manifests (if already loaded)\n // For external packages not yet loaded, manifest discovery happens lazily during schema generation\n // Issue #713: Use lookupInManifest for qualified name support\n let manifestEntry: ReturnType<typeof lookupInManifest> | undefined;\n if (config._manifest) {\n manifestEntry = lookupInManifest(config._manifest, name);\n }\n if (!manifestEntry) {\n manifestEntry = discoverManifestSync(name);\n }\n const fields = new Map<string, RegisteredField>();\n const methods = new Map<string, MethodDefinition>();\n let packageName: string | undefined;\n\n verboseLog(\n `[registry] Registering ${name}: manifestEntry =`,\n manifestEntry ? 'found' : 'not found',\n );\n if (manifestEntry?.fields) {\n verboseLog(\n `[registry] Manifest has ${Object.keys(manifestEntry.fields).length} fields:`,\n Object.keys(manifestEntry.fields),\n );\n }\n\n if (manifestEntry?.fields) {\n // Use manifest fields (from build-time AST scanning)\n // Store field definitions as plain objects with nested options\n for (const [fieldName, fieldDef] of Object.entries(\n manifestEntry.fields,\n ) as [string, FieldDefinition][]) {\n // Build options object, only including defined values\n const options: Record<string, unknown> = { ...fieldDef._meta };\n if (fieldDef.required !== undefined) options.required = fieldDef.required;\n if (fieldDef.default !== undefined) options.default = fieldDef.default;\n if (fieldDef.description !== undefined)\n options.description = fieldDef.description;\n if (fieldDef.transient !== undefined)\n options.transient = fieldDef.transient;\n\n // Store field definition as plain object maintaining Field-like structure\n const field: RegisteredField = {\n type: fieldDef.type,\n };\n\n // Only include options if not empty\n if (Object.keys(options).length > 0) {\n field._meta = options;\n }\n\n // Preserve top-level flags from manifest\n if (fieldDef.transient !== undefined) {\n field.transient = fieldDef.transient;\n }\n if (fieldDef.required !== undefined) {\n field.required = fieldDef.required;\n }\n\n // Hoist related to top level for relationship fields\n // Check both fieldDef.related (new manifests) and _meta.related (old manifests)\n if (fieldDef.related !== undefined) {\n field.related = fieldDef.related;\n } else if (options.related !== undefined) {\n // `related` carries the related class name (always a string at runtime).\n field.related = options.related as string;\n delete field._meta?.related;\n }\n\n fields.set(fieldName, field);\n }\n\n verboseLog(\n `[registry] ✅ Loaded ${fields.size} fields for ${name} from manifest`,\n );\n\n // Use packageName from manifest if available, otherwise from stack trace\n // Priority: explicit manifest > manifestEntry > stack trace\n packageName =\n explicitPackageName ||\n config._manifest?.packageName ||\n manifestEntry.packageName ||\n packageNameFromStack;\n } else {\n // No manifest found yet - use package name from stack trace\n // This will be used later by ensureManifestLoaded() to load the external manifest\n verboseLog(\n `[registry] ⚠️ No manifest entry for ${name} - fields will be loaded later`,\n );\n packageName = packageNameFromStack;\n }\n\n // Apply decorator metadata to override/extend manifest fields\n // Decorators take priority over AST-scanned types (Issue #316)\n const decorators = getFieldDecorators().get(name);\n if (decorators && decorators.size > 0) {\n verboseLog(\n `[registry] Applying ${decorators.size} field decorators for ${name}`,\n );\n\n for (const [fieldName, decoratorOptions] of decorators) {\n // Typed read-view of the raw `Record<string, unknown>` decorator bag so\n // the member reads below narrow to the `RegisteredField` member types\n // they feed (instead of `unknown`). Same runtime object, type-only view.\n const opts: DecoratorFieldOptions = decoratorOptions;\n const existingField = fields.get(fieldName);\n\n if (existingField) {\n // Merge decorator options with manifest field\n // Decorator type takes priority over AST-scanned type\n const mergedMeta: FieldMeta = {\n ...existingField._meta,\n ...decoratorOptions,\n };\n const mergedField: RegisteredField = {\n type: opts.type || existingField.type,\n _meta: mergedMeta,\n };\n\n // Remove 'type' from _meta if it was moved to top level\n if (mergedMeta.type) {\n delete mergedMeta.type;\n }\n\n // Preserve top-level flags (transient, required, etc.)\n if (opts.transient !== undefined) {\n mergedField.transient = opts.transient;\n } else if (existingField.transient !== undefined) {\n mergedField.transient = existingField.transient;\n }\n\n // Handle required flag: nullable fields should not be required\n if (opts.nullable === true) {\n // Nullable explicitly set to true means field is NOT required\n mergedField.required = false;\n mergedMeta.required = false;\n } else if (opts.required !== undefined) {\n mergedField.required = opts.required;\n } else if (existingField.required !== undefined) {\n mergedField.required = existingField.required;\n }\n\n // Hoist related to top level for relationship fields\n if (opts.related !== undefined) {\n mergedField.related = opts.related;\n delete mergedField._meta?.related;\n } else if (existingField.related !== undefined) {\n mergedField.related = existingField.related;\n }\n\n fields.set(fieldName, mergedField);\n verboseLog(\n `[registry] ✅ Merged decorator for ${fieldName}: type=${mergedField.type}`,\n );\n } else {\n // Decorator for field not in manifest - add it\n const newMeta: FieldMeta = decoratorOptions;\n const newField: RegisteredField = {\n type: opts.type || 'text',\n _meta: newMeta,\n };\n\n // Set top-level flags from decorator options\n if (opts.transient !== undefined) {\n newField.transient = opts.transient;\n }\n // Handle required flag: nullable fields should not be required\n if (opts.nullable === true) {\n newField.required = false;\n newMeta.required = false;\n } else if (opts.required !== undefined) {\n newField.required = opts.required;\n }\n\n // Hoist related to top level for relationship fields (Issue #746)\n // This is critical for getRelationshipMap() to detect relationships\n if (opts.related !== undefined) {\n newField.related = opts.related;\n delete newField._meta?.related;\n }\n\n fields.set(fieldName, newField);\n verboseLog(\n `[registry] ✅ Added field ${fieldName} from decorator: type=${opts.type || 'text'}`,\n );\n }\n }\n }\n\n // Handle tenantScoped configuration (Issue #688)\n // External manifests can carry tenantScoped only in decoratorConfig, so\n // registration must honor the merged view rather than only explicit config.\n let tenantScopedConfig: RegisteredClass['tenantScopedConfig'] | undefined;\n const effectiveTenantScoped =\n config.tenantScoped ?? manifestEntry?.decoratorConfig?.tenantScoped;\n if (effectiveTenantScoped) {\n // Normalize boolean or object config into full options\n const tenantOpts =\n typeof effectiveTenantScoped === 'boolean' ? {} : effectiveTenantScoped;\n tenantScopedConfig = {\n mode: tenantOpts.mode ?? 'required',\n field: tenantOpts.field ?? 'tenantId',\n autoFilter: tenantOpts.autoFilter ?? true,\n autoPopulate: tenantOpts.autoPopulate ?? true,\n allowSuperAdminBypass: tenantOpts.allowSuperAdminBypass ?? false,\n };\n\n // Inject or enrich tenantId field\n const fieldName = tenantScopedConfig.field;\n const hadTenantField = fields.has(fieldName);\n ensureTenantScopedField(fields, tenantScopedConfig);\n if (!hadTenantField) {\n verboseLog(\n `[registry] ✅ Injected ${fieldName} field for tenant-scoped class ${name}`,\n );\n }\n }\n\n if (manifestEntry?.methods) {\n // Load method definitions from manifest (for custom CLI/API/MCP generation)\n for (const [methodName, methodDef] of Object.entries(\n manifestEntry.methods,\n )) {\n methods.set(methodName, methodDef);\n }\n }\n\n // Also load methods from _manifestMethods in config (from consumer plugin register.js)\n // This is how external package methods are passed to the registry.\n // `_manifestMethods` is an undeclared escape-hatch carried on the config bag\n // by generated consumer `register.js`, so read it through a narrowed view.\n const manifestMethods = (\n config as unknown as {\n _manifestMethods?: Record<string, MethodDefinition>;\n }\n )._manifestMethods;\n if (manifestMethods) {\n for (const [methodName, methodDef] of Object.entries(manifestMethods)) {\n methods.set(methodName, methodDef);\n }\n verboseLog(\n `[registry] Loaded ${methods.size} methods for ${name} from _manifestMethods`,\n );\n }\n\n // Note: If manifest not found here, it will be loaded asynchronously when needed\n // via ensureManifestLoaded(). This allows decorators to remain synchronous while\n // supporting dynamic external package manifest loading.\n\n // Build inheritance chain from constructor (needed for STI table name resolution)\n const inheritanceChain = buildInheritanceChain(ctor);\n\n // Validate table strategy compatibility with parent (STI requirement)\n if (inheritanceChain.length > 1) {\n // This class has a parent - validate strategy compatibility\n const parentName = inheritanceChain[inheritanceChain.length - 2]; // Second-to-last is parent\n const parentEntry = findClass(parentName);\n\n if (parentEntry) {\n const parentStrategy = parentEntry.config?.tableStrategy || 'default';\n const childStrategy = config.tableStrategy; // Don't default - undefined means inherit\n\n // Only validate if child has an EXPLICIT strategy that differs from parent\n // undefined childStrategy means it will inherit from parent\n if (childStrategy !== undefined && parentStrategy !== childStrategy) {\n throw ConfigurationError.incompatibleStrategy(\n name,\n childStrategy,\n parentName,\n parentStrategy,\n );\n }\n }\n }\n\n // Defer schema generation until needed (generateSchema now uses dynamic import)\n // Store table name for lazy schema generation\n // Priority for STI: manifest's tableName > decorator config > derived from class name\n // The manifest's tableName is computed at build-time when full class hierarchy is known,\n // which correctly handles STI inheritance. The decorator may derive wrong tableName\n // if parent class isn't registered yet at decorator execution time.\n const tableName = resolveTableName(ctor, name, config);\n setSmrtTableName(ctor, tableName);\n\n // Load pre-generated schema from manifest if available, otherwise placeholder\n let schema: SchemaDefinition;\n if (manifestEntry?.schema) {\n // Pre-generated schema from manifest (build-time)\n // Keep indexes as IndexDefinition objects for DDL strategies\n schema = {\n ddl: manifestEntry.schema.ddl,\n indexes:\n manifestEntry.schema.indexes?.map((idx: IndexDefinition) => ({\n name: idx.name,\n columns: idx.columns || [],\n unique: idx.unique || false,\n where: idx.where,\n description: idx.description,\n jsonPath: idx.jsonPath,\n })) || [],\n triggers: [],\n tableName: manifestEntry.schema.tableName || tableName,\n // Cast manifest columns to ColumnDefinition (same shape, TypeScript just needs help)\n columns: cloneManifestSchemaColumns(manifestEntry.schema.columns),\n foreignKeys: [],\n dependencies: [],\n version: manifestEntry.schema.version || '',\n packageName: manifestEntry.packageName,\n };\n verboseLog(\n `[registry] Loaded pre-generated schema for ${name} (${Object.keys(manifestEntry.schema.columns || {}).length} columns)`,\n );\n } else {\n // Placeholder schema - will be generated lazily when first needed\n schema = {\n ddl: '', // Generated lazily\n indexes: [], // Parsed from DDL lazily\n triggers: [], // No longer using database triggers - timestamps managed by application\n tableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n packageName: undefined,\n };\n }\n\n if (schema.columns) {\n applySqlTypeOverrides(schema.columns, fields.entries());\n if (decorators && decorators.size > 0) {\n applySqlTypeOverrides(schema.columns, decorators.entries());\n }\n }\n\n // Use pre-computed validation rules from manifest if available (Issue #782)\n // For decorator-based registration, manifest may have pre-computed rules\n const validationRules = manifestEntry?.validationRules;\n let validators: ValidatorFunction[] | undefined;\n\n // Only compile validators if no pre-computed rules exist\n if (!validationRules || validationRules.length === 0) {\n validators = compileValidators(name, fields);\n } else {\n verboseLog(\n `[registry] Using ${validationRules.length} pre-computed validation rules for ${name}`,\n );\n }\n\n // Derive extends from prototype chain if not available from manifest\n // This is critical for inline test classes that use decorators\n let extendsClass: string | undefined = manifestEntry?.extends;\n if (!extendsClass) {\n const proto = Object.getPrototypeOf(ctor);\n if (proto?.name && proto.name !== 'SmrtObject' && proto.name !== 'Object') {\n extendsClass = proto.name;\n }\n }\n\n // Merge manifest's decoratorConfig into config\n // Use the computed tableName which prioritizes manifest's value for STI correctness\n const mergedConfig = {\n ...manifestEntry?.decoratorConfig,\n ...config,\n tableName, // Override with correctly computed tableName\n };\n\n // Generate qualified name if we have a package name\n // Format: \"@package/name:ClassName\"\n const qualifiedName = packageName\n ? createQualifiedName(packageName, name)\n : undefined;\n\n // Determine visibility from config, manifest, or default to 'public'\n // Priority: explicit config > manifest > default\n const visibility: SmrtVisibility =\n config.visibility || manifestEntry?.visibility || 'public';\n\n // Issue #951: Use qualified name as primary key when available\n const registrationKey = qualifiedName || name;\n\n getClasses().set(registrationKey, {\n name,\n qualifiedName, // Qualified name for cross-package identification\n constructor: ctor,\n config: mergedConfig,\n fields,\n methods,\n schema,\n validators,\n validationRules, // Pre-computed rules from manifest (Issue #782)\n tools: manifestEntry?.tools, // AI-callable tool schemas (CLI param schemas)\n // Pluralized endpoint name. Prefer the manifest's computed value (handles\n // STI inheritance + any future inflection changes); fall back to the same\n // simple pluralization the scanner uses for inline/test classes that have\n // no manifest entry. (smrt#1311.)\n collection: manifestEntry?.collection ?? pluralizeCollection(name),\n packageName, // Store package name from manifest for getPackageName() lookup\n sourceFilePath, // Store source file for collision detection (Issue #555)\n extends: extendsClass, // Capture parent class name from manifest OR prototype chain\n extendsTypeArg: manifestEntry?.extendsTypeArg, // SmrtCollection<T> generic arg\n tenantScopedConfig, // Multi-tenancy config (Issue #688)\n visibility, // Visibility control for manifest filtering\n // NOTE: Don't pre-compute inheritanceChain here - let getInheritanceChain() compute\n // it lazily using the `extends` field. This ensures correct chain for both\n // decorator-registered and manifest-loaded classes.\n });\n\n // Release B (#1133): case-insensitive lookups iterate the classes Map\n // directly instead of maintaining a parallel classNameMap index.\n\n // Index constructor for O(1) reverse lookups (Issue #713: constructor-based lookup)\n getConstructorIndex().set(ctor, registrationKey);\n // Stamp the qualified name on the constructor itself as a fallback for\n // runtime code that has a constructor reference but might hit a\n // WeakMap miss (HMR / federated modules / multiple-copy edge cases).\n setSmrtQualifiedName(ctor, qualifiedName);\n\n verboseLog(\n `🎯 Registered smrt object: ${name} (key: ${registrationKey}) with schema for ${schema.tableName} and ${(validators?.length || 0) + (validationRules?.length || 0)} validators/rules`,\n );\n\n // STI sibling auto-loading (Issue #430)\n // When a class is registered that shares a table with other classes (STI),\n // we need to discover and register ALL siblings so that getAllSchemas()\n // can merge columns from all subtypes for the database adapter.\n //\n // IMPORTANT: Only auto-load siblings from EXTERNAL packages.\n // For test classes or classes in the same package, they will be registered\n // by their own @smrt() decorators. Auto-loading them as stubs would cause collisions.\n const collection = manifestEntry?.collection;\n if (collection && !getStiSiblingsLoaded().has(collection)) {\n // Mark this collection as processed to prevent infinite recursion\n getStiSiblingsLoaded().add(collection);\n\n verboseLog(\n `[registry] Checking for STI siblings for collection: ${collection}`,\n );\n\n // Discover all classes that share this collection (table)\n const siblings = discoverSTISiblingsSync(collection);\n\n // Register any siblings that aren't already registered\n // Only load siblings from DIFFERENT packages to avoid collisions with local classes\n for (const sibling of siblings) {\n // Use case-insensitive check to prevent registering 'Praeco' when 'praeco' exists\n if (!hasClassCaseInsensitive(sibling.className)) {\n // Skip siblings from the same package - they will be registered by their own decorators\n if (\n sibling.packageName &&\n packageName &&\n sibling.packageName === packageName\n ) {\n verboseLog(\n `[registry] Skipping STI sibling ${sibling.className} from same package: ${packageName}`,\n );\n continue;\n }\n\n verboseLog(\n `[registry] Auto-loading STI sibling: ${sibling.className} for collection: ${collection}`,\n );\n registerFromManifest(\n sibling.className,\n sibling.entry,\n sibling.packageName,\n );\n }\n }\n }\n}\n\nexport function registerCollection(\n objectName: string,\n // `new (options: any) => SmrtCollection<any>` is the irreducible\n // collection-constructor shape (S4 #1579): the public\n // `ObjectRegistry.registerCollection` wrapper hands a non-generic constructor\n // of exactly this form, which is not assignable to the generic\n // `typeof SmrtCollection`. Mirrors `RegisteredClass.collectionConstructor`.\n collectionConstructor: NonNullable<RegisteredClass['collectionConstructor']>,\n): void {\n const registered = findClass(objectName);\n if (registered) {\n registered.collectionConstructor = collectionConstructor;\n }\n\n // The collections map stores `typeof SmrtCollection`; the runtime value is a\n // SmrtCollection subclass constructor. Bridge the loose constructor shape to\n // the map's element type without `any`.\n getCollections().set(\n objectName,\n collectionConstructor as unknown as Parameters<\n ReturnType<typeof getCollections>['set']\n >[1],\n );\n}\n\n// resolveManifestCollision was removed in Release C (#1134). Its three-branch\n// decision is now represented as explicit rows in the collision-policy\n// decision table — see `manifest-same-source-file`, `manifest-sti-child-wins`,\n// `manifest-sti-parent-skip`, `manifest-default-skip` scenarios in\n// packages/core/src/registry/collision-policy.ts.\n\ntype TenantScopedOptions = Exclude<\n SmartObjectConfig['tenantScoped'],\n boolean | undefined\n>;\n\nfunction normalizeTenantScopedConfig(\n tenantScoped: SmartObjectConfig['tenantScoped'],\n): RegisteredClass['tenantScopedConfig'] | undefined {\n if (!tenantScoped) {\n return undefined;\n }\n\n const tenantOpts: TenantScopedOptions =\n typeof tenantScoped === 'boolean' ? {} : tenantScoped;\n return {\n mode: tenantOpts.mode ?? 'required',\n field: tenantOpts.field ?? 'tenantId',\n autoFilter: tenantOpts.autoFilter ?? true,\n autoPopulate: tenantOpts.autoPopulate ?? true,\n allowSuperAdminBypass: tenantOpts.allowSuperAdminBypass ?? false,\n };\n}\n\nfunction ensureTenantScopedField(\n fields: Map<string, RegisteredField>,\n tenantScopedConfig: RegisteredClass['tenantScopedConfig'] | undefined,\n): void {\n if (!tenantScopedConfig) {\n return;\n }\n\n const fieldName = tenantScopedConfig.field;\n const existingField = fields.get(fieldName);\n if (existingField) {\n fields.set(fieldName, {\n ...existingField,\n required:\n existingField.required ?? tenantScopedConfig.mode === 'required',\n _meta: {\n ...existingField._meta,\n reference: existingField._meta?.reference ?? 'Tenant',\n sqlType: 'UUID',\n __tenancy: {\n ...existingField._meta?.__tenancy,\n isTenantIdField: true,\n ...tenantScopedConfig,\n },\n },\n });\n return;\n }\n\n fields.set(fieldName, {\n type: 'foreignKey',\n related: 'Tenant',\n required: tenantScopedConfig.mode === 'required',\n _meta: {\n reference: 'Tenant',\n sqlType: 'UUID',\n __tenancy: {\n isTenantIdField: true,\n ...tenantScopedConfig,\n },\n },\n });\n}\n\nfunction mergeIndexDefinitions(\n existingIndexes: IndexDefinition[] | undefined,\n manifestIndexes: IndexDefinition[] | undefined,\n): IndexDefinition[] {\n const merged = [...(existingIndexes || [])];\n const seen = new Set(merged.map((index) => index?.name).filter(Boolean));\n\n for (const index of manifestIndexes || []) {\n if (!index?.name || seen.has(index.name)) {\n continue;\n }\n merged.push(index);\n seen.add(index.name);\n }\n\n return merged;\n}\n\n// cloneManifestSchemaColumns lives in ../manifest/store.ts — imported above.\n// (Hoisted out of the duplicate definition here and in registry.ts.)\n\n/**\n * Typed read-view of a raw decorator option bag.\n *\n * `getFieldDecorators()` stores decorator options as\n * `Map<string, Record<string, unknown>>`, so the individual reads come back as\n * `unknown` and won't narrow to the typed `RegisteredField` members they feed.\n * This interface re-types only the members the decorator-merge loop reads, with\n * exactly the target member types, and keeps the open index signature so a raw\n * `Record<string, unknown>` bag is assignable to it without `any`. (The bag's\n * runtime values are field-helper options; this view mirrors that contract.)\n */\ninterface DecoratorFieldOptions {\n type?: FieldDefinition['type'];\n required?: boolean;\n nullable?: boolean;\n transient?: boolean;\n related?: string;\n [key: string]: unknown;\n}\n\n/**\n * Read-only field shape consumed by the SQL-type override helpers.\n *\n * `applySqlTypeOverrides` runs over two sources: the canonical\n * `Map<string, RegisteredField>` AND the raw decorator bag\n * (`Map<string, Record<string, unknown>>` from `getFieldDecorators()`). Both\n * satisfy this shape — every member is optional and the open index signature\n * matches both `RegisteredField`'s index signature and `Record<string, unknown>`\n * — so the helpers read the same overrides off either source without `any`.\n */\ninterface SqlTypeOverrideField {\n type?: unknown;\n sqlType?: unknown;\n __tenancy?: FieldMeta['__tenancy'];\n _meta?: FieldMeta;\n [key: string]: unknown;\n}\n\nfunction getReferenceKindFromFieldOptions(\n fieldOptions: SqlTypeOverrideField,\n): ColumnDefinition['referenceKind'] | undefined {\n if (\n fieldOptions?.__tenancy?.isTenantIdField ||\n fieldOptions?._meta?.__tenancy?.isTenantIdField\n ) {\n return 'tenantId';\n }\n\n if (fieldOptions?.type === 'foreignKey') {\n return 'foreignKey';\n }\n\n if (fieldOptions?.type === 'crossPackageRef') {\n return 'crossPackageRef';\n }\n\n return undefined;\n}\n\nfunction applySqlTypeOverrides(\n columns: Record<string, ColumnDefinition>,\n fieldEntries: Iterable<[string, SqlTypeOverrideField]> | undefined,\n): void {\n if (!fieldEntries) {\n return;\n }\n\n for (const [fieldName, fieldOptions] of fieldEntries) {\n const sqlType = fieldOptions?.sqlType ?? fieldOptions?._meta?.sqlType;\n const referenceKind = getReferenceKindFromFieldOptions(fieldOptions);\n if (!sqlType && !referenceKind) {\n continue;\n }\n\n const columnName = toSnakeCase(fieldName);\n const existingColumn = columns[columnName];\n if (!existingColumn) {\n continue;\n }\n\n columns[columnName] = {\n ...existingColumn,\n ...(sqlType\n ? { type: String(sqlType).toUpperCase() as ColumnDefinition['type'] }\n : {}),\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n}\n\n/**\n * Invalidate cached inheritance state (chain + inheritedFields +\n * inheritedMethods) on `existing` AND every descendant whose `extends` or\n * `inheritanceChain` references it.\n *\n * This covers both simple-name and qualified-name `extends` matches, which\n * is why `ObjectRegistry.invalidateInheritanceCache` delegates here (Release\n * C follow-up #1139 Gap 1 — the old recursive public helper only matched\n * `extends === className` by exact string, silently missing qualified\n * descendants like `@pkg:Parent`).\n *\n * `previousIdentity` is used by `upsertExistingEntry` (Gap 2): when a class\n * is promoted (its name/packageName/qualifiedName are mutated in place),\n * descendants registered against the pre-mutation identity need to be\n * invalidated too. Pass the pre-mutation `{ name, qualifiedName }` so this\n * sweep matches against both the old and new identity.\n *\n * Exported for the public `ObjectRegistry.invalidateInheritanceCache`\n * shim in registry.ts.\n */\nexport function invalidateInheritanceEntries(\n existing: RegisteredClass,\n previousIdentity?: {\n name?: string;\n qualifiedName?: string;\n },\n): void {\n const cache = getInheritanceCache();\n const affectedNames = new Set<string>();\n\n const remember = (name: string | undefined): void => {\n if (name) {\n affectedNames.add(name);\n }\n };\n\n const matchNames = new Set<string>();\n const rememberMatch = (name: string | undefined): void => {\n if (name) matchNames.add(name);\n };\n rememberMatch(existing.name);\n rememberMatch(existing.qualifiedName);\n rememberMatch(previousIdentity?.name);\n rememberMatch(previousIdentity?.qualifiedName);\n\n for (const name of matchNames) {\n remember(name);\n }\n\n for (const [key, candidate] of getClasses()) {\n const extendsMatches =\n !!candidate.extends && matchNames.has(candidate.extends);\n const chainMatches =\n !!candidate.inheritanceChain &&\n candidate.inheritanceChain.some((n) => matchNames.has(n));\n\n if (candidate === existing || extendsMatches || chainMatches) {\n candidate.inheritanceChain = undefined;\n candidate.inheritedFields = undefined;\n candidate.inheritedMethods = undefined;\n remember(key);\n remember(candidate.name);\n remember(candidate.qualifiedName);\n }\n }\n\n for (const name of affectedNames) {\n cache.delete(name);\n }\n}\n\nfunction mergeManifestIntoExistingRegistration(\n existing: RegisteredClass,\n objectDef: SmartObjectDefinition,\n packageName?: string,\n): void {\n const manifestConfig = objectDef.decoratorConfig || {};\n const manifestTableName =\n objectDef.schema?.tableName ||\n manifestConfig.tableName ||\n existing.schema?.tableName ||\n existing.config.tableName ||\n tableNameFromClass(existing.constructor);\n\n existing.config = {\n ...manifestConfig,\n ...existing.config,\n tableName: manifestTableName,\n };\n\n if (objectDef.fields) {\n for (const [fieldName, fd] of Object.entries(objectDef.fields)) {\n if (!existing.fields.has(fieldName)) {\n existing.fields.set(fieldName, createFieldFromManifest(fd));\n continue;\n }\n\n const existingField = existing.fields.get(fieldName);\n if (!existingField) {\n continue;\n }\n\n existing.fields.set(fieldName, mergeManifestField(existingField, fd));\n }\n }\n\n const tenantScopedConfig = normalizeTenantScopedConfig(\n existing.config.tenantScoped,\n );\n if (tenantScopedConfig) {\n existing.tenantScopedConfig = tenantScopedConfig;\n ensureTenantScopedField(existing.fields, tenantScopedConfig);\n }\n\n if (objectDef.methods) {\n for (const [methodName, methodDef] of Object.entries(objectDef.methods)) {\n existing.methods.set(methodName, methodDef);\n }\n }\n\n if (objectDef.schema) {\n const manifestSchema: SchemaDefinition = {\n ddl: objectDef.schema.ddl || '',\n indexes:\n objectDef.schema.indexes?.map((idx: IndexDefinition) => ({\n name: idx.name,\n columns: idx.columns || [],\n unique: idx.unique || false,\n where: idx.where,\n description: idx.description,\n jsonPath: idx.jsonPath,\n })) || [],\n triggers: [],\n tableName: objectDef.schema.tableName || manifestTableName,\n columns: cloneManifestSchemaColumns(objectDef.schema.columns),\n foreignKeys: [],\n dependencies: [],\n version: objectDef.schema.version || '',\n packageName: packageName,\n };\n\n applySqlTypeOverrides(manifestSchema.columns, existing.fields.entries());\n\n existing.schema = {\n ...(existing.schema || {}),\n ...manifestSchema,\n tableName: manifestSchema.tableName,\n ddl: manifestSchema.ddl || existing.schema?.ddl || '',\n columns: {\n ...(existing.schema?.columns || {}),\n ...(manifestSchema.columns || {}),\n },\n indexes: mergeIndexDefinitions(\n existing.schema?.indexes,\n manifestSchema.indexes,\n ),\n packageName: packageName || existing.schema?.packageName,\n };\n } else if (!existing.schema) {\n existing.schema = {\n ddl: '',\n indexes: [],\n triggers: [],\n tableName: manifestTableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n packageName: packageName,\n };\n } else {\n existing.schema.tableName = manifestTableName;\n existing.schema.packageName = packageName || existing.schema.packageName;\n }\n\n if (objectDef.validationRules !== undefined) {\n existing.validationRules = objectDef.validationRules;\n existing.validators = undefined;\n } else if (!existing.validationRules && !existing.validators) {\n existing.validators = compileValidators(existing.name, existing.fields);\n }\n\n if (packageName) {\n existing.packageName = packageName;\n existing.qualifiedName = createQualifiedName(\n packageName,\n existing.name,\n ) as QualifiedClassName;\n } else if (!existing.packageName && objectDef.packageName) {\n existing.packageName = objectDef.packageName;\n }\n\n if (objectDef.extends) {\n existing.extends = packageName\n ? qualifyExtendsName(objectDef.extends, packageName)\n : objectDef.extends;\n }\n\n if (!existing.sourceFilePath && objectDef.filePath) {\n existing.sourceFilePath = objectDef.filePath;\n }\n\n existing.visibility =\n objectDef.visibility || manifestConfig.visibility || existing.visibility;\n invalidateInheritanceEntries(existing);\n}\n\nexport function registerFromManifest(\n name: string,\n objectDef: SmartObjectDefinition,\n packageName?: string,\n): void {\n // Issue #951: Compute simple class name and registration key early\n // `name` may be a qualified key from manifest (e.g., '@happyvertical/smrt-events:Event')\n // `simpleClassName` is always the plain class name (e.g., 'Event')\n const simpleClassName = objectDef.className || name;\n const qualifiedNameEarly = packageName\n ? createQualifiedName(packageName, simpleClassName)\n : objectDef.qualifiedName;\n const registrationKey = (qualifiedNameEarly || name) as string;\n\n // Release C (#1134): collision resolution routes through\n // decideCollisionPolicy. See collision-policy.ts for the 16-row decision\n // table; applyManifestCollisionPolicy below turns a policy into registry\n // mutations.\n //\n // Two collision checks, matching pre-C behavior:\n // 1. Canonical-name lookup (case-insensitive simple-name match; covers\n // issue #950's STI child-wins and issue #951's qualified coexistence).\n // 2. Exact-key match (handles re-registration with same qualified key).\n //\n // The ambiguous case (multiple existing entries share the simple name —\n // `getCanonicalClassName` returns undefined) is NOT fed through the table.\n // It falls through both checks into the new-registration code below,\n // preserving issue #951's coexistence semantics.\n const existingCanonical = getCanonicalClassName(simpleClassName);\n if (existingCanonical) {\n const existing = getClasses().get(existingCanonical);\n if (existing) {\n const outcome = applyManifestCollisionPolicy({\n name,\n objectDef,\n packageName,\n existing,\n existingKey: existingCanonical,\n registrationKey,\n matchKind: 'canonical-name',\n });\n if (outcome === 'return') return;\n // outcome === 'continue' means policy was replace (child-wins) or\n // coexist — fall through into the new-registration code below.\n }\n // else: stale map entry (key exists but class was removed) — allow registration\n }\n\n if (getClasses().has(registrationKey)) {\n const existing = getClasses().get(registrationKey);\n if (!existing) return;\n const outcome = applyManifestCollisionPolicy({\n name,\n objectDef,\n packageName,\n existing,\n existingKey: registrationKey,\n registrationKey,\n matchKind: 'exact-key',\n });\n if (outcome === 'return') return;\n }\n\n // Issue #1004: Qualify the `extends` value BEFORE inserting this class\n // into the classes Map. Otherwise qualifyExtendsName would iterate and\n // find the child's own entry, creating a circular extends chain\n // (e.g., @test/sports:TestEvent extends itself). Release B (#1133) swapped\n // the underlying classNameMap lookup for an iteration over `classes`, but\n // the ordering invariant still holds.\n const qualifiedExtends =\n objectDef.extends && packageName\n ? qualifyExtendsName(objectDef.extends, packageName)\n : objectDef.extends;\n\n // Release B (#1133): case-insensitive lookups iterate the classes Map\n // directly; no parallel classNameMap index to maintain.\n\n // Create stub constructor - not needed for CLI command generation\n // The CLI only needs metadata (fields, methods, config)\n // Mark as manifest stub so real class can replace it during decorator registration\n const stubConstructor = class extends SmrtObject {\n static readonly _isManifestStub = true;\n } as typeof SmrtObject;\n Object.defineProperty(stubConstructor, 'name', { value: simpleClassName });\n\n // Convert manifest field definitions to Field objects\n const fields = new Map<string, RegisteredField>();\n const decorators = getFieldDecorators().get(simpleClassName);\n if (objectDef.fields) {\n for (const [fieldName, fd] of Object.entries(objectDef.fields)) {\n fields.set(fieldName, createFieldFromManifest(fd));\n }\n }\n\n // Load method definitions\n const methods = new Map<string, MethodDefinition>();\n if (objectDef.methods) {\n for (const [methodName, methodDef] of Object.entries(objectDef.methods)) {\n methods.set(methodName, methodDef);\n }\n }\n\n // Get config from manifest\n const config = objectDef.decoratorConfig || {};\n const tableName = config.tableName || tableNameFromClass(stubConstructor);\n\n // Load pre-generated schema from manifest if available\n // This enables efficient external package consumption without runtime schema generation\n let schema: SchemaDefinition;\n if (objectDef.schema) {\n // Pre-generated schema from manifest (build-time)\n // Keep indexes as IndexDefinition objects for DDL strategies\n schema = {\n ddl: objectDef.schema.ddl,\n indexes:\n objectDef.schema.indexes?.map((idx: IndexDefinition) => ({\n name: idx.name,\n columns: idx.columns || [],\n unique: idx.unique || false,\n where: idx.where,\n description: idx.description,\n jsonPath: idx.jsonPath,\n })) || [],\n triggers: [],\n tableName: objectDef.schema.tableName,\n columns: cloneManifestSchemaColumns(objectDef.schema.columns),\n foreignKeys: [],\n dependencies: [],\n version: objectDef.schema.version || '',\n packageName: packageName,\n };\n applySqlTypeOverrides(schema.columns, fields.entries());\n applySqlTypeOverrides(schema.columns, decorators?.entries());\n verboseLog(\n `[registry] Loaded pre-generated schema for ${name} (${Object.keys(objectDef.schema.columns || {}).length} columns)`,\n );\n } else {\n // Placeholder schema - will be generated at runtime if needed\n schema = {\n ddl: '',\n indexes: [],\n triggers: [],\n tableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n packageName: packageName,\n };\n }\n\n // Use pre-computed validation rules from manifest if available (Issue #782)\n // This avoids compiling validator closures at runtime, reducing startup time\n const validationRules = objectDef.validationRules;\n let validators: ValidatorFunction[] | undefined;\n\n // Only compile validators if no pre-computed rules exist\n // (backward compatibility for older manifests)\n if (!validationRules || validationRules.length === 0) {\n validators = compileValidators(name, fields);\n verboseLog(\n `[registry] No pre-computed rules for ${name}, compiled ${validators.length} validators`,\n );\n } else {\n verboseLog(\n `[registry] Using ${validationRules.length} pre-computed validation rules for ${name}`,\n );\n }\n\n // Issue #951: Use qualifiedNameEarly computed at the top of this function\n // (uses simpleClassName, not the manifest key which may already be qualified)\n const qualifiedName = qualifiedNameEarly;\n\n // Get visibility from manifest (defaults to 'public')\n const visibility: SmrtVisibility =\n objectDef.visibility || config.visibility || 'public';\n\n // Register in ObjectRegistry (metadata only, no collection constructor)\n // Manifest registration is for command discovery and help text.\n // Runtime execution requires real classes loaded from entry point.\n //\n // Issue #847: Use className from objectDef (simple name like 'Council') for the name\n // property, not the qualified name key. This enables getFields() lookups by simple\n // class name to work correctly. simpleClassName is defined earlier in this function.\n //\n // Issue #951: Use registrationKey (qualified when available) as the primary key\n getClasses().set(registrationKey, {\n name: simpleClassName,\n qualifiedName, // Qualified name for cross-package identification\n constructor: stubConstructor,\n config,\n fields,\n methods,\n schema,\n validators,\n validationRules, // Pre-computed rules from manifest (Issue #782)\n tools: objectDef.tools, // AI-callable tool schemas (used for CLI param schemas)\n // Pluralized endpoint name; fall back to simple pluralization if a\n // (legacy) manifest lacks it. (smrt#1311.)\n collection: objectDef.collection ?? pluralizeCollection(simpleClassName),\n packageName,\n sourceFilePath: objectDef.filePath, // Store source file for collision detection (Issue #555)\n extends: qualifiedExtends, // Issue #1004: Pre-computed qualified parent\n extendsTypeArg: objectDef.extendsTypeArg, // SmrtCollection<T> generic arg\n visibility, // New: Visibility control for manifest filtering\n });\n // Tag the synthetic stub constructor with the qualified name so the\n // same constructor-side identity convention holds for manifest-loaded\n // classes as well as decorator-registered ones.\n setSmrtQualifiedName(stubConstructor, qualifiedName);\n\n verboseLog(\n `📦 Registered ${simpleClassName} from manifest (key: ${registrationKey}, ${fields.size} fields, ${methods.size} methods)`,\n );\n\n // STI sibling auto-loading (Issue #430)\n // When a class is registered that shares a table with other classes (STI),\n // we need to discover and register ALL siblings so that getAllSchemas()\n // can merge columns from all subtypes for the database adapter.\n //\n // IMPORTANT: Only auto-load siblings from EXTERNAL packages.\n // For test classes or classes in the same package, they will be registered\n // by their own @smrt() decorators. Auto-loading them as stubs would cause collisions.\n const collection = objectDef.collection;\n if (collection && !getStiSiblingsLoaded().has(collection)) {\n // Mark this collection as processed to prevent infinite recursion\n getStiSiblingsLoaded().add(collection);\n\n verboseLog(\n `[registry] Checking for STI siblings for collection: ${collection}`,\n );\n\n // Discover all classes that share this collection (table)\n const siblings = discoverSTISiblingsSync(collection);\n\n // Register any siblings that aren't already registered\n // Only load siblings from DIFFERENT packages to avoid collisions with local classes\n for (const sibling of siblings) {\n // Use case-insensitive check to prevent registering 'Praeco' when 'praeco' exists\n if (!hasClassCaseInsensitive(sibling.className)) {\n // Skip siblings from the same package - they will be registered by their own decorators\n if (\n sibling.packageName &&\n packageName &&\n sibling.packageName === packageName\n ) {\n verboseLog(\n `[registry] Skipping STI sibling ${sibling.className} from same package: ${packageName}`,\n );\n continue;\n }\n\n verboseLog(\n `[registry] Auto-loading STI sibling: ${sibling.className} for collection: ${collection}`,\n );\n registerFromManifest(\n sibling.className,\n sibling.entry,\n sibling.packageName,\n );\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmFA,SAAS,oBAAoB,WAA2B;CACtD,MAAM,QAAQ,UAAU,YAAY;CACpC,IAAI,MAAM,SAAS,GAAG,GAAG,OAAO,GAAG,MAAM,MAAM,GAAG,EAAE,EAAE;CACtD,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,GAClE,OAAO,GAAG,MAAM;CAElB,IAAI,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,GAAG,OAAO,GAAG,MAAM;CAClE,OAAO,GAAG,MAAM;AAClB;;;;;;AAOA,SAAS,oBAAoB,YAAyC;CACpE,IAAI,CAAC,YAAY,OAAO;CACxB,OACE,WAAW,SAAS,qBAAqB,KACzC,WAAW,SAAS,QAAQ,KAC5B,WAAW,SAAS,SAAS,KAC7B,WAAW,SAAS,QAAQ,KAC5B,WAAW,SAAS,QAAQ;AAEhC;;;;;AAMA,SAAS,8BAA8B,MASnB;CAClB,MAAM,EACJ,MACA,MACA,gBACA,eACA,qBACA,UACA,aACA,cACE;CAEJ,IAAI,qBAAqB;CACzB,IAAI,qBAAqB;CACzB,IAAI;EACF,qBAAqB,KAAK,qBAAqB,SAAS;EACxD,qBAAqB,SAAS,YAAY,qBAAqB;CACjE,QAAQ,CAER;CAEA,MAAM,uBAAuB,CAAC,EAAE,iBAAiB,SAAS;CAC1D,MAAM,iBACJ,wBAAwB,kBAAkB,SAAS;CACrD,MAAM,6BAA6B,CAAC,CAAC,SAAS,aAAa,WAAW,GAAG;CACzE,MAAM,cACJ,CAAC,CAAC,kBACF,CAAC,CAAC,SAAS,eACX,mBAAmB,SAAS;CAC9B,MAAM,qBAAqB,CAAC,CAAC;CAC7B,MAAM,yBAAyB,gBAAgB,WAAW;CAE1D,OAAO;EACL,QAAQ;EACR;EACA,iBAAiB,SAAS,gBAAgB;EAC1C;EACA;EACA,wBACG,SAAS,YACP,oBAAoB;EACzB;EACA;EACA;EACA;EACA;EACA,UAAU,SAAS,SAAS;EAC5B;EACA;EACA,uBAAuB,CAAC,CAAC,SAAS;EAElC,oBAAoB;EACpB,uCAAuC;EACvC,sBAAsB,CAAC,SAAS;CAClC;AACF;;;;;;;;;;AAWA,SAAS,6BAA6B,MAa1B;CAEV,MAAM,WAAW,sBADF,8BAA8B,IACN,CAAM;CAE7C,QAAQ,SAAS,QAAjB;EACE,KAAK;EACL,KAAK,WAAW;GAKd,MAAM,SAAS,KAAK,oBAAoB,KAAK,aAAa,KAAK,QAAQ;GACvE,IACE,SAAS,aAAa,oBACtB,SAAS,aAAa,6BAEtB,WACE,cAAc,SAAS,SAAS,KAAK,KAAK,KAAK,WAAW,OAAO,KAAK,SAAS,OAAO,EACxF;GAEF,OAAO;EACT;EACA,KAAK;GACH,WACE,cAAc,SAAS,SAAS,cAAc,KAAK,KAAK,KAAK,SAAS,OAAO,EAC/E;GACA,OAAO;EACT,KAAK,SAAS;GACZ,MAAM,cACJ,KAAK,cAAc,qBACf,kCAAkC,KAAK,YAAY,MACnD;GACN,MAAM,IAAI,MACR,+BAA+B,KAAK,KAAK,GAAG,YAAY,mZAQjB,KAAK,KAAK,wFAEnD;EACF;EACA,KAAK;EACL,KAAK,qBAGH,MAAM,IAAI,MACR,mCAAmC,SAAS,OAAO,oCAAoC,SAAS,SAAS,kBAC3G;CACJ;AACF;;;;;;;AAQA,SAAS,6BAA6B,MAQlB;CAClB,MAAM,EACJ,MACA,WACA,aACA,UACA,aACA,iBACA,cACE;CACJ,MAAM,eAAe,UAAU,aAAa;CAC5C,MAAM,aAAa,UAAU;CAe7B,MAAM,qBAAqB,CAAC,EAC1B,eACC,eAAe,SAAS,QACvB,eAAe,eACf,WAAW,YAAY,MAAM,SAAS,KAAK,YAAY;CAE3D,MAAM,qBAAqB,CAAC,EAC1B,SAAS,YACR,SAAS,YAAY,gBACpB,SAAS,YAAY,QACrB,SAAS,YAAY,mBACrB,SAAS,QAAQ,YAAY,MAAM,aAAa,YAAY;CAGhE,MAAM,gBAAgB,UAAU;CAChC,MAAM,uBAAuB,CAAC,EAAE,iBAAiB,SAAS;CAC1D,MAAM,iBACJ,wBAAwB,kBAAkB,SAAS;CACrD,MAAM,6BAA6B,CAAC,CAAC,SAAS,aAAa,WAAW,GAAG;CACzE,MAAM,cACJ,CAAC,CAAC,eACF,CAAC,CAAC,SAAS,eACX,gBAAgB,SAAS;CAE3B,OAAO;EACL,QAAQ;EACR;EACA,iBAAiB;EACjB;EACA;EACA,wBACG,SAAS,YACP,oBAAoB;EACzB,qBAAqB;EACrB;EACA;EACA;EACA;EACA,UAAU,iBAAiB,SAAS;EACpC,oBAAoB,gBAAgB,eAAe;EACnD,wBAAwB,gBAAgB,WAAW;EACnD,uBAAuB,CAAC,CAAC,SAAS;EAClC,oBAAoB,CAAC,EACnB,UAAU,UACV,UAAU,WACV,UAAU,UACV,UAAU;EAEZ,uCAAuC,oBAAoB;EAC3D,sBAAsB,CAAC,SAAS;CAClC;AAMF;;;;;;;;;;;;;;AAeA,SAAS,6BAA6B,MAQZ;CAExB,MAAM,WAAW,sBADF,6BAA6B,IACL,CAAM;CAE7C,QAAQ,SAAS,QAAjB;EACE,KAAK;GACH,WACE,cAAc,SAAS,SAAS,uBAAuB,KAAK,KAAK,KAAK,SAAS,OAAO,EACxF;GACA,OAAO;EACT,KAAK;GAEH,WAAW,CAAC,CAAC,OAAO,KAAK,WAAW;GACpC,WACE,cAAc,SAAS,SAAS,KAAK,KAAK,KAAK,cAAc,KAAK,YAAY,KAAK,SAAS,OAAO,EACrG;GACA,OAAO;EACT,KAAK;GACH,sCACE,KAAK,UACL,KAAK,WACL,KAAK,WACP;GACA,IAAI,KAAK,oBAAoB,KAAK,aAAa;IAE7C,WAAA,CAAA,CAAQ,IAAI,KAAK,iBAAiB,KAAK,QAAQ;IAC/C,oBAAoB,CAAC,CAAC,IACpB,KAAK,SAAS,aACd,KAAK,eACP;GACF;GACA,OAAO;EAET,KAAK,qBAGH,OAAO;EACT,KAAK,UAGH,OAAO;EACT,KAAK,SAGH,MAAM,IAAI,MACR,yEAAyE,SAAS,SAAS,kBAC7F;CACJ;AACF;AAEA,SAAS,iBACP,MACA,MACA,QACQ;CACR,MAAM,gBAAgB,OAAO,YACzB,iBAAiB,OAAO,WAAW,IAAI,IACvC,qBAAqB,IAAI;CAE7B,OACE,eAAe,QAAQ,aACvB,eAAe,iBAAiB,aAChC,OAAO,aACP,mBAAmB,IAAI;AAE3B;AAEA,SAAS,iBAAiB,MAAyB,WAAyB;CAE1E,IADiB,OAAO,yBAAyB,MAAM,iBACnD,CAAA,EAAU,UAAU,WACtB;CAGF,OAAO,eAAe,MAAM,mBAAmB;EAC7C,OAAO;EACP,UAAU;EACV,YAAY;EACZ,cAAc;CAChB,CAAC;AACH;;;;;;;;;;;;;;;AAgBA,SAAS,qBACP,MACA,eACM;CACN,IAAI,CAAC,eAAe;CAEpB,IADiB,OAAO,yBAAyB,MAAM,oBACnD,CAAA,EAAU,UAAU,eACtB;CAGF,OAAO,eAAe,MAAM,sBAAsB;EAChD,OAAO;EACP,UAAU;EACV,YAAY;EACZ,cAAc;CAChB,CAAC;AACH;AAEA,SAAgB,SACd,MACA,SAA4B,CAAC,GACvB;CACN,MAAM,OAAO,OAAO,QAAQ,KAAK;CACjC,MAAM,sBAAsB,OAAO;CAEnC,SAAS,oBACP,aACA,UACQ;EACR,MAAM,kBAAkB,uBAAuB,SAAS;EACxD,MAAM,UAAU,kBACZ,oBAAoB,iBAAiB,IAAI,IACzC;EAIJ,MAAM,mBAAmB;GACvB,MAAM,SAAS;GACf,eAAe,SAAS;EAC1B;EAEA,SAAS,OAAO;EAChB,SAAS,cAAc;EACvB,SAAS,gBAAgB,kBACpB,oBAAoB,iBAAiB,IAAI,IAC1C,KAAA;EACJ,MAAM,gBAAgB,iBAAiB,MAAM,MAAM;GACjD,GAAG,SAAS;GACZ,GAAG;EACL,CAAC;EACD,SAAS,SAAS;GAChB,GAAG,SAAS;GACZ,GAAG;GACH,WAAW;EACb;EACA,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;GAChB,KAAK;GACL,SAAS,CAAC;GACV,UAAU,CAAC;GACX,WAAW;GACX,SAAS,CAAC;GACV,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS;EACX;EAEF,SAAS,OAAO,YAAY;EAC5B,SAAS,cAAc;EACvB,iBAAiB,MAAM,aAAa;EACpC,qBAAqB,MAAM,SAAS,aAAa;EAEjD,IAAI,gBAAgB,SAAS;GAC3B,MAAM,UAAU,WAAW;GAC3B,MAAM,oBAAoB,QAAQ,IAAI,OAAO;GAE7C,IAAI,qBAAqB,sBAAsB,UAC7C,MAAM,IAAI,mBACR,6CAA6C,YAAY,QAAQ,QAAQ,mEAEzE,uCACA;IACE;IACA;IACA,WAAW;GACb,CACF;GAGF,QAAQ,OAAO,WAAW;GAC1B,QAAQ,IAAI,SAAS,QAAQ;EAC/B;EAEA,oBAAoB,CAAC,CAAC,IAAI,MAAM,OAAO;EASvC,6BAA6B,UAAU,gBAAgB;EAEvD,OAAO;CACT;CAKA,MAAM,gBAAgB,uBAAuB;CAC7C,MAAM,iBACJ,uBAAuB,eAAe,MAAM,IAAI,KAAK,KAAA;CACvD,MAAM,sBAAsB,oBAAoB,aAAa;CAG7D,IAAI,WAAW,CAAC,CAAC,IAAI,IAAI,GAAG;EAC1B,MAAM,WAAW,WAAW,CAAC,CAAC,IAAI,IAAI;EACtC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,2BAA2B,KAAK,oDAClC;EAcF,IAXgB,6BAA6B;GAC3C;GACA;GACA;GACA;GACA;GACA;GACA,aAAa;GACb,WAAW;GACX;EACF,CACI,GAAS;CACf;CAIA,MAAM,YAAY,KAAK,YAAY;CACnC,KAAK,MAAM,CAAC,aAAa,aAAa,WAAW,CAAC,CAAC,QAAQ,GAAG;EAC5D,MAAM,aAAa,YAAY,YAAY,MAAM;EACjD,MAAM,cAAc,SAAS,MAAM,YAAY,MAAM;EACrD,IAAI,EAAE,cAAc,gBAAgB,gBAAgB,MAAM;EAa1D,IAXgB,6BAA6B;GAC3C;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW;GACX;EACF,CACI,GAAS;CACf;CAOA,MAAM,uBACJ,uBAAuB,eAAe,MAAM,IAAI,KAAK,KAAA;CAIvD,MAAM,iBAAiB,uBAAuB;CAU9C,IAAI;CACJ,IAAI,OAAO,WACT,gBAAgB,iBAAiB,OAAO,WAAW,IAAI;CAEzD,IAAI,CAAC,eACH,gBAAgB,qBAAqB,IAAI;CAE3C,MAAM,yBAAS,IAAI,IAA6B;CAChD,MAAM,0BAAU,IAAI,IAA8B;CAClD,IAAI;CAEJ,WACE,0BAA0B,KAAK,oBAC/B,gBAAgB,UAAU,WAC5B;CACA,IAAI,eAAe,QACjB,WACE,2BAA2B,OAAO,KAAK,cAAc,MAAM,CAAC,CAAC,OAAO,WACpE,OAAO,KAAK,cAAc,MAAM,CAClC;CAGF,IAAI,eAAe,QAAQ;EAGzB,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QACzC,cAAc,MAChB,GAAkC;GAEhC,MAAM,UAAmC,EAAE,GAAG,SAAS,MAAM;GAC7D,IAAI,SAAS,aAAa,KAAA,GAAW,QAAQ,WAAW,SAAS;GACjE,IAAI,SAAS,YAAY,KAAA,GAAW,QAAQ,UAAU,SAAS;GAC/D,IAAI,SAAS,gBAAgB,KAAA,GAC3B,QAAQ,cAAc,SAAS;GACjC,IAAI,SAAS,cAAc,KAAA,GACzB,QAAQ,YAAY,SAAS;GAG/B,MAAM,QAAyB,EAC7B,MAAM,SAAS,KACjB;GAGA,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAChC,MAAM,QAAQ;GAIhB,IAAI,SAAS,cAAc,KAAA,GACzB,MAAM,YAAY,SAAS;GAE7B,IAAI,SAAS,aAAa,KAAA,GACxB,MAAM,WAAW,SAAS;GAK5B,IAAI,SAAS,YAAY,KAAA,GACvB,MAAM,UAAU,SAAS;QACpB,IAAI,QAAQ,YAAY,KAAA,GAAW;IAExC,MAAM,UAAU,QAAQ;IACxB,OAAO,MAAM,OAAO;GACtB;GAEA,OAAO,IAAI,WAAW,KAAK;EAC7B;EAEA,WACE,uBAAuB,OAAO,KAAK,cAAc,KAAK,eACxD;EAIA,cACE,uBACA,OAAO,WAAW,eAClB,cAAc,eACd;CACJ,OAAO;EAGL,WACE,wCAAwC,KAAK,+BAC/C;EACA,cAAc;CAChB;CAIA,MAAM,aAAa,mBAAmB,CAAC,CAAC,IAAI,IAAI;CAChD,IAAI,cAAc,WAAW,OAAO,GAAG;EACrC,WACE,uBAAuB,WAAW,KAAK,wBAAwB,MACjE;EAEA,KAAK,MAAM,CAAC,WAAW,qBAAqB,YAAY;GAItD,MAAM,OAA8B;GACpC,MAAM,gBAAgB,OAAO,IAAI,SAAS;GAE1C,IAAI,eAAe;IAGjB,MAAM,aAAwB;KAC5B,GAAG,cAAc;KACjB,GAAG;IACL;IACA,MAAM,cAA+B;KACnC,MAAM,KAAK,QAAQ,cAAc;KACjC,OAAO;IACT;IAGA,IAAI,WAAW,MACb,OAAO,WAAW;IAIpB,IAAI,KAAK,cAAc,KAAA,GACrB,YAAY,YAAY,KAAK;SACxB,IAAI,cAAc,cAAc,KAAA,GACrC,YAAY,YAAY,cAAc;IAIxC,IAAI,KAAK,aAAa,MAAM;KAE1B,YAAY,WAAW;KACvB,WAAW,WAAW;IACxB,OAAO,IAAI,KAAK,aAAa,KAAA,GAC3B,YAAY,WAAW,KAAK;SACvB,IAAI,cAAc,aAAa,KAAA,GACpC,YAAY,WAAW,cAAc;IAIvC,IAAI,KAAK,YAAY,KAAA,GAAW;KAC9B,YAAY,UAAU,KAAK;KAC3B,OAAO,YAAY,OAAO;IAC5B,OAAO,IAAI,cAAc,YAAY,KAAA,GACnC,YAAY,UAAU,cAAc;IAGtC,OAAO,IAAI,WAAW,WAAW;IACjC,WACE,uCAAuC,UAAU,SAAS,YAAY,MACxE;GACF,OAAO;IAEL,MAAM,UAAqB;IAC3B,MAAM,WAA4B;KAChC,MAAM,KAAK,QAAQ;KACnB,OAAO;IACT;IAGA,IAAI,KAAK,cAAc,KAAA,GACrB,SAAS,YAAY,KAAK;IAG5B,IAAI,KAAK,aAAa,MAAM;KAC1B,SAAS,WAAW;KACpB,QAAQ,WAAW;IACrB,OAAO,IAAI,KAAK,aAAa,KAAA,GAC3B,SAAS,WAAW,KAAK;IAK3B,IAAI,KAAK,YAAY,KAAA,GAAW;KAC9B,SAAS,UAAU,KAAK;KACxB,OAAO,SAAS,OAAO;IACzB;IAEA,OAAO,IAAI,WAAW,QAAQ;IAC9B,WACE,8BAA8B,UAAU,wBAAwB,KAAK,QAAQ,QAC/E;GACF;EACF;CACF;CAKA,IAAI;CACJ,MAAM,wBACJ,OAAO,gBAAgB,eAAe,iBAAiB;CACzD,IAAI,uBAAuB;EAEzB,MAAM,aACJ,OAAO,0BAA0B,YAAY,CAAC,IAAI;EACpD,qBAAqB;GACnB,MAAM,WAAW,QAAQ;GACzB,OAAO,WAAW,SAAS;GAC3B,YAAY,WAAW,cAAc;GACrC,cAAc,WAAW,gBAAgB;GACzC,uBAAuB,WAAW,yBAAyB;EAC7D;EAGA,MAAM,YAAY,mBAAmB;EACrC,MAAM,iBAAiB,OAAO,IAAI,SAAS;EAC3C,wBAAwB,QAAQ,kBAAkB;EAClD,IAAI,CAAC,gBACH,WACE,yBAAyB,UAAU,iCAAiC,MACtE;CAEJ;CAEA,IAAI,eAAe,SAEjB,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAC3C,cAAc,OAChB,GACE,QAAQ,IAAI,YAAY,SAAS;CAQrC,MAAM,kBACJ,OAGA;CACF,IAAI,iBAAiB;EACnB,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,eAAe,GAClE,QAAQ,IAAI,YAAY,SAAS;EAEnC,WACE,qBAAqB,QAAQ,KAAK,eAAe,KAAK,uBACxD;CACF;CAOA,MAAM,mBAAmB,sBAAsB,IAAI;CAGnD,IAAI,iBAAiB,SAAS,GAAG;EAE/B,MAAM,aAAa,iBAAiB,iBAAiB,SAAS;EAC9D,MAAM,cAAc,UAAU,UAAU;EAExC,IAAI,aAAa;GACf,MAAM,iBAAiB,YAAY,QAAQ,iBAAiB;GAC5D,MAAM,gBAAgB,OAAO;GAI7B,IAAI,kBAAkB,KAAA,KAAa,mBAAmB,eACpD,MAAM,mBAAmB,qBACvB,MACA,eACA,YACA,cACF;EAEJ;CACF;CAQA,MAAM,YAAY,iBAAiB,MAAM,MAAM,MAAM;CACrD,iBAAiB,MAAM,SAAS;CAGhC,IAAI;CACJ,IAAI,eAAe,QAAQ;EAGzB,SAAS;GACP,KAAK,cAAc,OAAO;GAC1B,SACE,cAAc,OAAO,SAAS,KAAK,SAA0B;IAC3D,MAAM,IAAI;IACV,SAAS,IAAI,WAAW,CAAC;IACzB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;GAChB,EAAE,KAAK,CAAC;GACV,UAAU,CAAC;GACX,WAAW,cAAc,OAAO,aAAa;GAE7C,SAAS,2BAA2B,cAAc,OAAO,OAAO;GAChE,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS,cAAc,OAAO,WAAW;GACzC,aAAa,cAAc;EAC7B;EACA,WACE,8CAA8C,KAAK,IAAI,OAAO,KAAK,cAAc,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,UAChH;CACF,OAEE,SAAS;EACP,KAAK;EACL,SAAS,CAAC;EACV,UAAU,CAAC;EACX;EACA,SAAS,CAAC;EACV,aAAa,CAAC;EACd,cAAc,CAAC;EACf,SAAS;EACT,aAAa,KAAA;CACf;CAGF,IAAI,OAAO,SAAS;EAClB,sBAAsB,OAAO,SAAS,OAAO,QAAQ,CAAC;EACtD,IAAI,cAAc,WAAW,OAAO,GAClC,sBAAsB,OAAO,SAAS,WAAW,QAAQ,CAAC;CAE9D;CAIA,MAAM,kBAAkB,eAAe;CACvC,IAAI;CAGJ,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,GACjD,aAAa,kBAAkB,MAAM,MAAM;MAE3C,WACE,oBAAoB,gBAAgB,OAAO,qCAAqC,MAClF;CAKF,IAAI,eAAmC,eAAe;CACtD,IAAI,CAAC,cAAc;EACjB,MAAM,QAAQ,OAAO,eAAe,IAAI;EACxC,IAAI,OAAO,QAAQ,MAAM,SAAS,gBAAgB,MAAM,SAAS,UAC/D,eAAe,MAAM;CAEzB;CAIA,MAAM,eAAe;EACnB,GAAG,eAAe;EAClB,GAAG;EACH;CACF;CAIA,MAAM,gBAAgB,cAClB,oBAAoB,aAAa,IAAI,IACrC,KAAA;CAIJ,MAAM,aACJ,OAAO,cAAc,eAAe,cAAc;CAGpD,MAAM,kBAAkB,iBAAiB;CAEzC,WAAW,CAAC,CAAC,IAAI,iBAAiB;EAChC;EACA;EACA,aAAa;EACb,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA,OAAO,eAAe;EAKtB,YAAY,eAAe,cAAc,oBAAoB,IAAI;EACjE;EACA;EACA,SAAS;EACT,gBAAgB,eAAe;EAC/B;EACA;CAIF,CAAC;CAMD,oBAAoB,CAAC,CAAC,IAAI,MAAM,eAAe;CAI/C,qBAAqB,MAAM,aAAa;CAExC,WACE,8BAA8B,KAAK,SAAS,gBAAgB,oBAAoB,OAAO,UAAU,QAAQ,YAAY,UAAU,MAAM,iBAAiB,UAAU,GAAG,kBACrK;CAUA,MAAM,aAAa,eAAe;CAClC,IAAI,cAAc,CAAC,qBAAqB,CAAC,CAAC,IAAI,UAAU,GAAG;EAEzD,qBAAqB,CAAC,CAAC,IAAI,UAAU;EAErC,WACE,wDAAwD,YAC1D;EAGA,MAAM,WAAW,wBAAwB,UAAU;EAInD,KAAK,MAAM,WAAW,UAEpB,IAAI,CAAC,wBAAwB,QAAQ,SAAS,GAAG;GAE/C,IACE,QAAQ,eACR,eACA,QAAQ,gBAAgB,aACxB;IACA,WACE,mCAAmC,QAAQ,UAAU,sBAAsB,aAC7E;IACA;GACF;GAEA,WACE,wCAAwC,QAAQ,UAAU,mBAAmB,YAC/E;GACA,qBACE,QAAQ,WACR,QAAQ,OACR,QAAQ,WACV;EACF;CAEJ;AACF;AAEA,SAAgB,mBACd,YAMA,uBACM;CACN,MAAM,aAAa,UAAU,UAAU;CACvC,IAAI,YACF,WAAW,wBAAwB;CAMrC,eAAe,CAAC,CAAC,IACf,YACA,qBAGF;AACF;AAaA,SAAS,4BACP,cACmD;CACnD,IAAI,CAAC,cACH;CAGF,MAAM,aACJ,OAAO,iBAAiB,YAAY,CAAC,IAAI;CAC3C,OAAO;EACL,MAAM,WAAW,QAAQ;EACzB,OAAO,WAAW,SAAS;EAC3B,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB;EACzC,uBAAuB,WAAW,yBAAyB;CAC7D;AACF;AAEA,SAAS,wBACP,QACA,oBACM;CACN,IAAI,CAAC,oBACH;CAGF,MAAM,YAAY,mBAAmB;CACrC,MAAM,gBAAgB,OAAO,IAAI,SAAS;CAC1C,IAAI,eAAe;EACjB,OAAO,IAAI,WAAW;GACpB,GAAG;GACH,UACE,cAAc,YAAY,mBAAmB,SAAS;GACxD,OAAO;IACL,GAAG,cAAc;IACjB,WAAW,cAAc,OAAO,aAAa;IAC7C,SAAS;IACT,WAAW;KACT,GAAG,cAAc,OAAO;KACxB,iBAAiB;KACjB,GAAG;IACL;GACF;EACF,CAAC;EACD;CACF;CAEA,OAAO,IAAI,WAAW;EACpB,MAAM;EACN,SAAS;EACT,UAAU,mBAAmB,SAAS;EACtC,OAAO;GACL,WAAW;GACX,SAAS;GACT,WAAW;IACT,iBAAiB;IACjB,GAAG;GACL;EACF;CACF,CAAC;AACH;AAEA,SAAS,sBACP,iBACA,iBACmB;CACnB,MAAM,SAAS,CAAC,GAAI,mBAAmB,CAAC,CAAE;CAC1C,MAAM,OAAO,IAAI,IAAI,OAAO,KAAK,UAAU,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC;CAEvE,KAAK,MAAM,SAAS,mBAAmB,CAAC,GAAG;EACzC,IAAI,CAAC,OAAO,QAAQ,KAAK,IAAI,MAAM,IAAI,GACrC;EAEF,OAAO,KAAK,KAAK;EACjB,KAAK,IAAI,MAAM,IAAI;CACrB;CAEA,OAAO;AACT;AA2CA,SAAS,iCACP,cAC+C;CAC/C,IACE,cAAc,WAAW,mBACzB,cAAc,OAAO,WAAW,iBAEhC,OAAO;CAGT,IAAI,cAAc,SAAS,cACzB,OAAO;CAGT,IAAI,cAAc,SAAS,mBACzB,OAAO;AAIX;AAEA,SAAS,sBACP,SACA,cACM;CACN,IAAI,CAAC,cACH;CAGF,KAAK,MAAM,CAAC,WAAW,iBAAiB,cAAc;EACpD,MAAM,UAAU,cAAc,WAAW,cAAc,OAAO;EAC9D,MAAM,gBAAgB,iCAAiC,YAAY;EACnE,IAAI,CAAC,WAAW,CAAC,eACf;EAGF,MAAM,aAAa,YAAY,SAAS;EACxC,MAAM,iBAAiB,QAAQ;EAC/B,IAAI,CAAC,gBACH;EAGF,QAAQ,cAAc;GACpB,GAAG;GACH,GAAI,UACA,EAAE,MAAM,OAAO,OAAO,CAAC,CAAC,YAAY,EAA8B,IAClE,CAAC;GACL,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;EAC3C;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,6BACd,UACA,kBAIM;CACN,MAAM,QAAQ,oBAAoB;CAClC,MAAM,gCAAgB,IAAI,IAAY;CAEtC,MAAM,YAAY,SAAmC;EACnD,IAAI,MACF,cAAc,IAAI,IAAI;CAE1B;CAEA,MAAM,6BAAa,IAAI,IAAY;CACnC,MAAM,iBAAiB,SAAmC;EACxD,IAAI,MAAM,WAAW,IAAI,IAAI;CAC/B;CACA,cAAc,SAAS,IAAI;CAC3B,cAAc,SAAS,aAAa;CACpC,cAAc,kBAAkB,IAAI;CACpC,cAAc,kBAAkB,aAAa;CAE7C,KAAK,MAAM,QAAQ,YACjB,SAAS,IAAI;CAGf,KAAK,MAAM,CAAC,KAAK,cAAc,WAAW,GAAG;EAC3C,MAAM,iBACJ,CAAC,CAAC,UAAU,WAAW,WAAW,IAAI,UAAU,OAAO;EACzD,MAAM,eACJ,CAAC,CAAC,UAAU,oBACZ,UAAU,iBAAiB,MAAM,MAAM,WAAW,IAAI,CAAC,CAAC;EAE1D,IAAI,cAAc,YAAY,kBAAkB,cAAc;GAC5D,UAAU,mBAAmB,KAAA;GAC7B,UAAU,kBAAkB,KAAA;GAC5B,UAAU,mBAAmB,KAAA;GAC7B,SAAS,GAAG;GACZ,SAAS,UAAU,IAAI;GACvB,SAAS,UAAU,aAAa;EAClC;CACF;CAEA,KAAK,MAAM,QAAQ,eACjB,MAAM,OAAO,IAAI;AAErB;AAEA,SAAS,sCACP,UACA,WACA,aACM;CACN,MAAM,iBAAiB,UAAU,mBAAmB,CAAC;CACrD,MAAM,oBACJ,UAAU,QAAQ,aAClB,eAAe,aACf,SAAS,QAAQ,aACjB,SAAS,OAAO,aAChB,mBAAmB,SAAS,WAAW;CAEzC,SAAS,SAAS;EAChB,GAAG;EACH,GAAG,SAAS;EACZ,WAAW;CACb;CAEA,IAAI,UAAU,QACZ,KAAK,MAAM,CAAC,WAAW,OAAO,OAAO,QAAQ,UAAU,MAAM,GAAG;EAC9D,IAAI,CAAC,SAAS,OAAO,IAAI,SAAS,GAAG;GACnC,SAAS,OAAO,IAAI,WAAW,wBAAwB,EAAE,CAAC;GAC1D;EACF;EAEA,MAAM,gBAAgB,SAAS,OAAO,IAAI,SAAS;EACnD,IAAI,CAAC,eACH;EAGF,SAAS,OAAO,IAAI,WAAW,mBAAmB,eAAe,EAAE,CAAC;CACtE;CAGF,MAAM,qBAAqB,4BACzB,SAAS,OAAO,YAClB;CACA,IAAI,oBAAoB;EACtB,SAAS,qBAAqB;EAC9B,wBAAwB,SAAS,QAAQ,kBAAkB;CAC7D;CAEA,IAAI,UAAU,SACZ,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,UAAU,OAAO,GACpE,SAAS,QAAQ,IAAI,YAAY,SAAS;CAI9C,IAAI,UAAU,QAAQ;EACpB,MAAM,iBAAmC;GACvC,KAAK,UAAU,OAAO,OAAO;GAC7B,SACE,UAAU,OAAO,SAAS,KAAK,SAA0B;IACvD,MAAM,IAAI;IACV,SAAS,IAAI,WAAW,CAAC;IACzB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;GAChB,EAAE,KAAK,CAAC;GACV,UAAU,CAAC;GACX,WAAW,UAAU,OAAO,aAAa;GACzC,SAAS,2BAA2B,UAAU,OAAO,OAAO;GAC5D,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS,UAAU,OAAO,WAAW;GACxB;EACf;EAEA,sBAAsB,eAAe,SAAS,SAAS,OAAO,QAAQ,CAAC;EAEvE,SAAS,SAAS;GAChB,GAAI,SAAS,UAAU,CAAC;GACxB,GAAG;GACH,WAAW,eAAe;GAC1B,KAAK,eAAe,OAAO,SAAS,QAAQ,OAAO;GACnD,SAAS;IACP,GAAI,SAAS,QAAQ,WAAW,CAAC;IACjC,GAAI,eAAe,WAAW,CAAC;GACjC;GACA,SAAS,sBACP,SAAS,QAAQ,SACjB,eAAe,OACjB;GACA,aAAa,eAAe,SAAS,QAAQ;EAC/C;CACF,OAAO,IAAI,CAAC,SAAS,QACnB,SAAS,SAAS;EAChB,KAAK;EACL,SAAS,CAAC;EACV,UAAU,CAAC;EACX,WAAW;EACX,SAAS,CAAC;EACV,aAAa,CAAC;EACd,cAAc,CAAC;EACf,SAAS;EACI;CACf;MACK;EACL,SAAS,OAAO,YAAY;EAC5B,SAAS,OAAO,cAAc,eAAe,SAAS,OAAO;CAC/D;CAEA,IAAI,UAAU,oBAAoB,KAAA,GAAW;EAC3C,SAAS,kBAAkB,UAAU;EACrC,SAAS,aAAa,KAAA;CACxB,OAAO,IAAI,CAAC,SAAS,mBAAmB,CAAC,SAAS,YAChD,SAAS,aAAa,kBAAkB,SAAS,MAAM,SAAS,MAAM;CAGxE,IAAI,aAAa;EACf,SAAS,cAAc;EACvB,SAAS,gBAAgB,oBACvB,aACA,SAAS,IACX;CACF,OAAO,IAAI,CAAC,SAAS,eAAe,UAAU,aAC5C,SAAS,cAAc,UAAU;CAGnC,IAAI,UAAU,SACZ,SAAS,UAAU,cACf,mBAAmB,UAAU,SAAS,WAAW,IACjD,UAAU;CAGhB,IAAI,CAAC,SAAS,kBAAkB,UAAU,UACxC,SAAS,iBAAiB,UAAU;CAGtC,SAAS,aACP,UAAU,cAAc,eAAe,cAAc,SAAS;CAChE,6BAA6B,QAAQ;AACvC;AAEA,SAAgB,qBACd,MACA,WACA,aACM;CAIN,MAAM,kBAAkB,UAAU,aAAa;CAC/C,MAAM,qBAAqB,cACvB,oBAAoB,aAAa,eAAe,IAChD,UAAU;CACd,MAAM,kBAAmB,sBAAsB;CAgB/C,MAAM,oBAAoB,sBAAsB,eAAe;CAC/D,IAAI,mBAAmB;EACrB,MAAM,WAAW,WAAW,CAAC,CAAC,IAAI,iBAAiB;EACnD,IAAI;OACc,6BAA6B;IAC3C;IACA;IACA;IACA;IACA,aAAa;IACb;IACA,WAAW;GACb,CACI,MAAY,UAAU;EAAA;CAK9B;CAEA,IAAI,WAAW,CAAC,CAAC,IAAI,eAAe,GAAG;EACrC,MAAM,WAAW,WAAW,CAAC,CAAC,IAAI,eAAe;EACjD,IAAI,CAAC,UAAU;EAUf,IATgB,6BAA6B;GAC3C;GACA;GACA;GACA;GACA,aAAa;GACb;GACA,WAAW;EACb,CACI,MAAY,UAAU;CAC5B;CAQA,MAAM,mBACJ,UAAU,WAAW,cACjB,mBAAmB,UAAU,SAAS,WAAW,IACjD,UAAU;CAQhB,MAAM,kBAAkB,cAAc,WAAW;EAC/C,OAAgB,kBAAkB;CACpC;CACA,OAAO,eAAe,iBAAiB,QAAQ,EAAE,OAAO,gBAAgB,CAAC;CAGzE,MAAM,yBAAS,IAAI,IAA6B;CAChD,MAAM,aAAa,mBAAmB,CAAC,CAAC,IAAI,eAAe;CAC3D,IAAI,UAAU,QACZ,KAAK,MAAM,CAAC,WAAW,OAAO,OAAO,QAAQ,UAAU,MAAM,GAC3D,OAAO,IAAI,WAAW,wBAAwB,EAAE,CAAC;CAKrD,MAAM,0BAAU,IAAI,IAA8B;CAClD,IAAI,UAAU,SACZ,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,UAAU,OAAO,GACpE,QAAQ,IAAI,YAAY,SAAS;CAKrC,MAAM,SAAS,UAAU,mBAAmB,CAAC;CAC7C,MAAM,YAAY,OAAO,aAAa,mBAAmB,eAAe;CAIxE,IAAI;CACJ,IAAI,UAAU,QAAQ;EAGpB,SAAS;GACP,KAAK,UAAU,OAAO;GACtB,SACE,UAAU,OAAO,SAAS,KAAK,SAA0B;IACvD,MAAM,IAAI;IACV,SAAS,IAAI,WAAW,CAAC;IACzB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;GAChB,EAAE,KAAK,CAAC;GACV,UAAU,CAAC;GACX,WAAW,UAAU,OAAO;GAC5B,SAAS,2BAA2B,UAAU,OAAO,OAAO;GAC5D,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS,UAAU,OAAO,WAAW;GACxB;EACf;EACA,sBAAsB,OAAO,SAAS,OAAO,QAAQ,CAAC;EACtD,sBAAsB,OAAO,SAAS,YAAY,QAAQ,CAAC;EAC3D,WACE,8CAA8C,KAAK,IAAI,OAAO,KAAK,UAAU,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,UAC5G;CACF,OAEE,SAAS;EACP,KAAK;EACL,SAAS,CAAC;EACV,UAAU,CAAC;EACX;EACA,SAAS,CAAC;EACV,aAAa,CAAC;EACd,cAAc,CAAC;EACf,SAAS;EACI;CACf;CAKF,MAAM,kBAAkB,UAAU;CAClC,IAAI;CAIJ,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,GAAG;EACpD,aAAa,kBAAkB,MAAM,MAAM;EAC3C,WACE,wCAAwC,KAAK,aAAa,WAAW,OAAO,YAC9E;CACF,OACE,WACE,oBAAoB,gBAAgB,OAAO,qCAAqC,MAClF;CAKF,MAAM,gBAAgB;CAGtB,MAAM,aACJ,UAAU,cAAc,OAAO,cAAc;CAW/C,WAAW,CAAC,CAAC,IAAI,iBAAiB;EAChC,MAAM;EACN;EACA,aAAa;EACb;EACA;EACA;EACA;EACA;EACA;EACA,OAAO,UAAU;EAGjB,YAAY,UAAU,cAAc,oBAAoB,eAAe;EACvE;EACA,gBAAgB,UAAU;EAC1B,SAAS;EACT,gBAAgB,UAAU;EAC1B;CACF,CAAC;CAID,qBAAqB,iBAAiB,aAAa;CAEnD,WACE,iBAAiB,gBAAgB,uBAAuB,gBAAgB,IAAI,OAAO,KAAK,WAAW,QAAQ,KAAK,UAClH;CAUA,MAAM,aAAa,UAAU;CAC7B,IAAI,cAAc,CAAC,qBAAqB,CAAC,CAAC,IAAI,UAAU,GAAG;EAEzD,qBAAqB,CAAC,CAAC,IAAI,UAAU;EAErC,WACE,wDAAwD,YAC1D;EAGA,MAAM,WAAW,wBAAwB,UAAU;EAInD,KAAK,MAAM,WAAW,UAEpB,IAAI,CAAC,wBAAwB,QAAQ,SAAS,GAAG;GAE/C,IACE,QAAQ,eACR,eACA,QAAQ,gBAAgB,aACxB;IACA,WACE,mCAAmC,QAAQ,UAAU,sBAAsB,aAC7E;IACA;GACF;GAEA,WACE,wCAAwC,QAAQ,UAAU,mBAAmB,YAC/E;GACA,qBACE,QAAQ,WACR,QAAQ,OACR,QAAQ,WACV;EACF;CAEJ;AACF"}
1
+ {"version":3,"file":"class-registration.js","names":[],"sources":["../../src/registry/class-registration.ts"],"sourcesContent":["/**\n * Class registration module for the SMRT ObjectRegistry.\n *\n * Handles registering classes, collections, and manifest entries.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n */\n\nimport { ConfigurationError } from '../errors';\nimport {\n discoverManifestSync,\n discoverSTISiblingsSync,\n getPackageName,\n lookupInManifest,\n} from '../manifest/manifest-loader.js';\nimport { cloneManifestSchemaColumns } from '../manifest/store.js';\nimport { SmrtObject } from '../object';\nimport type {\n FieldDefinition,\n FieldMeta,\n MethodDefinition,\n QualifiedClassName,\n SmartObjectDefinition,\n SmrtVisibility,\n} from '../scanner/types.js';\nimport type {\n ColumnDefinition,\n IndexDefinition,\n SchemaDefinition,\n} from '../schema/types.js';\nimport { tableNameFromClass, toSnakeCase } from '../utils';\nimport {\n createQualifiedName,\n isQualifiedName,\n} from '../utils/qualified-names.js';\nimport {\n type CollisionInputs,\n decideCollisionPolicy,\n type MatchKind,\n} from './collision-policy.js';\nimport { buildInheritanceChain } from './inheritance-resolver';\nimport {\n createFieldFromManifest,\n mergeManifestField,\n} from './manifest-field-merge.js';\nimport {\n findClass,\n getCanonicalClassName,\n hasClassCaseInsensitive,\n qualifyExtendsName,\n} from './name-resolver';\nimport {\n getClasses,\n getCollections,\n getConstructorIndex,\n getFieldDecorators,\n getInheritanceCache,\n getSourceFileFromStack,\n getStiSiblingsLoaded,\n verboseLog,\n} from './shared-state';\nimport type {\n RegisteredClass,\n RegisteredField,\n SmartObjectConfig,\n ValidatorFunction,\n} from './types.js';\nimport { compileValidators } from './validator';\n\n/**\n * Compute the pluralized endpoint/collection name from a class name, using\n * the SAME simple inflection rules as the scanner's manifest adapter\n * (`packages/scanner/src/manifest-adapter.ts`). Used only as a fallback when\n * a registered class has no manifest-provided `collection` (e.g. inline test\n * classes registered purely via the decorator path). Keep in sync with the\n * scanner; the manifest value is always preferred when present. (smrt#1311.)\n *\n * Currency -> currencies (y -> ies)\n * CompanyResearch -> companyresearches (ch -> ches)\n * SourceCrawl -> sourcecrawls (+ s)\n * EmploymentPerson -> employmentpersons (naive + s, NOT \"people\")\n */\nfunction pluralizeCollection(className: string): string {\n const lower = className.toLowerCase();\n if (lower.endsWith('y')) return `${lower.slice(0, -1)}ies`;\n if (lower.endsWith('s') || lower.endsWith('x') || lower.endsWith('z')) {\n return `${lower}es`;\n }\n if (lower.endsWith('ch') || lower.endsWith('sh')) return `${lower}es`;\n return `${lower}s`;\n}\n\n/**\n * Shared bundled-context detector. Source files in these output\n * directories come from a bundler (Vite library mode, webpack, Next.js,\n * Nuxt, svelte-kit) that can duplicate module code across chunks.\n */\nfunction isBundledOutputPath(sourceFile: string | undefined): boolean {\n if (!sourceFile) return false;\n return (\n sourceFile.includes('.svelte-kit/output/') ||\n sourceFile.includes('/dist/') ||\n sourceFile.includes('/build/') ||\n sourceFile.includes('.next/') ||\n sourceFile.includes('.nuxt/')\n );\n}\n\n/**\n * Build `CollisionInputs` for the decorator-origin path (`register()`).\n * Manifest-origin inputs are built separately inside `registerFromManifest`.\n */\nfunction buildDecoratorCollisionInputs(args: {\n ctor: typeof SmrtObject;\n name: string;\n newPackageName: string | undefined;\n newSourceFile: string | undefined;\n newInBundledContext: boolean;\n existing: RegisteredClass;\n existingKey: string;\n matchKind: MatchKind;\n}): CollisionInputs {\n const {\n ctor,\n name,\n newPackageName,\n newSourceFile,\n newInBundledContext,\n existing,\n existingKey,\n matchKind,\n } = args;\n\n let newExtendsExisting = false;\n let existingExtendsNew = false;\n try {\n newExtendsExisting = ctor.prototype instanceof existing.constructor;\n existingExtendsNew = existing.constructor.prototype instanceof ctor;\n } catch {\n // `instanceof` can throw for non-function constructors. Keep both false.\n }\n\n const bothSourceFilesKnown = !!(newSourceFile && existing.sourceFilePath);\n const sameSourceFile =\n bothSourceFilesKnown && newSourceFile === existing.sourceFilePath;\n const existingIsPackageQualified = !!existing.packageName?.startsWith('@');\n const samePackage =\n !!newPackageName &&\n !!existing.packageName &&\n newPackageName === existing.packageName;\n const hasNewQualifiedKey = !!newPackageName;\n const existingKeyIsQualified = isQualifiedName(existingKey);\n\n return {\n origin: 'decorator',\n matchKind,\n sameConstructor: existing.constructor === ctor,\n sameSourceFile,\n bothSourceFilesKnown,\n existingIsManifestStub:\n (existing.constructor as { _isManifestStub?: boolean })\n ._isManifestStub === true,\n newInBundledContext,\n newExtendsExisting,\n existingExtendsNew,\n samePackage,\n existingIsPackageQualified,\n sameName: name === existing.name,\n hasNewQualifiedKey,\n existingKeyIsQualified,\n existingHasAnyPackage: !!existing.packageName,\n // Manifest-only fields; filled with safe defaults for decorator origin.\n hasManifestContent: false,\n registrationKeyDiffersFromExistingKey: false,\n existingHasNoPackage: !existing.packageName,\n };\n}\n\n/**\n * Execute the collision policy for `register()`. Returns `true` if the\n * policy resolved the collision (caller should return from `register`);\n * `false` means no policy match (the decision table is exhaustive so\n * that never actually happens — the default row throws).\n *\n * All state mutation (`upsertExistingEntry`, verboseLog) stays in this\n * caller-side helper; the policy function itself is pure.\n */\nfunction applyRegisterCollisionPolicy(args: {\n ctor: typeof SmrtObject;\n name: string;\n newPackageName: string | undefined;\n newSourceFile: string | undefined;\n newInBundledContext: boolean;\n existing: RegisteredClass;\n existingKey: string;\n matchKind: MatchKind;\n upsertExistingEntry: (\n existingKey: string,\n existing: RegisteredClass,\n ) => string;\n}): boolean {\n const inputs = buildDecoratorCollisionInputs(args);\n const decision = decideCollisionPolicy(inputs);\n\n switch (decision.policy) {\n case 'accept':\n case 'replace': {\n // Both map to the same operation for the decorator path:\n // update the existing slot's constructor + metadata. `replace`\n // only differs in verboseLog flavor so a reviewer reading the log\n // can tell which scenario fired.\n const newKey = args.upsertExistingEntry(args.existingKey, args.existing);\n if (\n decision.scenario === 'sti-child-wins' ||\n decision.scenario === 'manifest-stub-replacement'\n ) {\n verboseLog(\n `[registry] ${decision.scenario}: '${args.name}' → key='${newKey}' (${decision.reason})`,\n );\n }\n return true;\n }\n case 'skip':\n verboseLog(\n `[registry] ${decision.scenario}: skipping '${args.name}' (${decision.reason})`,\n );\n return true;\n case 'throw': {\n const matchSuffix =\n args.matchKind === 'case-insensitive'\n ? ` (case-insensitive match with \"${args.existingKey}\")`\n : '';\n throw new Error(\n `SMRT Class Name Collision: \"${args.name}\"${matchSuffix}\\n\\n` +\n `A class with this name is already registered, but with a different constructor.\\n` +\n `This usually happens when:\\n` +\n ` 1. Multiple test files define classes with the same name\\n` +\n ` 2. Different packages export classes with the same name\\n\\n` +\n `The collision will cause the wrong field definitions to be used,\\n` +\n `leading to properties not being initialized correctly.\\n\\n` +\n `To fix:\\n` +\n ` - Use unique class names (e.g., ${args.name}_UniqueId)\\n` +\n ` - Or use @smrt({ name: 'unique_name' }) to override the registration name`,\n );\n }\n case 'merge-manifest':\n case 'coexist-qualified':\n // These policies are only reachable from the manifest path; if we\n // somehow get here from `register()`, something is miswired.\n throw new Error(\n `decideCollisionPolicy returned '${decision.policy}' for decorator origin (scenario: ${decision.scenario}). This is a bug.`,\n );\n }\n}\n\n/**\n * Build `CollisionInputs` for the manifest-origin path\n * (`registerFromManifest`). Manifest entries have string-based inheritance\n * (`objectDef.extends`) rather than prototype-based, and no real\n * constructor to compare — `sameConstructor` is always false here.\n */\nfunction buildManifestCollisionInputs(args: {\n name: string;\n objectDef: SmartObjectDefinition;\n packageName: string | undefined;\n existing: RegisteredClass;\n existingKey: string;\n registrationKey: string;\n matchKind: MatchKind;\n}): CollisionInputs {\n const {\n name,\n objectDef,\n packageName,\n existing,\n existingKey,\n registrationKey,\n matchKind,\n } = args;\n const newClassName = objectDef.className || name;\n const newExtends = objectDef.extends;\n\n // When an incoming manifest's `extends` points at the existing entry,\n // OR the existing entry's `extends` points at the incoming class, we\n // have an STI inheritance relationship. Each side checks:\n // - the simple name of the other\n // - the key under which the other is registered\n // - a case-insensitive fallback\n //\n // The `existingKey` comparison is load-bearing for cross-package STI:\n // `existing.extends` is stored post-`qualifyExtendsName()`, so it may\n // be the fully-qualified parent key like `@pkg:Parent`. Without this\n // comparison, `existingExtendsNew` falsely reads as false and the\n // arriving parent manifest would be registered as a duplicate instead\n // of skipped. See the deep-review report on #1140 for the fix rationale.\n const newExtendsExisting = !!(\n newExtends &&\n (newExtends === existing.name ||\n newExtends === existingKey ||\n newExtends.toLowerCase() === existing.name.toLowerCase())\n );\n const existingExtendsNew = !!(\n existing.extends &&\n (existing.extends === newClassName ||\n existing.extends === name ||\n existing.extends === registrationKey ||\n existing.extends.toLowerCase() === newClassName.toLowerCase())\n );\n\n const newSourceFile = objectDef.filePath;\n const bothSourceFilesKnown = !!(newSourceFile && existing.sourceFilePath);\n const sameSourceFile =\n bothSourceFilesKnown && newSourceFile === existing.sourceFilePath;\n const existingIsPackageQualified = !!existing.packageName?.startsWith('@');\n const samePackage =\n !!packageName &&\n !!existing.packageName &&\n packageName === existing.packageName;\n\n return {\n origin: 'manifest',\n matchKind,\n sameConstructor: false,\n sameSourceFile,\n bothSourceFilesKnown,\n existingIsManifestStub:\n (existing.constructor as { _isManifestStub?: boolean })\n ._isManifestStub === true,\n newInBundledContext: false,\n newExtendsExisting,\n existingExtendsNew,\n samePackage,\n existingIsPackageQualified,\n sameName: newClassName === existing.name,\n hasNewQualifiedKey: isQualifiedName(registrationKey),\n existingKeyIsQualified: isQualifiedName(existingKey),\n existingHasAnyPackage: !!existing.packageName,\n hasManifestContent: !!(\n objectDef.fields ||\n objectDef.methods ||\n objectDef.schema ||\n objectDef.decoratorConfig\n ),\n registrationKeyDiffersFromExistingKey: registrationKey !== existingKey,\n existingHasNoPackage: !existing.packageName,\n };\n // Note: manifest-origin sets hasNewQualifiedKey from the final\n // registrationKey (not packageName) because registerFromManifest accepts\n // an objectDef.qualifiedName fallback when packageName is omitted —\n // deriving from packageName alone would miss that case and misroute the\n // coexistence rows. See PR #1140 review (Copilot P1 at line 287).\n}\n\n/**\n * Execute the collision policy for `registerFromManifest()`. Returns\n * `'return'` when the policy fully resolves the collision (caller should\n * early-return from `registerFromManifest`) or `'continue'` when the new\n * registration should proceed to the fresh-entry code below the collision\n * block (either a child-wins delete-then-register or a different-package\n * coexist-qualified scenario).\n *\n * Implements the `merge-manifest` contract end-to-end: fold fields via\n * `mergeManifestIntoExistingRegistration`, then conditionally alias the\n * existing entry under `registrationKey` when it differs from the\n * canonical key — both steps preserve `issue-951:144-200`.\n */\nfunction applyManifestCollisionPolicy(args: {\n name: string;\n objectDef: SmartObjectDefinition;\n packageName: string | undefined;\n existing: RegisteredClass;\n existingKey: string;\n registrationKey: string;\n matchKind: MatchKind;\n}): 'return' | 'continue' {\n const inputs = buildManifestCollisionInputs(args);\n const decision = decideCollisionPolicy(inputs);\n\n switch (decision.policy) {\n case 'skip':\n verboseLog(\n `[registry] ${decision.scenario}: skipping manifest '${args.name}' (${decision.reason})`,\n );\n return 'return';\n case 'replace':\n // STI child-wins: delete existing so the new entry can register fresh.\n getClasses().delete(args.existingKey);\n verboseLog(\n `[registry] ${decision.scenario}: '${args.name}' replaces '${args.existingKey}' (${decision.reason})`,\n );\n return 'continue';\n case 'merge-manifest': {\n mergeManifestIntoExistingRegistration(\n args.existing,\n args.objectDef,\n args.packageName,\n );\n if (args.registrationKey !== args.existingKey) {\n const classes = getClasses();\n classes.set(args.registrationKey, args.existing);\n getConstructorIndex().set(\n args.existing.constructor,\n args.registrationKey,\n );\n }\n return 'return';\n }\n case 'coexist-qualified':\n // Both registrations live under their qualified keys; caller falls\n // through to register the new one fresh.\n return 'continue';\n case 'accept':\n // Manifest origin never returns 'accept' (that's a decorator-path\n // policy). Treat defensively as return-no-op.\n return 'return';\n case 'throw':\n // Manifest collisions default to skip, never throw — this branch is\n // unreachable from the manifest origin. Guard against future drift.\n throw new Error(\n `decideCollisionPolicy returned 'throw' for manifest origin (scenario: ${decision.scenario}). This is a bug.`,\n );\n }\n}\n\nfunction resolveTableName(\n ctor: typeof SmrtObject,\n name: string,\n config: SmartObjectConfig,\n): string {\n const manifestEntry = config._manifest\n ? lookupInManifest(config._manifest, name)\n : discoverManifestSync(name);\n\n return (\n manifestEntry?.schema?.tableName ||\n manifestEntry?.decoratorConfig?.tableName ||\n config.tableName ||\n tableNameFromClass(ctor)\n );\n}\n\nfunction setSmrtTableName(ctor: typeof SmrtObject, tableName: string): void {\n const existing = Object.getOwnPropertyDescriptor(ctor, 'SMRT_TABLE_NAME');\n if (existing?.value === tableName) {\n return;\n }\n\n Object.defineProperty(ctor, 'SMRT_TABLE_NAME', {\n value: tableName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n}\n\n/**\n * Attach the registry's qualified name to the constructor as a static\n * property. Mirrors the `SMRT_TABLE_NAME` pattern.\n *\n * Survives:\n * - Minification (the property is set on the constructor itself).\n * - HMR / module duplication / federated-module boundaries where the\n * `constructorIndex` WeakMap holds an older constructor identity than\n * the one a caller has in hand.\n *\n * Acts as a belt-and-suspenders fallback for runtime code that has a\n * constructor reference and wants its registry identity without going\n * through a Map lookup (e.g. `SmrtHierarchical._hierarchyCollection`).\n */\nfunction setSmrtQualifiedName(\n ctor: typeof SmrtObject,\n qualifiedName: string | undefined,\n): void {\n if (!qualifiedName) return;\n const existing = Object.getOwnPropertyDescriptor(ctor, '_smrtQualifiedName');\n if (existing?.value === qualifiedName) {\n return;\n }\n\n Object.defineProperty(ctor, '_smrtQualifiedName', {\n value: qualifiedName,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n}\n\nexport function register(\n ctor: typeof SmrtObject,\n config: SmartObjectConfig = {},\n): void {\n const name = config.name || ctor.name;\n const explicitPackageName = config.packageName;\n\n function upsertExistingEntry(\n existingKey: string,\n existing: RegisteredClass,\n ): string {\n const nextPackageName = explicitPackageName || existing.packageName;\n const nextKey = nextPackageName\n ? createQualifiedName(nextPackageName, name)\n : name;\n // Capture pre-mutation identity so descendants whose `extends` still\n // references the old name/qualifiedName are caught by the post-mutation\n // invalidation sweep (#1139 Gap 2).\n const previousIdentity = {\n name: existing.name,\n qualifiedName: existing.qualifiedName as string | undefined,\n };\n\n existing.name = name;\n existing.packageName = nextPackageName;\n existing.qualifiedName = nextPackageName\n ? (createQualifiedName(nextPackageName, name) as QualifiedClassName)\n : undefined;\n const nextTableName = resolveTableName(ctor, name, {\n ...existing.config,\n ...config,\n });\n existing.config = {\n ...existing.config,\n ...config,\n tableName: nextTableName,\n };\n if (!existing.schema) {\n existing.schema = {\n ddl: '',\n indexes: [],\n triggers: [],\n tableName: nextTableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n existing.schema.tableName = nextTableName;\n existing.constructor = ctor;\n setSmrtTableName(ctor, nextTableName);\n setSmrtQualifiedName(ctor, existing.qualifiedName);\n\n if (existingKey !== nextKey) {\n const classes = getClasses();\n const existingAtNextKey = classes.get(nextKey);\n\n if (existingAtNextKey && existingAtNextKey !== existing) {\n throw new ConfigurationError(\n `Class registry collision while promoting \"${existingKey}\" to \"${nextKey}\". ` +\n 'A different class is already registered under the target key.',\n 'CONFIG_REGISTRY_PROMOTION_COLLISION',\n {\n existingKey,\n nextKey,\n className: name,\n },\n );\n }\n\n classes.delete(existingKey);\n classes.set(nextKey, existing);\n }\n\n getConstructorIndex().set(ctor, nextKey);\n\n // Release C follow-up #1139 Gap 2: we just mutated `existing.name`,\n // `existing.packageName`, `existing.qualifiedName`, `existing.config`,\n // `existing.constructor`, and `existing.schema.tableName`. Any\n // descendant whose cached `inheritedFields` was computed against the\n // prior identity now carries stale data. Invalidate `existing` and\n // every descendant whose `extends` matches the pre- or post-mutation\n // identity so the next field read re-merges from the current state.\n invalidateInheritanceEntries(existing, previousIdentity);\n\n return nextKey;\n }\n\n // Release C (#1134): collision resolution for both exact-match and\n // case-insensitive paths now routes through decideCollisionPolicy, which\n // consolidates the 11-branch if/else tree from pre-C.\n const newSourceFile = getSourceFileFromStack();\n const newPackageName =\n explicitPackageName || getPackageName(ctor, true) || undefined;\n const newInBundledContext = isBundledOutputPath(newSourceFile);\n\n // 1. Exact-match check (existingKey === name)\n if (getClasses().has(name)) {\n const existing = getClasses().get(name);\n if (!existing) {\n throw new Error(\n `Registry inconsistency: ${name} exists in classes Map but get() returned undefined`,\n );\n }\n\n const handled = applyRegisterCollisionPolicy({\n ctor,\n name,\n newPackageName,\n newSourceFile,\n newInBundledContext,\n existing,\n existingKey: name,\n matchKind: 'exact-key',\n upsertExistingEntry,\n });\n if (handled) return;\n }\n\n // 2. Case-insensitive check for manifest stubs (Issue #531, #847) and\n // other same-name-different-case collisions.\n const lowerName = name.toLowerCase();\n for (const [existingKey, existing] of getClasses().entries()) {\n const keyMatches = existingKey.toLowerCase() === lowerName;\n const nameMatches = existing.name?.toLowerCase() === lowerName;\n if (!(keyMatches || nameMatches) || existingKey === name) continue;\n\n const handled = applyRegisterCollisionPolicy({\n ctor,\n name,\n newPackageName,\n newSourceFile,\n newInBundledContext,\n existing,\n existingKey,\n matchKind: 'case-insensitive',\n upsertExistingEntry,\n });\n if (handled) return;\n }\n\n // CRITICAL: Capture package name NOW, while stack trace still shows external package\n // This is called from the @smrt() decorator during import, so the stack trace\n // includes the external package file path. Later calls won't have this context.\n // Skip registry check to avoid circular dependency - class isn't registered yet!\n // This solves issue #159 where external package manifests couldn't be loaded.\n const packageNameFromStack =\n explicitPackageName || getPackageName(ctor, true) || undefined;\n\n // Capture source file path for collision detection during module re-evaluation\n // (Issue #555: Test isolation - class name collision during vitest collection)\n const sourceFilePath = getSourceFileFromStack();\n\n // Get field definitions from manifest\n // Priority order (Issue #270 Phase 1 - synchronous manifest loading):\n // 1. Explicitly provided manifest (_manifest parameter)\n // 2. Test manifests (for test classes)\n // 3. Static manifests (for core framework classes)\n // 4. Cached external manifests (if already loaded)\n // For external packages not yet loaded, manifest discovery happens lazily during schema generation\n // Issue #713: Use lookupInManifest for qualified name support\n let manifestEntry: ReturnType<typeof lookupInManifest> | undefined;\n if (config._manifest) {\n manifestEntry = lookupInManifest(config._manifest, name);\n }\n if (!manifestEntry) {\n manifestEntry = discoverManifestSync(name);\n }\n const fields = new Map<string, RegisteredField>();\n const methods = new Map<string, MethodDefinition>();\n let packageName: string | undefined;\n\n verboseLog(\n `[registry] Registering ${name}: manifestEntry =`,\n manifestEntry ? 'found' : 'not found',\n );\n if (manifestEntry?.fields) {\n verboseLog(\n `[registry] Manifest has ${Object.keys(manifestEntry.fields).length} fields:`,\n Object.keys(manifestEntry.fields),\n );\n }\n\n if (manifestEntry?.fields) {\n // Use manifest fields (from build-time AST scanning)\n // Store field definitions as plain objects with nested options\n for (const [fieldName, fieldDef] of Object.entries(\n manifestEntry.fields,\n ) as [string, FieldDefinition][]) {\n // Build options object, only including defined values\n const options: Record<string, unknown> = { ...fieldDef._meta };\n if (fieldDef.required !== undefined) options.required = fieldDef.required;\n if (fieldDef.default !== undefined) options.default = fieldDef.default;\n if (fieldDef.description !== undefined)\n options.description = fieldDef.description;\n if (fieldDef.transient !== undefined)\n options.transient = fieldDef.transient;\n\n // Store field definition as plain object maintaining Field-like structure\n const field: RegisteredField = {\n type: fieldDef.type,\n };\n\n // Only include options if not empty\n if (Object.keys(options).length > 0) {\n field._meta = options;\n }\n\n // Preserve top-level flags from manifest\n if (fieldDef.transient !== undefined) {\n field.transient = fieldDef.transient;\n }\n if (fieldDef.required !== undefined) {\n field.required = fieldDef.required;\n }\n if (fieldDef.sensitive !== undefined) {\n field.sensitive = fieldDef.sensitive;\n }\n if (fieldDef.readonly !== undefined) {\n field.readonly = fieldDef.readonly;\n }\n if (fieldDef.readPermission !== undefined) {\n field.readPermission = fieldDef.readPermission;\n }\n\n // Hoist related to top level for relationship fields\n // Check both fieldDef.related (new manifests) and _meta.related (old manifests)\n if (fieldDef.related !== undefined) {\n field.related = fieldDef.related;\n } else if (options.related !== undefined) {\n // `related` carries the related class name (always a string at runtime).\n field.related = options.related as string;\n delete field._meta?.related;\n }\n\n fields.set(fieldName, field);\n }\n\n verboseLog(\n `[registry] ✅ Loaded ${fields.size} fields for ${name} from manifest`,\n );\n\n // Use packageName from manifest if available, otherwise from stack trace\n // Priority: explicit manifest > manifestEntry > stack trace\n packageName =\n explicitPackageName ||\n config._manifest?.packageName ||\n manifestEntry.packageName ||\n packageNameFromStack;\n } else {\n // No manifest found yet - use package name from stack trace\n // This will be used later by ensureManifestLoaded() to load the external manifest\n verboseLog(\n `[registry] ⚠️ No manifest entry for ${name} - fields will be loaded later`,\n );\n packageName = packageNameFromStack;\n }\n\n // Apply decorator metadata to override/extend manifest fields\n // Decorators take priority over AST-scanned types (Issue #316)\n const decorators = getFieldDecorators().get(name);\n if (decorators && decorators.size > 0) {\n verboseLog(\n `[registry] Applying ${decorators.size} field decorators for ${name}`,\n );\n\n for (const [fieldName, decoratorOptions] of decorators) {\n // Typed read-view of the raw `Record<string, unknown>` decorator bag so\n // the member reads below narrow to the `RegisteredField` member types\n // they feed (instead of `unknown`). Same runtime object, type-only view.\n const opts: DecoratorFieldOptions = decoratorOptions;\n const existingField = fields.get(fieldName);\n\n if (existingField) {\n // Merge decorator options with manifest field\n // Decorator type takes priority over AST-scanned type\n const mergedMeta: FieldMeta = {\n ...existingField._meta,\n ...decoratorOptions,\n };\n const mergedField: RegisteredField = {\n type: opts.type || existingField.type,\n _meta: mergedMeta,\n };\n\n // Remove 'type' from _meta if it was moved to top level\n if (mergedMeta.type) {\n delete mergedMeta.type;\n }\n\n // Preserve top-level flags (transient, required, etc.)\n if (opts.transient !== undefined) {\n mergedField.transient = opts.transient;\n } else if (existingField.transient !== undefined) {\n mergedField.transient = existingField.transient;\n }\n if (opts.sensitive !== undefined) {\n mergedField.sensitive = opts.sensitive;\n } else if (existingField.sensitive !== undefined) {\n mergedField.sensitive = existingField.sensitive;\n }\n if (opts.readonly !== undefined) {\n mergedField.readonly = opts.readonly;\n } else if (existingField.readonly !== undefined) {\n mergedField.readonly = existingField.readonly;\n }\n if (typeof opts.readPermission === 'string') {\n mergedField.readPermission = opts.readPermission;\n } else if (typeof existingField.readPermission === 'string') {\n mergedField.readPermission = existingField.readPermission;\n }\n\n // Handle required flag: nullable fields should not be required\n if (opts.nullable === true) {\n // Nullable explicitly set to true means field is NOT required\n mergedField.required = false;\n mergedMeta.required = false;\n } else if (opts.required !== undefined) {\n mergedField.required = opts.required;\n } else if (existingField.required !== undefined) {\n mergedField.required = existingField.required;\n }\n\n // Hoist related to top level for relationship fields\n if (opts.related !== undefined) {\n mergedField.related = opts.related;\n delete mergedField._meta?.related;\n } else if (existingField.related !== undefined) {\n mergedField.related = existingField.related;\n }\n\n fields.set(fieldName, mergedField);\n verboseLog(\n `[registry] ✅ Merged decorator for ${fieldName}: type=${mergedField.type}`,\n );\n } else {\n // Decorator for field not in manifest - add it\n const newMeta: FieldMeta = decoratorOptions;\n const newField: RegisteredField = {\n type: opts.type || 'text',\n _meta: newMeta,\n };\n\n // Set top-level flags from decorator options\n if (opts.transient !== undefined) {\n newField.transient = opts.transient;\n }\n if (opts.sensitive !== undefined) {\n newField.sensitive = opts.sensitive;\n }\n if (opts.readonly !== undefined) {\n newField.readonly = opts.readonly;\n }\n if (typeof opts.readPermission === 'string') {\n newField.readPermission = opts.readPermission;\n }\n // Handle required flag: nullable fields should not be required\n if (opts.nullable === true) {\n newField.required = false;\n newMeta.required = false;\n } else if (opts.required !== undefined) {\n newField.required = opts.required;\n }\n\n // Hoist related to top level for relationship fields (Issue #746)\n // This is critical for getRelationshipMap() to detect relationships\n if (opts.related !== undefined) {\n newField.related = opts.related;\n delete newField._meta?.related;\n }\n\n fields.set(fieldName, newField);\n verboseLog(\n `[registry] ✅ Added field ${fieldName} from decorator: type=${opts.type || 'text'}`,\n );\n }\n }\n }\n\n // Handle tenantScoped configuration (Issue #688)\n // External manifests can carry tenantScoped only in decoratorConfig, so\n // registration must honor the merged view rather than only explicit config.\n let tenantScopedConfig: RegisteredClass['tenantScopedConfig'] | undefined;\n const effectiveTenantScoped =\n config.tenantScoped ?? manifestEntry?.decoratorConfig?.tenantScoped;\n if (effectiveTenantScoped) {\n // Normalize boolean or object config into full options\n const tenantOpts =\n typeof effectiveTenantScoped === 'boolean' ? {} : effectiveTenantScoped;\n tenantScopedConfig = {\n mode: tenantOpts.mode ?? 'required',\n field: tenantOpts.field ?? 'tenantId',\n autoFilter: tenantOpts.autoFilter ?? true,\n autoPopulate: tenantOpts.autoPopulate ?? true,\n allowSuperAdminBypass: tenantOpts.allowSuperAdminBypass ?? false,\n };\n\n // Inject or enrich tenantId field\n const fieldName = tenantScopedConfig.field;\n const hadTenantField = fields.has(fieldName);\n ensureTenantScopedField(fields, tenantScopedConfig);\n if (!hadTenantField) {\n verboseLog(\n `[registry] ✅ Injected ${fieldName} field for tenant-scoped class ${name}`,\n );\n }\n }\n\n if (manifestEntry?.methods) {\n // Load method definitions from manifest (for custom CLI/API/MCP generation)\n for (const [methodName, methodDef] of Object.entries(\n manifestEntry.methods,\n )) {\n methods.set(methodName, methodDef);\n }\n }\n\n // Also load methods from _manifestMethods in config (from consumer plugin register.js)\n // This is how external package methods are passed to the registry.\n // `_manifestMethods` is an undeclared escape-hatch carried on the config bag\n // by generated consumer `register.js`, so read it through a narrowed view.\n const manifestMethods = (\n config as unknown as {\n _manifestMethods?: Record<string, MethodDefinition>;\n }\n )._manifestMethods;\n if (manifestMethods) {\n for (const [methodName, methodDef] of Object.entries(manifestMethods)) {\n methods.set(methodName, methodDef);\n }\n verboseLog(\n `[registry] Loaded ${methods.size} methods for ${name} from _manifestMethods`,\n );\n }\n\n // Note: If manifest not found here, it will be loaded asynchronously when needed\n // via ensureManifestLoaded(). This allows decorators to remain synchronous while\n // supporting dynamic external package manifest loading.\n\n // Build inheritance chain from constructor (needed for STI table name resolution)\n const inheritanceChain = buildInheritanceChain(ctor);\n\n // Validate table strategy compatibility with parent (STI requirement)\n if (inheritanceChain.length > 1) {\n // This class has a parent - validate strategy compatibility\n const parentName = inheritanceChain[inheritanceChain.length - 2]; // Second-to-last is parent\n const parentEntry = findClass(parentName);\n\n if (parentEntry) {\n const parentStrategy = parentEntry.config?.tableStrategy || 'default';\n const childStrategy = config.tableStrategy; // Don't default - undefined means inherit\n\n // Only validate if child has an EXPLICIT strategy that differs from parent\n // undefined childStrategy means it will inherit from parent\n if (childStrategy !== undefined && parentStrategy !== childStrategy) {\n throw ConfigurationError.incompatibleStrategy(\n name,\n childStrategy,\n parentName,\n parentStrategy,\n );\n }\n }\n }\n\n // Defer schema generation until needed (generateSchema now uses dynamic import)\n // Store table name for lazy schema generation\n // Priority for STI: manifest's tableName > decorator config > derived from class name\n // The manifest's tableName is computed at build-time when full class hierarchy is known,\n // which correctly handles STI inheritance. The decorator may derive wrong tableName\n // if parent class isn't registered yet at decorator execution time.\n const tableName = resolveTableName(ctor, name, config);\n setSmrtTableName(ctor, tableName);\n\n // Load pre-generated schema from manifest if available, otherwise placeholder\n let schema: SchemaDefinition;\n if (manifestEntry?.schema) {\n // Pre-generated schema from manifest (build-time)\n // Keep indexes as IndexDefinition objects for DDL strategies\n schema = {\n ddl: manifestEntry.schema.ddl,\n indexes:\n manifestEntry.schema.indexes?.map((idx: IndexDefinition) => ({\n name: idx.name,\n columns: idx.columns || [],\n unique: idx.unique || false,\n where: idx.where,\n description: idx.description,\n jsonPath: idx.jsonPath,\n })) || [],\n triggers: [],\n tableName: manifestEntry.schema.tableName || tableName,\n // Cast manifest columns to ColumnDefinition (same shape, TypeScript just needs help)\n columns: cloneManifestSchemaColumns(manifestEntry.schema.columns),\n foreignKeys: [],\n dependencies: [],\n version: manifestEntry.schema.version || '',\n packageName: manifestEntry.packageName,\n };\n verboseLog(\n `[registry] Loaded pre-generated schema for ${name} (${Object.keys(manifestEntry.schema.columns || {}).length} columns)`,\n );\n } else {\n // Placeholder schema - will be generated lazily when first needed\n schema = {\n ddl: '', // Generated lazily\n indexes: [], // Parsed from DDL lazily\n triggers: [], // No longer using database triggers - timestamps managed by application\n tableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n packageName: undefined,\n };\n }\n\n if (schema.columns) {\n applySqlTypeOverrides(schema.columns, fields.entries());\n if (decorators && decorators.size > 0) {\n applySqlTypeOverrides(schema.columns, decorators.entries());\n }\n }\n\n // Use pre-computed validation rules from manifest if available (Issue #782)\n // For decorator-based registration, manifest may have pre-computed rules\n const validationRules = manifestEntry?.validationRules;\n let validators: ValidatorFunction[] | undefined;\n\n // Only compile validators if no pre-computed rules exist\n if (!validationRules || validationRules.length === 0) {\n validators = compileValidators(name, fields);\n } else {\n verboseLog(\n `[registry] Using ${validationRules.length} pre-computed validation rules for ${name}`,\n );\n }\n\n // Derive extends from prototype chain if not available from manifest\n // This is critical for inline test classes that use decorators\n let extendsClass: string | undefined = manifestEntry?.extends;\n if (!extendsClass) {\n const proto = Object.getPrototypeOf(ctor);\n if (proto?.name && proto.name !== 'SmrtObject' && proto.name !== 'Object') {\n extendsClass = proto.name;\n }\n }\n\n // Merge manifest's decoratorConfig into config\n // Use the computed tableName which prioritizes manifest's value for STI correctness\n const mergedConfig = {\n ...manifestEntry?.decoratorConfig,\n ...config,\n tableName, // Override with correctly computed tableName\n };\n\n // Generate qualified name if we have a package name\n // Format: \"@package/name:ClassName\"\n const qualifiedName = packageName\n ? createQualifiedName(packageName, name)\n : undefined;\n\n // Determine visibility from config, manifest, or default to 'public'\n // Priority: explicit config > manifest > default\n const visibility: SmrtVisibility =\n config.visibility || manifestEntry?.visibility || 'public';\n\n // Issue #951: Use qualified name as primary key when available\n const registrationKey = qualifiedName || name;\n\n getClasses().set(registrationKey, {\n name,\n qualifiedName, // Qualified name for cross-package identification\n constructor: ctor,\n config: mergedConfig,\n fields,\n methods,\n schema,\n validators,\n validationRules, // Pre-computed rules from manifest (Issue #782)\n tools: manifestEntry?.tools, // AI-callable tool schemas (CLI param schemas)\n // Pluralized endpoint name. Prefer the manifest's computed value (handles\n // STI inheritance + any future inflection changes); fall back to the same\n // simple pluralization the scanner uses for inline/test classes that have\n // no manifest entry. (smrt#1311.)\n collection: manifestEntry?.collection ?? pluralizeCollection(name),\n packageName, // Store package name from manifest for getPackageName() lookup\n sourceFilePath, // Store source file for collision detection (Issue #555)\n extends: extendsClass, // Capture parent class name from manifest OR prototype chain\n extendsTypeArg: manifestEntry?.extendsTypeArg, // SmrtCollection<T> generic arg\n tenantScopedConfig, // Multi-tenancy config (Issue #688)\n visibility, // Visibility control for manifest filtering\n // NOTE: Don't pre-compute inheritanceChain here - let getInheritanceChain() compute\n // it lazily using the `extends` field. This ensures correct chain for both\n // decorator-registered and manifest-loaded classes.\n });\n\n // Release B (#1133): case-insensitive lookups iterate the classes Map\n // directly instead of maintaining a parallel classNameMap index.\n\n // Index constructor for O(1) reverse lookups (Issue #713: constructor-based lookup)\n getConstructorIndex().set(ctor, registrationKey);\n // Stamp the qualified name on the constructor itself as a fallback for\n // runtime code that has a constructor reference but might hit a\n // WeakMap miss (HMR / federated modules / multiple-copy edge cases).\n setSmrtQualifiedName(ctor, qualifiedName);\n\n verboseLog(\n `🎯 Registered smrt object: ${name} (key: ${registrationKey}) with schema for ${schema.tableName} and ${(validators?.length || 0) + (validationRules?.length || 0)} validators/rules`,\n );\n\n // STI sibling auto-loading (Issue #430)\n // When a class is registered that shares a table with other classes (STI),\n // we need to discover and register ALL siblings so that getAllSchemas()\n // can merge columns from all subtypes for the database adapter.\n //\n // IMPORTANT: Only auto-load siblings from EXTERNAL packages.\n // For test classes or classes in the same package, they will be registered\n // by their own @smrt() decorators. Auto-loading them as stubs would cause collisions.\n const collection = manifestEntry?.collection;\n if (collection && !getStiSiblingsLoaded().has(collection)) {\n // Mark this collection as processed to prevent infinite recursion\n getStiSiblingsLoaded().add(collection);\n\n verboseLog(\n `[registry] Checking for STI siblings for collection: ${collection}`,\n );\n\n // Discover all classes that share this collection (table)\n const siblings = discoverSTISiblingsSync(collection);\n\n // Register any siblings that aren't already registered\n // Only load siblings from DIFFERENT packages to avoid collisions with local classes\n for (const sibling of siblings) {\n // Use case-insensitive check to prevent registering 'Praeco' when 'praeco' exists\n if (!hasClassCaseInsensitive(sibling.className)) {\n // Skip siblings from the same package - they will be registered by their own decorators\n if (\n sibling.packageName &&\n packageName &&\n sibling.packageName === packageName\n ) {\n verboseLog(\n `[registry] Skipping STI sibling ${sibling.className} from same package: ${packageName}`,\n );\n continue;\n }\n\n verboseLog(\n `[registry] Auto-loading STI sibling: ${sibling.className} for collection: ${collection}`,\n );\n registerFromManifest(\n sibling.className,\n sibling.entry,\n sibling.packageName,\n );\n }\n }\n }\n}\n\nexport function registerCollection(\n objectName: string,\n // `new (options: any) => SmrtCollection<any>` is the irreducible\n // collection-constructor shape (S4 #1579): the public\n // `ObjectRegistry.registerCollection` wrapper hands a non-generic constructor\n // of exactly this form, which is not assignable to the generic\n // `typeof SmrtCollection`. Mirrors `RegisteredClass.collectionConstructor`.\n collectionConstructor: NonNullable<RegisteredClass['collectionConstructor']>,\n): void {\n const registered = findClass(objectName);\n if (registered) {\n registered.collectionConstructor = collectionConstructor;\n }\n\n // The collections map stores `typeof SmrtCollection`; the runtime value is a\n // SmrtCollection subclass constructor. Bridge the loose constructor shape to\n // the map's element type without `any`.\n getCollections().set(\n objectName,\n collectionConstructor as unknown as Parameters<\n ReturnType<typeof getCollections>['set']\n >[1],\n );\n}\n\n// resolveManifestCollision was removed in Release C (#1134). Its three-branch\n// decision is now represented as explicit rows in the collision-policy\n// decision table — see `manifest-same-source-file`, `manifest-sti-child-wins`,\n// `manifest-sti-parent-skip`, `manifest-default-skip` scenarios in\n// packages/core/src/registry/collision-policy.ts.\n\ntype TenantScopedOptions = Exclude<\n SmartObjectConfig['tenantScoped'],\n boolean | undefined\n>;\n\nfunction normalizeTenantScopedConfig(\n tenantScoped: SmartObjectConfig['tenantScoped'],\n): RegisteredClass['tenantScopedConfig'] | undefined {\n if (!tenantScoped) {\n return undefined;\n }\n\n const tenantOpts: TenantScopedOptions =\n typeof tenantScoped === 'boolean' ? {} : tenantScoped;\n return {\n mode: tenantOpts.mode ?? 'required',\n field: tenantOpts.field ?? 'tenantId',\n autoFilter: tenantOpts.autoFilter ?? true,\n autoPopulate: tenantOpts.autoPopulate ?? true,\n allowSuperAdminBypass: tenantOpts.allowSuperAdminBypass ?? false,\n };\n}\n\nfunction ensureTenantScopedField(\n fields: Map<string, RegisteredField>,\n tenantScopedConfig: RegisteredClass['tenantScopedConfig'] | undefined,\n): void {\n if (!tenantScopedConfig) {\n return;\n }\n\n const fieldName = tenantScopedConfig.field;\n const existingField = fields.get(fieldName);\n if (existingField) {\n fields.set(fieldName, {\n ...existingField,\n required:\n existingField.required ?? tenantScopedConfig.mode === 'required',\n _meta: {\n ...existingField._meta,\n reference: existingField._meta?.reference ?? 'Tenant',\n sqlType: 'UUID',\n __tenancy: {\n ...existingField._meta?.__tenancy,\n isTenantIdField: true,\n ...tenantScopedConfig,\n },\n },\n });\n return;\n }\n\n fields.set(fieldName, {\n type: 'foreignKey',\n related: 'Tenant',\n required: tenantScopedConfig.mode === 'required',\n _meta: {\n reference: 'Tenant',\n sqlType: 'UUID',\n __tenancy: {\n isTenantIdField: true,\n ...tenantScopedConfig,\n },\n },\n });\n}\n\nfunction mergeIndexDefinitions(\n existingIndexes: IndexDefinition[] | undefined,\n manifestIndexes: IndexDefinition[] | undefined,\n): IndexDefinition[] {\n const merged = [...(existingIndexes || [])];\n const seen = new Set(merged.map((index) => index?.name).filter(Boolean));\n\n for (const index of manifestIndexes || []) {\n if (!index?.name || seen.has(index.name)) {\n continue;\n }\n merged.push(index);\n seen.add(index.name);\n }\n\n return merged;\n}\n\n// cloneManifestSchemaColumns lives in ../manifest/store.ts — imported above.\n// (Hoisted out of the duplicate definition here and in registry.ts.)\n\n/**\n * Typed read-view of a raw decorator option bag.\n *\n * `getFieldDecorators()` stores decorator options as\n * `Map<string, Record<string, unknown>>`, so the individual reads come back as\n * `unknown` and won't narrow to the typed `RegisteredField` members they feed.\n * This interface re-types only the members the decorator-merge loop reads, with\n * exactly the target member types, and keeps the open index signature so a raw\n * `Record<string, unknown>` bag is assignable to it without `any`. (The bag's\n * runtime values are field-helper options; this view mirrors that contract.)\n */\ninterface DecoratorFieldOptions {\n type?: FieldDefinition['type'];\n required?: boolean;\n nullable?: boolean;\n transient?: boolean;\n sensitive?: boolean;\n readonly?: boolean;\n readPermission?: unknown;\n related?: string;\n [key: string]: unknown;\n}\n\n/**\n * Read-only field shape consumed by the SQL-type override helpers.\n *\n * `applySqlTypeOverrides` runs over two sources: the canonical\n * `Map<string, RegisteredField>` AND the raw decorator bag\n * (`Map<string, Record<string, unknown>>` from `getFieldDecorators()`). Both\n * satisfy this shape — every member is optional and the open index signature\n * matches both `RegisteredField`'s index signature and `Record<string, unknown>`\n * — so the helpers read the same overrides off either source without `any`.\n */\ninterface SqlTypeOverrideField {\n type?: unknown;\n sqlType?: unknown;\n __tenancy?: FieldMeta['__tenancy'];\n _meta?: FieldMeta;\n [key: string]: unknown;\n}\n\nfunction getReferenceKindFromFieldOptions(\n fieldOptions: SqlTypeOverrideField,\n): ColumnDefinition['referenceKind'] | undefined {\n if (\n fieldOptions?.__tenancy?.isTenantIdField ||\n fieldOptions?._meta?.__tenancy?.isTenantIdField\n ) {\n return 'tenantId';\n }\n\n if (fieldOptions?.type === 'foreignKey') {\n return 'foreignKey';\n }\n\n if (fieldOptions?.type === 'crossPackageRef') {\n return 'crossPackageRef';\n }\n\n return undefined;\n}\n\nfunction applySqlTypeOverrides(\n columns: Record<string, ColumnDefinition>,\n fieldEntries: Iterable<[string, SqlTypeOverrideField]> | undefined,\n): void {\n if (!fieldEntries) {\n return;\n }\n\n for (const [fieldName, fieldOptions] of fieldEntries) {\n const sqlType = fieldOptions?.sqlType ?? fieldOptions?._meta?.sqlType;\n const referenceKind = getReferenceKindFromFieldOptions(fieldOptions);\n if (!sqlType && !referenceKind) {\n continue;\n }\n\n const columnName = toSnakeCase(fieldName);\n const existingColumn = columns[columnName];\n if (!existingColumn) {\n continue;\n }\n\n columns[columnName] = {\n ...existingColumn,\n ...(sqlType\n ? { type: String(sqlType).toUpperCase() as ColumnDefinition['type'] }\n : {}),\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n}\n\n/**\n * Invalidate cached inheritance state (chain + inheritedFields +\n * inheritedMethods) on `existing` AND every descendant whose `extends` or\n * `inheritanceChain` references it.\n *\n * This covers both simple-name and qualified-name `extends` matches, which\n * is why `ObjectRegistry.invalidateInheritanceCache` delegates here (Release\n * C follow-up #1139 Gap 1 — the old recursive public helper only matched\n * `extends === className` by exact string, silently missing qualified\n * descendants like `@pkg:Parent`).\n *\n * `previousIdentity` is used by `upsertExistingEntry` (Gap 2): when a class\n * is promoted (its name/packageName/qualifiedName are mutated in place),\n * descendants registered against the pre-mutation identity need to be\n * invalidated too. Pass the pre-mutation `{ name, qualifiedName }` so this\n * sweep matches against both the old and new identity.\n *\n * Exported for the public `ObjectRegistry.invalidateInheritanceCache`\n * shim in registry.ts.\n */\nexport function invalidateInheritanceEntries(\n existing: RegisteredClass,\n previousIdentity?: {\n name?: string;\n qualifiedName?: string;\n },\n): void {\n const cache = getInheritanceCache();\n const affectedNames = new Set<string>();\n\n const remember = (name: string | undefined): void => {\n if (name) {\n affectedNames.add(name);\n }\n };\n\n const matchNames = new Set<string>();\n const rememberMatch = (name: string | undefined): void => {\n if (name) matchNames.add(name);\n };\n rememberMatch(existing.name);\n rememberMatch(existing.qualifiedName);\n rememberMatch(previousIdentity?.name);\n rememberMatch(previousIdentity?.qualifiedName);\n\n for (const name of matchNames) {\n remember(name);\n }\n\n for (const [key, candidate] of getClasses()) {\n const extendsMatches =\n !!candidate.extends && matchNames.has(candidate.extends);\n const chainMatches =\n !!candidate.inheritanceChain &&\n candidate.inheritanceChain.some((n) => matchNames.has(n));\n\n if (candidate === existing || extendsMatches || chainMatches) {\n candidate.inheritanceChain = undefined;\n candidate.inheritedFields = undefined;\n candidate.inheritedMethods = undefined;\n remember(key);\n remember(candidate.name);\n remember(candidate.qualifiedName);\n }\n }\n\n for (const name of affectedNames) {\n cache.delete(name);\n }\n}\n\nfunction mergeManifestIntoExistingRegistration(\n existing: RegisteredClass,\n objectDef: SmartObjectDefinition,\n packageName?: string,\n): void {\n const manifestConfig = objectDef.decoratorConfig || {};\n const manifestTableName =\n objectDef.schema?.tableName ||\n manifestConfig.tableName ||\n existing.schema?.tableName ||\n existing.config.tableName ||\n tableNameFromClass(existing.constructor);\n\n existing.config = {\n ...manifestConfig,\n ...existing.config,\n tableName: manifestTableName,\n };\n\n if (objectDef.fields) {\n for (const [fieldName, fd] of Object.entries(objectDef.fields)) {\n if (!existing.fields.has(fieldName)) {\n existing.fields.set(fieldName, createFieldFromManifest(fd));\n continue;\n }\n\n const existingField = existing.fields.get(fieldName);\n if (!existingField) {\n continue;\n }\n\n existing.fields.set(fieldName, mergeManifestField(existingField, fd));\n }\n }\n\n const tenantScopedConfig = normalizeTenantScopedConfig(\n existing.config.tenantScoped,\n );\n if (tenantScopedConfig) {\n existing.tenantScopedConfig = tenantScopedConfig;\n ensureTenantScopedField(existing.fields, tenantScopedConfig);\n }\n\n if (objectDef.methods) {\n for (const [methodName, methodDef] of Object.entries(objectDef.methods)) {\n existing.methods.set(methodName, methodDef);\n }\n }\n\n if (objectDef.schema) {\n const manifestSchema: SchemaDefinition = {\n ddl: objectDef.schema.ddl || '',\n indexes:\n objectDef.schema.indexes?.map((idx: IndexDefinition) => ({\n name: idx.name,\n columns: idx.columns || [],\n unique: idx.unique || false,\n where: idx.where,\n description: idx.description,\n jsonPath: idx.jsonPath,\n })) || [],\n triggers: [],\n tableName: objectDef.schema.tableName || manifestTableName,\n columns: cloneManifestSchemaColumns(objectDef.schema.columns),\n foreignKeys: [],\n dependencies: [],\n version: objectDef.schema.version || '',\n packageName: packageName,\n };\n\n applySqlTypeOverrides(manifestSchema.columns, existing.fields.entries());\n\n existing.schema = {\n ...(existing.schema || {}),\n ...manifestSchema,\n tableName: manifestSchema.tableName,\n ddl: manifestSchema.ddl || existing.schema?.ddl || '',\n columns: {\n ...(existing.schema?.columns || {}),\n ...(manifestSchema.columns || {}),\n },\n indexes: mergeIndexDefinitions(\n existing.schema?.indexes,\n manifestSchema.indexes,\n ),\n packageName: packageName || existing.schema?.packageName,\n };\n } else if (!existing.schema) {\n existing.schema = {\n ddl: '',\n indexes: [],\n triggers: [],\n tableName: manifestTableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n packageName: packageName,\n };\n } else {\n existing.schema.tableName = manifestTableName;\n existing.schema.packageName = packageName || existing.schema.packageName;\n }\n\n if (objectDef.validationRules !== undefined) {\n existing.validationRules = objectDef.validationRules;\n existing.validators = undefined;\n } else if (!existing.validationRules && !existing.validators) {\n existing.validators = compileValidators(existing.name, existing.fields);\n }\n\n if (packageName) {\n existing.packageName = packageName;\n existing.qualifiedName = createQualifiedName(\n packageName,\n existing.name,\n ) as QualifiedClassName;\n } else if (!existing.packageName && objectDef.packageName) {\n existing.packageName = objectDef.packageName;\n }\n\n if (objectDef.extends) {\n existing.extends = packageName\n ? qualifyExtendsName(objectDef.extends, packageName)\n : objectDef.extends;\n }\n\n if (!existing.sourceFilePath && objectDef.filePath) {\n existing.sourceFilePath = objectDef.filePath;\n }\n\n existing.visibility =\n objectDef.visibility || manifestConfig.visibility || existing.visibility;\n invalidateInheritanceEntries(existing);\n}\n\nexport function registerFromManifest(\n name: string,\n objectDef: SmartObjectDefinition,\n packageName?: string,\n): void {\n // Issue #951: Compute simple class name and registration key early\n // `name` may be a qualified key from manifest (e.g., '@happyvertical/smrt-events:Event')\n // `simpleClassName` is always the plain class name (e.g., 'Event')\n const simpleClassName = objectDef.className || name;\n const qualifiedNameEarly = packageName\n ? createQualifiedName(packageName, simpleClassName)\n : objectDef.qualifiedName;\n const registrationKey = (qualifiedNameEarly || name) as string;\n\n // Release C (#1134): collision resolution routes through\n // decideCollisionPolicy. See collision-policy.ts for the 16-row decision\n // table; applyManifestCollisionPolicy below turns a policy into registry\n // mutations.\n //\n // Two collision checks, matching pre-C behavior:\n // 1. Canonical-name lookup (case-insensitive simple-name match; covers\n // issue #950's STI child-wins and issue #951's qualified coexistence).\n // 2. Exact-key match (handles re-registration with same qualified key).\n //\n // The ambiguous case (multiple existing entries share the simple name —\n // `getCanonicalClassName` returns undefined) is NOT fed through the table.\n // It falls through both checks into the new-registration code below,\n // preserving issue #951's coexistence semantics.\n const existingCanonical = getCanonicalClassName(simpleClassName);\n if (existingCanonical) {\n const existing = getClasses().get(existingCanonical);\n if (existing) {\n const outcome = applyManifestCollisionPolicy({\n name,\n objectDef,\n packageName,\n existing,\n existingKey: existingCanonical,\n registrationKey,\n matchKind: 'canonical-name',\n });\n if (outcome === 'return') return;\n // outcome === 'continue' means policy was replace (child-wins) or\n // coexist — fall through into the new-registration code below.\n }\n // else: stale map entry (key exists but class was removed) — allow registration\n }\n\n if (getClasses().has(registrationKey)) {\n const existing = getClasses().get(registrationKey);\n if (!existing) return;\n const outcome = applyManifestCollisionPolicy({\n name,\n objectDef,\n packageName,\n existing,\n existingKey: registrationKey,\n registrationKey,\n matchKind: 'exact-key',\n });\n if (outcome === 'return') return;\n }\n\n // Issue #1004: Qualify the `extends` value BEFORE inserting this class\n // into the classes Map. Otherwise qualifyExtendsName would iterate and\n // find the child's own entry, creating a circular extends chain\n // (e.g., @test/sports:TestEvent extends itself). Release B (#1133) swapped\n // the underlying classNameMap lookup for an iteration over `classes`, but\n // the ordering invariant still holds.\n const qualifiedExtends =\n objectDef.extends && packageName\n ? qualifyExtendsName(objectDef.extends, packageName)\n : objectDef.extends;\n\n // Release B (#1133): case-insensitive lookups iterate the classes Map\n // directly; no parallel classNameMap index to maintain.\n\n // Create stub constructor - not needed for CLI command generation\n // The CLI only needs metadata (fields, methods, config)\n // Mark as manifest stub so real class can replace it during decorator registration\n const stubConstructor = class extends SmrtObject {\n static readonly _isManifestStub = true;\n } as typeof SmrtObject;\n Object.defineProperty(stubConstructor, 'name', { value: simpleClassName });\n\n // Convert manifest field definitions to Field objects\n const fields = new Map<string, RegisteredField>();\n const decorators = getFieldDecorators().get(simpleClassName);\n if (objectDef.fields) {\n for (const [fieldName, fd] of Object.entries(objectDef.fields)) {\n fields.set(fieldName, createFieldFromManifest(fd));\n }\n }\n\n // Load method definitions\n const methods = new Map<string, MethodDefinition>();\n if (objectDef.methods) {\n for (const [methodName, methodDef] of Object.entries(objectDef.methods)) {\n methods.set(methodName, methodDef);\n }\n }\n\n // Get config from manifest\n const config = objectDef.decoratorConfig || {};\n const tableName = config.tableName || tableNameFromClass(stubConstructor);\n\n // Load pre-generated schema from manifest if available\n // This enables efficient external package consumption without runtime schema generation\n let schema: SchemaDefinition;\n if (objectDef.schema) {\n // Pre-generated schema from manifest (build-time)\n // Keep indexes as IndexDefinition objects for DDL strategies\n schema = {\n ddl: objectDef.schema.ddl,\n indexes:\n objectDef.schema.indexes?.map((idx: IndexDefinition) => ({\n name: idx.name,\n columns: idx.columns || [],\n unique: idx.unique || false,\n where: idx.where,\n description: idx.description,\n jsonPath: idx.jsonPath,\n })) || [],\n triggers: [],\n tableName: objectDef.schema.tableName,\n columns: cloneManifestSchemaColumns(objectDef.schema.columns),\n foreignKeys: [],\n dependencies: [],\n version: objectDef.schema.version || '',\n packageName: packageName,\n };\n applySqlTypeOverrides(schema.columns, fields.entries());\n applySqlTypeOverrides(schema.columns, decorators?.entries());\n verboseLog(\n `[registry] Loaded pre-generated schema for ${name} (${Object.keys(objectDef.schema.columns || {}).length} columns)`,\n );\n } else {\n // Placeholder schema - will be generated at runtime if needed\n schema = {\n ddl: '',\n indexes: [],\n triggers: [],\n tableName,\n columns: {},\n foreignKeys: [],\n dependencies: [],\n version: '',\n packageName: packageName,\n };\n }\n\n // Use pre-computed validation rules from manifest if available (Issue #782)\n // This avoids compiling validator closures at runtime, reducing startup time\n const validationRules = objectDef.validationRules;\n let validators: ValidatorFunction[] | undefined;\n\n // Only compile validators if no pre-computed rules exist\n // (backward compatibility for older manifests)\n if (!validationRules || validationRules.length === 0) {\n validators = compileValidators(name, fields);\n verboseLog(\n `[registry] No pre-computed rules for ${name}, compiled ${validators.length} validators`,\n );\n } else {\n verboseLog(\n `[registry] Using ${validationRules.length} pre-computed validation rules for ${name}`,\n );\n }\n\n // Issue #951: Use qualifiedNameEarly computed at the top of this function\n // (uses simpleClassName, not the manifest key which may already be qualified)\n const qualifiedName = qualifiedNameEarly;\n\n // Get visibility from manifest (defaults to 'public')\n const visibility: SmrtVisibility =\n objectDef.visibility || config.visibility || 'public';\n\n // Register in ObjectRegistry (metadata only, no collection constructor)\n // Manifest registration is for command discovery and help text.\n // Runtime execution requires real classes loaded from entry point.\n //\n // Issue #847: Use className from objectDef (simple name like 'Council') for the name\n // property, not the qualified name key. This enables getFields() lookups by simple\n // class name to work correctly. simpleClassName is defined earlier in this function.\n //\n // Issue #951: Use registrationKey (qualified when available) as the primary key\n getClasses().set(registrationKey, {\n name: simpleClassName,\n qualifiedName, // Qualified name for cross-package identification\n constructor: stubConstructor,\n config,\n fields,\n methods,\n schema,\n validators,\n validationRules, // Pre-computed rules from manifest (Issue #782)\n tools: objectDef.tools, // AI-callable tool schemas (used for CLI param schemas)\n // Pluralized endpoint name; fall back to simple pluralization if a\n // (legacy) manifest lacks it. (smrt#1311.)\n collection: objectDef.collection ?? pluralizeCollection(simpleClassName),\n packageName,\n sourceFilePath: objectDef.filePath, // Store source file for collision detection (Issue #555)\n extends: qualifiedExtends, // Issue #1004: Pre-computed qualified parent\n extendsTypeArg: objectDef.extendsTypeArg, // SmrtCollection<T> generic arg\n visibility, // New: Visibility control for manifest filtering\n });\n // Tag the synthetic stub constructor with the qualified name so the\n // same constructor-side identity convention holds for manifest-loaded\n // classes as well as decorator-registered ones.\n setSmrtQualifiedName(stubConstructor, qualifiedName);\n\n verboseLog(\n `📦 Registered ${simpleClassName} from manifest (key: ${registrationKey}, ${fields.size} fields, ${methods.size} methods)`,\n );\n\n // STI sibling auto-loading (Issue #430)\n // When a class is registered that shares a table with other classes (STI),\n // we need to discover and register ALL siblings so that getAllSchemas()\n // can merge columns from all subtypes for the database adapter.\n //\n // IMPORTANT: Only auto-load siblings from EXTERNAL packages.\n // For test classes or classes in the same package, they will be registered\n // by their own @smrt() decorators. Auto-loading them as stubs would cause collisions.\n const collection = objectDef.collection;\n if (collection && !getStiSiblingsLoaded().has(collection)) {\n // Mark this collection as processed to prevent infinite recursion\n getStiSiblingsLoaded().add(collection);\n\n verboseLog(\n `[registry] Checking for STI siblings for collection: ${collection}`,\n );\n\n // Discover all classes that share this collection (table)\n const siblings = discoverSTISiblingsSync(collection);\n\n // Register any siblings that aren't already registered\n // Only load siblings from DIFFERENT packages to avoid collisions with local classes\n for (const sibling of siblings) {\n // Use case-insensitive check to prevent registering 'Praeco' when 'praeco' exists\n if (!hasClassCaseInsensitive(sibling.className)) {\n // Skip siblings from the same package - they will be registered by their own decorators\n if (\n sibling.packageName &&\n packageName &&\n sibling.packageName === packageName\n ) {\n verboseLog(\n `[registry] Skipping STI sibling ${sibling.className} from same package: ${packageName}`,\n );\n continue;\n }\n\n verboseLog(\n `[registry] Auto-loading STI sibling: ${sibling.className} for collection: ${collection}`,\n );\n registerFromManifest(\n sibling.className,\n sibling.entry,\n sibling.packageName,\n );\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmFA,SAAS,oBAAoB,WAA2B;CACtD,MAAM,QAAQ,UAAU,YAAY;CACpC,IAAI,MAAM,SAAS,GAAG,GAAG,OAAO,GAAG,MAAM,MAAM,GAAG,EAAE,EAAE;CACtD,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,GAClE,OAAO,GAAG,MAAM;CAElB,IAAI,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,IAAI,GAAG,OAAO,GAAG,MAAM;CAClE,OAAO,GAAG,MAAM;AAClB;;;;;;AAOA,SAAS,oBAAoB,YAAyC;CACpE,IAAI,CAAC,YAAY,OAAO;CACxB,OACE,WAAW,SAAS,qBAAqB,KACzC,WAAW,SAAS,QAAQ,KAC5B,WAAW,SAAS,SAAS,KAC7B,WAAW,SAAS,QAAQ,KAC5B,WAAW,SAAS,QAAQ;AAEhC;;;;;AAMA,SAAS,8BAA8B,MASnB;CAClB,MAAM,EACJ,MACA,MACA,gBACA,eACA,qBACA,UACA,aACA,cACE;CAEJ,IAAI,qBAAqB;CACzB,IAAI,qBAAqB;CACzB,IAAI;EACF,qBAAqB,KAAK,qBAAqB,SAAS;EACxD,qBAAqB,SAAS,YAAY,qBAAqB;CACjE,QAAQ,CAER;CAEA,MAAM,uBAAuB,CAAC,EAAE,iBAAiB,SAAS;CAC1D,MAAM,iBACJ,wBAAwB,kBAAkB,SAAS;CACrD,MAAM,6BAA6B,CAAC,CAAC,SAAS,aAAa,WAAW,GAAG;CACzE,MAAM,cACJ,CAAC,CAAC,kBACF,CAAC,CAAC,SAAS,eACX,mBAAmB,SAAS;CAC9B,MAAM,qBAAqB,CAAC,CAAC;CAC7B,MAAM,yBAAyB,gBAAgB,WAAW;CAE1D,OAAO;EACL,QAAQ;EACR;EACA,iBAAiB,SAAS,gBAAgB;EAC1C;EACA;EACA,wBACG,SAAS,YACP,oBAAoB;EACzB;EACA;EACA;EACA;EACA;EACA,UAAU,SAAS,SAAS;EAC5B;EACA;EACA,uBAAuB,CAAC,CAAC,SAAS;EAElC,oBAAoB;EACpB,uCAAuC;EACvC,sBAAsB,CAAC,SAAS;CAClC;AACF;;;;;;;;;;AAWA,SAAS,6BAA6B,MAa1B;CAEV,MAAM,WAAW,sBADF,8BAA8B,IACN,CAAM;CAE7C,QAAQ,SAAS,QAAjB;EACE,KAAK;EACL,KAAK,WAAW;GAKd,MAAM,SAAS,KAAK,oBAAoB,KAAK,aAAa,KAAK,QAAQ;GACvE,IACE,SAAS,aAAa,oBACtB,SAAS,aAAa,6BAEtB,WACE,cAAc,SAAS,SAAS,KAAK,KAAK,KAAK,WAAW,OAAO,KAAK,SAAS,OAAO,EACxF;GAEF,OAAO;EACT;EACA,KAAK;GACH,WACE,cAAc,SAAS,SAAS,cAAc,KAAK,KAAK,KAAK,SAAS,OAAO,EAC/E;GACA,OAAO;EACT,KAAK,SAAS;GACZ,MAAM,cACJ,KAAK,cAAc,qBACf,kCAAkC,KAAK,YAAY,MACnD;GACN,MAAM,IAAI,MACR,+BAA+B,KAAK,KAAK,GAAG,YAAY,mZAQjB,KAAK,KAAK,wFAEnD;EACF;EACA,KAAK;EACL,KAAK,qBAGH,MAAM,IAAI,MACR,mCAAmC,SAAS,OAAO,oCAAoC,SAAS,SAAS,kBAC3G;CACJ;AACF;;;;;;;AAQA,SAAS,6BAA6B,MAQlB;CAClB,MAAM,EACJ,MACA,WACA,aACA,UACA,aACA,iBACA,cACE;CACJ,MAAM,eAAe,UAAU,aAAa;CAC5C,MAAM,aAAa,UAAU;CAe7B,MAAM,qBAAqB,CAAC,EAC1B,eACC,eAAe,SAAS,QACvB,eAAe,eACf,WAAW,YAAY,MAAM,SAAS,KAAK,YAAY;CAE3D,MAAM,qBAAqB,CAAC,EAC1B,SAAS,YACR,SAAS,YAAY,gBACpB,SAAS,YAAY,QACrB,SAAS,YAAY,mBACrB,SAAS,QAAQ,YAAY,MAAM,aAAa,YAAY;CAGhE,MAAM,gBAAgB,UAAU;CAChC,MAAM,uBAAuB,CAAC,EAAE,iBAAiB,SAAS;CAC1D,MAAM,iBACJ,wBAAwB,kBAAkB,SAAS;CACrD,MAAM,6BAA6B,CAAC,CAAC,SAAS,aAAa,WAAW,GAAG;CACzE,MAAM,cACJ,CAAC,CAAC,eACF,CAAC,CAAC,SAAS,eACX,gBAAgB,SAAS;CAE3B,OAAO;EACL,QAAQ;EACR;EACA,iBAAiB;EACjB;EACA;EACA,wBACG,SAAS,YACP,oBAAoB;EACzB,qBAAqB;EACrB;EACA;EACA;EACA;EACA,UAAU,iBAAiB,SAAS;EACpC,oBAAoB,gBAAgB,eAAe;EACnD,wBAAwB,gBAAgB,WAAW;EACnD,uBAAuB,CAAC,CAAC,SAAS;EAClC,oBAAoB,CAAC,EACnB,UAAU,UACV,UAAU,WACV,UAAU,UACV,UAAU;EAEZ,uCAAuC,oBAAoB;EAC3D,sBAAsB,CAAC,SAAS;CAClC;AAMF;;;;;;;;;;;;;;AAeA,SAAS,6BAA6B,MAQZ;CAExB,MAAM,WAAW,sBADF,6BAA6B,IACL,CAAM;CAE7C,QAAQ,SAAS,QAAjB;EACE,KAAK;GACH,WACE,cAAc,SAAS,SAAS,uBAAuB,KAAK,KAAK,KAAK,SAAS,OAAO,EACxF;GACA,OAAO;EACT,KAAK;GAEH,WAAW,CAAC,CAAC,OAAO,KAAK,WAAW;GACpC,WACE,cAAc,SAAS,SAAS,KAAK,KAAK,KAAK,cAAc,KAAK,YAAY,KAAK,SAAS,OAAO,EACrG;GACA,OAAO;EACT,KAAK;GACH,sCACE,KAAK,UACL,KAAK,WACL,KAAK,WACP;GACA,IAAI,KAAK,oBAAoB,KAAK,aAAa;IAE7C,WAAA,CAAA,CAAQ,IAAI,KAAK,iBAAiB,KAAK,QAAQ;IAC/C,oBAAoB,CAAC,CAAC,IACpB,KAAK,SAAS,aACd,KAAK,eACP;GACF;GACA,OAAO;EAET,KAAK,qBAGH,OAAO;EACT,KAAK,UAGH,OAAO;EACT,KAAK,SAGH,MAAM,IAAI,MACR,yEAAyE,SAAS,SAAS,kBAC7F;CACJ;AACF;AAEA,SAAS,iBACP,MACA,MACA,QACQ;CACR,MAAM,gBAAgB,OAAO,YACzB,iBAAiB,OAAO,WAAW,IAAI,IACvC,qBAAqB,IAAI;CAE7B,OACE,eAAe,QAAQ,aACvB,eAAe,iBAAiB,aAChC,OAAO,aACP,mBAAmB,IAAI;AAE3B;AAEA,SAAS,iBAAiB,MAAyB,WAAyB;CAE1E,IADiB,OAAO,yBAAyB,MAAM,iBACnD,CAAA,EAAU,UAAU,WACtB;CAGF,OAAO,eAAe,MAAM,mBAAmB;EAC7C,OAAO;EACP,UAAU;EACV,YAAY;EACZ,cAAc;CAChB,CAAC;AACH;;;;;;;;;;;;;;;AAgBA,SAAS,qBACP,MACA,eACM;CACN,IAAI,CAAC,eAAe;CAEpB,IADiB,OAAO,yBAAyB,MAAM,oBACnD,CAAA,EAAU,UAAU,eACtB;CAGF,OAAO,eAAe,MAAM,sBAAsB;EAChD,OAAO;EACP,UAAU;EACV,YAAY;EACZ,cAAc;CAChB,CAAC;AACH;AAEA,SAAgB,SACd,MACA,SAA4B,CAAC,GACvB;CACN,MAAM,OAAO,OAAO,QAAQ,KAAK;CACjC,MAAM,sBAAsB,OAAO;CAEnC,SAAS,oBACP,aACA,UACQ;EACR,MAAM,kBAAkB,uBAAuB,SAAS;EACxD,MAAM,UAAU,kBACZ,oBAAoB,iBAAiB,IAAI,IACzC;EAIJ,MAAM,mBAAmB;GACvB,MAAM,SAAS;GACf,eAAe,SAAS;EAC1B;EAEA,SAAS,OAAO;EAChB,SAAS,cAAc;EACvB,SAAS,gBAAgB,kBACpB,oBAAoB,iBAAiB,IAAI,IAC1C,KAAA;EACJ,MAAM,gBAAgB,iBAAiB,MAAM,MAAM;GACjD,GAAG,SAAS;GACZ,GAAG;EACL,CAAC;EACD,SAAS,SAAS;GAChB,GAAG,SAAS;GACZ,GAAG;GACH,WAAW;EACb;EACA,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;GAChB,KAAK;GACL,SAAS,CAAC;GACV,UAAU,CAAC;GACX,WAAW;GACX,SAAS,CAAC;GACV,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS;EACX;EAEF,SAAS,OAAO,YAAY;EAC5B,SAAS,cAAc;EACvB,iBAAiB,MAAM,aAAa;EACpC,qBAAqB,MAAM,SAAS,aAAa;EAEjD,IAAI,gBAAgB,SAAS;GAC3B,MAAM,UAAU,WAAW;GAC3B,MAAM,oBAAoB,QAAQ,IAAI,OAAO;GAE7C,IAAI,qBAAqB,sBAAsB,UAC7C,MAAM,IAAI,mBACR,6CAA6C,YAAY,QAAQ,QAAQ,mEAEzE,uCACA;IACE;IACA;IACA,WAAW;GACb,CACF;GAGF,QAAQ,OAAO,WAAW;GAC1B,QAAQ,IAAI,SAAS,QAAQ;EAC/B;EAEA,oBAAoB,CAAC,CAAC,IAAI,MAAM,OAAO;EASvC,6BAA6B,UAAU,gBAAgB;EAEvD,OAAO;CACT;CAKA,MAAM,gBAAgB,uBAAuB;CAC7C,MAAM,iBACJ,uBAAuB,eAAe,MAAM,IAAI,KAAK,KAAA;CACvD,MAAM,sBAAsB,oBAAoB,aAAa;CAG7D,IAAI,WAAW,CAAC,CAAC,IAAI,IAAI,GAAG;EAC1B,MAAM,WAAW,WAAW,CAAC,CAAC,IAAI,IAAI;EACtC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,2BAA2B,KAAK,oDAClC;EAcF,IAXgB,6BAA6B;GAC3C;GACA;GACA;GACA;GACA;GACA;GACA,aAAa;GACb,WAAW;GACX;EACF,CACI,GAAS;CACf;CAIA,MAAM,YAAY,KAAK,YAAY;CACnC,KAAK,MAAM,CAAC,aAAa,aAAa,WAAW,CAAC,CAAC,QAAQ,GAAG;EAC5D,MAAM,aAAa,YAAY,YAAY,MAAM;EACjD,MAAM,cAAc,SAAS,MAAM,YAAY,MAAM;EACrD,IAAI,EAAE,cAAc,gBAAgB,gBAAgB,MAAM;EAa1D,IAXgB,6BAA6B;GAC3C;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW;GACX;EACF,CACI,GAAS;CACf;CAOA,MAAM,uBACJ,uBAAuB,eAAe,MAAM,IAAI,KAAK,KAAA;CAIvD,MAAM,iBAAiB,uBAAuB;CAU9C,IAAI;CACJ,IAAI,OAAO,WACT,gBAAgB,iBAAiB,OAAO,WAAW,IAAI;CAEzD,IAAI,CAAC,eACH,gBAAgB,qBAAqB,IAAI;CAE3C,MAAM,yBAAS,IAAI,IAA6B;CAChD,MAAM,0BAAU,IAAI,IAA8B;CAClD,IAAI;CAEJ,WACE,0BAA0B,KAAK,oBAC/B,gBAAgB,UAAU,WAC5B;CACA,IAAI,eAAe,QACjB,WACE,2BAA2B,OAAO,KAAK,cAAc,MAAM,CAAC,CAAC,OAAO,WACpE,OAAO,KAAK,cAAc,MAAM,CAClC;CAGF,IAAI,eAAe,QAAQ;EAGzB,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QACzC,cAAc,MAChB,GAAkC;GAEhC,MAAM,UAAmC,EAAE,GAAG,SAAS,MAAM;GAC7D,IAAI,SAAS,aAAa,KAAA,GAAW,QAAQ,WAAW,SAAS;GACjE,IAAI,SAAS,YAAY,KAAA,GAAW,QAAQ,UAAU,SAAS;GAC/D,IAAI,SAAS,gBAAgB,KAAA,GAC3B,QAAQ,cAAc,SAAS;GACjC,IAAI,SAAS,cAAc,KAAA,GACzB,QAAQ,YAAY,SAAS;GAG/B,MAAM,QAAyB,EAC7B,MAAM,SAAS,KACjB;GAGA,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAChC,MAAM,QAAQ;GAIhB,IAAI,SAAS,cAAc,KAAA,GACzB,MAAM,YAAY,SAAS;GAE7B,IAAI,SAAS,aAAa,KAAA,GACxB,MAAM,WAAW,SAAS;GAE5B,IAAI,SAAS,cAAc,KAAA,GACzB,MAAM,YAAY,SAAS;GAE7B,IAAI,SAAS,aAAa,KAAA,GACxB,MAAM,WAAW,SAAS;GAE5B,IAAI,SAAS,mBAAmB,KAAA,GAC9B,MAAM,iBAAiB,SAAS;GAKlC,IAAI,SAAS,YAAY,KAAA,GACvB,MAAM,UAAU,SAAS;QACpB,IAAI,QAAQ,YAAY,KAAA,GAAW;IAExC,MAAM,UAAU,QAAQ;IACxB,OAAO,MAAM,OAAO;GACtB;GAEA,OAAO,IAAI,WAAW,KAAK;EAC7B;EAEA,WACE,uBAAuB,OAAO,KAAK,cAAc,KAAK,eACxD;EAIA,cACE,uBACA,OAAO,WAAW,eAClB,cAAc,eACd;CACJ,OAAO;EAGL,WACE,wCAAwC,KAAK,+BAC/C;EACA,cAAc;CAChB;CAIA,MAAM,aAAa,mBAAmB,CAAC,CAAC,IAAI,IAAI;CAChD,IAAI,cAAc,WAAW,OAAO,GAAG;EACrC,WACE,uBAAuB,WAAW,KAAK,wBAAwB,MACjE;EAEA,KAAK,MAAM,CAAC,WAAW,qBAAqB,YAAY;GAItD,MAAM,OAA8B;GACpC,MAAM,gBAAgB,OAAO,IAAI,SAAS;GAE1C,IAAI,eAAe;IAGjB,MAAM,aAAwB;KAC5B,GAAG,cAAc;KACjB,GAAG;IACL;IACA,MAAM,cAA+B;KACnC,MAAM,KAAK,QAAQ,cAAc;KACjC,OAAO;IACT;IAGA,IAAI,WAAW,MACb,OAAO,WAAW;IAIpB,IAAI,KAAK,cAAc,KAAA,GACrB,YAAY,YAAY,KAAK;SACxB,IAAI,cAAc,cAAc,KAAA,GACrC,YAAY,YAAY,cAAc;IAExC,IAAI,KAAK,cAAc,KAAA,GACrB,YAAY,YAAY,KAAK;SACxB,IAAI,cAAc,cAAc,KAAA,GACrC,YAAY,YAAY,cAAc;IAExC,IAAI,KAAK,aAAa,KAAA,GACpB,YAAY,WAAW,KAAK;SACvB,IAAI,cAAc,aAAa,KAAA,GACpC,YAAY,WAAW,cAAc;IAEvC,IAAI,OAAO,KAAK,mBAAmB,UACjC,YAAY,iBAAiB,KAAK;SAC7B,IAAI,OAAO,cAAc,mBAAmB,UACjD,YAAY,iBAAiB,cAAc;IAI7C,IAAI,KAAK,aAAa,MAAM;KAE1B,YAAY,WAAW;KACvB,WAAW,WAAW;IACxB,OAAO,IAAI,KAAK,aAAa,KAAA,GAC3B,YAAY,WAAW,KAAK;SACvB,IAAI,cAAc,aAAa,KAAA,GACpC,YAAY,WAAW,cAAc;IAIvC,IAAI,KAAK,YAAY,KAAA,GAAW;KAC9B,YAAY,UAAU,KAAK;KAC3B,OAAO,YAAY,OAAO;IAC5B,OAAO,IAAI,cAAc,YAAY,KAAA,GACnC,YAAY,UAAU,cAAc;IAGtC,OAAO,IAAI,WAAW,WAAW;IACjC,WACE,uCAAuC,UAAU,SAAS,YAAY,MACxE;GACF,OAAO;IAEL,MAAM,UAAqB;IAC3B,MAAM,WAA4B;KAChC,MAAM,KAAK,QAAQ;KACnB,OAAO;IACT;IAGA,IAAI,KAAK,cAAc,KAAA,GACrB,SAAS,YAAY,KAAK;IAE5B,IAAI,KAAK,cAAc,KAAA,GACrB,SAAS,YAAY,KAAK;IAE5B,IAAI,KAAK,aAAa,KAAA,GACpB,SAAS,WAAW,KAAK;IAE3B,IAAI,OAAO,KAAK,mBAAmB,UACjC,SAAS,iBAAiB,KAAK;IAGjC,IAAI,KAAK,aAAa,MAAM;KAC1B,SAAS,WAAW;KACpB,QAAQ,WAAW;IACrB,OAAO,IAAI,KAAK,aAAa,KAAA,GAC3B,SAAS,WAAW,KAAK;IAK3B,IAAI,KAAK,YAAY,KAAA,GAAW;KAC9B,SAAS,UAAU,KAAK;KACxB,OAAO,SAAS,OAAO;IACzB;IAEA,OAAO,IAAI,WAAW,QAAQ;IAC9B,WACE,8BAA8B,UAAU,wBAAwB,KAAK,QAAQ,QAC/E;GACF;EACF;CACF;CAKA,IAAI;CACJ,MAAM,wBACJ,OAAO,gBAAgB,eAAe,iBAAiB;CACzD,IAAI,uBAAuB;EAEzB,MAAM,aACJ,OAAO,0BAA0B,YAAY,CAAC,IAAI;EACpD,qBAAqB;GACnB,MAAM,WAAW,QAAQ;GACzB,OAAO,WAAW,SAAS;GAC3B,YAAY,WAAW,cAAc;GACrC,cAAc,WAAW,gBAAgB;GACzC,uBAAuB,WAAW,yBAAyB;EAC7D;EAGA,MAAM,YAAY,mBAAmB;EACrC,MAAM,iBAAiB,OAAO,IAAI,SAAS;EAC3C,wBAAwB,QAAQ,kBAAkB;EAClD,IAAI,CAAC,gBACH,WACE,yBAAyB,UAAU,iCAAiC,MACtE;CAEJ;CAEA,IAAI,eAAe,SAEjB,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAC3C,cAAc,OAChB,GACE,QAAQ,IAAI,YAAY,SAAS;CAQrC,MAAM,kBACJ,OAGA;CACF,IAAI,iBAAiB;EACnB,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,eAAe,GAClE,QAAQ,IAAI,YAAY,SAAS;EAEnC,WACE,qBAAqB,QAAQ,KAAK,eAAe,KAAK,uBACxD;CACF;CAOA,MAAM,mBAAmB,sBAAsB,IAAI;CAGnD,IAAI,iBAAiB,SAAS,GAAG;EAE/B,MAAM,aAAa,iBAAiB,iBAAiB,SAAS;EAC9D,MAAM,cAAc,UAAU,UAAU;EAExC,IAAI,aAAa;GACf,MAAM,iBAAiB,YAAY,QAAQ,iBAAiB;GAC5D,MAAM,gBAAgB,OAAO;GAI7B,IAAI,kBAAkB,KAAA,KAAa,mBAAmB,eACpD,MAAM,mBAAmB,qBACvB,MACA,eACA,YACA,cACF;EAEJ;CACF;CAQA,MAAM,YAAY,iBAAiB,MAAM,MAAM,MAAM;CACrD,iBAAiB,MAAM,SAAS;CAGhC,IAAI;CACJ,IAAI,eAAe,QAAQ;EAGzB,SAAS;GACP,KAAK,cAAc,OAAO;GAC1B,SACE,cAAc,OAAO,SAAS,KAAK,SAA0B;IAC3D,MAAM,IAAI;IACV,SAAS,IAAI,WAAW,CAAC;IACzB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;GAChB,EAAE,KAAK,CAAC;GACV,UAAU,CAAC;GACX,WAAW,cAAc,OAAO,aAAa;GAE7C,SAAS,2BAA2B,cAAc,OAAO,OAAO;GAChE,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS,cAAc,OAAO,WAAW;GACzC,aAAa,cAAc;EAC7B;EACA,WACE,8CAA8C,KAAK,IAAI,OAAO,KAAK,cAAc,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,UAChH;CACF,OAEE,SAAS;EACP,KAAK;EACL,SAAS,CAAC;EACV,UAAU,CAAC;EACX;EACA,SAAS,CAAC;EACV,aAAa,CAAC;EACd,cAAc,CAAC;EACf,SAAS;EACT,aAAa,KAAA;CACf;CAGF,IAAI,OAAO,SAAS;EAClB,sBAAsB,OAAO,SAAS,OAAO,QAAQ,CAAC;EACtD,IAAI,cAAc,WAAW,OAAO,GAClC,sBAAsB,OAAO,SAAS,WAAW,QAAQ,CAAC;CAE9D;CAIA,MAAM,kBAAkB,eAAe;CACvC,IAAI;CAGJ,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,GACjD,aAAa,kBAAkB,MAAM,MAAM;MAE3C,WACE,oBAAoB,gBAAgB,OAAO,qCAAqC,MAClF;CAKF,IAAI,eAAmC,eAAe;CACtD,IAAI,CAAC,cAAc;EACjB,MAAM,QAAQ,OAAO,eAAe,IAAI;EACxC,IAAI,OAAO,QAAQ,MAAM,SAAS,gBAAgB,MAAM,SAAS,UAC/D,eAAe,MAAM;CAEzB;CAIA,MAAM,eAAe;EACnB,GAAG,eAAe;EAClB,GAAG;EACH;CACF;CAIA,MAAM,gBAAgB,cAClB,oBAAoB,aAAa,IAAI,IACrC,KAAA;CAIJ,MAAM,aACJ,OAAO,cAAc,eAAe,cAAc;CAGpD,MAAM,kBAAkB,iBAAiB;CAEzC,WAAW,CAAC,CAAC,IAAI,iBAAiB;EAChC;EACA;EACA,aAAa;EACb,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA,OAAO,eAAe;EAKtB,YAAY,eAAe,cAAc,oBAAoB,IAAI;EACjE;EACA;EACA,SAAS;EACT,gBAAgB,eAAe;EAC/B;EACA;CAIF,CAAC;CAMD,oBAAoB,CAAC,CAAC,IAAI,MAAM,eAAe;CAI/C,qBAAqB,MAAM,aAAa;CAExC,WACE,8BAA8B,KAAK,SAAS,gBAAgB,oBAAoB,OAAO,UAAU,QAAQ,YAAY,UAAU,MAAM,iBAAiB,UAAU,GAAG,kBACrK;CAUA,MAAM,aAAa,eAAe;CAClC,IAAI,cAAc,CAAC,qBAAqB,CAAC,CAAC,IAAI,UAAU,GAAG;EAEzD,qBAAqB,CAAC,CAAC,IAAI,UAAU;EAErC,WACE,wDAAwD,YAC1D;EAGA,MAAM,WAAW,wBAAwB,UAAU;EAInD,KAAK,MAAM,WAAW,UAEpB,IAAI,CAAC,wBAAwB,QAAQ,SAAS,GAAG;GAE/C,IACE,QAAQ,eACR,eACA,QAAQ,gBAAgB,aACxB;IACA,WACE,mCAAmC,QAAQ,UAAU,sBAAsB,aAC7E;IACA;GACF;GAEA,WACE,wCAAwC,QAAQ,UAAU,mBAAmB,YAC/E;GACA,qBACE,QAAQ,WACR,QAAQ,OACR,QAAQ,WACV;EACF;CAEJ;AACF;AAEA,SAAgB,mBACd,YAMA,uBACM;CACN,MAAM,aAAa,UAAU,UAAU;CACvC,IAAI,YACF,WAAW,wBAAwB;CAMrC,eAAe,CAAC,CAAC,IACf,YACA,qBAGF;AACF;AAaA,SAAS,4BACP,cACmD;CACnD,IAAI,CAAC,cACH;CAGF,MAAM,aACJ,OAAO,iBAAiB,YAAY,CAAC,IAAI;CAC3C,OAAO;EACL,MAAM,WAAW,QAAQ;EACzB,OAAO,WAAW,SAAS;EAC3B,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB;EACzC,uBAAuB,WAAW,yBAAyB;CAC7D;AACF;AAEA,SAAS,wBACP,QACA,oBACM;CACN,IAAI,CAAC,oBACH;CAGF,MAAM,YAAY,mBAAmB;CACrC,MAAM,gBAAgB,OAAO,IAAI,SAAS;CAC1C,IAAI,eAAe;EACjB,OAAO,IAAI,WAAW;GACpB,GAAG;GACH,UACE,cAAc,YAAY,mBAAmB,SAAS;GACxD,OAAO;IACL,GAAG,cAAc;IACjB,WAAW,cAAc,OAAO,aAAa;IAC7C,SAAS;IACT,WAAW;KACT,GAAG,cAAc,OAAO;KACxB,iBAAiB;KACjB,GAAG;IACL;GACF;EACF,CAAC;EACD;CACF;CAEA,OAAO,IAAI,WAAW;EACpB,MAAM;EACN,SAAS;EACT,UAAU,mBAAmB,SAAS;EACtC,OAAO;GACL,WAAW;GACX,SAAS;GACT,WAAW;IACT,iBAAiB;IACjB,GAAG;GACL;EACF;CACF,CAAC;AACH;AAEA,SAAS,sBACP,iBACA,iBACmB;CACnB,MAAM,SAAS,CAAC,GAAI,mBAAmB,CAAC,CAAE;CAC1C,MAAM,OAAO,IAAI,IAAI,OAAO,KAAK,UAAU,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC;CAEvE,KAAK,MAAM,SAAS,mBAAmB,CAAC,GAAG;EACzC,IAAI,CAAC,OAAO,QAAQ,KAAK,IAAI,MAAM,IAAI,GACrC;EAEF,OAAO,KAAK,KAAK;EACjB,KAAK,IAAI,MAAM,IAAI;CACrB;CAEA,OAAO;AACT;AA8CA,SAAS,iCACP,cAC+C;CAC/C,IACE,cAAc,WAAW,mBACzB,cAAc,OAAO,WAAW,iBAEhC,OAAO;CAGT,IAAI,cAAc,SAAS,cACzB,OAAO;CAGT,IAAI,cAAc,SAAS,mBACzB,OAAO;AAIX;AAEA,SAAS,sBACP,SACA,cACM;CACN,IAAI,CAAC,cACH;CAGF,KAAK,MAAM,CAAC,WAAW,iBAAiB,cAAc;EACpD,MAAM,UAAU,cAAc,WAAW,cAAc,OAAO;EAC9D,MAAM,gBAAgB,iCAAiC,YAAY;EACnE,IAAI,CAAC,WAAW,CAAC,eACf;EAGF,MAAM,aAAa,YAAY,SAAS;EACxC,MAAM,iBAAiB,QAAQ;EAC/B,IAAI,CAAC,gBACH;EAGF,QAAQ,cAAc;GACpB,GAAG;GACH,GAAI,UACA,EAAE,MAAM,OAAO,OAAO,CAAC,CAAC,YAAY,EAA8B,IAClE,CAAC;GACL,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;EAC3C;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,6BACd,UACA,kBAIM;CACN,MAAM,QAAQ,oBAAoB;CAClC,MAAM,gCAAgB,IAAI,IAAY;CAEtC,MAAM,YAAY,SAAmC;EACnD,IAAI,MACF,cAAc,IAAI,IAAI;CAE1B;CAEA,MAAM,6BAAa,IAAI,IAAY;CACnC,MAAM,iBAAiB,SAAmC;EACxD,IAAI,MAAM,WAAW,IAAI,IAAI;CAC/B;CACA,cAAc,SAAS,IAAI;CAC3B,cAAc,SAAS,aAAa;CACpC,cAAc,kBAAkB,IAAI;CACpC,cAAc,kBAAkB,aAAa;CAE7C,KAAK,MAAM,QAAQ,YACjB,SAAS,IAAI;CAGf,KAAK,MAAM,CAAC,KAAK,cAAc,WAAW,GAAG;EAC3C,MAAM,iBACJ,CAAC,CAAC,UAAU,WAAW,WAAW,IAAI,UAAU,OAAO;EACzD,MAAM,eACJ,CAAC,CAAC,UAAU,oBACZ,UAAU,iBAAiB,MAAM,MAAM,WAAW,IAAI,CAAC,CAAC;EAE1D,IAAI,cAAc,YAAY,kBAAkB,cAAc;GAC5D,UAAU,mBAAmB,KAAA;GAC7B,UAAU,kBAAkB,KAAA;GAC5B,UAAU,mBAAmB,KAAA;GAC7B,SAAS,GAAG;GACZ,SAAS,UAAU,IAAI;GACvB,SAAS,UAAU,aAAa;EAClC;CACF;CAEA,KAAK,MAAM,QAAQ,eACjB,MAAM,OAAO,IAAI;AAErB;AAEA,SAAS,sCACP,UACA,WACA,aACM;CACN,MAAM,iBAAiB,UAAU,mBAAmB,CAAC;CACrD,MAAM,oBACJ,UAAU,QAAQ,aAClB,eAAe,aACf,SAAS,QAAQ,aACjB,SAAS,OAAO,aAChB,mBAAmB,SAAS,WAAW;CAEzC,SAAS,SAAS;EAChB,GAAG;EACH,GAAG,SAAS;EACZ,WAAW;CACb;CAEA,IAAI,UAAU,QACZ,KAAK,MAAM,CAAC,WAAW,OAAO,OAAO,QAAQ,UAAU,MAAM,GAAG;EAC9D,IAAI,CAAC,SAAS,OAAO,IAAI,SAAS,GAAG;GACnC,SAAS,OAAO,IAAI,WAAW,wBAAwB,EAAE,CAAC;GAC1D;EACF;EAEA,MAAM,gBAAgB,SAAS,OAAO,IAAI,SAAS;EACnD,IAAI,CAAC,eACH;EAGF,SAAS,OAAO,IAAI,WAAW,mBAAmB,eAAe,EAAE,CAAC;CACtE;CAGF,MAAM,qBAAqB,4BACzB,SAAS,OAAO,YAClB;CACA,IAAI,oBAAoB;EACtB,SAAS,qBAAqB;EAC9B,wBAAwB,SAAS,QAAQ,kBAAkB;CAC7D;CAEA,IAAI,UAAU,SACZ,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,UAAU,OAAO,GACpE,SAAS,QAAQ,IAAI,YAAY,SAAS;CAI9C,IAAI,UAAU,QAAQ;EACpB,MAAM,iBAAmC;GACvC,KAAK,UAAU,OAAO,OAAO;GAC7B,SACE,UAAU,OAAO,SAAS,KAAK,SAA0B;IACvD,MAAM,IAAI;IACV,SAAS,IAAI,WAAW,CAAC;IACzB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;GAChB,EAAE,KAAK,CAAC;GACV,UAAU,CAAC;GACX,WAAW,UAAU,OAAO,aAAa;GACzC,SAAS,2BAA2B,UAAU,OAAO,OAAO;GAC5D,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS,UAAU,OAAO,WAAW;GACxB;EACf;EAEA,sBAAsB,eAAe,SAAS,SAAS,OAAO,QAAQ,CAAC;EAEvE,SAAS,SAAS;GAChB,GAAI,SAAS,UAAU,CAAC;GACxB,GAAG;GACH,WAAW,eAAe;GAC1B,KAAK,eAAe,OAAO,SAAS,QAAQ,OAAO;GACnD,SAAS;IACP,GAAI,SAAS,QAAQ,WAAW,CAAC;IACjC,GAAI,eAAe,WAAW,CAAC;GACjC;GACA,SAAS,sBACP,SAAS,QAAQ,SACjB,eAAe,OACjB;GACA,aAAa,eAAe,SAAS,QAAQ;EAC/C;CACF,OAAO,IAAI,CAAC,SAAS,QACnB,SAAS,SAAS;EAChB,KAAK;EACL,SAAS,CAAC;EACV,UAAU,CAAC;EACX,WAAW;EACX,SAAS,CAAC;EACV,aAAa,CAAC;EACd,cAAc,CAAC;EACf,SAAS;EACI;CACf;MACK;EACL,SAAS,OAAO,YAAY;EAC5B,SAAS,OAAO,cAAc,eAAe,SAAS,OAAO;CAC/D;CAEA,IAAI,UAAU,oBAAoB,KAAA,GAAW;EAC3C,SAAS,kBAAkB,UAAU;EACrC,SAAS,aAAa,KAAA;CACxB,OAAO,IAAI,CAAC,SAAS,mBAAmB,CAAC,SAAS,YAChD,SAAS,aAAa,kBAAkB,SAAS,MAAM,SAAS,MAAM;CAGxE,IAAI,aAAa;EACf,SAAS,cAAc;EACvB,SAAS,gBAAgB,oBACvB,aACA,SAAS,IACX;CACF,OAAO,IAAI,CAAC,SAAS,eAAe,UAAU,aAC5C,SAAS,cAAc,UAAU;CAGnC,IAAI,UAAU,SACZ,SAAS,UAAU,cACf,mBAAmB,UAAU,SAAS,WAAW,IACjD,UAAU;CAGhB,IAAI,CAAC,SAAS,kBAAkB,UAAU,UACxC,SAAS,iBAAiB,UAAU;CAGtC,SAAS,aACP,UAAU,cAAc,eAAe,cAAc,SAAS;CAChE,6BAA6B,QAAQ;AACvC;AAEA,SAAgB,qBACd,MACA,WACA,aACM;CAIN,MAAM,kBAAkB,UAAU,aAAa;CAC/C,MAAM,qBAAqB,cACvB,oBAAoB,aAAa,eAAe,IAChD,UAAU;CACd,MAAM,kBAAmB,sBAAsB;CAgB/C,MAAM,oBAAoB,sBAAsB,eAAe;CAC/D,IAAI,mBAAmB;EACrB,MAAM,WAAW,WAAW,CAAC,CAAC,IAAI,iBAAiB;EACnD,IAAI;OACc,6BAA6B;IAC3C;IACA;IACA;IACA;IACA,aAAa;IACb;IACA,WAAW;GACb,CACI,MAAY,UAAU;EAAA;CAK9B;CAEA,IAAI,WAAW,CAAC,CAAC,IAAI,eAAe,GAAG;EACrC,MAAM,WAAW,WAAW,CAAC,CAAC,IAAI,eAAe;EACjD,IAAI,CAAC,UAAU;EAUf,IATgB,6BAA6B;GAC3C;GACA;GACA;GACA;GACA,aAAa;GACb;GACA,WAAW;EACb,CACI,MAAY,UAAU;CAC5B;CAQA,MAAM,mBACJ,UAAU,WAAW,cACjB,mBAAmB,UAAU,SAAS,WAAW,IACjD,UAAU;CAQhB,MAAM,kBAAkB,cAAc,WAAW;EAC/C,OAAgB,kBAAkB;CACpC;CACA,OAAO,eAAe,iBAAiB,QAAQ,EAAE,OAAO,gBAAgB,CAAC;CAGzE,MAAM,yBAAS,IAAI,IAA6B;CAChD,MAAM,aAAa,mBAAmB,CAAC,CAAC,IAAI,eAAe;CAC3D,IAAI,UAAU,QACZ,KAAK,MAAM,CAAC,WAAW,OAAO,OAAO,QAAQ,UAAU,MAAM,GAC3D,OAAO,IAAI,WAAW,wBAAwB,EAAE,CAAC;CAKrD,MAAM,0BAAU,IAAI,IAA8B;CAClD,IAAI,UAAU,SACZ,KAAK,MAAM,CAAC,YAAY,cAAc,OAAO,QAAQ,UAAU,OAAO,GACpE,QAAQ,IAAI,YAAY,SAAS;CAKrC,MAAM,SAAS,UAAU,mBAAmB,CAAC;CAC7C,MAAM,YAAY,OAAO,aAAa,mBAAmB,eAAe;CAIxE,IAAI;CACJ,IAAI,UAAU,QAAQ;EAGpB,SAAS;GACP,KAAK,UAAU,OAAO;GACtB,SACE,UAAU,OAAO,SAAS,KAAK,SAA0B;IACvD,MAAM,IAAI;IACV,SAAS,IAAI,WAAW,CAAC;IACzB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;GAChB,EAAE,KAAK,CAAC;GACV,UAAU,CAAC;GACX,WAAW,UAAU,OAAO;GAC5B,SAAS,2BAA2B,UAAU,OAAO,OAAO;GAC5D,aAAa,CAAC;GACd,cAAc,CAAC;GACf,SAAS,UAAU,OAAO,WAAW;GACxB;EACf;EACA,sBAAsB,OAAO,SAAS,OAAO,QAAQ,CAAC;EACtD,sBAAsB,OAAO,SAAS,YAAY,QAAQ,CAAC;EAC3D,WACE,8CAA8C,KAAK,IAAI,OAAO,KAAK,UAAU,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,UAC5G;CACF,OAEE,SAAS;EACP,KAAK;EACL,SAAS,CAAC;EACV,UAAU,CAAC;EACX;EACA,SAAS,CAAC;EACV,aAAa,CAAC;EACd,cAAc,CAAC;EACf,SAAS;EACI;CACf;CAKF,MAAM,kBAAkB,UAAU;CAClC,IAAI;CAIJ,IAAI,CAAC,mBAAmB,gBAAgB,WAAW,GAAG;EACpD,aAAa,kBAAkB,MAAM,MAAM;EAC3C,WACE,wCAAwC,KAAK,aAAa,WAAW,OAAO,YAC9E;CACF,OACE,WACE,oBAAoB,gBAAgB,OAAO,qCAAqC,MAClF;CAKF,MAAM,gBAAgB;CAGtB,MAAM,aACJ,UAAU,cAAc,OAAO,cAAc;CAW/C,WAAW,CAAC,CAAC,IAAI,iBAAiB;EAChC,MAAM;EACN;EACA,aAAa;EACb;EACA;EACA;EACA;EACA;EACA;EACA,OAAO,UAAU;EAGjB,YAAY,UAAU,cAAc,oBAAoB,eAAe;EACvE;EACA,gBAAgB,UAAU;EAC1B,SAAS;EACT,gBAAgB,UAAU;EAC1B;CACF,CAAC;CAID,qBAAqB,iBAAiB,aAAa;CAEnD,WACE,iBAAiB,gBAAgB,uBAAuB,gBAAgB,IAAI,OAAO,KAAK,WAAW,QAAQ,KAAK,UAClH;CAUA,MAAM,aAAa,UAAU;CAC7B,IAAI,cAAc,CAAC,qBAAqB,CAAC,CAAC,IAAI,UAAU,GAAG;EAEzD,qBAAqB,CAAC,CAAC,IAAI,UAAU;EAErC,WACE,wDAAwD,YAC1D;EAGA,MAAM,WAAW,wBAAwB,UAAU;EAInD,KAAK,MAAM,WAAW,UAEpB,IAAI,CAAC,wBAAwB,QAAQ,SAAS,GAAG;GAE/C,IACE,QAAQ,eACR,eACA,QAAQ,gBAAgB,aACxB;IACA,WACE,mCAAmC,QAAQ,UAAU,sBAAsB,aAC7E;IACA;GACF;GAEA,WACE,wCAAwC,QAAQ,UAAU,mBAAmB,YAC/E;GACA,qBACE,QAAQ,WACR,QAAQ,OACR,QAAQ,WACV;EACF;CAEJ;AACF"}
@@ -4,7 +4,8 @@ import { RegisteredClass, SmrtObjectConstructor } from './types';
4
4
  declare global {
5
5
  var __smrtRegistryClasses: Map<string, RegisteredClass> | undefined;
6
6
  var __smrtRegistryCollections: Map<string, typeof SmrtCollection> | undefined;
7
- var __smrtRegistryCollectionCache: LRUCache<string, SmrtCollection<any>> | undefined;
7
+ var __smrtRegistryCollectionCache: // 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.
8
+ LRUCache<string, SmrtCollection<any>> | undefined;
8
9
  var __smrtRegistryDbInstanceIds: WeakMap<object, number> | undefined;
9
10
  var __smrtRegistryNextDbId: number | undefined;
10
11
  var __smrtRegistryInheritanceChainCache: LRUCache<string, string[]> | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"shared-state.d.ts","sourceRoot":"","sources":["../../src/registry/shared-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,CAAC;IAEpE,IAAI,yBAAyB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC,GAAG,SAAS,CAAC;IAO9E,IAAI,6BAA6B,EAE7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GACrC,SAAS,CAAC;IAEd,IAAI,2BAA2B,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAErE,IAAI,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/C,IAAI,mCAAmC,EACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC1B,SAAS,CAAC;IAEd,IAAI,6BAA6B,EAC7B,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GACjD,SAAS,CAAC;IAEd,IAAI,+BAA+B,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAE7D,IAAI,kCAAkC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAMxE,IAAI,gCAAgC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAElE,IAAI,8BAA8B,EAC9B,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GACtC,SAAS,CAAC;IAEd,IAAI,mCAAmC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC3E;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SAEoB,CAAC;AAMjD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAQnD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB;;;EAOnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,GAAG,SAAS,CA0CpB;AAID,wBAAgB,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAKzD;AAED,wBAAgB,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC,CAQnE;AAED,wBAAgB,uBAAuB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAK7D;AAGD,wBAAgB,kBAAkB,IAAI,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAS1E;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1D;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,kBAAkB,IAAI,GAAG,CACvC,MAAM,EACN,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CACrC,CAQA;AAED,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAKlD;AAQD,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAKvD;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAQ5E;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAShE;AAED,wBAAgB,wBAAwB,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAK/D"}
1
+ {"version":3,"file":"shared-state.d.ts","sourceRoot":"","sources":["../../src/registry/shared-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,CAAC;IAEpE,IAAI,yBAAyB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC,GAAG,SAAS,CAAC;IAO9E,IAAI,6BAA6B,EACjC,AADmC,oNAAoN;IACvP,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;IAElD,IAAI,2BAA2B,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAErE,IAAI,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/C,IAAI,mCAAmC,EACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC1B,SAAS,CAAC;IAEd,IAAI,6BAA6B,EAC7B,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GACjD,SAAS,CAAC;IAEd,IAAI,+BAA+B,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAE7D,IAAI,kCAAkC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAMxE,IAAI,gCAAgC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAElE,IAAI,8BAA8B,EAC9B,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GACtC,SAAS,CAAC;IAEd,IAAI,mCAAmC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC3E;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SAEoB,CAAC;AAMjD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAQnD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB;;;EAOnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,GAAG,SAAS,CA0CpB;AAID,wBAAgB,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAKzD;AAED,wBAAgB,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC,CAQnE;AAED,wBAAgB,uBAAuB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAK7D;AAGD,wBAAgB,kBAAkB,IAAI,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAS1E;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1D;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,kBAAkB,IAAI,GAAG,CACvC,MAAM,EACN,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CACrC,CAQA;AAED,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAKlD;AAQD,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAKvD;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAQ5E;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAShE;AAED,wBAAgB,wBAAwB,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAK/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared-state.js","names":[],"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 { isDecoratorRuntimeFramePath } from '../utils/stack-frames';\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 * Frames from decorator-lowering runtime helpers (`@oxc-project/runtime` under\n * Vite 8, `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped too, so\n * the lowered `@smrt()` application resolves to the consumer module that declares\n * the class rather than the helper frame the compiler inserted (#1785).\n *\n * @param stackOverride - Optional stack string to parse instead of a fresh\n * `Error().stack`. Used by tests to simulate a lowered call pattern.\n * @returns The file path where the @smrt() decorator was called, or undefined\n */\nexport function getSourceFileFromStack(\n stackOverride?: string,\n): string | undefined {\n const stack = stackOverride ?? new 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 // Decorator-lowering runtime helper (oxc/tslib/swc/babel) — the frame\n // the compiler inserts between @smrt() and the declaring module (#1785)\n isDecoratorRuntimeFramePath(normalizedPath)\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA0EA,IAAa,kBACX,QAAQ,IAAI,iBAAiB,WAC5B,QAAQ,IAAI,OAAO,SAAS,MAAM,KAAK;AAI1C,IAAM,SAAS,aAAa,EAAE,OAAO,kBAAkB,UAAU,OAAO,CAAC;;;;;;AAOzE,SAAgB,WAAW,GAAG,MAAuB;CACnD,IAAI,iBAAiB;EACnB,MAAM,CAAC,SAAS,GAAG,QAAQ;EAC3B,OAAO,MACL,OAAO,YAAY,WAAW,UAAU,OAAO,OAAO,GACtD,KAAK,SAAS,IAAI,EAAE,MAAM,KAAK,IAAI,KAAA,CACrC;CACF;AACF;;;;;AAMA,SAAgB,uBAAuB;CACrC,MAAM,SAAS,oBAAsC,QAAQ,CAAC,CAAC;CAC/D,OAAO;EACL,WAAW,OAAO,aAAa,aAAa;EAC5C,mBACE,OAAO,aAAa,qBAAsB;CAC9C;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,uBACd,eACoB;CAEpB,MAAM,cADQ,kCAAiB,IAAI,MAAM,EAAA,CAAE,SAAS,GAAA,CAC3B,MAAM,IAAI;CAKnC,KAAK,MAAM,QAAQ,YAAY;EAE7B,MAAM,YAAY,KAAK,MACrB,yEACF;EACA,IAAI,WAAW;GAKb,MAAM,iBAJU,UAAU,EAIH,CACpB,QAAQ,aAAa,EAAE,CAAC,CACxB,QAAQ,OAAO,GAAG;GACrB,MAAM,YAAY,eAAe,YAAY;GAI7C,IAEE,UAAU,SAAS,yCAAyC,KAE3D,UAAU,SAAS,qBAAqB,KACvC,CAAC,UAAU,SAAS,WAAW,KAEjC,UAAU,SAAS,2BAA2B,KAG9C,4BAA4B,cAAc,GAE1C;GAEF,OAAO;EACT;CACF;AAEF;AAIA,SAAgB,aAA2C;CACzD,IAAI,CAAC,WAAW,uBACd,WAAW,wCAAwB,IAAI,IAA6B;CAEtE,OAAO,WAAW;AACpB;AAEA,SAAgB,iBAAqD;CACnE,IAAI,CAAC,WAAW,2BACd,WAAW,4CAA4B,IAAI,IAGzC;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,0BAA+C;CAC7D,IAAI,CAAC,WAAW,oCACd,WAAW,qDAAqC,IAAI,IAAoB;CAE1E,OAAO,WAAW;AACpB;AAGA,SAAgB,qBAA4D;CAC1E,IAAI,CAAC,WAAW,+BACd,WAAW,gCAAgC,IAAI,SAI7C,GAAG;CAEP,OAAO,WAAW;AACpB;AAEA,SAAgB,mBAA4C;CAC1D,IAAI,CAAC,WAAW,6BACd,WAAW,8CAA8B,IAAI,QAAwB;CAEvE,OAAO,WAAW;AACpB;AAEA,SAAgB,cAAsB;CACpC,IAAI,WAAW,2BAA2B,KAAA,GACxC,WAAW,yBAAyB;CAEtC,OAAO,WAAW;AACpB;AAEA,SAAgB,YAAY,OAAqB;CAC/C,WAAW,yBAAyB;AACtC;AAEA,SAAgB,qBAGd;CACA,IAAI,CAAC,WAAW,+BACd,WAAW,gDAAgC,IAAI,IAG7C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,uBAAoC;CAClD,IAAI,CAAC,WAAW,iCACd,WAAW,kDAAkC,IAAI,IAAY;CAE/D,OAAO,WAAW;AACpB;AAeA,SAAgB,sBAA8D;CAC5E,IAAI,CAAC,WAAW,gCACd,WAAW,iDAAiC,IAAI,QAG9C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,sBAAkD;CAChE,IAAI,CAAC,WAAW,qCAAqC;EACnD,MAAM,SAAS,qBAAqB;EACpC,WAAW,sCAAsC,IAAI,SAGnD,OAAO,SAAS;CACpB;CACA,OAAO,WAAW;AACpB;AAEA,SAAgB,2BAAiD;CAC/D,IAAI,CAAC,WAAW,qCACd,WAAW,sDAAsC,IAAI,IAAqB;CAE5E,OAAO,WAAW;AACpB"}
1
+ {"version":3,"file":"shared-state.js","names":[],"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 { isDecoratorRuntimeFramePath } from '../utils/stack-frames';\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: // 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>> | 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 * Frames from decorator-lowering runtime helpers (`@oxc-project/runtime` under\n * Vite 8, `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped too, so\n * the lowered `@smrt()` application resolves to the consumer module that declares\n * the class rather than the helper frame the compiler inserted (#1785).\n *\n * @param stackOverride - Optional stack string to parse instead of a fresh\n * `Error().stack`. Used by tests to simulate a lowered call pattern.\n * @returns The file path where the @smrt() decorator was called, or undefined\n */\nexport function getSourceFileFromStack(\n stackOverride?: string,\n): string | undefined {\n const stack = stackOverride ?? new 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 // Decorator-lowering runtime helper (oxc/tslib/swc/babel) — the frame\n // the compiler inserts between @smrt() and the declaring module (#1785)\n isDecoratorRuntimeFramePath(normalizedPath)\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuEA,IAAa,kBACX,QAAQ,IAAI,iBAAiB,WAC5B,QAAQ,IAAI,OAAO,SAAS,MAAM,KAAK;AAI1C,IAAM,SAAS,aAAa,EAAE,OAAO,kBAAkB,UAAU,OAAO,CAAC;;;;;;AAOzE,SAAgB,WAAW,GAAG,MAAuB;CACnD,IAAI,iBAAiB;EACnB,MAAM,CAAC,SAAS,GAAG,QAAQ;EAC3B,OAAO,MACL,OAAO,YAAY,WAAW,UAAU,OAAO,OAAO,GACtD,KAAK,SAAS,IAAI,EAAE,MAAM,KAAK,IAAI,KAAA,CACrC;CACF;AACF;;;;;AAMA,SAAgB,uBAAuB;CACrC,MAAM,SAAS,oBAAsC,QAAQ,CAAC,CAAC;CAC/D,OAAO;EACL,WAAW,OAAO,aAAa,aAAa;EAC5C,mBACE,OAAO,aAAa,qBAAsB;CAC9C;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,uBACd,eACoB;CAEpB,MAAM,cADQ,kCAAiB,IAAI,MAAM,EAAA,CAAE,SAAS,GAAA,CAC3B,MAAM,IAAI;CAKnC,KAAK,MAAM,QAAQ,YAAY;EAE7B,MAAM,YAAY,KAAK,MACrB,yEACF;EACA,IAAI,WAAW;GAKb,MAAM,iBAJU,UAAU,EAIH,CACpB,QAAQ,aAAa,EAAE,CAAC,CACxB,QAAQ,OAAO,GAAG;GACrB,MAAM,YAAY,eAAe,YAAY;GAI7C,IAEE,UAAU,SAAS,yCAAyC,KAE3D,UAAU,SAAS,qBAAqB,KACvC,CAAC,UAAU,SAAS,WAAW,KAEjC,UAAU,SAAS,2BAA2B,KAG9C,4BAA4B,cAAc,GAE1C;GAEF,OAAO;EACT;CACF;AAEF;AAIA,SAAgB,aAA2C;CACzD,IAAI,CAAC,WAAW,uBACd,WAAW,wCAAwB,IAAI,IAA6B;CAEtE,OAAO,WAAW;AACpB;AAEA,SAAgB,iBAAqD;CACnE,IAAI,CAAC,WAAW,2BACd,WAAW,4CAA4B,IAAI,IAGzC;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,0BAA+C;CAC7D,IAAI,CAAC,WAAW,oCACd,WAAW,qDAAqC,IAAI,IAAoB;CAE1E,OAAO,WAAW;AACpB;AAGA,SAAgB,qBAA4D;CAC1E,IAAI,CAAC,WAAW,+BACd,WAAW,gCAAgC,IAAI,SAI7C,GAAG;CAEP,OAAO,WAAW;AACpB;AAEA,SAAgB,mBAA4C;CAC1D,IAAI,CAAC,WAAW,6BACd,WAAW,8CAA8B,IAAI,QAAwB;CAEvE,OAAO,WAAW;AACpB;AAEA,SAAgB,cAAsB;CACpC,IAAI,WAAW,2BAA2B,KAAA,GACxC,WAAW,yBAAyB;CAEtC,OAAO,WAAW;AACpB;AAEA,SAAgB,YAAY,OAAqB;CAC/C,WAAW,yBAAyB;AACtC;AAEA,SAAgB,qBAGd;CACA,IAAI,CAAC,WAAW,+BACd,WAAW,gDAAgC,IAAI,IAG7C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,uBAAoC;CAClD,IAAI,CAAC,WAAW,iCACd,WAAW,kDAAkC,IAAI,IAAY;CAE/D,OAAO,WAAW;AACpB;AAeA,SAAgB,sBAA8D;CAC5E,IAAI,CAAC,WAAW,gCACd,WAAW,iDAAiC,IAAI,QAG9C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,sBAAkD;CAChE,IAAI,CAAC,WAAW,qCAAqC;EACnD,MAAM,SAAS,qBAAqB;EACpC,WAAW,sCAAsC,IAAI,SAGnD,OAAO,SAAS;CACpB;CACA,OAAO,WAAW;AACpB;AAEA,SAAgB,2BAAiD;CAC/D,IAAI,CAAC,WAAW,qCACd,WAAW,sDAAsC,IAAI,IAAqB;CAE5E,OAAO,WAAW;AACpB"}