@happyvertical/smrt-core 0.57.45 → 0.57.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +11 -3
- package/agents/browser-entry.md +16 -0
- package/agents/build-knowledge.md +58 -14
- package/agents/change-feed.md +20 -0
- package/agents/generators.md +69 -3
- package/agents/schema-paths.md +82 -0
- package/dist/_virtual/_rolldown/runtime.js +22 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/cascade.d.ts +46 -0
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +96 -7
- package/dist/cascade.js.map +1 -1
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +7 -4
- package/dist/change-feed.js.map +1 -1
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +3 -3
- package/dist/class.js.map +1 -1
- package/dist/consumer-plugin/index.d.ts +13 -0
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +69 -13
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/consumer-plugin.js +2 -2
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +3 -3
- package/dist/database.js.map +1 -1
- package/dist/dispatch/bus.d.ts.map +1 -1
- package/dist/dispatch/bus.js +1 -4
- package/dist/dispatch/bus.js.map +1 -1
- package/dist/dispatch/index.d.ts +1 -1
- package/dist/dispatch/index.d.ts.map +1 -1
- package/dist/dispatch/types.d.ts +22 -3
- package/dist/dispatch/types.d.ts.map +1 -1
- package/dist/generators/changes-route.d.ts.map +1 -1
- package/dist/generators/changes-route.js +2 -2
- package/dist/generators/changes-route.js.map +1 -1
- package/dist/generators/custom-action.d.ts +11 -0
- package/dist/generators/custom-action.d.ts.map +1 -1
- package/dist/generators/custom-action.js +23 -4
- package/dist/generators/custom-action.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/lazy-config.d.ts +120 -3
- package/dist/lazy-config.d.ts.map +1 -1
- package/dist/lazy-config.js +81 -10
- package/dist/lazy-config.js.map +1 -1
- package/dist/manifest/generator.d.ts +8 -2
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +6 -5
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/index.d.ts +1 -0
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +2 -1
- package/dist/manifest/index.js.map +1 -1
- package/dist/manifest/local-manifest-reader.d.ts +13 -2
- package/dist/manifest/local-manifest-reader.d.ts.map +1 -1
- package/dist/manifest/local-manifest-reader.js +38 -6
- package/dist/manifest/local-manifest-reader.js.map +1 -1
- package/dist/manifest/manifest-loader.d.ts +9 -8
- package/dist/manifest/manifest-loader.d.ts.map +1 -1
- package/dist/manifest/manifest-loader.js +67 -27
- package/dist/manifest/manifest-loader.js.map +1 -1
- package/dist/manifest/object-declarations.d.ts +116 -0
- package/dist/manifest/object-declarations.d.ts.map +1 -0
- package/dist/manifest/object-declarations.js +161 -0
- package/dist/manifest/object-declarations.js.map +1 -0
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +5 -3
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.d.ts.map +1 -1
- package/dist/manifest/store.js +18 -2
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest/test-artifacts.d.ts +57 -0
- package/dist/manifest/test-artifacts.d.ts.map +1 -0
- package/dist/manifest/test-artifacts.js +111 -0
- package/dist/manifest/test-artifacts.js.map +1 -0
- package/dist/manifest.js +2 -1
- package/dist/manifest.json +5 -3
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +11 -3
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -1
- package/dist/migrations/qualified-name-aliases.d.ts.map +1 -1
- package/dist/migrations/qualified-name-aliases.js +1 -1
- package/dist/migrations/qualified-name-aliases.js.map +1 -1
- package/dist/migrations.js +3 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +2 -2
- package/dist/object.js.map +1 -1
- package/dist/postgres-permissions.d.ts.map +1 -1
- package/dist/postgres-permissions.js +4 -3
- package/dist/postgres-permissions.js.map +1 -1
- package/dist/postgres-timeouts.d.ts +30 -3
- package/dist/postgres-timeouts.d.ts.map +1 -1
- package/dist/postgres-timeouts.js +39 -2
- package/dist/postgres-timeouts.js.map +1 -1
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +14 -11
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +24 -2
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/collection-exposure.d.ts +27 -0
- package/dist/registry/collection-exposure.d.ts.map +1 -1
- package/dist/registry/collection-exposure.js +74 -1
- package/dist/registry/collection-exposure.js.map +1 -1
- package/dist/registry/collection-resolution.d.ts.map +1 -1
- package/dist/registry/collection-resolution.js +1 -0
- package/dist/registry/collection-resolution.js.map +1 -1
- package/dist/registry/framework-base-classes.d.ts.map +1 -1
- package/dist/registry/framework-base-classes.js +5 -0
- package/dist/registry/framework-base-classes.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +8 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +17 -4
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +12 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +2 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +30 -10
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +10 -8
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +29 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/generator.d.ts +32 -0
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +59 -0
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/live-parity.d.ts.map +1 -1
- package/dist/schema/live-parity.js +4 -2
- package/dist/schema/live-parity.js.map +1 -1
- package/dist/scripts/browser-unbundled-dev.mjs +90 -0
- package/dist/scripts/generate-test-manifest.js +9 -49
- package/dist/smrt-knowledge.json +16 -16
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +12 -4
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/schema.d.ts +70 -8
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +231 -24
- package/dist/system/schema.js.map +1 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +14 -2
- package/dist/testing/database.js.map +1 -1
- package/dist/tools/tool-generator.d.ts.map +1 -1
- package/dist/tools/tool-generator.js +2 -2
- package/dist/tools/tool-generator.js.map +1 -1
- package/dist/utils/import-workspace-module.d.ts.map +1 -1
- package/dist/utils/import-workspace-module.js +23 -0
- package/dist/utils/import-workspace-module.js.map +1 -1
- package/dist/utils/naming.js +5 -3
- package/dist/utils/naming.js.map +1 -1
- package/dist/vendor/pluralize/pluralize.js +421 -0
- package/dist/vendor/pluralize/pluralize.js.map +1 -0
- package/dist/vite-plugin/api-client-entries.d.ts +5 -1
- package/dist/vite-plugin/api-client-entries.d.ts.map +1 -1
- package/dist/vite-plugin/api-client-entries.js +11 -6
- package/dist/vite-plugin/api-client-entries.js.map +1 -1
- package/dist/vite-plugin/consumer-import.d.ts +28 -0
- package/dist/vite-plugin/consumer-import.d.ts.map +1 -0
- package/dist/vite-plugin/consumer-import.js +42 -0
- package/dist/vite-plugin/consumer-import.js.map +1 -0
- package/dist/vite-plugin/dev-plane-route.d.ts +6 -1
- package/dist/vite-plugin/dev-plane-route.d.ts.map +1 -1
- package/dist/vite-plugin/dev-plane-route.js +8 -9
- package/dist/vite-plugin/dev-plane-route.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +12 -9
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/resources-route.d.ts.map +1 -1
- package/dist/vite-plugin/resources-route.js +3 -8
- package/dist/vite-plugin/resources-route.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +7 -11
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/sync-apply-route.d.ts +16 -0
- package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
- package/dist/vite-plugin/sync-apply-route.js +48 -6
- package/dist/vite-plugin/sync-apply-route.js.map +1 -1
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-loader.js","names":[],"sources":["../../src/manifest/manifest-loader.ts"],"sourcesContent":["/**\n * Manifest Loader - Automatic discovery of external package manifests\n *\n * This module provides automatic loading of SMRT manifests from external packages,\n * solving issue #131 where external package schemas weren't being detected.\n *\n * Architecture:\n * - Build-time: AST scanner generates manifests in dist/manifest.json\n * - Run-time: This module discovers and loads those manifests automatically\n * - Caching: Loaded manifests are cached to avoid repeated imports\n *\n * Flow:\n * 1. Check testManifest (for test classes)\n * 2. Check staticManifest (for core framework classes)\n * 3. Resolve external package manifests from explicit JSON exports\n * 4. Return manifest entry or undefined\n *\n * External manifest loading intentionally follows a pure-ESM JSON contract.\n * Packages are expected to publish a JSON manifest via \"./manifest\" and/or\n * \"./manifest.json\" exports. Runtime discovery does not guess sibling workspace\n * paths or load JavaScript manifest modules.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { bumpRegistryGeneration } from '../registry/generation.js';\nimport type { SmrtObjectConstructor } from '../registry/types.js';\nimport { ObjectRegistry } from '../registry.js';\nimport type {\n FieldDefinition,\n MethodDefinition,\n SmartObjectDefinition,\n SmartObjectManifest,\n} from '../scanner/types.js';\nimport { MANIFEST_TIMESTAMP } from '../scanner/types.js';\nimport { parse } from '../utils/json.js';\nimport {\n createQualifiedName,\n isQualifiedName,\n parseQualifiedName,\n} from '../utils/qualified-names.js';\nimport {\n isDecoratorRuntimeFramePath,\n isDecoratorRuntimePackageName,\n isModuleRunnerFramePath,\n isSmrtCoreFramePath,\n} from '../utils/stack-frames.js';\nimport { LocalManifestReader } from './local-manifest-reader.js';\nimport {\n dirname,\n existsSync,\n join,\n readdirSync,\n readFileSync,\n require,\n statSync,\n} from './node-host.js';\nimport { getDefaultCompositeSource } from './sources/composite.js';\nimport {\n getLocalTestManifestCache as getLocalTestManifestCacheFromStore,\n getManifestCache as getManifestCacheFromStore,\n getStaticManifestCache as getStaticManifestCacheFromStore,\n getTestManifestCache as getTestManifestCacheFromStore,\n isTestEnvironment as isTestEnvFromStore,\n shouldLoadCoreTestManifest,\n} from './store.js';\n\n/**\n * Extend globalThis to include manifest loader state.\n * Using globalThis ensures all module instances share the same manifest caches,\n * which is critical in monorepos where the same package can be loaded\n * from different paths (e.g., pnpm store vs workspace symlink).\n *\n * @see https://github.com/happyvertical/smrt/issues/543\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtManifestStatic: SmartObjectManifest | null | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestStaticLoadAttempted: boolean | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestTest: SmartObjectManifest | null | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestTestLoadAttempted: boolean | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestCache: Map<string, SmartObjectManifest> | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestLocalTest: SmartObjectManifest | null | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestCollisions:\n | Map<\n string,\n Array<{\n packageName: string;\n filePath?: string;\n manifestSource: string;\n }>\n >\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtSTISiblingCache:\n | Map<\n string,\n Array<{ className: string; entry: ManifestEntry; packageName?: string }>\n >\n | undefined;\n}\n\n// Use globalThis for cross-module state sharing\n// This ensures loadConfig() in one module instance affects all packages\n\n// ── Cache access (delegates to manifest/store.ts — the single source of\n// truth added in Release B #1133 for globalThis manifest state).\n// manifest-loader.ts keeps its own setters + load-attempted flags below\n// because those are internal to this module's async loader state machine;\n// the read-only getters just forward to store.ts to eliminate drift.\n\nconst getStaticManifestCache = getStaticManifestCacheFromStore;\nconst getTestManifestCache = getTestManifestCacheFromStore;\nconst getLocalTestManifestCache = getLocalTestManifestCacheFromStore;\nconst getManifestCacheMap = getManifestCacheFromStore;\n\nfunction setStaticManifestCache(\n manifest: SmartObjectManifest | null | undefined,\n): void {\n globalThis.__smrtManifestStatic = manifest;\n bumpRegistryGeneration();\n}\n\nfunction getStaticManifestLoadAttempted(): boolean {\n return globalThis.__smrtManifestStaticLoadAttempted ?? false;\n}\n\nfunction setStaticManifestLoadAttempted(value: boolean): void {\n globalThis.__smrtManifestStaticLoadAttempted = value;\n}\n\nfunction setTestManifestCache(\n manifest: SmartObjectManifest | null | undefined,\n): void {\n globalThis.__smrtManifestTest = manifest;\n bumpRegistryGeneration();\n}\n\nfunction getTestManifestLoadAttempted(): boolean {\n return globalThis.__smrtManifestTestLoadAttempted ?? false;\n}\n\nfunction setTestManifestLoadAttempted(value: boolean): void {\n globalThis.__smrtManifestTestLoadAttempted = value;\n}\n\nfunction setLocalTestManifestCache(\n manifest: SmartObjectManifest | null | undefined,\n): void {\n globalThis.__smrtManifestLocalTest = manifest;\n bumpRegistryGeneration();\n}\n\n/**\n * Get the manifest collisions Map from globalThis\n */\nfunction getManifestCollisionsMap(): Map<\n string,\n Array<{ packageName: string; filePath?: string; manifestSource: string }>\n> {\n if (!globalThis.__smrtManifestCollisions) {\n globalThis.__smrtManifestCollisions = new Map();\n }\n return globalThis.__smrtManifestCollisions;\n}\n\n/**\n * Get the STI sibling cache Map from globalThis\n * Caches discoverSTISiblingsSync results per collection to avoid repeated scans\n */\nfunction getSTISiblingCache(): Map<\n string,\n Array<{ className: string; entry: ManifestEntry; packageName?: string }>\n> {\n if (!globalThis.__smrtSTISiblingCache) {\n globalThis.__smrtSTISiblingCache = new Map();\n }\n return globalThis.__smrtSTISiblingCache;\n}\n\n/**\n * Cached debug flag evaluated once at module load time.\n * Environment variables don't change at runtime, so this is safe.\n * @see https://github.com/happyvertical/smrt/issues/729\n */\nconst DEBUG_ENABLED =\n process.env.DEBUG_MANIFEST === 'true' ||\n process.env.DEBUG_MANIFEST === '1' ||\n process.env.DEBUG?.includes('manifest') ||\n false;\n\n// The debug traces below are gated by DEBUG_MANIFEST / DEBUG_TEST_ENV, so the\n// logger level must allow debug when either is set (a fixed 'info' would filter\n// them out and silently break those debug switches).\nconst logger = createLogger({\n level: DEBUG_ENABLED || process.env.DEBUG_TEST_ENV ? 'debug' : 'info',\n});\n\n/**\n * Log a debug message only if DEBUG_MANIFEST is enabled.\n * Uses cached boolean check instead of repeated env var access.\n */\nfunction debugLog(message: string): void {\n if (DEBUG_ENABLED) {\n logger.debug(message);\n }\n}\n\n/**\n * Cache for className-to-entry index per manifest.\n * This enables O(1) lookup by className instead of O(n) iteration.\n * @see https://github.com/happyvertical/smrt/issues/729\n */\nconst classNameIndexCache = new WeakMap<\n SmartObjectManifest,\n Map<string, SmartObjectDefinition>\n>();\n\n/**\n * Get or build the className index for a manifest.\n * Index maps lowercase className to entry for O(1) lookup.\n */\nfunction getClassNameIndex(\n manifest: SmartObjectManifest,\n): Map<string, SmartObjectDefinition> {\n let index = classNameIndexCache.get(manifest);\n if (!index) {\n index = new Map<string, SmartObjectDefinition>();\n for (const [key, entry] of Object.entries(manifest.objects)) {\n const name = (entry.className || key).toLowerCase();\n if (!index.has(name)) {\n index.set(name, entry);\n } else {\n const existing = index.get(name)!;\n const existingName = (existing.className || '').toLowerCase();\n const newExtends = (entry.extends || '').toLowerCase();\n const existingExtends = (existing.extends || '').toLowerCase();\n\n // Issue #950: STI child-wins — if new entry extends existing, replace\n if (\n newExtends &&\n (newExtends === existingName || newExtends === name)\n ) {\n index.set(name, entry);\n debugLog(\n `Manifest className '${name}': child '${key}' replaces parent`,\n );\n } else if (\n existingExtends &&\n (existingExtends === (entry.className || key).toLowerCase() ||\n existingExtends === name)\n ) {\n // Existing is already the child, keep it\n debugLog(\n `Manifest className '${name}': keeping child, ignoring parent '${key}'`,\n );\n } else {\n debugLog(\n `Manifest className collision for '${name}': keeping first, ignoring key '${key}'`,\n );\n }\n }\n }\n classNameIndexCache.set(manifest, index);\n }\n return index;\n}\n\n/**\n * Test-environment detection with optional DEBUG_TEST_ENV logging.\n *\n * Delegates to the single source of truth in `store.ts` so the\n * ManifestSource implementations (which also gate on test-env) cannot\n * drift from the legacy sync loader. The logging wrapper stays here\n * because the trace is specific to this module's loader state machine.\n */\nfunction isTestEnvironment(): boolean {\n const result = isTestEnvFromStore();\n\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug('[manifest-loader] isTestEnvironment check', {\n NODE_ENV: process.env.NODE_ENV,\n VITEST: process.env.VITEST,\n JEST_WORKER_ID: process.env.JEST_WORKER_ID,\n result,\n });\n }\n\n return result;\n}\n\nfunction getStaticManifest(): SmartObjectManifest {\n if (!getStaticManifestLoadAttempted()) {\n setStaticManifestLoadAttempted(true);\n try {\n // Try to import the generated static manifest\n const imported = require('./static-manifest.js');\n setStaticManifestCache(imported.staticManifest || imported.default);\n } catch {\n // Fallback to empty manifest if file doesn't exist yet (during build)\n setStaticManifestCache({\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n packageName: '@happyvertical/smrt-core',\n });\n }\n }\n return getStaticManifestCache()!;\n}\n\n/**\n * Lazy-load smrt-core's internal test manifest only for smrt-core's own tests.\n * Consumer packages should never see these fixtures, or they can collide with\n * real external classes during downstream test runs.\n */\nfunction getTestManifest(): SmartObjectManifest | null {\n if (!getTestManifestLoadAttempted()) {\n setTestManifestLoadAttempted(true);\n\n // CRITICAL: Scope the core test manifest to smrt-core's own test suite.\n if (!shouldLoadCoreTestManifest()) {\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug(\n '[manifest-loader] ⚠️ Skipping core test manifest load (not smrt-core test environment)',\n );\n }\n setTestManifestCache(null);\n return null;\n }\n\n try {\n // Read the generated JSON only inside the core-test environment gate.\n const manifest: SmartObjectManifest = parse(\n readFileSync(\n join(process.cwd(), 'src/manifest/test-manifest.json'),\n 'utf-8',\n ),\n );\n setTestManifestCache(manifest);\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug(\n `[manifest-loader] ✅ Loaded test manifest (${Object.keys(manifest?.objects || {}).length} objects)`,\n );\n }\n } catch (error) {\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug(\n '[manifest-loader] ⚠️ Test manifest not found (this is normal in production)',\n );\n }\n setTestManifestCache(null);\n }\n }\n return getTestManifestCache();\n}\n\n// Re-export types for convenience\nexport type Manifest = SmartObjectManifest;\nexport type ManifestEntry = SmartObjectDefinition;\nexport type { FieldDefinition, MethodDefinition };\n\n// Note: manifestCache and localTestManifest are now accessed via globalThis helper functions\n// getManifestCacheMap() and getLocalTestManifestCache()/setLocalTestManifestCache()\n\n/**\n * Load local test manifest from current package (synchronous)\n *\n * During test runs, packages can have manifests in two locations:\n * 1. src/manifest/test-manifest.json - Domain packages during development\n * 2. dist/manifest.json - Built packages (consuming apps like praeco)\n *\n * This function attempts to load the manifest from either location.\n *\n * @returns Loaded manifest or null if not found or undefined if not yet attempted\n */\nexport function loadLocalTestManifestSync(): Manifest | null | undefined {\n // Only return cached manifest if it was successfully loaded\n // Don't cache null (failed loads) - allow retries\n const cached = getLocalTestManifestCache();\n if (cached !== undefined && cached !== null) {\n return cached;\n }\n\n // Use LocalManifestReader for unified local loading\n // This checks: .smrt/manifest.json -> dist/manifest.json\n const manager = new LocalManifestReader(process.cwd());\n const manifest = manager.loadLocal();\n\n // Fallback location: src/manifest/test-manifest.json\n // This is still used by smrt-core and other packages that generate test manifests here\n const testManifestPath = join(\n process.cwd(),\n 'src/manifest/test-manifest.json',\n );\n\n // If LocalManifestReader found a manifest, check if it has objects\n // If it has 0 objects but the fallback exists with objects, use the fallback instead\n // This handles the case where dist/manifest.json is the static-manifest.json (0 objects)\n // but src/manifest/test-manifest.json has the real test classes\n if (manifest) {\n const objectCount = Object.keys(manifest.objects).length;\n\n // If manifest has objects, use it\n if (objectCount > 0) {\n setLocalTestManifestCache(manifest);\n debugLog(\n `[manifest-loader] ✅ Loaded local manifest via LocalManifestReader (${objectCount} objects)`,\n );\n return manifest;\n }\n\n // Manifest has 0 objects - check if fallback has more\n if (existsSync(testManifestPath)) {\n try {\n const testManifest: Manifest = parse(\n readFileSync(testManifestPath, 'utf-8'),\n );\n const testObjectCount = Object.keys(testManifest.objects).length;\n\n if (testObjectCount > 0) {\n setLocalTestManifestCache(testManifest);\n debugLog(\n `[manifest-loader] ✅ Loaded test manifest from ${testManifestPath} (${testObjectCount} objects) - preferred over empty LocalManifestReader result`,\n );\n return testManifest;\n }\n } catch {\n // Fallback also failed, use the empty manifest\n }\n }\n\n // No better option, cache and use the empty manifest\n setLocalTestManifestCache(manifest);\n debugLog(\n `[manifest-loader] ✅ Loaded local manifest via LocalManifestReader (${objectCount} objects)`,\n );\n return manifest;\n }\n\n // LocalManifestReader returned null - check fallback\n if (existsSync(testManifestPath)) {\n try {\n const testManifest: Manifest = parse(\n readFileSync(testManifestPath, 'utf-8'),\n );\n setLocalTestManifestCache(testManifest);\n const objectCount = Object.keys(testManifest.objects).length;\n debugLog(\n `[manifest-loader] ✅ Loaded test manifest from ${testManifestPath} (${objectCount} objects)`,\n );\n return testManifest;\n } catch (error) {\n debugLog(\n `[manifest-loader] ✗ Failed to load test manifest from ${testManifestPath}: ${error instanceof Error ? error.message : 'unknown'}`,\n );\n }\n }\n\n // No manifest found - DON'T cache null, allow retries\n // This is important because the manifest may be generated later\n debugLog(\n '[manifest-loader] ⚠️ No local manifest found (will retry on next call)',\n );\n return null;\n}\n\nlet projectManifestRead:\n | { path: string; mtimeMs: number; manifest: SmartObjectManifest | null }\n | undefined;\n\n/**\n * The manifest of the project the process runs in (the first of\n * `<cwd>/.smrt/manifest.json`, `<cwd>/dist/manifest.json`,\n * `<cwd>/src/manifest/manifest.json`, as `LocalManifestReader.loadLocal()` reads\n * them), never seeded into the lookup caches. Only consulted to attribute a\n * decorated class whose declaring file that manifest describes, when no loaded\n * manifest does: a plain Node/tsx script importing an app's workspace-package\n * models has not loaded the app's manifest yet (#3109). The read is reused\n * only while that file is unchanged, so a dev server that regenerates it is\n * seen; a missing manifest is never cached.\n */\nexport function readProjectManifestSync(): SmartObjectManifest | null {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') {\n return null;\n }\n const manager = new LocalManifestReader(process.cwd());\n const path = [\n manager.getOutputPath('dev'),\n manager.getOutputPath('build'),\n manager.getOutputPath('source'),\n ].find((candidate) => existsSync(candidate));\n if (!path) return null;\n let mtimeMs: number;\n try {\n mtimeMs = statSync(path).mtimeMs;\n } catch {\n return null;\n }\n if (\n projectManifestRead?.path === path &&\n projectManifestRead.mtimeMs === mtimeMs\n ) {\n return projectManifestRead.manifest;\n }\n let manifest: SmartObjectManifest | null = null;\n try {\n manifest = parse<SmartObjectManifest>(readFileSync(path, 'utf-8'));\n } catch {\n manifest = null;\n }\n projectManifestRead = { path, mtimeMs, manifest };\n return manifest;\n}\n\n/**\n * Extract package name from class constructor\n *\n * Uses the ObjectRegistry (preferred) or fallback methods to determine\n * which package the class belongs to.\n *\n * Search order:\n * 1. ObjectRegistry (packageName injected at build time from manifest) - SKIPPED during initial registration\n * 2. __package__ metadata (build tooling can inject this)\n * 3. require.resolve() (resolves package.json from constructor file location)\n * 4. Error stack trace parsing (fallback, fragile in pnpm workspaces)\n *\n * Decorator-lowering runtime helpers (`@oxc-project/runtime` under Vite 8,\n * `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped by both\n * stack-based methods (3 and 4): the compiler inserts such a frame between the\n * `@smrt()` decorator and the consumer module, and without skipping it the class\n * misattributes to the helper package (e.g. `@oxc-project/runtime`) instead of\n * the declaring package (#1785).\n *\n * @param ctor - Class constructor\n * @param skipRegistry - If true, skip checking ObjectRegistry (used during initial registration to avoid circular dependency)\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 Package name (e.g., '@happyvertical/smrt-places') or null\n */\nexport function getPackageName(\n ctor: SmrtObjectConstructor,\n skipRegistry: boolean = false,\n stackOverride?: string,\n): string | null {\n try {\n // 1. Try ObjectRegistry first (most reliable - from build-time manifest)\n // This solves issue #143 where pnpm workspace symlinks break stack trace parsing\n // CRITICAL: Skip during initial registration to avoid circular dependency (issue #159)\n if (!skipRegistry) {\n const className = ctor.name;\n if (className) {\n const registered = ObjectRegistry.getClass(className);\n if (registered?.packageName) {\n return registered.packageName;\n }\n }\n }\n\n // 2. Check if class has __package__ metadata (could be added by build tooling)\n const packageMeta = (ctor as { __package__?: string }).__package__;\n if (packageMeta) {\n return packageMeta;\n }\n\n // 3. Try require.resolve() to find package.json from constructor location\n // This is more reliable than stack trace parsing for published packages\n try {\n const stack = stackOverride ?? new Error().stack ?? '';\n const stackLines = stack.split('\\n');\n\n // Find the first line with a file path that's NOT from smrt-core\n // Skip manifest-loader, registry, and other smrt-core files\n for (const line of stackLines) {\n // Both frame forms: `at fn (path:line:col)` and the anonymous\n // `at path:line:col` a module's top level reports. Under a module\n // runner (Vitest, Vite SSR) the class-declaring module is the\n // anonymous one; matching only the parenthesized form skipped it and\n // attributed the class to the runner's package (#3098).\n // The Vite dev server's module runner reports an installed\n // dependency's file with its URL query (`dist/x.js?v=030af238:1:2`).\n // Rejecting that frame fell through to the app's own module below it\n // and attributed every such dependency class to the app (#3490), so\n // the query is matched and dropped.\n const fileMatch = line.match(\n /(?:\\(|\\bat\\s+)((?:file:\\/\\/)?[^()\\s?]+\\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?:\\?[^()\\s]*?)?(?::\\d+:\\d+)?\\)?\\s*$/,\n );\n if (fileMatch) {\n const filePath = fileMatch[1];\n // Skip smrt-core internal files and decorator-lowering runtime\n // helpers (#1785) — the class is declared in neither.\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n if (\n isSmrtCoreFramePath(normalizedPath) ||\n isDecoratorRuntimeFramePath(normalizedPath) ||\n isModuleRunnerFramePath(normalizedPath)\n ) {\n continue; // Skip smrt-core files, look for external package\n }\n\n // Try to resolve package.json from this file\n try {\n // Handle file:// URLs\n const cleanPath = filePath.replace(/^file:\\/\\//, '');\n let dir = dirname(cleanPath);\n // Walk up until we find a package.json\n for (let i = 0; i < 10; i++) {\n const pkgPath = join(dir, 'package.json');\n try {\n if (existsSync(pkgPath)) {\n const pkg = parse<{ name?: string }>(\n readFileSync(pkgPath, 'utf-8'),\n );\n if (pkg.name?.startsWith('@')) {\n return pkg.name;\n }\n }\n } catch {\n // Keep walking up\n }\n const parent = dirname(dir);\n if (parent === dir) break; // Reached root\n dir = parent;\n }\n } catch {\n // Fall through to next method\n }\n break;\n }\n }\n } catch {\n // Fall through to next method\n }\n\n // 4. Final fallback: Try to extract from Error stack trace with node_modules pattern\n // This method is fragile and fails in pnpm workspaces, but kept for backward compatibility\n const stack = stackOverride ?? new Error().stack ?? '';\n const stackLines = stack.split('\\n');\n\n // Look for line with 'node_modules/@scope/package' pattern\n for (const line of stackLines) {\n const match = line.match(/node_modules\\/(@[^/]+\\/[^/]+)/);\n if (match) {\n // Skip decorator-lowering runtime helpers (e.g. @oxc-project/runtime) —\n // the compiler inserts them between @smrt() and the declaring module, so\n // attributing to them is wrong; keep walking to the real package (#1785).\n if (\n isDecoratorRuntimePackageName(match[1]) ||\n isModuleRunnerFramePath(line)\n ) {\n continue;\n }\n return match[1];\n }\n }\n\n // Package name is now injected at build time into the manifest and stored in ObjectRegistry\n // If we reach here, the class is likely not from an external package\n return null;\n } catch {\n return null;\n }\n}\n\ninterface ManifestExportConditions {\n default?: string;\n import?: string;\n require?: string;\n}\n\ninterface ExternalPackageJson {\n exports?: Record<string, string | ManifestExportConditions>;\n}\n\nfunction findWorkspaceRoot(startDir: string): string | null {\n let currentDir = startDir;\n\n while (true) {\n if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) {\n return currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n return null;\n }\n\n currentDir = parentDir;\n }\n}\n\nfunction resolveInstalledPackageJsonPath(packageName: string): string | null {\n let currentDir = process.cwd();\n\n while (true) {\n const packageJsonPath = join(\n currentDir,\n 'node_modules',\n packageName,\n 'package.json',\n );\n\n if (existsSync(packageJsonPath)) {\n return packageJsonPath;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n return null;\n }\n\n currentDir = parentDir;\n }\n}\n\nfunction resolveWorkspacePackageJsonPath(packageName: string): string | null {\n const workspaceRoot = findWorkspaceRoot(process.cwd());\n if (!workspaceRoot) {\n return null;\n }\n\n const workspacePackagesDir = join(workspaceRoot, 'packages');\n if (!existsSync(workspacePackagesDir)) {\n return null;\n }\n\n for (const packageDirName of readdirSync(workspacePackagesDir)) {\n const packageJsonPath = join(\n workspacePackagesDir,\n packageDirName,\n 'package.json',\n );\n if (!existsSync(packageJsonPath)) {\n continue;\n }\n\n try {\n const packageJson = parse<{ name?: string }>(\n readFileSync(packageJsonPath, 'utf-8'),\n );\n if (packageJson.name === packageName) {\n return packageJsonPath;\n }\n } catch {\n // Ignore unreadable workspace package metadata and continue scanning.\n }\n }\n\n return null;\n}\n\nfunction resolveWorkspaceSourceManifestPath(packageDir: string): string | null {\n const candidates = [\n join(packageDir, 'src', 'manifest', 'manifest.json'),\n join(packageDir, '.smrt', 'manifest.json'),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\ninterface ManifestLoadOptions {\n warn?: boolean;\n}\n\nfunction resolveManifestExportPath(\n packageName: string,\n options: ManifestLoadOptions = {},\n): string | null {\n const shouldWarn = options.warn ?? true;\n const packageJsonPath =\n resolveInstalledPackageJsonPath(packageName) ||\n resolveWorkspacePackageJsonPath(packageName);\n\n if (!packageJsonPath) {\n return null;\n }\n\n const packageDir = dirname(packageJsonPath);\n const packageJson = parse<ExternalPackageJson>(\n readFileSync(packageJsonPath, 'utf-8'),\n );\n const manifestExports = ['./manifest.json', './manifest'];\n\n for (const exportKey of manifestExports) {\n const manifestExport = packageJson.exports?.[exportKey];\n if (!manifestExport) {\n continue;\n }\n\n const manifestRelativePath =\n typeof manifestExport === 'string'\n ? manifestExport\n : manifestExport.import ||\n manifestExport.default ||\n manifestExport.require;\n\n if (!manifestRelativePath) {\n if (shouldWarn) {\n logger.warn(\n `Package ${packageName} has invalid manifest export configuration for ${exportKey}`,\n );\n }\n return null;\n }\n\n if (!manifestRelativePath.endsWith('.json')) {\n if (shouldWarn) {\n logger.warn(\n `Package ${packageName} must export a JSON manifest for ${exportKey}, received ${manifestRelativePath}`,\n );\n }\n return null;\n }\n\n const manifestPath = join(packageDir, manifestRelativePath);\n if (existsSync(manifestPath)) {\n return manifestPath;\n }\n\n const workspaceSourceManifest =\n resolveWorkspaceSourceManifestPath(packageDir);\n if (workspaceSourceManifest) {\n return workspaceSourceManifest;\n }\n\n if (shouldWarn) {\n logger.warn(\n `Package ${packageName} declares manifest export ${manifestRelativePath}, but no manifest file was found.`,\n );\n }\n return null;\n }\n\n return null;\n}\n\nfunction collectDeclaredSmrtDependencies(\n manifests: Array<Manifest | null | undefined>,\n): string[] {\n const dependencies = new Set<string>();\n\n for (const manifest of manifests) {\n for (const pkg of manifest?.smrtDependencies || []) {\n dependencies.add(pkg);\n }\n }\n\n return Array.from(dependencies);\n}\n\n/**\n * Load manifest from external package\n *\n * Uses createRequire from process.cwd() to resolve packages from the calling\n * application's context, not from smrt-core's context. This allows finding\n * packages that are dependencies of the app but not of smrt-core.\n *\n * External packages export manifests via package.json:\n * \"exports\": { \"./manifest\": \"./dist/manifest.json\" }\n *\n * @param packageName - Package name (e.g., '@happyvertical/smrt-places')\n * @returns Manifest object or null if not found\n */\n/**\n * Load external package manifest synchronously\n * This is the synchronous version of loadExternalManifest for use during class registration\n */\nexport function loadExternalManifestSync(\n packageName: string,\n options: ManifestLoadOptions = {},\n): Manifest | null {\n // Check cache first\n if (getManifestCacheMap().has(packageName)) {\n debugLog(`[manifest-loader] Using cached manifest for ${packageName}`);\n return getManifestCacheMap().get(packageName)!;\n }\n\n debugLog(\n `[manifest-loader] Attempting to load external manifest for ${packageName}`,\n );\n\n const manifestPath = resolveManifestExportPath(packageName, options);\n\n if (!manifestPath) {\n debugLog(\n `[manifest-loader] Package ${packageName} does not expose a JSON manifest export`,\n );\n return null;\n }\n\n try {\n const manifest = parse<Manifest>(readFileSync(manifestPath, 'utf-8'));\n\n if (!manifest.objects || typeof manifest.objects !== 'object') {\n if (options.warn ?? true) {\n logger.warn(`Invalid manifest structure for package ${packageName}`);\n }\n return null;\n }\n\n const cachedManifest = manifest.packageName\n ? manifest\n : { ...manifest, packageName };\n\n getManifestCacheMap().set(packageName, cachedManifest);\n debugLog(\n `[manifest-loader] ✅ Loaded external manifest for ${packageName} (${Object.keys(cachedManifest.objects).length} objects)`,\n );\n\n return cachedManifest;\n } catch (error) {\n if (options.warn ?? true) {\n logger.warn(\n `Failed to load manifest for package ${packageName}: ${error instanceof Error ? error.message : error}`,\n );\n }\n return null;\n }\n}\n\nexport async function loadExternalManifest(\n packageName: string,\n options: ManifestLoadOptions = {},\n): Promise<Manifest | null> {\n // Delegate to synchronous version since all operations are sync anyway\n return loadExternalManifestSync(packageName, options);\n}\n\n/**\n * Load a manifest directly from a known file path and cache it by package name.\n *\n * This is used by workspace/dev flows where sibling packages may not be\n * installed into node_modules yet, but their generated manifests are still\n * available on disk.\n */\nexport function loadManifestFromPathSync(\n manifestPath: string,\n): Manifest | null {\n try {\n const manifestJson = readFileSync(manifestPath, 'utf-8');\n const manifest: Manifest = parse(manifestJson);\n\n if (!manifest.objects || typeof manifest.objects !== 'object') {\n logger.warn(`Invalid manifest structure at ${manifestPath}`);\n return null;\n }\n\n const cacheKey = manifest.packageName || manifestPath;\n getManifestCacheMap().set(cacheKey, manifest);\n debugLog(\n `[manifest-loader] ✅ Loaded manifest from path ${manifestPath} (${Object.keys(manifest.objects).length} objects)`,\n );\n\n return manifest;\n } catch (error) {\n logger.warn(\n `Failed to load manifest from path ${manifestPath}: ${error instanceof Error ? error.message : error}`,\n );\n return null;\n }\n}\n\n/**\n * Discover manifest entry synchronously (checks only loaded manifests)\n *\n * Search order:\n * 1. localTestManifest (for domain package test classes)\n * 2. testManifest (for core test classes)\n * 3. staticManifest (for core framework classes)\n * 4. Cached external manifests\n *\n * @param className - Class name (for lookup)\n * @returns ManifestEntry or undefined if not found\n */\nexport function discoverManifestSync(\n className: string,\n): ManifestEntry | undefined {\n debugLog(`[manifest-loader] discoverManifestSync called for: ${className}`);\n\n // Ensure test-env caches are seeded before the composite queries them.\n // Historically, steps 1-2 of discoverManifestSync did this inline. The\n // composite's TestManifestSource and LocalTestManifestSource read directly\n // from cache state — without this seeding the first sync lookup in a\n // clean test process would miss core test classes on their first access\n // (regression noted in #1138 review).\n if (isTestEnvironment()) {\n if (!getLocalTestManifestCache()) {\n loadLocalTestManifestSync();\n }\n if (!getTestManifestCache()) {\n getTestManifest();\n }\n }\n\n // Carry qualified-name / package context into the composite lookup so\n // multi-package same-simple-name scenarios (issue #951) resolve to the\n // right manifest even via the sync path.\n const query = isQualifiedName(className)\n ? (() => {\n const parsed = parseQualifiedName(className);\n return {\n className: parsed.className,\n packageName: parsed.packageName,\n qualifiedName: className,\n };\n })()\n : { className };\n\n // Steps 1-4 (local-test → test → static → embedded cache) now live in\n // CompositeManifestSource at the exact same priority order.\n const compositeHit = getDefaultCompositeSource().lookup(query);\n if (compositeHit) {\n debugLog(\n `[manifest-loader] ✅ Found ${className} via ${compositeHit.source} source`,\n );\n const entry = compositeHit.def;\n if (!entry.packageName && compositeHit.packageName) {\n return { ...entry, packageName: compositeHit.packageName };\n }\n return entry;\n }\n\n // 5. Try loading from explicitly declared external SMRT package dependencies.\n debugLog(\n `[manifest-loader] ${className} not found in cached manifests, trying external packages...`,\n );\n\n const pendingPackages = collectDeclaredSmrtDependencies([\n getLocalTestManifestCache(),\n isTestEnvironment() ? getTestManifest() : null,\n getStaticManifest(),\n ...getManifestCacheMap().values(),\n ]);\n\n if (pendingPackages.length === 0) {\n debugLog(\n '[manifest-loader] No SMRT dependencies discovered. Run manifest generation if external packages are expected.',\n );\n }\n\n const visitedPackages = new Set<string>();\n\n while (pendingPackages.length > 0) {\n const pkg = pendingPackages.shift();\n if (!pkg || visitedPackages.has(pkg)) {\n continue;\n }\n\n visitedPackages.add(pkg);\n const manifest = loadExternalManifestSync(pkg);\n if (!manifest) {\n continue;\n }\n\n for (const dependency of manifest.smrtDependencies || []) {\n if (!visitedPackages.has(dependency)) {\n pendingPackages.push(dependency);\n }\n }\n\n // Use lookupInManifest for qualified name support (Issue #713)\n const entry = lookupInManifest(manifest, className);\n if (entry) {\n debugLog(\n `[manifest-loader] ✅ Found ${className} in external package ${pkg}`,\n );\n // Enrich entry with packageName from manifest if not already present\n if (!entry.packageName && manifest.packageName) {\n return { ...entry, packageName: manifest.packageName };\n }\n return entry;\n }\n }\n\n debugLog(`[manifest-loader] ❌ ${className} not found in any manifest`);\n return undefined;\n}\n\n// Note: manifestCollisions is now accessed via globalThis helper function\n// getManifestCollisionsMap()\n\n/**\n * Look up a manifest entry in a manifest's objects map.\n * Supports both qualified names and simple class names.\n *\n * Lookup order:\n * 1. Direct qualified name lookup (e.g., \"@happyvertical/smrt-core:Product\")\n * 2. Constructed qualified name (packageName + className)\n * 3. Search by className property (case-insensitive)\n *\n * @param manifest - The manifest to search in\n * @param nameOrQualified - Either a qualified name or simple class name\n * @returns ManifestEntry or undefined if not found\n */\nexport function lookupInManifest(\n manifest: SmartObjectManifest,\n nameOrQualified: string,\n): ManifestEntry | undefined {\n // 1. First try direct lookup (handles both qualified names and exact matches)\n if (manifest.objects[nameOrQualified]) {\n return manifest.objects[nameOrQualified];\n }\n\n // 2. Get the className index for O(1) lookups (instead of O(n) iteration)\n // This is the key optimization for issue #729\n const classNameIndex = getClassNameIndex(manifest);\n\n // 3. If input is a qualified name, extract className and use index. The\n // package half of a qualified name is identity, not decoration: a simple-\n // name hit that belongs to a DIFFERENT package is a different class (both\n // smrt-ledgers and smrt-messages declare `Account`, #3098), so it is a miss.\n if (isQualifiedName(nameOrQualified)) {\n const { className, packageName } = parseQualifiedName(nameOrQualified);\n const entry = classNameIndex.get(className.toLowerCase());\n const entryPackage = entry?.packageName ?? manifest.packageName;\n return entryPackage && entryPackage !== packageName ? undefined : entry;\n }\n\n // 4. For simple class names, try constructing qualified name if manifest has packageName\n if (manifest.packageName) {\n const qualifiedKey = createQualifiedName(\n manifest.packageName,\n nameOrQualified,\n );\n if (manifest.objects[qualifiedKey]) {\n return manifest.objects[qualifiedKey];\n }\n }\n\n // 5. Use index for O(1) className lookup (instead of O(n) iteration)\n return classNameIndex.get(nameOrQualified.toLowerCase());\n}\n\n/**\n * Find a manifest entry by qualified name across all loaded manifests.\n *\n * @param qualifiedName - The fully qualified class name (e.g., \"@happyvertical/smrt-core:Product\")\n * @returns ManifestEntry or undefined if not found\n */\nexport function findManifestEntryByQualifiedName(\n qualifiedName: string,\n): ManifestEntry | undefined {\n if (!isQualifiedName(qualifiedName)) {\n return undefined;\n }\n\n const { packageName } = parseQualifiedName(qualifiedName);\n\n // Check if we have the package's manifest cached\n const manifest = getManifestCacheMap().get(packageName);\n if (manifest) {\n // Try qualified name first, then fallback to simple names\n return lookupInManifest(manifest, qualifiedName);\n }\n\n // Check static manifest\n const staticManifestData = getStaticManifest();\n if (staticManifestData.packageName === packageName) {\n return lookupInManifest(staticManifestData, qualifiedName);\n }\n\n // Check test manifest (if in test environment)\n if (isTestEnvironment()) {\n const testManifest = getTestManifest();\n if (testManifest?.packageName === packageName) {\n return lookupInManifest(testManifest, qualifiedName);\n }\n\n // Check local test manifest\n const localTest = getLocalTestManifestCache();\n if (localTest?.packageName === packageName) {\n return lookupInManifest(localTest, qualifiedName);\n }\n }\n\n return undefined;\n}\n\n/**\n * Discover manifest entry asynchronously (includes external package loading)\n *\n * Search order:\n * 1. External package manifest (for constructor's package) - PRIORITIZED\n * 2. testManifest (for test classes)\n * 3. staticManifest (for core framework classes)\n * 4. Cached external manifests\n *\n * This function now detects and reports class name collisions across packages.\n *\n * @param ctor - Class constructor\n * @param className - Class name (for lookup)\n * @returns ManifestEntry or undefined if not found\n * @throws {Error} If class name collision detected across different packages\n */\nexport async function discoverManifestEntry(\n ctor: SmrtObjectConstructor,\n className: string,\n): Promise<ManifestEntry | undefined> {\n // ✅ FAST PATH: O(1) constructor-based lookup for already-registered classes\n // Skip manifest scanning if we already know this constructor's qualified name via WeakMap\n // This provides instant resolution and prevents collisions (each constructor is unique in memory)\n if (!(ctor as { _isManifestStub?: boolean })._isManifestStub) {\n const registered = ObjectRegistry.getClassByConstructor(ctor);\n if (\n registered?.qualifiedName &&\n isQualifiedName(registered.qualifiedName)\n ) {\n // Parse package name from qualified name (format: \"@package/name:ClassName\")\n const { packageName } = parseQualifiedName(registered.qualifiedName);\n\n const cachedManifest = getManifestCacheMap().get(packageName);\n if (cachedManifest) {\n const cachedEntry = lookupInManifest(\n cachedManifest,\n registered.qualifiedName,\n );\n if (cachedEntry) {\n return !cachedEntry.packageName && cachedManifest.packageName\n ? { ...cachedEntry, packageName: cachedManifest.packageName }\n : cachedEntry;\n }\n }\n\n // Load the manifest for this specific package before trusting any\n // existing runtime field metadata. Imported external classes can be\n // registered with only a partial field set until their manifest is\n // hydrated, which is exactly what happens with STI parents like Event.\n const manifest = await loadExternalManifest(packageName, {\n warn: registered.fields.size === 0,\n });\n if (manifest) {\n // Look up the entry using the qualified name (exact match)\n const entry = lookupInManifest(manifest, registered.qualifiedName);\n if (entry) {\n // Enrich entry with package name if missing\n return !entry.packageName && manifest.packageName\n ? { ...entry, packageName: manifest.packageName }\n : entry;\n }\n }\n\n // Source-registered classes with explicit field metadata do not need a\n // second manifest probe from fallback discovery when the package did not\n // resolve to a manifest. This keeps workspace/dev runtimes from spamming\n // missing-dist-manifest warnings for packages that are already fully\n // usable from source.\n if (registered.fields.size > 0) {\n return undefined;\n }\n }\n }\n\n // FALLBACK: Original manifest scanning logic for:\n // - First-time registration (WeakMap not populated yet)\n // - Manifest stubs (not in WeakMap)\n // - Undecorated classes (never registered)\n const name = className.toLowerCase();\n const constructorPackage = getPackageName(ctor);\n\n // Track all manifest sources that define this class\n const foundEntries: Array<{\n entry: ManifestEntry;\n packageName: string;\n filePath?: string;\n manifestSource: string;\n }> = [];\n\n // Build qualified name for lookup if we have package context\n const qualifiedName = constructorPackage\n ? createQualifiedName(constructorPackage, className)\n : undefined;\n\n // 2. Check localTestManifest first (domain package test classes) - ONLY in test environment\n // Do this BEFORE loading external manifest to avoid duplicate loading\n let localEntry: SmartObjectDefinition | undefined;\n if (isTestEnvironment()) {\n if (getLocalTestManifestCache() === undefined) {\n loadLocalTestManifestSync();\n }\n const localManifest = getLocalTestManifestCache();\n if (localManifest) {\n // Use lookupInManifest for consistent qualified name handling\n localEntry = lookupInManifest(localManifest, qualifiedName || className);\n if (localEntry) {\n foundEntries.push({\n entry: localEntry,\n packageName: localManifest.packageName || 'local-test',\n filePath: localEntry.filePath,\n manifestSource: 'local test manifest',\n });\n }\n }\n }\n\n // 1. PRIORITY: Try loading from the constructor's package first\n // BUT skip if it's the same package as the local test manifest to avoid collisions\n if (constructorPackage) {\n const skipExternal =\n getLocalTestManifestCache()?.packageName &&\n constructorPackage === getLocalTestManifestCache()?.packageName;\n\n if (!skipExternal) {\n const manifest = await loadExternalManifest(constructorPackage);\n if (manifest) {\n // Use lookupInManifest for consistent qualified name handling\n const entry = lookupInManifest(manifest, qualifiedName || className);\n if (entry) {\n const enrichedEntry =\n !entry.packageName && manifest.packageName\n ? { ...entry, packageName: manifest.packageName }\n : entry;\n\n foundEntries.push({\n entry: enrichedEntry,\n packageName: manifest.packageName || constructorPackage,\n filePath: entry.filePath,\n manifestSource: `${manifest.packageName || constructorPackage}/manifest.json`,\n });\n }\n }\n }\n }\n\n // 3. Check testManifest (core test classes) - ONLY in test environment\n // Skip if we already loaded a local test manifest (avoids duplicate entries from same package)\n if (isTestEnvironment() && (!getLocalTestManifestCache() || !localEntry)) {\n const manifest = getTestManifest();\n if (manifest) {\n // Use lookupInManifest for consistent qualified name handling\n const testEntry = lookupInManifest(manifest, qualifiedName || className);\n if (testEntry) {\n foundEntries.push({\n entry: testEntry,\n packageName: manifest.packageName || '@happyvertical/smrt-core',\n filePath: testEntry.filePath,\n manifestSource: '@happyvertical/smrt-core test manifest',\n });\n }\n }\n }\n\n // 4. Check staticManifest (core framework classes)\n const staticManifestData = getStaticManifest();\n // Use lookupInManifest for consistent qualified name handling\n const staticEntry = lookupInManifest(\n staticManifestData,\n qualifiedName || className,\n );\n if (staticEntry) {\n foundEntries.push({\n entry: staticEntry,\n packageName: staticManifestData.packageName || '@happyvertical/smrt-core',\n filePath: staticEntry.filePath,\n manifestSource: '@happyvertical/smrt-core static manifest',\n });\n }\n\n // 5. Check other cached external manifests\n for (const [cachedPkgName, manifest] of getManifestCacheMap().entries()) {\n // Skip if this is the constructor's package (already checked above)\n if (cachedPkgName === constructorPackage) {\n continue;\n }\n\n // Use lookupInManifest for consistent qualified name handling\n const entry = lookupInManifest(manifest, qualifiedName || className);\n if (entry) {\n foundEntries.push({\n entry:\n !entry.packageName && manifest.packageName\n ? { ...entry, packageName: manifest.packageName }\n : entry,\n packageName: manifest.packageName || cachedPkgName,\n filePath: entry.filePath,\n manifestSource: `${manifest.packageName || cachedPkgName}/manifest.json`,\n });\n }\n }\n\n // Deduplicate entries that reference the same source file.\n // Consumer manifests (e.g. smrt-users, dashboard) re-export classes from\n // upstream packages (e.g. smrt-profiles). These appear in multiple manifests\n // but point to the same filePath — they are NOT real collisions.\n if (foundEntries.length > 1) {\n const uniqueByFile = new Map<string, (typeof foundEntries)[0]>();\n for (const entry of foundEntries) {\n // Normalize file paths so the same source file with different absolute\n // prefixes (local dev vs CI build) is recognized as identical.\n // Extract the relative path from the last \"packages/\" segment onward.\n let key = entry.filePath || `${entry.packageName}:${className}`;\n if (entry.filePath) {\n const pkgIdx = entry.filePath.lastIndexOf('packages/');\n if (pkgIdx !== -1) {\n key = entry.filePath.slice(pkgIdx);\n }\n }\n if (!uniqueByFile.has(key)) {\n uniqueByFile.set(key, entry);\n }\n }\n\n if (uniqueByFile.size === 1) {\n // All entries point to the same source file — not a real collision.\n // Use the first (highest priority) entry.\n foundEntries.splice(1);\n } else if (uniqueByFile.size > 1) {\n // True collision: different source files define the same class name\n const collisionInfo = foundEntries.map(\n (f) =>\n ` - ${f.packageName} (${f.filePath || 'unknown file'}) from ${f.manifestSource}`,\n );\n\n // Store collision info for reporting\n getManifestCollisionsMap().set(\n className,\n foundEntries.map((f) => ({\n packageName: f.packageName,\n filePath: f.filePath,\n manifestSource: f.manifestSource,\n })),\n );\n\n // Throw error on collision\n throw new Error(\n `SMRT Class Name Collision Detected: \"${className}\"\\n\\n` +\n `This class is defined in multiple packages:\\n${collisionInfo.join('\\n')}\\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 ` 1. Use unique class names across packages (e.g., ${className}_${constructorPackage?.split('/').pop() || 'Unique'})\\n` +\n ` 2. Or use @smrt({ name: 'unique_name' }) to override the registration name\\n` +\n ` 3. Remove test classes with conflicting names from production manifests\\n\\n` +\n `If this is a test class collision, ensure test manifests are not included in production builds.`,\n );\n }\n }\n\n // Return the found entry (priority given to constructor's package)\n if (foundEntries.length === 1) {\n return foundEntries[0].entry;\n }\n\n return undefined;\n}\n\n/**\n * Get all detected manifest collisions\n *\n * @returns Map of class names to array of collision info\n */\nexport function getManifestCollisions(): Map<\n string,\n Array<{ packageName: string; filePath?: string; manifestSource: string }>\n> {\n return new Map(getManifestCollisionsMap());\n}\n\n/**\n * Clear manifest cache\n *\n * Useful for testing or when packages are updated at runtime.\n */\nexport function clearManifestCache(): void {\n // Reset all cached manifest state so tests/dev tooling can force a full\n // rediscovery pass on the next lookup.\n setStaticManifestCache(undefined);\n setStaticManifestLoadAttempted(false);\n setTestManifestCache(undefined);\n setTestManifestLoadAttempted(false);\n setLocalTestManifestCache(undefined);\n getManifestCacheMap().clear();\n getManifestCollisionsMap().clear();\n getSTISiblingCache().clear();\n projectManifestRead = undefined;\n}\n\n/**\n * Get all loaded manifests\n *\n * Returns a copy of the manifest cache for inspection.\n *\n * @returns Array of [packageName, manifest] entries\n */\nexport function getLoadedManifests(): Array<[string, Manifest]> {\n return Array.from(getManifestCacheMap().entries());\n}\n\n/**\n * Discover all STI sibling classes that share the same collection (table)\n *\n * This is critical for STI schema merging: when one subtype is accessed,\n * we need to discover ALL subtypes sharing the same table so that the\n * database adapter receives a complete schema with all columns.\n *\n * Scans all available manifests:\n * 1. Local test manifest (for test classes)\n * 2. Test manifest (for core test classes)\n * 3. Static manifest (for core framework classes)\n * 4. Cached external manifests\n * 5. SMRT dependencies from local test manifest\n *\n * @param collection - The collection/table name to find siblings for\n * @returns Array of manifest entries that share the same collection\n *\n * Known limitation (#1579, won't-fix): the per-collection result is cached on\n * `globalThis.__smrtSTISiblingCache` after the first call and is not invalidated\n * when a manifest is registered *afterwards* and isn't reachable via\n * `smrtDependencies` (the Release-A self-register path). A sibling registered\n * after first discovery for a given collection can therefore be missed until the\n * cache is cleared. Accepted as low-risk: STI manifests are registered at\n * startup before query traffic, and the few self-register flows that could hit\n * it can reset the cache. Fixing it properly means a registration→cache\n * invalidation hook, which is disproportionate to the exposure.\n */\nexport function discoverSTISiblingsSync(\n collection: string,\n): Array<{ className: string; entry: ManifestEntry; packageName?: string }> {\n // Check cache first to avoid repeated scans (fixes #644)\n const cache = getSTISiblingCache();\n const cached = cache.get(collection);\n if (cached !== undefined) {\n return cached;\n }\n\n const siblings: Array<{\n className: string;\n entry: ManifestEntry;\n packageName?: string;\n }> = [];\n\n // Track already-found class names to avoid duplicates (case-insensitive)\n // Use lowercase keys to prevent both 'praeco' and 'Praeco' from being added\n const foundClasses = new Set<string>();\n\n debugLog(\n `[manifest-loader] discoverSTISiblingsSync called for collection: ${collection}`,\n );\n\n // Helper to add entries from a manifest\n const addFromManifest = (\n manifest: Manifest | null | undefined,\n source: string,\n ) => {\n if (!manifest?.objects) return;\n\n for (const [key, entry] of Object.entries(manifest.objects) as [\n string,\n ManifestEntry,\n ][]) {\n // Check if this entry uses the same collection (table)\n if (entry.collection === collection) {\n const className = entry.className || key;\n // Use case-insensitive check to prevent registering both 'praeco' and 'Praeco'\n const lowerClassName = className.toLowerCase();\n if (!foundClasses.has(lowerClassName)) {\n foundClasses.add(lowerClassName);\n siblings.push({\n className,\n entry,\n packageName: entry.packageName || manifest.packageName,\n });\n debugLog(\n `[manifest-loader] Found STI sibling: ${className} (collection: ${collection}) from ${source}`,\n );\n }\n }\n }\n };\n\n // 1. Check local test manifest (domain package test classes)\n if (isTestEnvironment()) {\n if (!getLocalTestManifestCache()) {\n loadLocalTestManifestSync();\n }\n addFromManifest(getLocalTestManifestCache(), 'localTestManifest');\n }\n\n // 2. Check test manifest (core test classes)\n const testManifestData = isTestEnvironment() ? getTestManifest() : null;\n if (testManifestData) {\n addFromManifest(testManifestData, 'testManifest');\n }\n\n // 3. Check static manifest (core framework classes)\n const staticManifestData = getStaticManifest();\n addFromManifest(staticManifestData, 'staticManifest');\n\n // 4. Check cached external manifests\n for (const [pkgName, manifest] of getManifestCacheMap().entries()) {\n addFromManifest(manifest, `manifestCache:${pkgName}`);\n }\n\n // 5. Try loading from SMRT dependencies from ALL manifests (not just localTestManifest)\n // This ensures production environments also discover STI siblings\n const pendingDependencies = collectDeclaredSmrtDependencies([\n getLocalTestManifestCache(),\n isTestEnvironment() ? testManifestData : null,\n staticManifestData,\n ...getManifestCacheMap().values(),\n ]);\n const visitedDependencies = new Set<string>();\n\n debugLog(\n `[manifest-loader] Scanning ${pendingDependencies.length} SMRT dependencies for STI siblings: ${pendingDependencies.join(', ')}`,\n );\n\n while (pendingDependencies.length > 0) {\n const pkg = pendingDependencies.shift();\n if (!pkg || visitedDependencies.has(pkg)) {\n continue;\n }\n\n visitedDependencies.add(pkg);\n\n const manifest = loadExternalManifestSync(pkg);\n if (manifest) {\n addFromManifest(manifest, `smrtDependency:${pkg}`);\n\n for (const dependency of manifest.smrtDependencies || []) {\n if (!visitedDependencies.has(dependency)) {\n pendingDependencies.push(dependency);\n }\n }\n }\n }\n\n debugLog(\n `[manifest-loader] discoverSTISiblingsSync found ${siblings.length} siblings for collection: ${collection}`,\n );\n\n // Cache the results for future calls (fixes #644)\n cache.set(collection, siblings);\n\n return siblings;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoHA,IAAM,yBAAyB;AAC/B,IAAM,uBAAuB;AAC7B,IAAM,4BAA4B;AAClC,IAAM,sBAAsB;AAE5B,SAAS,uBACP,UACM;CACN,WAAW,uBAAuB;CAClC,uBAAuB;AACzB;AAEA,SAAS,iCAA0C;CACjD,OAAO,WAAW,qCAAqC;AACzD;AAEA,SAAS,+BAA+B,OAAsB;CAC5D,WAAW,oCAAoC;AACjD;AAEA,SAAS,qBACP,UACM;CACN,WAAW,qBAAqB;CAChC,uBAAuB;AACzB;AAEA,SAAS,+BAAwC;CAC/C,OAAO,WAAW,mCAAmC;AACvD;AAEA,SAAS,6BAA6B,OAAsB;CAC1D,WAAW,kCAAkC;AAC/C;AAEA,SAAS,0BACP,UACM;CACN,WAAW,0BAA0B;CACrC,uBAAuB;AACzB;;;;AAKA,SAAS,2BAGP;CACA,IAAI,CAAC,WAAW,0BACd,WAAW,2CAA2B,IAAI,IAAI;CAEhD,OAAO,WAAW;AACpB;;;;;AAMA,SAAS,qBAGP;CACA,IAAI,CAAC,WAAW,uBACd,WAAW,wCAAwB,IAAI,IAAI;CAE7C,OAAO,WAAW;AACpB;;;;;;AAOA,IAAM,gBACJ,QAAQ,IAAI,mBAAmB,UAC/B,QAAQ,IAAI,mBAAmB,OAC/B,QAAQ,IAAI,OAAO,SAAS,UAAU,KACtC;AAKF,IAAM,SAAS,aAAa,EAC1B,OAAO,iBAAiB,QAAQ,IAAI,iBAAiB,UAAU,OACjE,CAAC;;;;;AAMD,SAAS,SAAS,SAAuB;CACvC,IAAI,eACF,OAAO,MAAM,OAAO;AAExB;;;;;;AAOA,IAAM,sCAAsB,IAAI,QAG9B;;;;;AAMF,SAAS,kBACP,UACoC;CACpC,IAAI,QAAQ,oBAAoB,IAAI,QAAQ;CAC5C,IAAI,CAAC,OAAO;EACV,wBAAQ,IAAI,IAAmC;EAC/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,OAAO,GAAG;GAC3D,MAAM,QAAQ,MAAM,aAAa,IAAA,CAAK,YAAY;GAClD,IAAI,CAAC,MAAM,IAAI,IAAI,GACjB,MAAM,IAAI,MAAM,KAAK;QAChB;IACL,MAAM,WAAW,MAAM,IAAI,IAAI;IAC/B,MAAM,gBAAgB,SAAS,aAAa,GAAA,CAAI,YAAY;IAC5D,MAAM,cAAc,MAAM,WAAW,GAAA,CAAI,YAAY;IACrD,MAAM,mBAAmB,SAAS,WAAW,GAAA,CAAI,YAAY;IAG7D,IACE,eACC,eAAe,gBAAgB,eAAe,OAC/C;KACA,MAAM,IAAI,MAAM,KAAK;KACrB,SACE,uBAAuB,KAAK,YAAY,IAAI,kBAC9C;IACF,OAAO,IACL,oBACC,qBAAqB,MAAM,aAAa,IAAA,CAAK,YAAY,KACxD,oBAAoB,OAGtB,SACE,uBAAuB,KAAK,qCAAqC,IAAI,EACvE;SAEA,SACE,qCAAqC,KAAK,kCAAkC,IAAI,EAClF;GAEJ;EACF;EACA,oBAAoB,IAAI,UAAU,KAAK;CACzC;CACA,OAAO;AACT;;;;;;;;;AAUA,SAAS,oBAA6B;CACpC,MAAM,SAAS,oBAAmB;CAElC,IAAI,QAAQ,IAAI,gBACd,OAAO,MAAM,6CAA6C;EACxD,UAAA,QAAA,IAAA;EACA,QAAQ,QAAQ,IAAI;EACpB,gBAAgB,QAAQ,IAAI;EAC5B;CACF,CAAC;CAGH,OAAO;AACT;AAEA,SAAS,oBAAyC;CAChD,IAAI,CAAC,+BAA+B,GAAG;EACrC,+BAA+B,IAAI;EACnC,IAAI;GAEF,MAAM,WAAW,QAAQ,sBAAsB;GAC/C,uBAAuB,SAAS,kBAAkB,SAAS,OAAO;EACpE,QAAQ;GAEN,uBAAuB;IACrB,SAAS;IACT,WAAA;IACA,SAAS,CAAC;IACV,aAAa;GACf,CAAC;EACH;CACF;CACA,OAAO,uBAAuB;AAChC;;;;;;AAOA,SAAS,kBAA8C;CACrD,IAAI,CAAC,6BAA6B,GAAG;EACnC,6BAA6B,IAAI;EAGjC,IAAI,CAAC,2BAA2B,GAAG;GACjC,IAAI,QAAQ,IAAI,gBACd,OAAO,MACL,yFACF;GAEF,qBAAqB,IAAI;GACzB,OAAO;EACT;EAEA,IAAI;GAEF,MAAM,WAAgC,MACpC,aACE,KAAK,QAAQ,IAAI,GAAG,iCAAiC,GACrD,OACF,CACF;GACA,qBAAqB,QAAQ;GAC7B,IAAI,QAAQ,IAAI,gBACd,OAAO,MACL,6CAA6C,OAAO,KAAK,UAAU,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,UAC3F;EAEJ,SAAS,OAAO;GACd,IAAI,QAAQ,IAAI,gBACd,OAAO,MACL,8EACF;GAEF,qBAAqB,IAAI;EAC3B;CACF;CACA,OAAO,qBAAqB;AAC9B;;;;;;;;;;;;AAqBA,SAAgB,4BAAyD;CAGvE,MAAM,SAAS,0BAA0B;CACzC,IAAI,WAAW,KAAA,KAAa,WAAW,MACrC,OAAO;CAMT,MAAM,WAAW,IADG,oBAAoB,QAAQ,IAAI,CACnC,CAAA,CAAQ,UAAU;CAInC,MAAM,mBAAmB,KACvB,QAAQ,IAAI,GACZ,iCACF;CAMA,IAAI,UAAU;EACZ,MAAM,cAAc,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC;EAGlD,IAAI,cAAc,GAAG;GACnB,0BAA0B,QAAQ;GAClC,SACE,sEAAsE,YAAY,UACpF;GACA,OAAO;EACT;EAGA,IAAI,WAAW,gBAAgB,GAC7B,IAAI;GACF,MAAM,eAAyB,MAC7B,aAAa,kBAAkB,OAAO,CACxC;GACA,MAAM,kBAAkB,OAAO,KAAK,aAAa,OAAO,CAAC,CAAC;GAE1D,IAAI,kBAAkB,GAAG;IACvB,0BAA0B,YAAY;IACtC,SACE,iDAAiD,iBAAiB,IAAI,gBAAgB,4DACxF;IACA,OAAO;GACT;EACF,QAAQ,CAER;EAIF,0BAA0B,QAAQ;EAClC,SACE,sEAAsE,YAAY,UACpF;EACA,OAAO;CACT;CAGA,IAAI,WAAW,gBAAgB,GAC7B,IAAI;EACF,MAAM,eAAyB,MAC7B,aAAa,kBAAkB,OAAO,CACxC;EACA,0BAA0B,YAAY;EACtC,MAAM,cAAc,OAAO,KAAK,aAAa,OAAO,CAAC,CAAC;EACtD,SACE,iDAAiD,iBAAiB,IAAI,YAAY,UACpF;EACA,OAAO;CACT,SAAS,OAAO;EACd,SACE,yDAAyD,iBAAiB,IAAI,iBAAiB,QAAQ,MAAM,UAAU,WACzH;CACF;CAKF,SACE,yEACF;CACA,OAAO;AACT;AAEA,IAAI;;;;;;;;;;;;AAeJ,SAAgB,0BAAsD;CACpE,IAAI,OAAO,YAAY,eAAe,OAAO,QAAQ,QAAQ,YAC3D,OAAO;CAET,MAAM,UAAU,IAAI,oBAAoB,QAAQ,IAAI,CAAC;CACrD,MAAM,OAAO;EACX,QAAQ,cAAc,KAAK;EAC3B,QAAQ,cAAc,OAAO;EAC7B,QAAQ,cAAc,QAAQ;CAChC,CAAC,CAAC,MAAM,cAAc,WAAW,SAAS,CAAC;CAC3C,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI;CACJ,IAAI;EACF,UAAU,SAAS,IAAI,CAAC,CAAC;CAC3B,QAAQ;EACN,OAAO;CACT;CACA,IACE,qBAAqB,SAAS,QAC9B,oBAAoB,YAAY,SAEhC,OAAO,oBAAoB;CAE7B,IAAI,WAAuC;CAC3C,IAAI;EACF,WAAW,MAA2B,aAAa,MAAM,OAAO,CAAC;CACnE,QAAQ;EACN,WAAW;CACb;CACA,sBAAsB;EAAE;EAAM;EAAS;CAAS;CAChD,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,eACd,MACA,eAAwB,OACxB,eACe;CACf,IAAI;EAIF,IAAI,CAAC,cAAc;GACjB,MAAM,YAAY,KAAK;GACvB,IAAI,WAAW;IACb,MAAM,aAAa,eAAe,SAAS,SAAS;IACpD,IAAI,YAAY,aACd,OAAO,WAAW;GAEtB;EACF;EAGA,MAAM,cAAe,KAAkC;EACvD,IAAI,aACF,OAAO;EAKT,IAAI;GAEF,MAAM,cADQ,kCAAiB,IAAI,MAAM,EAAA,CAAE,SAAS,GAAA,CAC3B,MAAM,IAAI;GAInC,KAAK,MAAM,QAAQ,YAAY;IAW7B,MAAM,YAAY,KAAK,MACrB,gHACF;IACA,IAAI,WAAW;KACb,MAAM,WAAW,UAAU;KAG3B,MAAM,iBAAiB,SAAS,QAAQ,OAAO,GAAG;KAClD,IACE,oBAAoB,cAAc,KAClC,4BAA4B,cAAc,KAC1C,wBAAwB,cAAc,GAEtC;KAIF,IAAI;MAGF,IAAI,MAAM,QADQ,SAAS,QAAQ,cAAc,EAC/B,CAAS;MAE3B,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;OAC3B,MAAM,UAAU,KAAK,KAAK,cAAc;OACxC,IAAI;QACF,IAAI,WAAW,OAAO,GAAG;SACvB,MAAM,MAAM,MACV,aAAa,SAAS,OAAO,CAC/B;SACA,IAAI,IAAI,MAAM,WAAW,GAAG,GAC1B,OAAO,IAAI;QAEf;OACF,QAAQ,CAER;OACA,MAAM,SAAS,QAAQ,GAAG;OAC1B,IAAI,WAAW,KAAK;OACpB,MAAM;MACR;KACF,QAAQ,CAER;KACA;IACF;GACF;EACF,QAAQ,CAER;EAKA,MAAM,cADQ,kCAAiB,IAAI,MAAM,EAAA,CAAE,SAAS,GAAA,CAC3B,MAAM,IAAI;EAGnC,KAAK,MAAM,QAAQ,YAAY;GAC7B,MAAM,QAAQ,KAAK,MAAM,+BAA+B;GACxD,IAAI,OAAO;IAIT,IACE,8BAA8B,MAAM,EAAE,KACtC,wBAAwB,IAAI,GAE5B;IAEF,OAAO,MAAM;GACf;EACF;EAIA,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAYA,SAAS,kBAAkB,UAAiC;CAC1D,IAAI,aAAa;CAEjB,OAAO,MAAM;EACX,IAAI,WAAW,KAAK,YAAY,qBAAqB,CAAC,GACpD,OAAO;EAGT,MAAM,YAAY,QAAQ,UAAU;EACpC,IAAI,cAAc,YAChB,OAAO;EAGT,aAAa;CACf;AACF;AAEA,SAAS,gCAAgC,aAAoC;CAC3E,IAAI,aAAa,QAAQ,IAAI;CAE7B,OAAO,MAAM;EACX,MAAM,kBAAkB,KACtB,YACA,gBACA,aACA,cACF;EAEA,IAAI,WAAW,eAAe,GAC5B,OAAO;EAGT,MAAM,YAAY,QAAQ,UAAU;EACpC,IAAI,cAAc,YAChB,OAAO;EAGT,aAAa;CACf;AACF;AAEA,SAAS,gCAAgC,aAAoC;CAC3E,MAAM,gBAAgB,kBAAkB,QAAQ,IAAI,CAAC;CACrD,IAAI,CAAC,eACH,OAAO;CAGT,MAAM,uBAAuB,KAAK,eAAe,UAAU;CAC3D,IAAI,CAAC,WAAW,oBAAoB,GAClC,OAAO;CAGT,KAAK,MAAM,kBAAkB,YAAY,oBAAoB,GAAG;EAC9D,MAAM,kBAAkB,KACtB,sBACA,gBACA,cACF;EACA,IAAI,CAAC,WAAW,eAAe,GAC7B;EAGF,IAAI;GAIF,IAHoB,MAClB,aAAa,iBAAiB,OAAO,CAEnC,CAAA,CAAY,SAAS,aACvB,OAAO;EAEX,QAAQ,CAER;CACF;CAEA,OAAO;AACT;AAEA,SAAS,mCAAmC,YAAmC;CAC7E,MAAM,aAAa,CACjB,KAAK,YAAY,OAAO,YAAY,eAAe,GACnD,KAAK,YAAY,SAAS,eAAe,CAC3C;CAEA,KAAK,MAAM,aAAa,YACtB,IAAI,WAAW,SAAS,GACtB,OAAO;CAIX,OAAO;AACT;AAMA,SAAS,0BACP,aACA,UAA+B,CAAC,GACjB;CACf,MAAM,aAAa,QAAQ,QAAQ;CACnC,MAAM,kBACJ,gCAAgC,WAAW,KAC3C,gCAAgC,WAAW;CAE7C,IAAI,CAAC,iBACH,OAAO;CAGT,MAAM,aAAa,QAAQ,eAAe;CAC1C,MAAM,cAAc,MAClB,aAAa,iBAAiB,OAAO,CACvC;CAGA,KAAK,MAAM,aAAa,CAFC,mBAAmB,YAEpB,GAAiB;EACvC,MAAM,iBAAiB,YAAY,UAAU;EAC7C,IAAI,CAAC,gBACH;EAGF,MAAM,uBACJ,OAAO,mBAAmB,WACtB,iBACA,eAAe,UACf,eAAe,WACf,eAAe;EAErB,IAAI,CAAC,sBAAsB;GACzB,IAAI,YACF,OAAO,KACL,WAAW,YAAY,iDAAiD,WAC1E;GAEF,OAAO;EACT;EAEA,IAAI,CAAC,qBAAqB,SAAS,OAAO,GAAG;GAC3C,IAAI,YACF,OAAO,KACL,WAAW,YAAY,mCAAmC,UAAU,aAAa,sBACnF;GAEF,OAAO;EACT;EAEA,MAAM,eAAe,KAAK,YAAY,oBAAoB;EAC1D,IAAI,WAAW,YAAY,GACzB,OAAO;EAGT,MAAM,0BACJ,mCAAmC,UAAU;EAC/C,IAAI,yBACF,OAAO;EAGT,IAAI,YACF,OAAO,KACL,WAAW,YAAY,4BAA4B,qBAAqB,kCAC1E;EAEF,OAAO;CACT;CAEA,OAAO;AACT;AAEA,SAAS,gCACP,WACU;CACV,MAAM,+BAAe,IAAI,IAAY;CAErC,KAAK,MAAM,YAAY,WACrB,KAAK,MAAM,OAAO,UAAU,oBAAoB,CAAC,GAC/C,aAAa,IAAI,GAAG;CAIxB,OAAO,MAAM,KAAK,YAAY;AAChC;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,yBACd,aACA,UAA+B,CAAC,GACf;CAEjB,IAAI,oBAAoB,CAAC,CAAC,IAAI,WAAW,GAAG;EAC1C,SAAS,+CAA+C,aAAa;EACrE,OAAO,oBAAoB,CAAC,CAAC,IAAI,WAAW;CAC9C;CAEA,SACE,8DAA8D,aAChE;CAEA,MAAM,eAAe,0BAA0B,aAAa,OAAO;CAEnE,IAAI,CAAC,cAAc;EACjB,SACE,6BAA6B,YAAY,wCAC3C;EACA,OAAO;CACT;CAEA,IAAI;EACF,MAAM,WAAW,MAAgB,aAAa,cAAc,OAAO,CAAC;EAEpE,IAAI,CAAC,SAAS,WAAW,OAAO,SAAS,YAAY,UAAU;GAC7D,IAAI,QAAQ,QAAQ,MAClB,OAAO,KAAK,0CAA0C,aAAa;GAErE,OAAO;EACT;EAEA,MAAM,iBAAiB,SAAS,cAC5B,WACA;GAAE,GAAG;GAAU;EAAY;EAE/B,oBAAoB,CAAC,CAAC,IAAI,aAAa,cAAc;EACrD,SACE,oDAAoD,YAAY,IAAI,OAAO,KAAK,eAAe,OAAO,CAAC,CAAC,OAAO,UACjH;EAEA,OAAO;CACT,SAAS,OAAO;EACd,IAAI,QAAQ,QAAQ,MAClB,OAAO,KACL,uCAAuC,YAAY,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAClG;EAEF,OAAO;CACT;AACF;AAEA,eAAsB,qBACpB,aACA,UAA+B,CAAC,GACN;CAE1B,OAAO,yBAAyB,aAAa,OAAO;AACtD;;;;;;;;AASA,SAAgB,yBACd,cACiB;CACjB,IAAI;EAEF,MAAM,WAAqB,MADN,aAAa,cAAc,OACf,CAAY;EAE7C,IAAI,CAAC,SAAS,WAAW,OAAO,SAAS,YAAY,UAAU;GAC7D,OAAO,KAAK,iCAAiC,cAAc;GAC3D,OAAO;EACT;EAEA,MAAM,WAAW,SAAS,eAAe;EACzC,oBAAoB,CAAC,CAAC,IAAI,UAAU,QAAQ;EAC5C,SACE,iDAAiD,aAAa,IAAI,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,OAAO,UACzG;EAEA,OAAO;CACT,SAAS,OAAO;EACd,OAAO,KACL,qCAAqC,aAAa,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OACjG;EACA,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAgB,qBACd,WAC2B;CAC3B,SAAS,sDAAsD,WAAW;CAQ1E,IAAI,kBAAkB,GAAG;EACvB,IAAI,CAAC,0BAA0B,GAC7B,0BAA0B;EAE5B,IAAI,CAAC,qBAAqB,GACxB,gBAAgB;CAEpB;CAKA,MAAM,QAAQ,gBAAgB,SAAS,WAC5B;EACL,MAAM,SAAS,mBAAmB,SAAS;EAC3C,OAAO;GACL,WAAW,OAAO;GAClB,aAAa,OAAO;GACpB,eAAe;EACjB;CACF,EAAA,CAAG,IACH,EAAE,UAAU;CAIhB,MAAM,eAAe,0BAA0B,CAAC,CAAC,OAAO,KAAK;CAC7D,IAAI,cAAc;EAChB,SACE,6BAA6B,UAAU,OAAO,aAAa,OAAO,QACpE;EACA,MAAM,QAAQ,aAAa;EAC3B,IAAI,CAAC,MAAM,eAAe,aAAa,aACrC,OAAO;GAAE,GAAG;GAAO,aAAa,aAAa;EAAY;EAE3D,OAAO;CACT;CAGA,SACE,qBAAqB,UAAU,4DACjC;CAEA,MAAM,kBAAkB,gCAAgC;EACtD,0BAA0B;EAC1B,kBAAkB,IAAI,gBAAgB,IAAI;EAC1C,kBAAkB;EAClB,GAAG,oBAAoB,CAAC,CAAC,OAAO;CAClC,CAAC;CAED,IAAI,gBAAgB,WAAW,GAC7B,SACE,+GACF;CAGF,MAAM,kCAAkB,IAAI,IAAY;CAExC,OAAO,gBAAgB,SAAS,GAAG;EACjC,MAAM,MAAM,gBAAgB,MAAM;EAClC,IAAI,CAAC,OAAO,gBAAgB,IAAI,GAAG,GACjC;EAGF,gBAAgB,IAAI,GAAG;EACvB,MAAM,WAAW,yBAAyB,GAAG;EAC7C,IAAI,CAAC,UACH;EAGF,KAAK,MAAM,cAAc,SAAS,oBAAoB,CAAC,GACrD,IAAI,CAAC,gBAAgB,IAAI,UAAU,GACjC,gBAAgB,KAAK,UAAU;EAKnC,MAAM,QAAQ,iBAAiB,UAAU,SAAS;EAClD,IAAI,OAAO;GACT,SACE,6BAA6B,UAAU,uBAAuB,KAChE;GAEA,IAAI,CAAC,MAAM,eAAe,SAAS,aACjC,OAAO;IAAE,GAAG;IAAO,aAAa,SAAS;GAAY;GAEvD,OAAO;EACT;CACF;CAEA,SAAS,uBAAuB,UAAU,2BAA2B;AAEvE;;;;;;;;;;;;;;AAkBA,SAAgB,iBACd,UACA,iBAC2B;CAE3B,IAAI,SAAS,QAAQ,kBACnB,OAAO,SAAS,QAAQ;CAK1B,MAAM,iBAAiB,kBAAkB,QAAQ;CAMjD,IAAI,gBAAgB,eAAe,GAAG;EACpC,MAAM,EAAE,WAAW,gBAAgB,mBAAmB,eAAe;EACrE,MAAM,QAAQ,eAAe,IAAI,UAAU,YAAY,CAAC;EACxD,MAAM,eAAe,OAAO,eAAe,SAAS;EACpD,OAAO,gBAAgB,iBAAiB,cAAc,KAAA,IAAY;CACpE;CAGA,IAAI,SAAS,aAAa;EACxB,MAAM,eAAe,oBACnB,SAAS,aACT,eACF;EACA,IAAI,SAAS,QAAQ,eACnB,OAAO,SAAS,QAAQ;CAE5B;CAGA,OAAO,eAAe,IAAI,gBAAgB,YAAY,CAAC;AACzD;;;;;;;AAQA,SAAgB,iCACd,eAC2B;CAC3B,IAAI,CAAC,gBAAgB,aAAa,GAChC;CAGF,MAAM,EAAE,gBAAgB,mBAAmB,aAAa;CAGxD,MAAM,WAAW,oBAAoB,CAAC,CAAC,IAAI,WAAW;CACtD,IAAI,UAEF,OAAO,iBAAiB,UAAU,aAAa;CAIjD,MAAM,qBAAqB,kBAAkB;CAC7C,IAAI,mBAAmB,gBAAgB,aACrC,OAAO,iBAAiB,oBAAoB,aAAa;CAI3D,IAAI,kBAAkB,GAAG;EACvB,MAAM,eAAe,gBAAgB;EACrC,IAAI,cAAc,gBAAgB,aAChC,OAAO,iBAAiB,cAAc,aAAa;EAIrD,MAAM,YAAY,0BAA0B;EAC5C,IAAI,WAAW,gBAAgB,aAC7B,OAAO,iBAAiB,WAAW,aAAa;CAEpD;AAGF;;;;;;;;;;;;;;;;;AAkBA,eAAsB,sBACpB,MACA,WACoC;CAIpC,IAAI,CAAE,KAAuC,iBAAiB;EAC5D,MAAM,aAAa,eAAe,sBAAsB,IAAI;EAC5D,IACE,YAAY,iBACZ,gBAAgB,WAAW,aAAa,GACxC;GAEA,MAAM,EAAE,gBAAgB,mBAAmB,WAAW,aAAa;GAEnE,MAAM,iBAAiB,oBAAoB,CAAC,CAAC,IAAI,WAAW;GAC5D,IAAI,gBAAgB;IAClB,MAAM,cAAc,iBAClB,gBACA,WAAW,aACb;IACA,IAAI,aACF,OAAO,CAAC,YAAY,eAAe,eAAe,cAC9C;KAAE,GAAG;KAAa,aAAa,eAAe;IAAY,IAC1D;GAER;GAMA,MAAM,WAAW,MAAM,qBAAqB,aAAa,EACvD,MAAM,WAAW,OAAO,SAAS,EACnC,CAAC;GACD,IAAI,UAAU;IAEZ,MAAM,QAAQ,iBAAiB,UAAU,WAAW,aAAa;IACjE,IAAI,OAEF,OAAO,CAAC,MAAM,eAAe,SAAS,cAClC;KAAE,GAAG;KAAO,aAAa,SAAS;IAAY,IAC9C;GAER;GAOA,IAAI,WAAW,OAAO,OAAO,GAC3B;EAEJ;CACF;CAMa,UAAU,YAAY;CACnC,MAAM,qBAAqB,eAAe,IAAI;CAG9C,MAAM,eAKD,CAAC;CAGN,MAAM,gBAAgB,qBAClB,oBAAoB,oBAAoB,SAAS,IACjD,KAAA;CAIJ,IAAI;CACJ,IAAI,kBAAkB,GAAG;EACvB,IAAI,0BAA0B,MAAM,KAAA,GAClC,0BAA0B;EAE5B,MAAM,gBAAgB,0BAA0B;EAChD,IAAI,eAAe;GAEjB,aAAa,iBAAiB,eAAe,iBAAiB,SAAS;GACvE,IAAI,YACF,aAAa,KAAK;IAChB,OAAO;IACP,aAAa,cAAc,eAAe;IAC1C,UAAU,WAAW;IACrB,gBAAgB;GAClB,CAAC;EAEL;CACF;CAIA,IAAI;MAKE,EAHF,0BAA0B,CAAC,EAAE,eAC7B,uBAAuB,0BAA0B,CAAC,EAAE,cAEnC;GACjB,MAAM,WAAW,MAAM,qBAAqB,kBAAkB;GAC9D,IAAI,UAAU;IAEZ,MAAM,QAAQ,iBAAiB,UAAU,iBAAiB,SAAS;IACnE,IAAI,OAAO;KACT,MAAM,gBACJ,CAAC,MAAM,eAAe,SAAS,cAC3B;MAAE,GAAG;MAAO,aAAa,SAAS;KAAY,IAC9C;KAEN,aAAa,KAAK;MAChB,OAAO;MACP,aAAa,SAAS,eAAe;MACrC,UAAU,MAAM;MAChB,gBAAgB,GAAG,SAAS,eAAe,mBAAmB;KAChE,CAAC;IACH;GACF;EACF;;CAKF,IAAI,kBAAkB,MAAM,CAAC,0BAA0B,KAAK,CAAC,aAAa;EACxE,MAAM,WAAW,gBAAgB;EACjC,IAAI,UAAU;GAEZ,MAAM,YAAY,iBAAiB,UAAU,iBAAiB,SAAS;GACvE,IAAI,WACF,aAAa,KAAK;IAChB,OAAO;IACP,aAAa,SAAS,eAAe;IACrC,UAAU,UAAU;IACpB,gBAAgB;GAClB,CAAC;EAEL;CACF;CAGA,MAAM,qBAAqB,kBAAkB;CAE7C,MAAM,cAAc,iBAClB,oBACA,iBAAiB,SACnB;CACA,IAAI,aACF,aAAa,KAAK;EAChB,OAAO;EACP,aAAa,mBAAmB,eAAe;EAC/C,UAAU,YAAY;EACtB,gBAAgB;CAClB,CAAC;CAIH,KAAK,MAAM,CAAC,eAAe,aAAa,oBAAoB,CAAC,CAAC,QAAQ,GAAG;EAEvE,IAAI,kBAAkB,oBACpB;EAIF,MAAM,QAAQ,iBAAiB,UAAU,iBAAiB,SAAS;EACnE,IAAI,OACF,aAAa,KAAK;GAChB,OACE,CAAC,MAAM,eAAe,SAAS,cAC3B;IAAE,GAAG;IAAO,aAAa,SAAS;GAAY,IAC9C;GACN,aAAa,SAAS,eAAe;GACrC,UAAU,MAAM;GAChB,gBAAgB,GAAG,SAAS,eAAe,cAAc;EAC3D,CAAC;CAEL;CAMA,IAAI,aAAa,SAAS,GAAG;EAC3B,MAAM,+BAAe,IAAI,IAAsC;EAC/D,KAAK,MAAM,SAAS,cAAc;GAIhC,IAAI,MAAM,MAAM,YAAY,GAAG,MAAM,YAAY,GAAG;GACpD,IAAI,MAAM,UAAU;IAClB,MAAM,SAAS,MAAM,SAAS,YAAY,WAAW;IACrD,IAAI,WAAW,IACb,MAAM,MAAM,SAAS,MAAM,MAAM;GAErC;GACA,IAAI,CAAC,aAAa,IAAI,GAAG,GACvB,aAAa,IAAI,KAAK,KAAK;EAE/B;EAEA,IAAI,aAAa,SAAS,GAGxB,aAAa,OAAO,CAAC;OAChB,IAAI,aAAa,OAAO,GAAG;GAEhC,MAAM,gBAAgB,aAAa,KAChC,MACC,OAAO,EAAE,YAAY,IAAI,EAAE,YAAY,eAAe,SAAS,EAAE,gBACrE;GAGA,yBAAyB,CAAC,CAAC,IACzB,WACA,aAAa,KAAK,OAAO;IACvB,aAAa,EAAE;IACf,UAAU,EAAE;IACZ,gBAAgB,EAAE;GACpB,EAAE,CACJ;GAGA,MAAM,IAAI,MACR,wCAAwC,UAAU,oDACA,cAAc,KAAK,IAAI,EAAE,8LAInB,UAAU,GAAG,oBAAoB,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,SAAS,8PAIxH;EACF;CACF;CAGA,IAAI,aAAa,WAAW,GAC1B,OAAO,aAAa,EAAE,CAAC;AAI3B;;;;;;AAOA,SAAgB,wBAGd;CACA,OAAO,IAAI,IAAI,yBAAyB,CAAC;AAC3C;;;;;;AAOA,SAAgB,qBAA2B;CAGzC,uBAAuB,KAAA,CAAS;CAChC,+BAA+B,KAAK;CACpC,qBAAqB,KAAA,CAAS;CAC9B,6BAA6B,KAAK;CAClC,0BAA0B,KAAA,CAAS;CACnC,oBAAoB,CAAC,CAAC,MAAM;CAC5B,yBAAyB,CAAC,CAAC,MAAM;CACjC,mBAAmB,CAAC,CAAC,MAAM;CAC3B,sBAAsB,KAAA;AACxB;;;;;;;;AASA,SAAgB,qBAAgD;CAC9D,OAAO,MAAM,KAAK,oBAAoB,CAAC,CAAC,QAAQ,CAAC;AACnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,wBACd,YAC0E;CAE1E,MAAM,QAAQ,mBAAmB;CACjC,MAAM,SAAS,MAAM,IAAI,UAAU;CACnC,IAAI,WAAW,KAAA,GACb,OAAO;CAGT,MAAM,WAID,CAAC;CAIN,MAAM,+BAAe,IAAI,IAAY;CAErC,SACE,oEAAoE,YACtE;CAGA,MAAM,mBACJ,UACA,WACG;EACH,IAAI,CAAC,UAAU,SAAS;EAExB,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,OAAO,GAKxD,IAAI,MAAM,eAAe,YAAY;GACnC,MAAM,YAAY,MAAM,aAAa;GAErC,MAAM,iBAAiB,UAAU,YAAY;GAC7C,IAAI,CAAC,aAAa,IAAI,cAAc,GAAG;IACrC,aAAa,IAAI,cAAc;IAC/B,SAAS,KAAK;KACZ;KACA;KACA,aAAa,MAAM,eAAe,SAAS;IAC7C,CAAC;IACD,SACE,wCAAwC,UAAU,gBAAgB,WAAW,SAAS,QACxF;GACF;EACF;CAEJ;CAGA,IAAI,kBAAkB,GAAG;EACvB,IAAI,CAAC,0BAA0B,GAC7B,0BAA0B;EAE5B,gBAAgB,0BAA0B,GAAG,mBAAmB;CAClE;CAGA,MAAM,mBAAmB,kBAAkB,IAAI,gBAAgB,IAAI;CACnE,IAAI,kBACF,gBAAgB,kBAAkB,cAAc;CAIlD,MAAM,qBAAqB,kBAAkB;CAC7C,gBAAgB,oBAAoB,gBAAgB;CAGpD,KAAK,MAAM,CAAC,SAAS,aAAa,oBAAoB,CAAC,CAAC,QAAQ,GAC9D,gBAAgB,UAAU,iBAAiB,SAAS;CAKtD,MAAM,sBAAsB,gCAAgC;EAC1D,0BAA0B;EAC1B,kBAAkB,IAAI,mBAAmB;EACzC;EACA,GAAG,oBAAoB,CAAC,CAAC,OAAO;CAClC,CAAC;CACD,MAAM,sCAAsB,IAAI,IAAY;CAE5C,SACE,8BAA8B,oBAAoB,OAAO,uCAAuC,oBAAoB,KAAK,IAAI,GAC/H;CAEA,OAAO,oBAAoB,SAAS,GAAG;EACrC,MAAM,MAAM,oBAAoB,MAAM;EACtC,IAAI,CAAC,OAAO,oBAAoB,IAAI,GAAG,GACrC;EAGF,oBAAoB,IAAI,GAAG;EAE3B,MAAM,WAAW,yBAAyB,GAAG;EAC7C,IAAI,UAAU;GACZ,gBAAgB,UAAU,kBAAkB,KAAK;GAEjD,KAAK,MAAM,cAAc,SAAS,oBAAoB,CAAC,GACrD,IAAI,CAAC,oBAAoB,IAAI,UAAU,GACrC,oBAAoB,KAAK,UAAU;EAGzC;CACF;CAEA,SACE,mDAAmD,SAAS,OAAO,4BAA4B,YACjG;CAGA,MAAM,IAAI,YAAY,QAAQ;CAE9B,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"manifest-loader.js","names":[],"sources":["../../src/manifest/manifest-loader.ts"],"sourcesContent":["/**\n * Manifest Loader - Automatic discovery of external package manifests\n *\n * This module provides automatic loading of SMRT manifests from external packages,\n * solving issue #131 where external package schemas weren't being detected.\n *\n * Architecture:\n * - Build-time: AST scanner generates manifests in dist/manifest.json\n * - Run-time: This module discovers and loads those manifests automatically\n * - Caching: Loaded manifests are cached to avoid repeated imports\n *\n * Flow:\n * 1. Check testManifest (for test classes)\n * 2. Check staticManifest (for core framework classes)\n * 3. Resolve external package manifests from explicit JSON exports\n * 4. Return manifest entry or undefined\n *\n * External manifest loading intentionally follows a pure-ESM JSON contract.\n * Packages are expected to publish a JSON manifest via \"./manifest\" and/or\n * \"./manifest.json\" exports. Runtime discovery does not guess sibling workspace\n * paths or load JavaScript manifest modules.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { bumpRegistryGeneration } from '../registry/generation.js';\nimport type { SmrtObjectConstructor } from '../registry/types.js';\nimport { ObjectRegistry } from '../registry.js';\nimport type {\n FieldDefinition,\n MethodDefinition,\n SmartObjectDefinition,\n SmartObjectManifest,\n} from '../scanner/types.js';\nimport { MANIFEST_TIMESTAMP } from '../scanner/types.js';\nimport { parse } from '../utils/json.js';\nimport {\n createQualifiedName,\n isQualifiedName,\n parseQualifiedName,\n} from '../utils/qualified-names.js';\nimport {\n isDecoratorRuntimeFramePath,\n isDecoratorRuntimePackageName,\n isModuleRunnerFramePath,\n isSmrtCoreFramePath,\n} from '../utils/stack-frames.js';\nimport { LocalManifestReader } from './local-manifest-reader.js';\nimport {\n dirname,\n existsSync,\n join,\n readdirSync,\n readFileSync,\n require,\n statSync,\n} from './node-host.js';\nimport { getDefaultCompositeSource } from './sources/composite.js';\nimport {\n getLocalTestManifestCache as getLocalTestManifestCacheFromStore,\n getManifestCache as getManifestCacheFromStore,\n getStaticManifestCache as getStaticManifestCacheFromStore,\n getTestManifestCache as getTestManifestCacheFromStore,\n isTestEnvironment as isTestEnvFromStore,\n shouldLoadCoreTestManifest,\n} from './store.js';\nimport { isRefusedTestArtifact } from './test-artifacts.js';\n\n/**\n * Extend globalThis to include manifest loader state.\n * Using globalThis ensures all module instances share the same manifest caches,\n * which is critical in monorepos where the same package can be loaded\n * from different paths (e.g., pnpm store vs workspace symlink).\n *\n * @see https://github.com/happyvertical/smrt/issues/543\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtManifestStatic: SmartObjectManifest | null | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestStaticLoadAttempted: boolean | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestTest: SmartObjectManifest | null | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestTestLoadAttempted: boolean | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestCache: Map<string, SmartObjectManifest> | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestLocalTest: SmartObjectManifest | null | undefined;\n // eslint-disable-next-line no-var\n var __smrtManifestCollisions:\n | Map<\n string,\n Array<{\n packageName: string;\n filePath?: string;\n manifestSource: string;\n }>\n >\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtSTISiblingCache:\n | Map<\n string,\n Array<{ className: string; entry: ManifestEntry; packageName?: string }>\n >\n | undefined;\n}\n\n// Use globalThis for cross-module state sharing\n// This ensures loadConfig() in one module instance affects all packages\n\n// ── Cache access (delegates to manifest/store.ts — the single source of\n// truth added in Release B #1133 for globalThis manifest state).\n// manifest-loader.ts keeps its own setters + load-attempted flags below\n// because those are internal to this module's async loader state machine;\n// the read-only getters just forward to store.ts to eliminate drift.\n\nconst getStaticManifestCache = getStaticManifestCacheFromStore;\nconst getTestManifestCache = getTestManifestCacheFromStore;\nconst getLocalTestManifestCache = getLocalTestManifestCacheFromStore;\nconst getManifestCacheMap = getManifestCacheFromStore;\n\nfunction setStaticManifestCache(\n manifest: SmartObjectManifest | null | undefined,\n): void {\n globalThis.__smrtManifestStatic = manifest;\n bumpRegistryGeneration();\n}\n\nfunction getStaticManifestLoadAttempted(): boolean {\n return globalThis.__smrtManifestStaticLoadAttempted ?? false;\n}\n\nfunction setStaticManifestLoadAttempted(value: boolean): void {\n globalThis.__smrtManifestStaticLoadAttempted = value;\n}\n\nfunction setTestManifestCache(\n manifest: SmartObjectManifest | null | undefined,\n): void {\n globalThis.__smrtManifestTest = manifest;\n bumpRegistryGeneration();\n}\n\nfunction getTestManifestLoadAttempted(): boolean {\n return globalThis.__smrtManifestTestLoadAttempted ?? false;\n}\n\nfunction setTestManifestLoadAttempted(value: boolean): void {\n globalThis.__smrtManifestTestLoadAttempted = value;\n}\n\nfunction setLocalTestManifestCache(\n manifest: SmartObjectManifest | null | undefined,\n): void {\n globalThis.__smrtManifestLocalTest = manifest;\n bumpRegistryGeneration();\n}\n\n/**\n * Get the manifest collisions Map from globalThis\n */\nfunction getManifestCollisionsMap(): Map<\n string,\n Array<{ packageName: string; filePath?: string; manifestSource: string }>\n> {\n if (!globalThis.__smrtManifestCollisions) {\n globalThis.__smrtManifestCollisions = new Map();\n }\n return globalThis.__smrtManifestCollisions;\n}\n\n/**\n * Get the STI sibling cache Map from globalThis\n * Caches discoverSTISiblingsSync results per collection to avoid repeated scans\n */\nfunction getSTISiblingCache(): Map<\n string,\n Array<{ className: string; entry: ManifestEntry; packageName?: string }>\n> {\n if (!globalThis.__smrtSTISiblingCache) {\n globalThis.__smrtSTISiblingCache = new Map();\n }\n return globalThis.__smrtSTISiblingCache;\n}\n\n/**\n * Cached debug flag evaluated once at module load time.\n * Environment variables don't change at runtime, so this is safe.\n * @see https://github.com/happyvertical/smrt/issues/729\n */\nconst DEBUG_ENABLED =\n process.env.DEBUG_MANIFEST === 'true' ||\n process.env.DEBUG_MANIFEST === '1' ||\n process.env.DEBUG?.includes('manifest') ||\n false;\n\n// The debug traces below are gated by DEBUG_MANIFEST / DEBUG_TEST_ENV, so the\n// logger level must allow debug when either is set (a fixed 'info' would filter\n// them out and silently break those debug switches).\nconst logger = createLogger({\n level: DEBUG_ENABLED || process.env.DEBUG_TEST_ENV ? 'debug' : 'info',\n});\n\n/**\n * Log a debug message only if DEBUG_MANIFEST is enabled.\n * Uses cached boolean check instead of repeated env var access.\n */\nfunction debugLog(message: string): void {\n if (DEBUG_ENABLED) {\n logger.debug(message);\n }\n}\n\n/**\n * Cache for className-to-entry index per manifest.\n * This enables O(1) lookup by className instead of O(n) iteration.\n * @see https://github.com/happyvertical/smrt/issues/729\n */\nconst classNameIndexCache = new WeakMap<\n SmartObjectManifest,\n Map<string, SmartObjectDefinition>\n>();\n\n/**\n * Get or build the className index for a manifest.\n * Index maps lowercase className to entry for O(1) lookup.\n */\nfunction getClassNameIndex(\n manifest: SmartObjectManifest,\n): Map<string, SmartObjectDefinition> {\n let index = classNameIndexCache.get(manifest);\n if (!index) {\n index = new Map<string, SmartObjectDefinition>();\n for (const [key, entry] of Object.entries(manifest.objects)) {\n const name = (entry.className || key).toLowerCase();\n if (!index.has(name)) {\n index.set(name, entry);\n } else {\n const existing = index.get(name)!;\n const existingName = (existing.className || '').toLowerCase();\n const newExtends = (entry.extends || '').toLowerCase();\n const existingExtends = (existing.extends || '').toLowerCase();\n\n // Issue #950: STI child-wins — if new entry extends existing, replace\n if (\n newExtends &&\n (newExtends === existingName || newExtends === name)\n ) {\n index.set(name, entry);\n debugLog(\n `Manifest className '${name}': child '${key}' replaces parent`,\n );\n } else if (\n existingExtends &&\n (existingExtends === (entry.className || key).toLowerCase() ||\n existingExtends === name)\n ) {\n // Existing is already the child, keep it\n debugLog(\n `Manifest className '${name}': keeping child, ignoring parent '${key}'`,\n );\n } else {\n debugLog(\n `Manifest className collision for '${name}': keeping first, ignoring key '${key}'`,\n );\n }\n }\n }\n classNameIndexCache.set(manifest, index);\n }\n return index;\n}\n\n/**\n * Test-environment detection with optional DEBUG_TEST_ENV logging.\n *\n * Delegates to the single source of truth in `store.ts` so the\n * ManifestSource implementations (which also gate on test-env) cannot\n * drift from the legacy sync loader. The logging wrapper stays here\n * because the trace is specific to this module's loader state machine.\n */\nfunction isTestEnvironment(): boolean {\n const result = isTestEnvFromStore();\n\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug('[manifest-loader] isTestEnvironment check', {\n NODE_ENV: process.env.NODE_ENV,\n VITEST: process.env.VITEST,\n JEST_WORKER_ID: process.env.JEST_WORKER_ID,\n result,\n });\n }\n\n return result;\n}\n\nfunction getStaticManifest(): SmartObjectManifest {\n if (!getStaticManifestLoadAttempted()) {\n setStaticManifestLoadAttempted(true);\n try {\n // Try to import the generated static manifest\n const imported = require('./static-manifest.js');\n setStaticManifestCache(imported.staticManifest || imported.default);\n } catch {\n // Fallback to empty manifest if file doesn't exist yet (during build)\n setStaticManifestCache({\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n packageName: '@happyvertical/smrt-core',\n });\n }\n }\n return getStaticManifestCache()!;\n}\n\n/**\n * Lazy-load smrt-core's internal test manifest only for smrt-core's own tests.\n * Consumer packages should never see these fixtures, or they can collide with\n * real external classes during downstream test runs.\n */\nfunction getTestManifest(): SmartObjectManifest | null {\n if (!getTestManifestLoadAttempted()) {\n setTestManifestLoadAttempted(true);\n\n // CRITICAL: Scope the core test manifest to smrt-core's own test suite.\n if (!shouldLoadCoreTestManifest()) {\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug(\n '[manifest-loader] ⚠️ Skipping core test manifest load (not smrt-core test environment)',\n );\n }\n setTestManifestCache(null);\n return null;\n }\n\n try {\n // Read the generated JSON only inside the core-test environment gate.\n const manifest: SmartObjectManifest = parse(\n readFileSync(\n join(process.cwd(), 'src/manifest/test-manifest.json'),\n 'utf-8',\n ),\n );\n setTestManifestCache(manifest);\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug(\n `[manifest-loader] ✅ Loaded test manifest (${Object.keys(manifest?.objects || {}).length} objects)`,\n );\n }\n } catch (error) {\n if (process.env.DEBUG_TEST_ENV) {\n logger.debug(\n '[manifest-loader] ⚠️ Test manifest not found (this is normal in production)',\n );\n }\n setTestManifestCache(null);\n }\n }\n return getTestManifestCache();\n}\n\n// Re-export types for convenience\nexport type Manifest = SmartObjectManifest;\nexport type ManifestEntry = SmartObjectDefinition;\nexport type { FieldDefinition, MethodDefinition };\n\n// Note: manifestCache and localTestManifest are now accessed via globalThis helper functions\n// getManifestCacheMap() and getLocalTestManifestCache()/setLocalTestManifestCache()\n\n/**\n * Load local test manifest from current package (synchronous)\n *\n * During test runs, packages can have manifests in two locations:\n * 1. src/manifest/test-manifest.json - Domain packages during development\n * 2. dist/manifest.json - Built packages (consuming apps like praeco)\n *\n * This function attempts to load the manifest from either location.\n *\n * @returns Loaded manifest or null if not found or undefined if not yet attempted\n */\nexport function loadLocalTestManifestSync(): Manifest | null | undefined {\n // Only return cached manifest if it was successfully loaded\n // Don't cache null (failed loads) - allow retries\n const cached = getLocalTestManifestCache();\n if (cached !== undefined && cached !== null) {\n return cached;\n }\n\n // Use LocalManifestReader for unified local loading\n // This checks: active test snapshot -> .smrt/manifest.json -> dist/manifest.json\n const manager = new LocalManifestReader(process.cwd());\n const manifest = manager.loadLocal();\n\n // Fallback location: src/manifest/test-manifest.json\n // This is still used by smrt-core and other packages that generate test manifests here\n const testManifestPath = join(\n process.cwd(),\n 'src/manifest/test-manifest.json',\n );\n\n // If LocalManifestReader found a manifest, check if it has objects\n // If it has 0 objects but the fallback exists with objects, use the fallback instead\n // This handles the case where dist/manifest.json is the static-manifest.json (0 objects)\n // but src/manifest/test-manifest.json has the real test classes\n if (manifest) {\n const objectCount = Object.keys(manifest.objects).length;\n\n // If manifest has objects, use it\n if (objectCount > 0) {\n setLocalTestManifestCache(manifest);\n debugLog(\n `[manifest-loader] ✅ Loaded local manifest via LocalManifestReader (${objectCount} objects)`,\n );\n return manifest;\n }\n\n // Manifest has 0 objects - check if fallback has more\n if (existsSync(testManifestPath)) {\n try {\n const testManifest: Manifest = parse(\n readFileSync(testManifestPath, 'utf-8'),\n );\n const testObjectCount = Object.keys(testManifest.objects).length;\n\n // Outside the test run that registered it, a test manifest's inline\n // fixtures are phantom objects (#2760).\n const refused = isRefusedTestArtifact(testManifest, testManifestPath);\n if (refused && testObjectCount > 0) {\n warnRefusedLocalTestManifest(testManifestPath);\n }\n if (testObjectCount > 0 && !refused) {\n setLocalTestManifestCache(testManifest);\n debugLog(\n `[manifest-loader] ✅ Loaded test manifest from ${testManifestPath} (${testObjectCount} objects) - preferred over empty LocalManifestReader result`,\n );\n return testManifest;\n }\n } catch {\n // Fallback also failed, use the empty manifest\n }\n }\n\n // No better option, cache and use the empty manifest\n setLocalTestManifestCache(manifest);\n debugLog(\n `[manifest-loader] ✅ Loaded local manifest via LocalManifestReader (${objectCount} objects)`,\n );\n return manifest;\n }\n\n // LocalManifestReader returned null - check fallback\n if (existsSync(testManifestPath)) {\n try {\n const testManifest: Manifest = parse(\n readFileSync(testManifestPath, 'utf-8'),\n );\n if (isRefusedTestArtifact(testManifest, testManifestPath)) {\n warnRefusedLocalTestManifest(testManifestPath);\n return null;\n }\n setLocalTestManifestCache(testManifest);\n const objectCount = Object.keys(testManifest.objects).length;\n debugLog(\n `[manifest-loader] ✅ Loaded test manifest from ${testManifestPath} (${objectCount} objects)`,\n );\n return testManifest;\n } catch (error) {\n debugLog(\n `[manifest-loader] ✗ Failed to load test manifest from ${testManifestPath}: ${error instanceof Error ? error.message : 'unknown'}`,\n );\n }\n }\n\n // No manifest found - DON'T cache null, allow retries\n // This is important because the manifest may be generated later\n debugLog(\n '[manifest-loader] ⚠️ No local manifest found (will retry on next call)',\n );\n return null;\n}\n\nconst warnedRefusedLocalTestManifests = new Set<string>();\n\n/**\n * Warn once that a local test manifest was refused: outside the test run that\n * registered it, its inline fixtures would be phantom objects (#2760), and\n * inside an unregistered run their metadata is silently missing otherwise.\n */\nfunction warnRefusedLocalTestManifest(path: string): void {\n if (warnedRefusedLocalTestManifests.has(path)) return;\n warnedRefusedLocalTestManifests.add(path);\n logger.warn(\n `[manifest-loader] Ignoring test manifest ${path}: it is not this process's active test manifest. Run tests under smrtVitestPlugin() (or register it with registerActiveTestManifestPath()) to use it.`,\n );\n}\n\nconst projectManifestReads = new Map<\n string,\n { mtimeMs: number; manifest: SmartObjectManifest | null }\n>();\n\n/** Parse `path`, reusing the previous parse while its mtime is unchanged. */\nfunction readProjectManifestCandidate(\n path: string,\n): SmartObjectManifest | null | undefined {\n let mtimeMs: number;\n try {\n mtimeMs = statSync(path).mtimeMs;\n } catch {\n return undefined;\n }\n const cached = projectManifestReads.get(path);\n if (cached?.mtimeMs === mtimeMs) return cached.manifest;\n let manifest: SmartObjectManifest | null = null;\n try {\n manifest = parse<SmartObjectManifest>(readFileSync(path, 'utf-8'));\n } catch {\n manifest = null;\n }\n projectManifestReads.set(path, { mtimeMs, manifest });\n return manifest;\n}\n\n/**\n * The manifest of the project the process runs in (the first of\n * `LocalManifestReader.getLocalCandidatePaths()`: a test run's active\n * snapshot, then `<cwd>/.smrt/manifest.json`, `<cwd>/dist/manifest.json`,\n * `<cwd>/src/manifest/manifest.json`, skipping a test artifact outside its\n * own run, #2760), never seeded into the lookup caches. Only consulted to\n * attribute a decorated class whose declaring file that manifest describes,\n * when no loaded manifest does: a plain Node/tsx script importing an app's\n * workspace-package models has not loaded the app's manifest yet (#3109).\n * Each read is reused only while that file is unchanged, so a dev server that\n * regenerates it is seen; a missing manifest is never cached.\n */\nexport function readProjectManifestSync(): SmartObjectManifest | null {\n if (typeof process === 'undefined' || typeof process.cwd !== 'function') {\n return null;\n }\n const manager = new LocalManifestReader(process.cwd());\n for (const candidate of manager.getLocalCandidatePaths()) {\n if (!existsSync(candidate)) continue;\n const manifest = readProjectManifestCandidate(candidate);\n if (manifest === undefined) return null;\n if (isRefusedTestArtifact(manifest, candidate)) continue;\n return manifest;\n }\n return null;\n}\n\n/**\n * Extract package name from class constructor\n *\n * Uses the ObjectRegistry (preferred) or fallback methods to determine\n * which package the class belongs to.\n *\n * Search order:\n * 1. ObjectRegistry (packageName injected at build time from manifest) - SKIPPED during initial registration\n * 2. __package__ metadata (build tooling can inject this)\n * 3. require.resolve() (resolves package.json from constructor file location)\n * 4. Error stack trace parsing (fallback, fragile in pnpm workspaces)\n *\n * Decorator-lowering runtime helpers (`@oxc-project/runtime` under Vite 8,\n * `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped by both\n * stack-based methods (3 and 4): the compiler inserts such a frame between the\n * `@smrt()` decorator and the consumer module, and without skipping it the class\n * misattributes to the helper package (e.g. `@oxc-project/runtime`) instead of\n * the declaring package (#1785).\n *\n * @param ctor - Class constructor\n * @param skipRegistry - If true, skip checking ObjectRegistry (used during initial registration to avoid circular dependency)\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 Package name (e.g., '@happyvertical/smrt-places') or null\n */\nexport function getPackageName(\n ctor: SmrtObjectConstructor,\n skipRegistry: boolean = false,\n stackOverride?: string,\n): string | null {\n try {\n // 1. Try ObjectRegistry first (most reliable - from build-time manifest)\n // This solves issue #143 where pnpm workspace symlinks break stack trace parsing\n // CRITICAL: Skip during initial registration to avoid circular dependency (issue #159)\n if (!skipRegistry) {\n const className = ctor.name;\n if (className) {\n const registered = ObjectRegistry.getClass(className);\n if (registered?.packageName) {\n return registered.packageName;\n }\n }\n }\n\n // 2. Check if class has __package__ metadata (could be added by build tooling)\n const packageMeta = (ctor as { __package__?: string }).__package__;\n if (packageMeta) {\n return packageMeta;\n }\n\n // 3. Try require.resolve() to find package.json from constructor location\n // This is more reliable than stack trace parsing for published packages\n try {\n const stack = stackOverride ?? new Error().stack ?? '';\n const stackLines = stack.split('\\n');\n\n // Find the first line with a file path that's NOT from smrt-core\n // Skip manifest-loader, registry, and other smrt-core files\n for (const line of stackLines) {\n // Both frame forms: `at fn (path:line:col)` and the anonymous\n // `at path:line:col` a module's top level reports. Under a module\n // runner (Vitest, Vite SSR) the class-declaring module is the\n // anonymous one; matching only the parenthesized form skipped it and\n // attributed the class to the runner's package (#3098).\n // The Vite dev server's module runner reports an installed\n // dependency's file with its URL query (`dist/x.js?v=030af238:1:2`).\n // Rejecting that frame fell through to the app's own module below it\n // and attributed every such dependency class to the app (#3490), so\n // the query is matched and dropped.\n const fileMatch = line.match(\n /(?:\\(|\\bat\\s+)((?:file:\\/\\/)?[^()\\s?]+\\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?:\\?[^()\\s]*?)?(?::\\d+:\\d+)?\\)?\\s*$/,\n );\n if (fileMatch) {\n const filePath = fileMatch[1];\n // Skip smrt-core internal files and decorator-lowering runtime\n // helpers (#1785) — the class is declared in neither.\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n if (\n isSmrtCoreFramePath(normalizedPath) ||\n isDecoratorRuntimeFramePath(normalizedPath) ||\n isModuleRunnerFramePath(normalizedPath)\n ) {\n continue; // Skip smrt-core files, look for external package\n }\n\n // Try to resolve package.json from this file\n try {\n // Handle file:// URLs\n const cleanPath = filePath.replace(/^file:\\/\\//, '');\n let dir = dirname(cleanPath);\n // Walk up until we find a package.json\n for (let i = 0; i < 10; i++) {\n const pkgPath = join(dir, 'package.json');\n try {\n if (existsSync(pkgPath)) {\n const pkg = parse<{ name?: string }>(\n readFileSync(pkgPath, 'utf-8'),\n );\n if (pkg.name?.startsWith('@')) {\n return pkg.name;\n }\n }\n } catch {\n // Keep walking up\n }\n const parent = dirname(dir);\n if (parent === dir) break; // Reached root\n dir = parent;\n }\n } catch {\n // Fall through to next method\n }\n break;\n }\n }\n } catch {\n // Fall through to next method\n }\n\n // 4. Final fallback: Try to extract from Error stack trace with node_modules pattern\n // This method is fragile and fails in pnpm workspaces, but kept for backward compatibility\n const stack = stackOverride ?? new Error().stack ?? '';\n const stackLines = stack.split('\\n');\n\n // Look for line with 'node_modules/@scope/package' pattern\n for (const line of stackLines) {\n const match = line.match(/node_modules\\/(@[^/]+\\/[^/]+)/);\n if (match) {\n // Skip decorator-lowering runtime helpers (e.g. @oxc-project/runtime) —\n // the compiler inserts them between @smrt() and the declaring module, so\n // attributing to them is wrong; keep walking to the real package (#1785).\n if (\n isDecoratorRuntimePackageName(match[1]) ||\n isModuleRunnerFramePath(line)\n ) {\n continue;\n }\n return match[1];\n }\n }\n\n // Package name is now injected at build time into the manifest and stored in ObjectRegistry\n // If we reach here, the class is likely not from an external package\n return null;\n } catch {\n return null;\n }\n}\n\ninterface ManifestExportConditions {\n default?: string;\n import?: string;\n require?: string;\n}\n\ninterface ExternalPackageJson {\n exports?: Record<string, string | ManifestExportConditions>;\n}\n\nfunction findWorkspaceRoot(startDir: string): string | null {\n let currentDir = startDir;\n\n while (true) {\n if (existsSync(join(currentDir, 'pnpm-workspace.yaml'))) {\n return currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n return null;\n }\n\n currentDir = parentDir;\n }\n}\n\nfunction resolveInstalledPackageJsonPath(packageName: string): string | null {\n let currentDir = process.cwd();\n\n while (true) {\n const packageJsonPath = join(\n currentDir,\n 'node_modules',\n packageName,\n 'package.json',\n );\n\n if (existsSync(packageJsonPath)) {\n return packageJsonPath;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n return null;\n }\n\n currentDir = parentDir;\n }\n}\n\nfunction resolveWorkspacePackageJsonPath(packageName: string): string | null {\n const workspaceRoot = findWorkspaceRoot(process.cwd());\n if (!workspaceRoot) {\n return null;\n }\n\n const workspacePackagesDir = join(workspaceRoot, 'packages');\n if (!existsSync(workspacePackagesDir)) {\n return null;\n }\n\n for (const packageDirName of readdirSync(workspacePackagesDir)) {\n const packageJsonPath = join(\n workspacePackagesDir,\n packageDirName,\n 'package.json',\n );\n if (!existsSync(packageJsonPath)) {\n continue;\n }\n\n try {\n const packageJson = parse<{ name?: string }>(\n readFileSync(packageJsonPath, 'utf-8'),\n );\n if (packageJson.name === packageName) {\n return packageJsonPath;\n }\n } catch {\n // Ignore unreadable workspace package metadata and continue scanning.\n }\n }\n\n return null;\n}\n\n/** Whether the manifest file at `path` is a refused test artifact (#2760). */\nfunction isRefusedTestArtifactFile(path: string): boolean {\n try {\n return isRefusedTestArtifact(parse(readFileSync(path, 'utf-8')), path);\n } catch {\n // Unreadable files are handled (and reported) by the caller's own read.\n return false;\n }\n}\n\nfunction resolveWorkspaceSourceManifestPath(packageDir: string): string | null {\n const candidates = [\n join(packageDir, 'src', 'manifest', 'manifest.json'),\n join(packageDir, '.smrt', 'manifest.json'),\n ];\n\n for (const candidate of candidates) {\n if (existsSync(candidate) && !isRefusedTestArtifactFile(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\ninterface ManifestLoadOptions {\n warn?: boolean;\n}\n\nfunction resolveManifestExportPath(\n packageName: string,\n options: ManifestLoadOptions = {},\n): string | null {\n const shouldWarn = options.warn ?? true;\n const packageJsonPath =\n resolveInstalledPackageJsonPath(packageName) ||\n resolveWorkspacePackageJsonPath(packageName);\n\n if (!packageJsonPath) {\n return null;\n }\n\n const packageDir = dirname(packageJsonPath);\n const packageJson = parse<ExternalPackageJson>(\n readFileSync(packageJsonPath, 'utf-8'),\n );\n const manifestExports = ['./manifest.json', './manifest'];\n\n for (const exportKey of manifestExports) {\n const manifestExport = packageJson.exports?.[exportKey];\n if (!manifestExport) {\n continue;\n }\n\n const manifestRelativePath =\n typeof manifestExport === 'string'\n ? manifestExport\n : manifestExport.import ||\n manifestExport.default ||\n manifestExport.require;\n\n if (!manifestRelativePath) {\n if (shouldWarn) {\n logger.warn(\n `Package ${packageName} has invalid manifest export configuration for ${exportKey}`,\n );\n }\n return null;\n }\n\n if (!manifestRelativePath.endsWith('.json')) {\n if (shouldWarn) {\n logger.warn(\n `Package ${packageName} must export a JSON manifest for ${exportKey}, received ${manifestRelativePath}`,\n );\n }\n return null;\n }\n\n const manifestPath = join(packageDir, manifestRelativePath);\n if (existsSync(manifestPath)) {\n return manifestPath;\n }\n\n const workspaceSourceManifest =\n resolveWorkspaceSourceManifestPath(packageDir);\n if (workspaceSourceManifest) {\n return workspaceSourceManifest;\n }\n\n if (shouldWarn) {\n logger.warn(\n `Package ${packageName} declares manifest export ${manifestRelativePath}, but no manifest file was found.`,\n );\n }\n return null;\n }\n\n return null;\n}\n\nfunction collectDeclaredSmrtDependencies(\n manifests: Array<Manifest | null | undefined>,\n): string[] {\n const dependencies = new Set<string>();\n\n for (const manifest of manifests) {\n for (const pkg of manifest?.smrtDependencies || []) {\n dependencies.add(pkg);\n }\n }\n\n return Array.from(dependencies);\n}\n\n/**\n * Load manifest from external package\n *\n * Uses createRequire from process.cwd() to resolve packages from the calling\n * application's context, not from smrt-core's context. This allows finding\n * packages that are dependencies of the app but not of smrt-core.\n *\n * External packages export manifests via package.json:\n * \"exports\": { \"./manifest\": \"./dist/manifest.json\" }\n *\n * @param packageName - Package name (e.g., '@happyvertical/smrt-places')\n * @returns Manifest object or null if not found\n */\n/**\n * Load external package manifest synchronously\n * This is the synchronous version of loadExternalManifest for use during class registration\n */\nexport function loadExternalManifestSync(\n packageName: string,\n options: ManifestLoadOptions = {},\n): Manifest | null {\n // Check cache first\n if (getManifestCacheMap().has(packageName)) {\n debugLog(`[manifest-loader] Using cached manifest for ${packageName}`);\n return getManifestCacheMap().get(packageName)!;\n }\n\n debugLog(\n `[manifest-loader] Attempting to load external manifest for ${packageName}`,\n );\n\n const manifestPath = resolveManifestExportPath(packageName, options);\n\n if (!manifestPath) {\n debugLog(\n `[manifest-loader] Package ${packageName} does not expose a JSON manifest export`,\n );\n return null;\n }\n\n try {\n const manifest = parse<Manifest>(readFileSync(manifestPath, 'utf-8'));\n\n if (!manifest.objects || typeof manifest.objects !== 'object') {\n if (options.warn ?? true) {\n logger.warn(`Invalid manifest structure for package ${packageName}`);\n }\n return null;\n }\n\n if (isRefusedTestArtifact(manifest, manifestPath)) {\n if (options.warn ?? true) {\n logger.warn(\n `Ignoring test artifact ${manifestPath} for package ${packageName}; test manifests are never dependency manifests`,\n );\n }\n return null;\n }\n\n const cachedManifest = manifest.packageName\n ? manifest\n : { ...manifest, packageName };\n\n getManifestCacheMap().set(packageName, cachedManifest);\n debugLog(\n `[manifest-loader] ✅ Loaded external manifest for ${packageName} (${Object.keys(cachedManifest.objects).length} objects)`,\n );\n\n return cachedManifest;\n } catch (error) {\n if (options.warn ?? true) {\n logger.warn(\n `Failed to load manifest for package ${packageName}: ${error instanceof Error ? error.message : error}`,\n );\n }\n return null;\n }\n}\n\nexport async function loadExternalManifest(\n packageName: string,\n options: ManifestLoadOptions = {},\n): Promise<Manifest | null> {\n // Delegate to synchronous version since all operations are sync anyway\n return loadExternalManifestSync(packageName, options);\n}\n\n/**\n * Load a manifest directly from a known file path and cache it by package name.\n *\n * This is used by workspace/dev flows where sibling packages may not be\n * installed into node_modules yet, but their generated manifests are still\n * available on disk.\n */\nexport function loadManifestFromPathSync(\n manifestPath: string,\n): Manifest | null {\n try {\n const manifestJson = readFileSync(manifestPath, 'utf-8');\n const manifest: Manifest = parse(manifestJson);\n\n if (!manifest.objects || typeof manifest.objects !== 'object') {\n logger.warn(`Invalid manifest structure at ${manifestPath}`);\n return null;\n }\n\n if (isRefusedTestArtifact(manifest, manifestPath)) {\n logger.warn(\n `Ignoring test artifact ${manifestPath}; it is not this process's active test manifest`,\n );\n return null;\n }\n\n const cacheKey = manifest.packageName || manifestPath;\n getManifestCacheMap().set(cacheKey, manifest);\n debugLog(\n `[manifest-loader] ✅ Loaded manifest from path ${manifestPath} (${Object.keys(manifest.objects).length} objects)`,\n );\n\n return manifest;\n } catch (error) {\n logger.warn(\n `Failed to load manifest from path ${manifestPath}: ${error instanceof Error ? error.message : error}`,\n );\n return null;\n }\n}\n\n/**\n * Discover manifest entry synchronously (checks only loaded manifests)\n *\n * Search order:\n * 1. localTestManifest (for domain package test classes)\n * 2. testManifest (for core test classes)\n * 3. staticManifest (for core framework classes)\n * 4. Cached external manifests\n *\n * @param className - Class name (for lookup)\n * @returns ManifestEntry or undefined if not found\n */\nexport function discoverManifestSync(\n className: string,\n): ManifestEntry | undefined {\n debugLog(`[manifest-loader] discoverManifestSync called for: ${className}`);\n\n // Ensure test-env caches are seeded before the composite queries them.\n // Historically, steps 1-2 of discoverManifestSync did this inline. The\n // composite's TestManifestSource and LocalTestManifestSource read directly\n // from cache state — without this seeding the first sync lookup in a\n // clean test process would miss core test classes on their first access\n // (regression noted in #1138 review).\n if (isTestEnvironment()) {\n if (!getLocalTestManifestCache()) {\n loadLocalTestManifestSync();\n }\n if (!getTestManifestCache()) {\n getTestManifest();\n }\n }\n\n // Carry qualified-name / package context into the composite lookup so\n // multi-package same-simple-name scenarios (issue #951) resolve to the\n // right manifest even via the sync path.\n const query = isQualifiedName(className)\n ? (() => {\n const parsed = parseQualifiedName(className);\n return {\n className: parsed.className,\n packageName: parsed.packageName,\n qualifiedName: className,\n };\n })()\n : { className };\n\n // Steps 1-4 (local-test → test → static → embedded cache) now live in\n // CompositeManifestSource at the exact same priority order.\n const compositeHit = getDefaultCompositeSource().lookup(query);\n if (compositeHit) {\n debugLog(\n `[manifest-loader] ✅ Found ${className} via ${compositeHit.source} source`,\n );\n const entry = compositeHit.def;\n if (!entry.packageName && compositeHit.packageName) {\n return { ...entry, packageName: compositeHit.packageName };\n }\n return entry;\n }\n\n // 5. Try loading from explicitly declared external SMRT package dependencies.\n debugLog(\n `[manifest-loader] ${className} not found in cached manifests, trying external packages...`,\n );\n\n const pendingPackages = collectDeclaredSmrtDependencies([\n getLocalTestManifestCache(),\n isTestEnvironment() ? getTestManifest() : null,\n getStaticManifest(),\n ...getManifestCacheMap().values(),\n ]);\n\n if (pendingPackages.length === 0) {\n debugLog(\n '[manifest-loader] No SMRT dependencies discovered. Run manifest generation if external packages are expected.',\n );\n }\n\n const visitedPackages = new Set<string>();\n\n while (pendingPackages.length > 0) {\n const pkg = pendingPackages.shift();\n if (!pkg || visitedPackages.has(pkg)) {\n continue;\n }\n\n visitedPackages.add(pkg);\n const manifest = loadExternalManifestSync(pkg);\n if (!manifest) {\n continue;\n }\n\n for (const dependency of manifest.smrtDependencies || []) {\n if (!visitedPackages.has(dependency)) {\n pendingPackages.push(dependency);\n }\n }\n\n // Use lookupInManifest for qualified name support (Issue #713)\n const entry = lookupInManifest(manifest, className);\n if (entry) {\n debugLog(\n `[manifest-loader] ✅ Found ${className} in external package ${pkg}`,\n );\n // Enrich entry with packageName from manifest if not already present\n if (!entry.packageName && manifest.packageName) {\n return { ...entry, packageName: manifest.packageName };\n }\n return entry;\n }\n }\n\n debugLog(`[manifest-loader] ❌ ${className} not found in any manifest`);\n return undefined;\n}\n\n// Note: manifestCollisions is now accessed via globalThis helper function\n// getManifestCollisionsMap()\n\n/**\n * Look up a manifest entry in a manifest's objects map.\n * Supports both qualified names and simple class names.\n *\n * Lookup order:\n * 1. Direct qualified name lookup (e.g., \"@happyvertical/smrt-core:Product\")\n * 2. Constructed qualified name (packageName + className)\n * 3. Search by className property (case-insensitive)\n *\n * @param manifest - The manifest to search in\n * @param nameOrQualified - Either a qualified name or simple class name\n * @returns ManifestEntry or undefined if not found\n */\nexport function lookupInManifest(\n manifest: SmartObjectManifest,\n nameOrQualified: string,\n): ManifestEntry | undefined {\n // 1. First try direct lookup (handles both qualified names and exact matches)\n if (manifest.objects[nameOrQualified]) {\n return manifest.objects[nameOrQualified];\n }\n\n // 2. Get the className index for O(1) lookups (instead of O(n) iteration)\n // This is the key optimization for issue #729\n const classNameIndex = getClassNameIndex(manifest);\n\n // 3. If input is a qualified name, extract className and use index. The\n // package half of a qualified name is identity, not decoration: a simple-\n // name hit that belongs to a DIFFERENT package is a different class (both\n // smrt-ledgers and smrt-messages declare `Account`, #3098), so it is a miss.\n if (isQualifiedName(nameOrQualified)) {\n const { className, packageName } = parseQualifiedName(nameOrQualified);\n const entry = classNameIndex.get(className.toLowerCase());\n const entryPackage = entry?.packageName ?? manifest.packageName;\n return entryPackage && entryPackage !== packageName ? undefined : entry;\n }\n\n // 4. For simple class names, try constructing qualified name if manifest has packageName\n if (manifest.packageName) {\n const qualifiedKey = createQualifiedName(\n manifest.packageName,\n nameOrQualified,\n );\n if (manifest.objects[qualifiedKey]) {\n return manifest.objects[qualifiedKey];\n }\n }\n\n // 5. Use index for O(1) className lookup (instead of O(n) iteration)\n return classNameIndex.get(nameOrQualified.toLowerCase());\n}\n\n/**\n * Find a manifest entry by qualified name across all loaded manifests.\n *\n * @param qualifiedName - The fully qualified class name (e.g., \"@happyvertical/smrt-core:Product\")\n * @returns ManifestEntry or undefined if not found\n */\nexport function findManifestEntryByQualifiedName(\n qualifiedName: string,\n): ManifestEntry | undefined {\n if (!isQualifiedName(qualifiedName)) {\n return undefined;\n }\n\n const { packageName } = parseQualifiedName(qualifiedName);\n\n // Check if we have the package's manifest cached\n const manifest = getManifestCacheMap().get(packageName);\n if (manifest) {\n // Try qualified name first, then fallback to simple names\n return lookupInManifest(manifest, qualifiedName);\n }\n\n // Check static manifest\n const staticManifestData = getStaticManifest();\n if (staticManifestData.packageName === packageName) {\n return lookupInManifest(staticManifestData, qualifiedName);\n }\n\n // Check test manifest (if in test environment)\n if (isTestEnvironment()) {\n const testManifest = getTestManifest();\n if (testManifest?.packageName === packageName) {\n return lookupInManifest(testManifest, qualifiedName);\n }\n\n // Check local test manifest\n const localTest = getLocalTestManifestCache();\n if (localTest?.packageName === packageName) {\n return lookupInManifest(localTest, qualifiedName);\n }\n }\n\n return undefined;\n}\n\n/**\n * Discover manifest entry asynchronously (includes external package loading)\n *\n * Search order:\n * 1. External package manifest (for constructor's package) - PRIORITIZED\n * 2. testManifest (for test classes)\n * 3. staticManifest (for core framework classes)\n * 4. Cached external manifests\n *\n * This function now detects and reports class name collisions across packages.\n *\n * @param ctor - Class constructor\n * @param className - Class name (for lookup)\n * @returns ManifestEntry or undefined if not found\n * @throws {Error} If class name collision detected across different packages\n */\nexport async function discoverManifestEntry(\n ctor: SmrtObjectConstructor,\n className: string,\n): Promise<ManifestEntry | undefined> {\n // ✅ FAST PATH: O(1) constructor-based lookup for already-registered classes\n // Skip manifest scanning if we already know this constructor's qualified name via WeakMap\n // This provides instant resolution and prevents collisions (each constructor is unique in memory)\n if (!(ctor as { _isManifestStub?: boolean })._isManifestStub) {\n const registered = ObjectRegistry.getClassByConstructor(ctor);\n if (\n registered?.qualifiedName &&\n isQualifiedName(registered.qualifiedName)\n ) {\n // Parse package name from qualified name (format: \"@package/name:ClassName\")\n const { packageName } = parseQualifiedName(registered.qualifiedName);\n\n const cachedManifest = getManifestCacheMap().get(packageName);\n if (cachedManifest) {\n const cachedEntry = lookupInManifest(\n cachedManifest,\n registered.qualifiedName,\n );\n if (cachedEntry) {\n return !cachedEntry.packageName && cachedManifest.packageName\n ? { ...cachedEntry, packageName: cachedManifest.packageName }\n : cachedEntry;\n }\n }\n\n // Load the manifest for this specific package before trusting any\n // existing runtime field metadata. Imported external classes can be\n // registered with only a partial field set until their manifest is\n // hydrated, which is exactly what happens with STI parents like Event.\n const manifest = await loadExternalManifest(packageName, {\n warn: registered.fields.size === 0,\n });\n if (manifest) {\n // Look up the entry using the qualified name (exact match)\n const entry = lookupInManifest(manifest, registered.qualifiedName);\n if (entry) {\n // Enrich entry with package name if missing\n return !entry.packageName && manifest.packageName\n ? { ...entry, packageName: manifest.packageName }\n : entry;\n }\n }\n\n // Source-registered classes with explicit field metadata do not need a\n // second manifest probe from fallback discovery when the package did not\n // resolve to a manifest. This keeps workspace/dev runtimes from spamming\n // missing-dist-manifest warnings for packages that are already fully\n // usable from source.\n if (registered.fields.size > 0) {\n return undefined;\n }\n }\n }\n\n // FALLBACK: Original manifest scanning logic for:\n // - First-time registration (WeakMap not populated yet)\n // - Manifest stubs (not in WeakMap)\n // - Undecorated classes (never registered)\n const name = className.toLowerCase();\n const constructorPackage = getPackageName(ctor);\n\n // Track all manifest sources that define this class\n const foundEntries: Array<{\n entry: ManifestEntry;\n packageName: string;\n filePath?: string;\n manifestSource: string;\n }> = [];\n\n // Build qualified name for lookup if we have package context\n const qualifiedName = constructorPackage\n ? createQualifiedName(constructorPackage, className)\n : undefined;\n\n // 2. Check localTestManifest first (domain package test classes) - ONLY in test environment\n // Do this BEFORE loading external manifest to avoid duplicate loading\n let localEntry: SmartObjectDefinition | undefined;\n if (isTestEnvironment()) {\n if (getLocalTestManifestCache() === undefined) {\n loadLocalTestManifestSync();\n }\n const localManifest = getLocalTestManifestCache();\n if (localManifest) {\n // Use lookupInManifest for consistent qualified name handling\n localEntry = lookupInManifest(localManifest, qualifiedName || className);\n if (localEntry) {\n foundEntries.push({\n entry: localEntry,\n packageName: localManifest.packageName || 'local-test',\n filePath: localEntry.filePath,\n manifestSource: 'local test manifest',\n });\n }\n }\n }\n\n // 1. PRIORITY: Try loading from the constructor's package first\n // BUT skip if it's the same package as the local test manifest to avoid collisions\n if (constructorPackage) {\n const skipExternal =\n getLocalTestManifestCache()?.packageName &&\n constructorPackage === getLocalTestManifestCache()?.packageName;\n\n if (!skipExternal) {\n const manifest = await loadExternalManifest(constructorPackage);\n if (manifest) {\n // Use lookupInManifest for consistent qualified name handling\n const entry = lookupInManifest(manifest, qualifiedName || className);\n if (entry) {\n const enrichedEntry =\n !entry.packageName && manifest.packageName\n ? { ...entry, packageName: manifest.packageName }\n : entry;\n\n foundEntries.push({\n entry: enrichedEntry,\n packageName: manifest.packageName || constructorPackage,\n filePath: entry.filePath,\n manifestSource: `${manifest.packageName || constructorPackage}/manifest.json`,\n });\n }\n }\n }\n }\n\n // 3. Check testManifest (core test classes) - ONLY in test environment\n // Skip if we already loaded a local test manifest (avoids duplicate entries from same package)\n if (isTestEnvironment() && (!getLocalTestManifestCache() || !localEntry)) {\n const manifest = getTestManifest();\n if (manifest) {\n // Use lookupInManifest for consistent qualified name handling\n const testEntry = lookupInManifest(manifest, qualifiedName || className);\n if (testEntry) {\n foundEntries.push({\n entry: testEntry,\n packageName: manifest.packageName || '@happyvertical/smrt-core',\n filePath: testEntry.filePath,\n manifestSource: '@happyvertical/smrt-core test manifest',\n });\n }\n }\n }\n\n // 4. Check staticManifest (core framework classes)\n const staticManifestData = getStaticManifest();\n // Use lookupInManifest for consistent qualified name handling\n const staticEntry = lookupInManifest(\n staticManifestData,\n qualifiedName || className,\n );\n if (staticEntry) {\n foundEntries.push({\n entry: staticEntry,\n packageName: staticManifestData.packageName || '@happyvertical/smrt-core',\n filePath: staticEntry.filePath,\n manifestSource: '@happyvertical/smrt-core static manifest',\n });\n }\n\n // 5. Check other cached external manifests\n for (const [cachedPkgName, manifest] of getManifestCacheMap().entries()) {\n // Skip if this is the constructor's package (already checked above)\n if (cachedPkgName === constructorPackage) {\n continue;\n }\n\n // Use lookupInManifest for consistent qualified name handling\n const entry = lookupInManifest(manifest, qualifiedName || className);\n if (entry) {\n foundEntries.push({\n entry:\n !entry.packageName && manifest.packageName\n ? { ...entry, packageName: manifest.packageName }\n : entry,\n packageName: manifest.packageName || cachedPkgName,\n filePath: entry.filePath,\n manifestSource: `${manifest.packageName || cachedPkgName}/manifest.json`,\n });\n }\n }\n\n // Deduplicate entries that reference the same source file.\n // Consumer manifests (e.g. smrt-users, dashboard) re-export classes from\n // upstream packages (e.g. smrt-profiles). These appear in multiple manifests\n // but point to the same filePath — they are NOT real collisions.\n if (foundEntries.length > 1) {\n const uniqueByFile = new Map<string, (typeof foundEntries)[0]>();\n for (const entry of foundEntries) {\n // Normalize file paths so the same source file with different absolute\n // prefixes (local dev vs CI build) is recognized as identical.\n // Extract the relative path from the last \"packages/\" segment onward.\n let key = entry.filePath || `${entry.packageName}:${className}`;\n if (entry.filePath) {\n const pkgIdx = entry.filePath.lastIndexOf('packages/');\n if (pkgIdx !== -1) {\n key = entry.filePath.slice(pkgIdx);\n }\n }\n if (!uniqueByFile.has(key)) {\n uniqueByFile.set(key, entry);\n }\n }\n\n if (uniqueByFile.size === 1) {\n // All entries point to the same source file — not a real collision.\n // Use the first (highest priority) entry.\n foundEntries.splice(1);\n } else if (uniqueByFile.size > 1) {\n // True collision: different source files define the same class name\n const collisionInfo = foundEntries.map(\n (f) =>\n ` - ${f.packageName} (${f.filePath || 'unknown file'}) from ${f.manifestSource}`,\n );\n\n // Store collision info for reporting\n getManifestCollisionsMap().set(\n className,\n foundEntries.map((f) => ({\n packageName: f.packageName,\n filePath: f.filePath,\n manifestSource: f.manifestSource,\n })),\n );\n\n // Throw error on collision\n throw new Error(\n `SMRT Class Name Collision Detected: \"${className}\"\\n\\n` +\n `This class is defined in multiple packages:\\n${collisionInfo.join('\\n')}\\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 ` 1. Use unique class names across packages (e.g., ${className}_${constructorPackage?.split('/').pop() || 'Unique'})\\n` +\n ` 2. Or use @smrt({ name: 'unique_name' }) to override the registration name\\n` +\n ` 3. Remove test classes with conflicting names from production manifests\\n\\n` +\n `If this is a test class collision, ensure test manifests are not included in production builds.`,\n );\n }\n }\n\n // Return the found entry (priority given to constructor's package)\n if (foundEntries.length === 1) {\n return foundEntries[0].entry;\n }\n\n return undefined;\n}\n\n/**\n * Get all detected manifest collisions\n *\n * @returns Map of class names to array of collision info\n */\nexport function getManifestCollisions(): Map<\n string,\n Array<{ packageName: string; filePath?: string; manifestSource: string }>\n> {\n return new Map(getManifestCollisionsMap());\n}\n\n/**\n * Clear manifest cache\n *\n * Useful for testing or when packages are updated at runtime.\n */\nexport function clearManifestCache(): void {\n // Reset all cached manifest state so tests/dev tooling can force a full\n // rediscovery pass on the next lookup.\n setStaticManifestCache(undefined);\n setStaticManifestLoadAttempted(false);\n setTestManifestCache(undefined);\n setTestManifestLoadAttempted(false);\n setLocalTestManifestCache(undefined);\n getManifestCacheMap().clear();\n getManifestCollisionsMap().clear();\n getSTISiblingCache().clear();\n projectManifestReads.clear();\n}\n\n/**\n * Get all loaded manifests\n *\n * Returns a copy of the manifest cache for inspection.\n *\n * @returns Array of [packageName, manifest] entries\n */\nexport function getLoadedManifests(): Array<[string, Manifest]> {\n return Array.from(getManifestCacheMap().entries());\n}\n\n/**\n * Discover all STI sibling classes that share the same collection (table)\n *\n * This is critical for STI schema merging: when one subtype is accessed,\n * we need to discover ALL subtypes sharing the same table so that the\n * database adapter receives a complete schema with all columns.\n *\n * Scans all available manifests:\n * 1. Local test manifest (for test classes)\n * 2. Test manifest (for core test classes)\n * 3. Static manifest (for core framework classes)\n * 4. Cached external manifests\n * 5. SMRT dependencies from local test manifest\n *\n * @param collection - The collection/table name to find siblings for\n * @returns Array of manifest entries that share the same collection\n *\n * Known limitation (#1579, won't-fix): the per-collection result is cached on\n * `globalThis.__smrtSTISiblingCache` after the first call and is not invalidated\n * when a manifest is registered *afterwards* and isn't reachable via\n * `smrtDependencies` (the Release-A self-register path). A sibling registered\n * after first discovery for a given collection can therefore be missed until the\n * cache is cleared. Accepted as low-risk: STI manifests are registered at\n * startup before query traffic, and the few self-register flows that could hit\n * it can reset the cache. Fixing it properly means a registration→cache\n * invalidation hook, which is disproportionate to the exposure.\n */\nexport function discoverSTISiblingsSync(\n collection: string,\n): Array<{ className: string; entry: ManifestEntry; packageName?: string }> {\n // Check cache first to avoid repeated scans (fixes #644)\n const cache = getSTISiblingCache();\n const cached = cache.get(collection);\n if (cached !== undefined) {\n return cached;\n }\n\n const siblings: Array<{\n className: string;\n entry: ManifestEntry;\n packageName?: string;\n }> = [];\n\n // Track already-found class names to avoid duplicates (case-insensitive)\n // Use lowercase keys to prevent both 'praeco' and 'Praeco' from being added\n const foundClasses = new Set<string>();\n\n debugLog(\n `[manifest-loader] discoverSTISiblingsSync called for collection: ${collection}`,\n );\n\n // Helper to add entries from a manifest\n const addFromManifest = (\n manifest: Manifest | null | undefined,\n source: string,\n ) => {\n if (!manifest?.objects) return;\n\n for (const [key, entry] of Object.entries(manifest.objects) as [\n string,\n ManifestEntry,\n ][]) {\n // Check if this entry uses the same collection (table)\n if (entry.collection === collection) {\n const className = entry.className || key;\n // Use case-insensitive check to prevent registering both 'praeco' and 'Praeco'\n const lowerClassName = className.toLowerCase();\n if (!foundClasses.has(lowerClassName)) {\n foundClasses.add(lowerClassName);\n siblings.push({\n className,\n entry,\n packageName: entry.packageName || manifest.packageName,\n });\n debugLog(\n `[manifest-loader] Found STI sibling: ${className} (collection: ${collection}) from ${source}`,\n );\n }\n }\n }\n };\n\n // 1. Check local test manifest (domain package test classes)\n if (isTestEnvironment()) {\n if (!getLocalTestManifestCache()) {\n loadLocalTestManifestSync();\n }\n addFromManifest(getLocalTestManifestCache(), 'localTestManifest');\n }\n\n // 2. Check test manifest (core test classes)\n const testManifestData = isTestEnvironment() ? getTestManifest() : null;\n if (testManifestData) {\n addFromManifest(testManifestData, 'testManifest');\n }\n\n // 3. Check static manifest (core framework classes)\n const staticManifestData = getStaticManifest();\n addFromManifest(staticManifestData, 'staticManifest');\n\n // 4. Check cached external manifests\n for (const [pkgName, manifest] of getManifestCacheMap().entries()) {\n addFromManifest(manifest, `manifestCache:${pkgName}`);\n }\n\n // 5. Try loading from SMRT dependencies from ALL manifests (not just localTestManifest)\n // This ensures production environments also discover STI siblings\n const pendingDependencies = collectDeclaredSmrtDependencies([\n getLocalTestManifestCache(),\n isTestEnvironment() ? testManifestData : null,\n staticManifestData,\n ...getManifestCacheMap().values(),\n ]);\n const visitedDependencies = new Set<string>();\n\n debugLog(\n `[manifest-loader] Scanning ${pendingDependencies.length} SMRT dependencies for STI siblings: ${pendingDependencies.join(', ')}`,\n );\n\n while (pendingDependencies.length > 0) {\n const pkg = pendingDependencies.shift();\n if (!pkg || visitedDependencies.has(pkg)) {\n continue;\n }\n\n visitedDependencies.add(pkg);\n\n const manifest = loadExternalManifestSync(pkg);\n if (manifest) {\n addFromManifest(manifest, `smrtDependency:${pkg}`);\n\n for (const dependency of manifest.smrtDependencies || []) {\n if (!visitedDependencies.has(dependency)) {\n pendingDependencies.push(dependency);\n }\n }\n }\n }\n\n debugLog(\n `[manifest-loader] discoverSTISiblingsSync found ${siblings.length} siblings for collection: ${collection}`,\n );\n\n // Cache the results for future calls (fixes #644)\n cache.set(collection, siblings);\n\n return siblings;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest -> generated object-type declaration selection.
|
|
3
|
+
*
|
|
4
|
+
* Every emitter of the per-object `XData` row interfaces (the physical
|
|
5
|
+
* `smrt-objects.d.ts` / `smrt-types.d.ts` prebuild files, the Vite ambient
|
|
6
|
+
* `virtual-modules.d.ts`, the `@smrt/types` virtual module in server and client
|
|
7
|
+
* mode, and the consumer fallback module) reads this selection, so they agree
|
|
8
|
+
* on how many declarations exist and in which order.
|
|
9
|
+
*
|
|
10
|
+
* A declaration describes one CLASS, not one manifest listing. An aggregated
|
|
11
|
+
* consumer manifest can list one class several times: when workspace libraries
|
|
12
|
+
* are built, each library that consumes another package's model scans that
|
|
13
|
+
* model's source under its own package name (#3110), so the class appears once
|
|
14
|
+
* under its declaring package and again under every consumer package. Those
|
|
15
|
+
* listings name the same declaring source file and class with the same fields,
|
|
16
|
+
* so they collapse to a single declaration (#3296). Listings without an
|
|
17
|
+
* absolute source path, or whose fields disagree, cannot be proven to be the
|
|
18
|
+
* same class and keep one declaration each.
|
|
19
|
+
*
|
|
20
|
+
* Pure and dependency-free: the scanner, the Vite plugin, the consumer plugin
|
|
21
|
+
* and the prebuild CLI all import it.
|
|
22
|
+
*/
|
|
23
|
+
import type { SmartObjectDefinition, SmartObjectManifest } from '../scanner/types.js';
|
|
24
|
+
/** One generated object-type declaration and the listings it describes. */
|
|
25
|
+
export interface ObjectDeclaration {
|
|
26
|
+
/**
|
|
27
|
+
* Interface name emitted for the class's row payload: `ProductData`, or a
|
|
28
|
+
* package-qualified name when distinct classes share the simple name.
|
|
29
|
+
*/
|
|
30
|
+
interfaceName: string;
|
|
31
|
+
/**
|
|
32
|
+
* The listing the declaration is rendered from: the first listing in
|
|
33
|
+
* deterministic qualified-identity order. Every collapsed listing has the
|
|
34
|
+
* same fields, so the choice never changes the declared type (only, at
|
|
35
|
+
* most, the order its properties are written in).
|
|
36
|
+
*/
|
|
37
|
+
obj: SmartObjectDefinition;
|
|
38
|
+
/** Manifest key of {@link obj}. */
|
|
39
|
+
manifestKey: string;
|
|
40
|
+
/** Every manifest key that names this class, in qualified-identity order. */
|
|
41
|
+
manifestKeys: string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The identity of the class a manifest listing describes.
|
|
45
|
+
*
|
|
46
|
+
* Two listings are the same class only when they name the same class in the
|
|
47
|
+
* same absolute declaring source file AND describe the same fields. A path is
|
|
48
|
+
* only machine-local evidence (separately built packages can share a
|
|
49
|
+
* `/app/src/...` layout), so the field shape the declaration is rendered from
|
|
50
|
+
* must agree too: listings that collapse always render identical
|
|
51
|
+
* declarations, and a stale or unrelated listing stays separate instead of
|
|
52
|
+
* silently supplying (or losing) fields. A relative `filePath` is relative to
|
|
53
|
+
* whichever root scanned it, so two equal relative paths from different
|
|
54
|
+
* packages may be different files; such listings fall back to their qualified
|
|
55
|
+
* name (or manifest key), which keeps them distinct.
|
|
56
|
+
*/
|
|
57
|
+
export declare function objectDeclarationIdentity(manifestKey: string, obj: SmartObjectDefinition): string;
|
|
58
|
+
/**
|
|
59
|
+
* Distinct classes that share one simple class name (#3115), with the
|
|
60
|
+
* qualified interface name each was given.
|
|
61
|
+
*/
|
|
62
|
+
export interface ObjectDeclarationCollision {
|
|
63
|
+
className: string;
|
|
64
|
+
/** Each declaration's name and every listing it describes. */
|
|
65
|
+
declarations: Array<{
|
|
66
|
+
interfaceName: string;
|
|
67
|
+
objectRefs: string[];
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
/** The declaration selection plus a lookup from any listing to its name. */
|
|
71
|
+
export interface ObjectDeclarationIndex {
|
|
72
|
+
declarations: ObjectDeclaration[];
|
|
73
|
+
collisions: ObjectDeclarationCollision[];
|
|
74
|
+
/**
|
|
75
|
+
* The declared row interface for a listing in this manifest (by object
|
|
76
|
+
* reference). A collection class resolves through the caller to its item
|
|
77
|
+
* listing first; an object outside the manifest falls back to its simple
|
|
78
|
+
* `${className}Data`.
|
|
79
|
+
*/
|
|
80
|
+
interfaceNameFor(obj: SmartObjectDefinition): string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Select one declaration per class described by the manifest, in deterministic
|
|
84
|
+
* qualified-identity order of each class's first listing, and name each one.
|
|
85
|
+
*
|
|
86
|
+
* Naming follows the qualified identity when simple names collide (#3115): a
|
|
87
|
+
* class whose simple name is unique keeps `${className}Data`; distinct classes
|
|
88
|
+
* sharing a simple name each get a package-qualified name
|
|
89
|
+
* (`@ergot/market-core:LicenseSale` -> `ErgotMarketCoreLicenseSaleData`) and
|
|
90
|
+
* none of them keeps the bare name, which would otherwise silently merge two
|
|
91
|
+
* classes' fields through TypeScript declaration merging. Every simple name
|
|
92
|
+
* is reserved first, and a qualified name that is still taken (or a stem with
|
|
93
|
+
* no known package) gets a stable numeric suffix, so every emitted name is
|
|
94
|
+
* unique and independent of manifest insertion order.
|
|
95
|
+
*
|
|
96
|
+
* The package prefix is the package of the declaration's FIRST listing in
|
|
97
|
+
* qualified-identity order. When one class is relisted by consumer packages
|
|
98
|
+
* (#3296) that is not necessarily the declaring package, and it can change
|
|
99
|
+
* when a relisting dependency is added or removed; the collision warning
|
|
100
|
+
* names every listing so a consumer can tell. A collision group also forms
|
|
101
|
+
* when one class's listings disagree on fields (a stale relisting), which the
|
|
102
|
+
* warning describes as well.
|
|
103
|
+
*/
|
|
104
|
+
export declare function createObjectDeclarationIndex(manifest: SmartObjectManifest): ObjectDeclarationIndex;
|
|
105
|
+
/**
|
|
106
|
+
* Select one declaration per class described by the manifest; see
|
|
107
|
+
* {@link createObjectDeclarationIndex} for identity and naming.
|
|
108
|
+
*/
|
|
109
|
+
export declare function selectObjectDeclarations(manifest: SmartObjectManifest): ObjectDeclaration[];
|
|
110
|
+
/**
|
|
111
|
+
* Report simple-name collisions once per generation, naming the qualified
|
|
112
|
+
* interface each class was given, so a consumer that referenced the bare
|
|
113
|
+
* name learns which one to use (#3115).
|
|
114
|
+
*/
|
|
115
|
+
export declare function warnObjectDeclarationCollisions(index: ObjectDeclarationIndex, log: (message: string) => void): void;
|
|
116
|
+
//# sourceMappingURL=object-declarations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-declarations.d.ts","sourceRoot":"","sources":["../../src/manifest/object-declarations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAE7B,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,GAAG,EAAE,qBAAqB,CAAC;IAC3B,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AA0CD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,qBAAqB,GACzB,MAAM,CAOR;AAwCD;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,YAAY,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACtE;AAED,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,UAAU,EAAE,0BAA0B,EAAE,CAAC;IACzC;;;;;OAKG;IACH,gBAAgB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAAC;CACtD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,sBAAsB,CAiExB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,mBAAmB,GAC5B,iBAAiB,EAAE,CAErB;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,sBAAsB,EAC7B,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7B,IAAI,CAcN"}
|