@happyvertical/smrt-core 0.37.8 → 0.37.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/change-feed.d.ts +261 -0
  3. package/dist/change-feed.d.ts.map +1 -0
  4. package/dist/change-feed.js +482 -0
  5. package/dist/change-feed.js.map +1 -0
  6. package/dist/class.d.ts.map +1 -1
  7. package/dist/class.js +4 -2
  8. package/dist/class.js.map +1 -1
  9. package/dist/collection.js +2 -2
  10. package/dist/consumer-plugin/index.d.ts.map +1 -1
  11. package/dist/consumer-plugin/index.js +24 -19
  12. package/dist/consumer-plugin/index.js.map +1 -1
  13. package/dist/dispatch/bus.js +2 -2
  14. package/dist/dispatch/index.js +1 -1
  15. package/dist/generated-client-runtime.d.ts +26 -0
  16. package/dist/generated-client-runtime.d.ts.map +1 -0
  17. package/dist/generated-client-runtime.js +88 -0
  18. package/dist/generated-client-runtime.js.map +1 -0
  19. package/dist/generators/changes-route.d.ts +52 -0
  20. package/dist/generators/changes-route.d.ts.map +1 -0
  21. package/dist/generators/changes-route.js +111 -0
  22. package/dist/generators/changes-route.js.map +1 -0
  23. package/dist/generators/rest.d.ts +28 -0
  24. package/dist/generators/rest.d.ts.map +1 -1
  25. package/dist/generators/rest.js +53 -15
  26. package/dist/generators/rest.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +6 -5
  30. package/dist/manifest/static-manifest.js +2 -2
  31. package/dist/manifest/static-manifest.js.map +1 -1
  32. package/dist/manifest/store.js +1 -1
  33. package/dist/manifest/store.js.map +1 -1
  34. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  35. package/dist/manifest/test-manifest-stub.js +502 -2
  36. package/dist/manifest/test-manifest-stub.js.map +1 -1
  37. package/dist/manifest.json +2 -2
  38. package/dist/object.js +2 -2
  39. package/dist/prebuild/index.d.ts.map +1 -1
  40. package/dist/prebuild/index.js +66 -7
  41. package/dist/prebuild/index.js.map +1 -1
  42. package/dist/smrt-knowledge.json +6 -6
  43. package/dist/system/schema.d.ts +24 -1
  44. package/dist/system/schema.d.ts.map +1 -1
  45. package/dist/system/schema.js +45 -3
  46. package/dist/system/schema.js.map +1 -1
  47. package/dist/testing/database.js +1 -1
  48. package/dist/vite-plugin/changes-route.d.ts +10 -0
  49. package/dist/vite-plugin/changes-route.d.ts.map +1 -0
  50. package/dist/vite-plugin/changes-route.js +174 -0
  51. package/dist/vite-plugin/changes-route.js.map +1 -0
  52. package/dist/vite-plugin/index.d.ts +7 -0
  53. package/dist/vite-plugin/index.d.ts.map +1 -1
  54. package/dist/vite-plugin/index.js +148 -21
  55. package/dist/vite-plugin/index.js.map +1 -1
  56. package/dist/vite-plugin/route-header.d.ts +10 -0
  57. package/dist/vite-plugin/route-header.d.ts.map +1 -0
  58. package/dist/vite-plugin/route-header.js +14 -0
  59. package/dist/vite-plugin/route-header.js.map +1 -0
  60. package/dist/vite-plugin/sveltekit-generator.d.ts +7 -0
  61. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  62. package/dist/vite-plugin/sveltekit-generator.js +5 -3
  63. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  64. package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
  65. package/dist/vite-plugin/sync-apply-route.js +2 -2
  66. package/dist/vite-plugin/sync-apply-route.js.map +1 -1
  67. package/dist/vite-plugin/web-collections.d.ts +73 -0
  68. package/dist/vite-plugin/web-collections.d.ts.map +1 -0
  69. package/dist/vite-plugin/web-collections.js +174 -0
  70. package/dist/vite-plugin/web-collections.js.map +1 -0
  71. package/package.json +4 -4
package/dist/class.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"class.js","names":[],"sources":["../src/class.ts"],"sourcesContent":["import type { AIClientOptions } from '@happyvertical/ai';\nimport { type AIClient, getAI } from '@happyvertical/ai';\nimport type { FilesystemAdapterOptions } from '@happyvertical/files';\nimport { FilesystemAdapter } from '@happyvertical/files';\nimport { createLogger, type LoggerConfig } from '@happyvertical/logger';\nimport type {\n AiTokenUsage,\n AiUsageHandler,\n AiUsageListOptions,\n AiUsageSnapshot,\n AiUsageStats,\n AiUsageSummaryOptions,\n SignalAdapter,\n SmrtAiUsageEvent,\n SmrtAiUsageRecord,\n} from '@happyvertical/smrt-types';\nimport {\n type DatabaseInterface,\n getDatabase,\n type TransactionHandle,\n} from '@happyvertical/sql';\nimport {\n AiUsageCollector,\n AiUsagePersistenceHandler,\n} from './adapters/ai-usage.js';\nimport { estimateAiUsageCost } from './adapters/cost-rates.js';\nimport type {\n AIConfig,\n AiUsageConfig,\n GlobalSignalConfig,\n MetricsConfig,\n PubSubConfig,\n} from './config.js';\nimport { config } from './config.js';\nimport type { DatabaseConfig } from './database.js';\nimport { detectEngine } from './schema/ddl/index.js';\nimport { SignalBus } from './signals/bus.js';\nimport {\n ensureLegacySystemTableCompatibility,\n tableExists,\n} from './system/compatibility.js';\nimport { ALL_SYSTEM_TABLES, SMRT_SCHEMA_VERSION } from './system/schema.js';\n\nconst SYSTEM_TABLE_BOOTSTRAP_LOCK_SQL =\n \"SELECT pg_advisory_xact_lock(hashtext('smrt'), hashtext('system-tables'))\";\n\nconst logger = createLogger({ level: 'info' });\n\ntype DatabaseWithConfig = DatabaseInterface & {\n config?: {\n type?: string;\n url?: string;\n };\n type?: string;\n};\n\ntype TransactionCapableDatabase = DatabaseInterface & {\n transaction?: <T>(\n this: DatabaseInterface,\n callback: (tx: DatabaseInterface) => Promise<T>,\n ) => Promise<T>;\n};\n\ninterface ResolvedAiUsageConfig {\n enabled: boolean;\n persist: boolean;\n estimateCosts: boolean;\n costRates?: Record<string, { input: number; output: number }>;\n handlers: AiUsageHandler[];\n}\n\ntype AiUsageFilterOptions = Pick<\n AiUsageListOptions,\n | 'since'\n | 'until'\n | 'provider'\n | 'model'\n | 'operation'\n | 'className'\n | 'tenantId'\n>;\n\ninterface AiUsageWhereClause {\n conditions: string[];\n params: unknown[];\n nextParamIndex: number;\n}\n\nfunction firstString(...candidates: unknown[]): string | undefined {\n return candidates.find((candidate): candidate is string => {\n return typeof candidate === 'string';\n });\n}\n\nfunction firstNumber(...candidates: unknown[]): number | undefined {\n return candidates.find((candidate): candidate is number => {\n return typeof candidate === 'number';\n });\n}\n\nfunction getDatabaseUrl(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseWithConfig;\n return db.url || dbWithConfig.config?.url || '';\n}\n\nfunction getDatabaseTypeHint(\n config: DatabaseConfig | undefined,\n): string | undefined {\n if (!config || typeof config === 'string') {\n return undefined;\n }\n\n const configWithType = config as DatabaseWithConfig & {\n client?: unknown;\n };\n\n if (typeof configWithType.type === 'string') {\n return configWithType.type;\n }\n\n if (typeof configWithType.config?.type === 'string') {\n return configWithType.config.type;\n }\n\n if ('query' in configWithType && typeof configWithType.query === 'function') {\n return undefined;\n }\n\n if ('client' in configWithType && configWithType.client) {\n return 'postgres';\n }\n\n return undefined;\n}\n\nfunction normalizeIncomingAiUsageTokens(\n value: unknown,\n): AiTokenUsage | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n const usage = value as Record<string, unknown>;\n const promptTokens =\n typeof usage.promptTokens === 'number'\n ? usage.promptTokens\n : typeof usage.inputTokens === 'number'\n ? usage.inputTokens\n : undefined;\n const completionTokens =\n typeof usage.completionTokens === 'number'\n ? usage.completionTokens\n : typeof usage.outputTokens === 'number'\n ? usage.outputTokens\n : undefined;\n const totalTokens =\n typeof usage.totalTokens === 'number'\n ? usage.totalTokens\n : promptTokens !== undefined || completionTokens !== undefined\n ? (promptTokens ?? 0) + (completionTokens ?? 0)\n : undefined;\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction hydratePersistedAiUsageTokens(row: {\n prompt_tokens?: unknown;\n completion_tokens?: unknown;\n total_tokens?: unknown;\n}): AiTokenUsage | undefined {\n const promptTokens =\n typeof row.prompt_tokens === 'number'\n ? row.prompt_tokens\n : row.prompt_tokens === null || row.prompt_tokens === undefined\n ? undefined\n : Number(row.prompt_tokens);\n const completionTokens =\n typeof row.completion_tokens === 'number'\n ? row.completion_tokens\n : row.completion_tokens === null || row.completion_tokens === undefined\n ? undefined\n : Number(row.completion_tokens);\n const totalTokens =\n typeof row.total_tokens === 'number'\n ? row.total_tokens\n : row.total_tokens === null || row.total_tokens === undefined\n ? undefined\n : Number(row.total_tokens);\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction normalizeAiUsageTags(\n value: unknown,\n): Record<string, string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const tags: Record<string, string> = {};\n for (const [key, tagValue] of Object.entries(value)) {\n if (tagValue === undefined || tagValue === null) continue;\n tags[key] = String(tagValue);\n }\n\n return Object.keys(tags).length > 0 ? tags : undefined;\n}\n\nfunction parseAiUsageTags(value: unknown): Record<string, string> | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n try {\n return normalizeAiUsageTags(JSON.parse(value));\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeAiUsageTimestamp(value: unknown): Date {\n if (value instanceof Date) {\n return value;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n return date;\n }\n }\n\n return new Date();\n}\n\nfunction getQueryRows(\n result: Awaited<ReturnType<DatabaseInterface['query']>>,\n): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : ((result as { rows?: Record<string, unknown>[] }).rows ?? []);\n}\n\nfunction buildAiUsageWhereClause(\n options: AiUsageFilterOptions,\n): AiUsageWhereClause {\n const conditions: string[] = [];\n const params: unknown[] = [];\n let nextParamIndex = 1;\n\n if (options.since) {\n conditions.push(`created_at >= $${nextParamIndex++}`);\n params.push(options.since.toISOString());\n }\n\n if (options.until) {\n conditions.push(`created_at <= $${nextParamIndex++}`);\n params.push(options.until.toISOString());\n }\n\n if (options.provider) {\n conditions.push(`provider = $${nextParamIndex++}`);\n params.push(options.provider);\n }\n\n if (options.model) {\n conditions.push(`model = $${nextParamIndex++}`);\n params.push(options.model);\n }\n\n if (options.operation) {\n conditions.push(`operation = $${nextParamIndex++}`);\n params.push(options.operation);\n }\n\n if (options.className) {\n conditions.push(`class_name = $${nextParamIndex++}`);\n params.push(options.className);\n }\n\n if (options.tenantId === null) {\n conditions.push(`tenant_id IS NULL`);\n } else if (options.tenantId) {\n conditions.push(`tenant_id = $${nextParamIndex++}`);\n params.push(options.tenantId);\n }\n\n return {\n conditions,\n params,\n nextParamIndex,\n };\n}\n\n/**\n * Configuration options for the SmrtClass\n */\nexport interface SmrtClassOptions {\n /**\n * Optional custom class name override\n */\n _className?: string;\n\n /**\n * Database configuration - unified approach matching @happyvertical/sql\n *\n * Supports three formats:\n * - String shortcut: 'products.db' (auto-detects database type)\n * - Config object: { type: 'sqlite', url: 'products.db' }\n * - DatabaseInterface instance: await getDatabase(...)\n *\n * @see DatabaseConfig for type definition\n */\n db?: DatabaseConfig;\n\n /**\n * Alias for db option - for backward compatibility with documentation\n *\n * @deprecated Use 'db' instead. This alias exists for backward compatibility.\n */\n persistence?: DatabaseConfig;\n\n /**\n * Filesystem adapter configuration options\n */\n fs?: FilesystemAdapterOptions;\n\n /**\n * AI client configuration options or instance\n */\n ai?: AIClientOptions | AIClient;\n\n /**\n * AI usage tracking configuration (overrides global defaults)\n */\n usage?: AiUsageConfig;\n\n /**\n * Logging configuration (overrides global default)\n */\n logging?: LoggerConfig;\n\n /**\n * Metrics configuration (overrides global default)\n */\n metrics?: MetricsConfig;\n\n /**\n * Pub/Sub configuration (overrides global default)\n */\n pubsub?: PubSubConfig;\n\n /**\n * Sanitization configuration (overrides global default)\n */\n sanitization?: import('./config.js').GlobalSignalConfig['sanitization'];\n\n /**\n * Custom signal configuration (overrides global default)\n */\n signals?: {\n /** Shared signal bus instance */\n bus?: SignalBus;\n /** Additional custom adapters */\n adapters?: SignalAdapter[];\n };\n\n /**\n * Internal flag to reuse an already initialized DatabaseInterface instance.\n *\n * Skips database resolution and system-table setup during lightweight hydration.\n * @internal\n */\n _reuseInitializedDb?: boolean;\n\n /**\n * Internal flag to defer runtime-only services such as signals and AI setup.\n *\n * Lightweight hydration paths set this so plain query reads avoid per-row\n * runtime bootstrap costs.\n * @internal\n */\n _deferRuntimeInitialization?: boolean;\n}\n\n/**\n * Foundation class providing core functionality for the SMRT framework\n *\n * SmrtClass provides unified access to database, filesystem, and AI client\n * interfaces. It serves as the foundation for all other classes in the\n * SMRT framework.\n */\nexport class SmrtClass {\n /**\n * AI client instance for interacting with AI models\n */\n protected _ai!: AIClient;\n\n /**\n * Filesystem adapter for file operations\n */\n protected _fs!: FilesystemAdapter;\n\n /**\n * Database interface for data persistence\n */\n protected _db!: DatabaseInterface;\n private _dbEngineHint?: string;\n\n /**\n * Class name used for identification\n */\n protected _className!: string;\n\n /**\n * Signal bus for method execution tracking\n */\n protected _signalBus?: SignalBus;\n\n /**\n * Adapters registered by this instance (for cleanup)\n */\n private _registeredAdapters: SignalAdapter[] = [];\n\n /**\n * In-memory AI usage collector for quick inspection.\n */\n private _aiUsageCollector?: AiUsageCollector;\n\n /**\n * Registered AI usage handlers for this instance.\n */\n private _aiUsageHandlers: AiUsageHandler[] = [];\n\n /**\n * Tracks whether optional runtime services (signals, AI, fs) are ready.\n */\n private _runtimeServicesInitialized = false;\n\n /**\n * Shared in-flight runtime initialization promise for single-flight setup.\n */\n private _runtimeServicesInitPromise?: Promise<void>;\n\n /**\n * Configuration options provided to the class\n */\n public options: SmrtClassOptions;\n\n /**\n * Track which databases have had system tables initialized\n * - WeakSet for :memory: databases (URL not unique, track by instance)\n * - Set<string> for all others (URL is unique identifier)\n */\n private static _systemTablesInitialized = new WeakSet<DatabaseInterface>();\n private static _systemTablesInitializedByUrl = new Set<string>();\n\n /**\n * Creates a new SmrtClass instance\n *\n * @param options - Configuration options for database, filesystem, and AI clients\n */\n constructor(options: SmrtClassOptions = {}) {\n this.options = options;\n this._className = this.constructor.name;\n }\n\n /**\n * Determines whether this class requires a database to function\n *\n * Override this method in subclasses that require database access\n * to enable early validation during initialization.\n *\n * @returns True if database is required, false otherwise\n * @example\n * ```typescript\n * class MyDataModel extends SmrtClass {\n * protected requiresDatabase(): boolean {\n * return true; // This class needs database access\n * }\n * }\n * ```\n */\n protected requiresDatabase(): boolean {\n return false; // Base class doesn't require database by default\n }\n\n /**\n * Initializes database, filesystem, and AI client connections\n *\n * This method sets up all required services based on the provided options.\n * It should be called before using any of the service interfaces.\n *\n * @returns Promise that resolves to this instance for chaining\n * @throws {Error} If database is required but not provided in options\n */\n protected async initialize(): Promise<this> {\n await this.initializeCoreResources();\n\n if (!this.options._deferRuntimeInitialization) {\n await this.initializeRuntimeServices();\n }\n\n return this;\n }\n\n /**\n * Initialize core resources required for ORM behavior.\n *\n * This setup is shared by both full runtime initialization and lightweight\n * query hydration. Hydrated objects reuse an existing DB connection and skip\n * repeated system-table checks.\n */\n protected async initializeCoreResources(): Promise<void> {\n // Map persistence to db for backward compatibility\n if (this.options.persistence && !this.options.db) {\n this.options.db = this.options.persistence;\n }\n\n // Validate database configuration if required\n if (this.requiresDatabase() && !this.options.db) {\n throw new Error(\n `${this._className} requires a database configuration. ` +\n `Please provide 'db' in options: { db: { url: '...' } } or { db: 'database.db' }`,\n );\n }\n\n if (this.options.db) {\n this._dbEngineHint = getDatabaseTypeHint(this.options.db);\n\n if (\n this.options._reuseInitializedDb &&\n typeof this.options.db === 'object' &&\n 'query' in this.options.db\n ) {\n this._db = this.options.db as DatabaseInterface;\n this.options.db = this._db;\n } else {\n // Handle four db config formats (in implementation order):\n // 1. String URL: 'products.db' (shortcut)\n // 2. DatabaseInterface instance: already initialized db (has 'query' method)\n // 3. Config with client: { type: 'postgres', client: pgPool } (SvelteKit pattern)\n // 4. Config object: { type: 'sqlite', url: 'products.db' }\n if (typeof this.options.db === 'string') {\n // Format 1: String shortcut - let getDatabase auto-detect type from URL\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const isMemoryDb = this.options.db === ':memory:';\n this._db = await getDatabase({\n url: this.options.db,\n ...(isMemoryDb ? {} : { dbid: `smrt:${this.options.db}` }),\n });\n } else if ('query' in this.options.db) {\n // Format 2: Already a DatabaseInterface instance - return as-is\n this._db = this.options.db as DatabaseInterface;\n } else if ('client' in this.options.db && this.options.db.client) {\n // Format 3: Config with pre-created client (e.g., from SvelteKit's $env-based connection)\n // Pass the client to getDatabase which will use it instead of creating a new connection\n const dbConfig = this.options.db as {\n type?: string;\n client: unknown;\n url?: string;\n };\n // `client` is a runtime-only property the postgres adapter reads\n // but the public `getDatabase` option union does not model, so the\n // literal is routed through the function's own parameter type.\n this._db = await getDatabase({\n type: dbConfig.type || 'postgres',\n client: dbConfig.client,\n url: dbConfig.url,\n } as unknown as Parameters<typeof getDatabase>[0]);\n } else {\n // Format 4: Config object - pass to getDatabase (handles all types uniformly)\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const dbConfig = this.options.db as { url?: string; type?: string };\n const dbUrl = dbConfig.url || 'memory';\n const isMemoryDb = dbUrl === ':memory:' || dbUrl === 'memory';\n // The loose config-object variant of `DatabaseConfig` carries an\n // index signature that the closed `getDatabase` option union does\n // not accept structurally, so route through its parameter type.\n this._db = await getDatabase({\n ...this.options.db,\n ...(isMemoryDb ? {} : { dbid: `smrt:${dbUrl}` }),\n } as unknown as Parameters<typeof getDatabase>[0]);\n }\n\n /**\n * INTENTIONAL MUTATION: After resolving the database config,\n * we replace options.db with the actual DatabaseInterface instance.\n * This enables child objects to share the same connection via:\n *\n * const child = new ChildObject({ db: parent.options.db });\n *\n * Without this, passing this.options to getCollection() would use the config object\n * which causes a NEW db instance to be created, losing data isolation.\n *\n * See issue #567 for context on why this pattern is necessary.\n */\n this.options.db = this._db;\n\n await this.ensureSystemTables();\n }\n }\n }\n\n /**\n * Initialize optional runtime services that are not required for plain ORM reads.\n */\n protected async initializeRuntimeServices(): Promise<void> {\n if (this._runtimeServicesInitialized) {\n return;\n }\n\n if (!this._runtimeServicesInitPromise) {\n this._runtimeServicesInitPromise = (async () => {\n if (this.options.fs && !this._fs) {\n this._fs = await FilesystemAdapter.create(this.options.fs);\n }\n\n // Initialize AI client with environment variable support\n // Priority: instance options > env vars > global config > defaults\n const globalConfig = config.toJSON();\n const usageConfig = this.mergeAiUsageConfig(globalConfig);\n this.initializeAiUsageHandlers(usageConfig);\n\n if (\n !this._ai &&\n (this.options.ai || globalConfig.ai || process.env.SMRT_AI_PROVIDER)\n ) {\n // Check if options.ai is already a client-like object with embed method\n // This allows passing mock AI clients for testing\n const aiOption = this.options.ai as\n | Record<string, unknown>\n | undefined;\n if (\n aiOption &&\n typeof aiOption === 'object' &&\n typeof aiOption.embed === 'function' &&\n !aiOption.provider\n ) {\n this._ai = aiOption as unknown as AIClient;\n } else {\n // CC-8 follow-up: ideally this would route through\n // `@happyvertical/smrt-config` for sanitization parity (e.g. via\n // `getPackageConfig('ai', ...)`), but smrt-config currently only\n // merges file-based config + runtime overrides — it does NOT\n // read from `process.env` with a typed prefix/schema. Until\n // smrt-config grows an env-loader (or wraps `loadEnvConfig`),\n // we continue to use the underlying utility directly. Tracked\n // alongside the CC-8 audit on issue #1199.\n const { loadEnvConfig } = await import('@happyvertical/utils');\n\n // Start with global defaults\n const baseConfig = globalConfig.ai || {};\n\n // Merge with instance options (takes priority over global)\n const userConfig = { ...baseConfig, ...this.options.ai };\n\n // Load environment variables and merge (user options take priority).\n // `AIConfig` carries an index signature, so provider-specific keys\n // (`type`, `onUsage`, `defaultModel`, …) read back as `unknown`.\n const aiConfig = loadEnvConfig<AIConfig>(userConfig, {\n packageName: 'ai',\n prefix: 'SMRT',\n schema: {\n provider: 'string',\n model: 'string',\n apiKey: 'string',\n timeout: 'number',\n maxRetries: 'number',\n temperature: 'number',\n maxTokens: 'number',\n },\n });\n\n const existingOnUsage =\n aiConfig.onUsage ??\n (userConfig as Record<string, unknown>).onUsage ??\n undefined;\n aiConfig.onUsage = async (event: unknown) => {\n if (typeof existingOnUsage === 'function') {\n await (existingOnUsage as (usageEvent: unknown) => unknown)(\n event,\n );\n }\n await this.handleAiUsageCallback(event, aiConfig, usageConfig);\n };\n\n // Only initialize if we have a provider configured\n if (aiConfig.provider || aiConfig.type || aiConfig.apiKey) {\n // Use getAI() factory to support all AI providers (OpenAI, Anthropic, Gemini, etc.)\n // getAI() returns AIInterface, which we narrow to AIClient for\n // backward compatibility. The index-signature `AIConfig` is routed\n // through getAI's own parameter type rather than the closed union.\n this._ai = (await getAI(\n aiConfig as unknown as Parameters<typeof getAI>[0],\n )) as unknown as AIClient;\n }\n }\n }\n\n await this.initializeSignals();\n this._runtimeServicesInitialized = true;\n })();\n }\n\n try {\n await this._runtimeServicesInitPromise;\n } finally {\n this._runtimeServicesInitPromise = undefined;\n }\n }\n\n /**\n * Ensure deferred runtime services are ready before using them.\n */\n protected async ensureRuntimeServicesInitialized(): Promise<void> {\n if (!this._runtimeServicesInitialized) {\n await this.initializeRuntimeServices();\n }\n }\n\n /**\n * Resolve the AI client, initializing deferred runtime services on demand.\n */\n protected async getAiClient(): Promise<AIClient> {\n await this.ensureRuntimeServicesInitialized();\n\n if (!this._ai) {\n throw new Error(\n `${this._className} does not have an AI client configured. ` +\n `Provide 'ai' in options or configure a global SMRT AI provider.`,\n );\n }\n\n return this._ai;\n }\n\n /**\n * Resolve the AI client if one is configured, otherwise return undefined.\n */\n protected async getOptionalAiClient(): Promise<AIClient | undefined> {\n await this.ensureRuntimeServicesInitialized();\n return this._ai;\n }\n\n private async isSystemSchemaVersionApplied(\n db: DatabaseInterface,\n version: string,\n ): Promise<boolean> {\n const engine = detectEngine(getDatabaseUrl(db), this._dbEngineHint);\n\n if (\n engine === 'postgres' &&\n !(await tableExists(db, '_smrt_migrations', this._dbEngineHint))\n ) {\n return false;\n }\n\n try {\n const versionParam = engine === 'postgres' ? '$1' : '?';\n const rows = await db.query(\n `SELECT 1 FROM _smrt_migrations WHERE version = ${versionParam} LIMIT 1`,\n version,\n );\n return getQueryRows(rows).length > 0;\n } catch (error) {\n if (engine === 'postgres') {\n throw error;\n }\n\n return false;\n }\n }\n\n /**\n * Ensure SMRT system tables exist in the database\n *\n * System tables use _smrt_ prefix and store framework metadata:\n * - _smrt_contexts: Context memory storage for remembered patterns\n * - _smrt_migrations: Schema version tracking\n * - _smrt_registry: Object registry persistence\n * - _smrt_signals: Signal history/audit log\n *\n * This method is idempotent and safe to call multiple times.\n * Tables are only created once per database connection.\n */\n private async ensureSystemTables(): Promise<void> {\n if (!this._db) return;\n\n const dbUrl = getDatabaseUrl(this._db);\n\n // Some databases share URLs but are different instances:\n // - :memory: databases (SQLite/DuckDB in-memory)\n // - JSON databases (may have undefined or shared URLs)\n // - Any database with undefined URL\n // Use WeakSet for instance tracking, Set<string> for URL tracking\n const dbConstructorName = this._db.constructor?.name || '';\n const isMemoryDb = dbUrl === ':memory:';\n const isJsonDb = dbConstructorName.toLowerCase().includes('json');\n const hasUndefinedUrl = !dbUrl;\n const useInstanceTracking = isMemoryDb || isJsonDb || hasUndefinedUrl;\n\n if (useInstanceTracking) {\n // Check WeakSet for databases that may share URLs (track by instance)\n if (SmrtClass._systemTablesInitialized.has(this._db)) {\n return;\n }\n } else {\n // Check Set<string> for URL-based databases (track by URL)\n if (SmrtClass._systemTablesInitializedByUrl.has(dbUrl)) {\n return;\n }\n }\n\n try {\n await this.withSystemTableBootstrapLock((db) =>\n this.bootstrapSystemTables(db),\n );\n await this.ensureNativeVectorStorage();\n\n // Mark as initialized using appropriate tracking mechanism\n if (useInstanceTracking) {\n SmrtClass._systemTablesInitialized.add(this._db);\n } else {\n SmrtClass._systemTablesInitializedByUrl.add(dbUrl);\n }\n } catch (error) {\n // DO NOT SWALLOW ERRORS - fail loudly so we know what's wrong\n const dbInfo = this._db.constructor?.name || 'unknown database';\n throw new Error(\n `Failed to create system tables for ${dbInfo}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n private async withSystemTableBootstrapLock<T>(\n callback: (db: DatabaseInterface) => Promise<T>,\n ): Promise<T> {\n if (!this._db) {\n throw new Error('Database is not initialized');\n }\n\n const beginTransaction = this._db.beginTransaction;\n const transaction = (this._db as TransactionCapableDatabase).transaction;\n const shouldUsePostgresLock =\n detectEngine(getDatabaseUrl(this._db), this._dbEngineHint) === 'postgres';\n\n if (!shouldUsePostgresLock) {\n return callback(this._db);\n }\n\n if (typeof beginTransaction === 'function') {\n const tx = await beginTransaction.call(this._db);\n if (!tx) {\n throw new Error('Database transaction could not be started');\n }\n\n try {\n await tx.query(SYSTEM_TABLE_BOOTSTRAP_LOCK_SQL);\n const result = await callback(tx);\n await tx.commit();\n return result;\n } catch (error) {\n await this.rollbackSystemTableBootstrap(tx);\n throw error;\n }\n }\n\n if (typeof transaction === 'function') {\n const runInTransaction = transaction.bind(this._db) as <R>(\n callback: (tx: DatabaseInterface) => Promise<R>,\n ) => Promise<R>;\n\n return runInTransaction(async (tx) => {\n await tx.query(SYSTEM_TABLE_BOOTSTRAP_LOCK_SQL);\n return callback(tx);\n });\n }\n\n throw new Error(\n 'Postgres system table bootstrap requires a transaction-capable database adapter',\n );\n }\n\n private async rollbackSystemTableBootstrap(\n tx: TransactionHandle,\n ): Promise<void> {\n try {\n if (typeof tx.isActive !== 'function' || tx.isActive()) {\n await tx.rollback();\n }\n } catch (rollbackError) {\n logger.warn(\n `[smrt] Failed to rollback system table bootstrap transaction: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`,\n );\n }\n }\n\n private async bootstrapSystemTables(db: DatabaseInterface): Promise<void> {\n // Fast path: check if system tables already exist by querying _smrt_migrations.\n // This avoids 29 sequential DDL round-trips on high-latency connections\n // (e.g. remote Postgres over Tailscale where each round-trip is ~650ms).\n // For Postgres, this runs after acquiring the advisory lock so concurrent\n // initializers can observe a completed bootstrap and skip replaying DDL.\n const version = SMRT_SCHEMA_VERSION;\n if (await this.isSystemSchemaVersionApplied(db, version)) {\n return;\n }\n\n // Older installs can have a subset of system columns already created.\n // Upgrade those tables before replaying idempotent DDL so index creation\n // does not fail on missing legacy columns.\n await ensureLegacySystemTableCompatibility(db, this._dbEngineHint);\n\n // Create all system tables\n // Split multi-statement SQL into individual statements to avoid race conditions\n // Each ALL_SYSTEM_TABLES entry contains CREATE TABLE + CREATE INDEX statements\n const allStatements: string[] = [];\n for (const multiStatementSQL of ALL_SYSTEM_TABLES) {\n // Split on semicolon, filter out empty statements\n const statements = multiStatementSQL\n .split(';')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n allStatements.push(...statements);\n }\n\n // Use db.query() for system tables — they use CREATE TABLE/INDEX IF NOT EXISTS\n // which databases handle natively in a single round-trip. The syncSchema()\n // approach does per-column existence checks (multiple round-trips per table)\n // which is unnecessary for framework-owned system tables and extremely slow\n // on high-latency connections (e.g. remote postgres over Tailscale).\n for (const statement of allStatements) {\n await db.query(statement);\n }\n\n // Record current schema version\n // Use ON CONFLICT for DuckDB compatibility (not INSERT OR IGNORE)\n const id = crypto.randomUUID();\n const description = 'Initial SMRT system tables';\n await db.execute`\n INSERT INTO _smrt_migrations (id, version, description)\n VALUES (${id}, ${version}, ${description})\n ON CONFLICT(version) DO NOTHING\n `;\n }\n\n private async ensureNativeVectorStorage(): Promise<void> {\n if (!this._db) {\n return;\n }\n\n try {\n const { ObjectRegistry } = await import('./registry.js');\n const embeddingConfig = ObjectRegistry.getProjectEmbeddingConfig();\n if (embeddingConfig?.storage === 'native') {\n const { EmbeddingStorage } = await import('./embeddings/storage.js');\n const vector = this._db.vector;\n if (vector) {\n const dimensions = embeddingConfig.dimensions || 768;\n await EmbeddingStorage.ensureVectorStorage(\n this._db,\n dimensions,\n vector,\n );\n } else {\n logger.warn(\n '[smrt] Embedding storage set to \"native\" but database has no vector capability. Falling back to JSON storage.',\n );\n }\n }\n } catch (error) {\n // Don't fail system table initialization for vector setup errors\n logger.warn(\n `[smrt] Failed to initialize vector storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n /**\n * Access system tables through standard database interface\n * System tables use _smrt_ prefix to avoid conflicts with user tables\n */\n protected get systemDb(): DatabaseInterface {\n return this._db;\n }\n\n /**\n * Initialize signal bus and adapters\n *\n * Merges global configuration with instance-specific overrides.\n * Registers built-in and custom adapters based on configuration.\n */\n private async initializeSignals(): Promise<void> {\n const globalConfig = config.toJSON();\n const effectiveConfig = this.mergeSignalConfig(globalConfig);\n\n // If a shared bus is provided, always use it (don't create new adapters)\n if (this.options.signals?.bus) {\n this._signalBus = this.options.signals.bus;\n return;\n }\n\n // Otherwise, check if we should initialize signals based on config\n if (!this.shouldInitializeSignals(effectiveConfig)) {\n return;\n }\n\n this._signalBus = new SignalBus({\n sanitization: effectiveConfig.sanitization,\n });\n await this.registerAdapters(effectiveConfig);\n }\n\n /**\n * Merge global and instance signal configuration\n *\n * Instance configuration takes priority over global defaults.\n *\n * @param globalConfig - Global configuration from smrt.configure()\n * @returns Merged configuration\n */\n private mergeSignalConfig(\n globalConfig: GlobalSignalConfig,\n ): GlobalSignalConfig {\n return {\n logging: this.options.logging ?? globalConfig.logging,\n metrics: this.options.metrics ?? globalConfig.metrics,\n pubsub: this.options.pubsub ?? globalConfig.pubsub,\n usage: this.mergeAiUsageConfig(globalConfig),\n sanitization: this.options.sanitization ?? globalConfig.sanitization,\n signals: {\n bus: this.options.signals?.bus ?? globalConfig.signals?.bus,\n adapters: [\n ...(globalConfig.signals?.adapters ?? []),\n ...(this.options.signals?.adapters ?? []),\n ],\n },\n };\n }\n\n /**\n * Check if signals should be initialized\n *\n * Signals are initialized if any adapter is configured.\n *\n * @param config - Effective signal configuration\n * @returns True if signals should be initialized\n */\n private shouldInitializeSignals(config: GlobalSignalConfig): boolean {\n return !!(\n config.logging !== false ||\n config.metrics?.enabled ||\n config.pubsub?.enabled ||\n config.signals?.adapters?.length\n );\n }\n\n /**\n * Register signal adapters based on configuration\n *\n * @param config - Effective signal configuration\n */\n private async registerAdapters(config: GlobalSignalConfig): Promise<void> {\n if (!this._signalBus) return;\n\n // Logging adapter (default: enabled with console)\n if (config.logging !== false) {\n const { createLogger, LoggerAdapter } = await import(\n '@happyvertical/logger'\n );\n const logger = createLogger(config.logging ?? true);\n const adapter = new LoggerAdapter(logger);\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Metrics adapter (default: disabled)\n if (config.metrics?.enabled) {\n const { MetricsAdapter } = await import('./adapters/metrics.js');\n const adapter = new MetricsAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Pub/Sub adapter (default: disabled)\n if (config.pubsub?.enabled) {\n const { PubSubAdapter } = await import('./adapters/pubsub.js');\n const adapter = new PubSubAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Custom adapters\n if (config.signals?.adapters) {\n for (const adapter of config.signals.adapters) {\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n }\n }\n\n /**\n * Gets the filesystem adapter instance\n */\n get fs() {\n return this._fs;\n }\n\n /**\n * Gets the database interface instance\n */\n get db() {\n // Throw helpful error if database is accessed before initialization\n if (!this._db) {\n throw new Error(\n `Database accessed before initialization. ` +\n `Please call await instance.initialize() before accessing the database.`,\n );\n }\n return this._db;\n }\n\n /**\n * Gets the AI client instance\n */\n get ai() {\n return this._ai;\n }\n\n /**\n * Get the in-memory AI usage snapshot for this instance.\n */\n getAiUsageSnapshot(): AiUsageSnapshot | undefined {\n return this._aiUsageCollector?.getSnapshot();\n }\n\n /**\n * Reset the in-memory AI usage collector.\n */\n resetAiUsage(): void {\n this._aiUsageCollector?.reset();\n }\n\n /**\n * List persisted AI usage records.\n */\n async listAiUsage(\n options: AiUsageListOptions = {},\n ): Promise<SmrtAiUsageRecord[]> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n let paramIndex = params.length + 1;\n\n let sql = 'SELECT * FROM _smrt_ai_usage';\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ` ORDER BY ${\n options.orderBy === 'timestamp ASC' ? 'created_at ASC' : 'created_at DESC'\n }`;\n\n if (options.limit !== undefined) {\n sql += ` LIMIT $${paramIndex++}`;\n params.push(options.limit);\n }\n\n if (options.offset !== undefined) {\n sql += ` OFFSET $${paramIndex++}`;\n params.push(options.offset);\n }\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n\n return rows.map((row) => ({\n id: String(row.id),\n provider: String(row.provider),\n model: String(row.model),\n operation: String(row.operation),\n usage: hydratePersistedAiUsageTokens(row),\n estimatedCost:\n row.estimated_cost === null || row.estimated_cost === undefined\n ? undefined\n : Number(row.estimated_cost),\n duration: Number(row.duration ?? 0),\n className:\n row.class_name === null || row.class_name === undefined\n ? undefined\n : String(row.class_name),\n tenantId:\n row.tenant_id === undefined\n ? undefined\n : (row.tenant_id as string | null),\n tags: parseAiUsageTags(row.tags),\n timestamp: normalizeAiUsageTimestamp(row.created_at),\n }));\n }\n\n /**\n * Summarize persisted AI usage records by a grouping dimension.\n */\n async summarizeAiUsage(\n options: AiUsageSummaryOptions = {},\n ): Promise<Record<string, AiUsageStats>> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const groupBy = options.groupBy ?? 'model';\n const bucketExpression =\n groupBy === 'provider'\n ? `provider`\n : groupBy === 'model'\n ? `provider || ':' || model`\n : groupBy === 'class'\n ? `COALESCE(class_name, 'unknown')`\n : groupBy === 'tenant'\n ? `COALESCE(tenant_id, 'global')`\n : groupBy === 'operation'\n ? `operation`\n : `substr(CAST(created_at AS TEXT), 1, 10)`;\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n\n let sql = `\n SELECT ${bucketExpression} AS bucket,\n COUNT(*) AS call_count,\n COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,\n COALESCE(SUM(completion_tokens), 0) AS completion_tokens,\n COALESCE(SUM(total_tokens), 0) AS total_tokens,\n COALESCE(SUM(duration), 0) AS total_duration,\n COALESCE(SUM(estimated_cost), 0) AS estimated_cost,\n MAX(created_at) AS last_used\n FROM _smrt_ai_usage\n `;\n\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ' GROUP BY bucket ORDER BY bucket ASC';\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n const summary: Record<string, AiUsageStats> = {};\n\n for (const row of rows) {\n const bucket = String(row.bucket);\n summary[bucket] = {\n callCount: Number(row.call_count ?? 0),\n promptTokens: Number(row.prompt_tokens ?? 0),\n completionTokens: Number(row.completion_tokens ?? 0),\n totalTokens: Number(row.total_tokens ?? 0),\n totalDuration: Number(row.total_duration ?? 0),\n estimatedCost: Number(row.estimated_cost ?? 0),\n lastUsed: row.last_used ? new Date(String(row.last_used)).getTime() : 0,\n };\n }\n\n return summary;\n }\n\n /**\n * Gets the signal bus instance\n *\n * @returns Signal bus if signals are enabled, undefined otherwise\n */\n get signalBus(): SignalBus | undefined {\n return this._signalBus;\n }\n\n /**\n * Cleanup method to prevent memory leaks\n *\n * Unregisters all adapters from the signal bus that were registered\n * by this instance. Call this when the SmrtClass instance is no longer\n * needed to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const product = new Product({ name: 'Widget' });\n * await product.initialize();\n * // ... use product ...\n * product.destroy(); // Clean up when done\n * ```\n */\n destroy(): void {\n // Only unregister adapters if we own the bus (not shared)\n if (this._signalBus && !this.options.signals?.bus) {\n for (const adapter of this._registeredAdapters) {\n this._signalBus.unregister(adapter);\n }\n this._registeredAdapters = [];\n }\n\n // TODO: If SmrtClass grows a broader async teardown lifecycle, move\n // AI usage handler cleanup there alongside other connection-bound state.\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n }\n\n private mergeAiUsageConfig(\n globalConfig: GlobalSignalConfig,\n ): ResolvedAiUsageConfig {\n const globalUsage = globalConfig.usage ?? {};\n const instanceUsage = this.options.usage ?? {};\n\n return {\n enabled: instanceUsage.enabled ?? globalUsage.enabled ?? true,\n persist: instanceUsage.persist ?? globalUsage.persist ?? true,\n estimateCosts:\n instanceUsage.estimateCosts ?? globalUsage.estimateCosts ?? true,\n costRates: {\n ...(globalUsage.costRates ?? {}),\n ...(instanceUsage.costRates ?? {}),\n },\n handlers: [\n ...(globalUsage.handlers ?? []),\n ...(instanceUsage.handlers ?? []),\n ],\n };\n }\n\n private initializeAiUsageHandlers(config: ResolvedAiUsageConfig): void {\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n\n if (!config.enabled) {\n return;\n }\n\n this._aiUsageCollector = new AiUsageCollector();\n this._aiUsageHandlers.push(this._aiUsageCollector);\n\n if (config.persist && this._db) {\n this._aiUsageHandlers.push(new AiUsagePersistenceHandler(this._db));\n }\n\n this._aiUsageHandlers.push(...config.handlers);\n }\n\n private async handleAiUsageCallback(\n event: unknown,\n aiConfig: Record<string, unknown>,\n usageConfig: ResolvedAiUsageConfig,\n ): Promise<void> {\n if (!usageConfig.enabled || this._aiUsageHandlers.length === 0) {\n return;\n }\n\n const normalizedEvent = this.normalizeAiUsageEvent(event, aiConfig);\n if (!normalizedEvent) {\n return;\n }\n\n if (usageConfig.estimateCosts) {\n normalizedEvent.estimatedCost = estimateAiUsageCost(\n normalizedEvent.provider,\n normalizedEvent.model,\n normalizedEvent.usage,\n usageConfig.costRates,\n );\n }\n\n const results = await Promise.allSettled(\n this._aiUsageHandlers.map((handler) => handler.handle(normalizedEvent)),\n );\n\n for (const result of results) {\n if (result.status === 'rejected') {\n logger.warn(\n `[smrt] AI usage handler failed for ${normalizedEvent.provider}:${normalizedEvent.model}: ${\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason)\n }`,\n );\n }\n }\n }\n\n private normalizeAiUsageEvent(\n event: unknown,\n aiConfig: Record<string, unknown>,\n ): SmrtAiUsageEvent | undefined {\n const raw = (event ?? {}) as Record<string, unknown>;\n const provider = firstString(\n raw.provider,\n raw.type,\n aiConfig.provider,\n aiConfig.type,\n );\n const model = firstString(\n raw.model,\n raw.defaultModel,\n aiConfig.model,\n aiConfig.defaultModel,\n );\n const operation =\n firstString(raw.operation, raw.kind, raw.method) ?? 'unknown';\n const usage =\n normalizeIncomingAiUsageTokens(raw.usage) ??\n normalizeIncomingAiUsageTokens(raw.tokenUsage) ??\n normalizeIncomingAiUsageTokens({\n promptTokens: raw.promptTokens,\n completionTokens: raw.completionTokens,\n totalTokens: raw.totalTokens,\n });\n\n if (!provider || !model) {\n return undefined;\n }\n\n const duration =\n firstNumber(raw.duration, raw.durationMs, raw.latency) ?? 0;\n const tenantId =\n 'tenantId' in this &&\n (this as { tenantId?: string | null }).tenantId !== undefined\n ? ((this as { tenantId?: string | null }).tenantId ?? null)\n : undefined;\n\n return {\n provider,\n model,\n operation,\n usage,\n duration,\n timestamp: normalizeAiUsageTimestamp(raw.timestamp),\n tags: normalizeAiUsageTags(raw.tags),\n className: this._className,\n tenantId,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"class.js","names":[],"sources":["../src/class.ts"],"sourcesContent":["import type { AIClientOptions } from '@happyvertical/ai';\nimport { type AIClient, getAI } from '@happyvertical/ai';\nimport type { FilesystemAdapterOptions } from '@happyvertical/files';\nimport { FilesystemAdapter } from '@happyvertical/files';\nimport { createLogger, type LoggerConfig } from '@happyvertical/logger';\nimport type {\n AiTokenUsage,\n AiUsageHandler,\n AiUsageListOptions,\n AiUsageSnapshot,\n AiUsageStats,\n AiUsageSummaryOptions,\n SignalAdapter,\n SmrtAiUsageEvent,\n SmrtAiUsageRecord,\n} from '@happyvertical/smrt-types';\nimport {\n type DatabaseInterface,\n getDatabase,\n type TransactionHandle,\n} from '@happyvertical/sql';\nimport {\n AiUsageCollector,\n AiUsagePersistenceHandler,\n} from './adapters/ai-usage.js';\nimport { estimateAiUsageCost } from './adapters/cost-rates.js';\nimport { registerChangeFeedWriter } from './change-feed.js';\nimport type {\n AIConfig,\n AiUsageConfig,\n GlobalSignalConfig,\n MetricsConfig,\n PubSubConfig,\n} from './config.js';\nimport { config } from './config.js';\nimport type { DatabaseConfig } from './database.js';\nimport { detectEngine } from './schema/ddl/index.js';\nimport { SignalBus } from './signals/bus.js';\nimport {\n ensureLegacySystemTableCompatibility,\n tableExists,\n} from './system/compatibility.js';\nimport { ALL_SYSTEM_TABLES, SMRT_SCHEMA_VERSION } from './system/schema.js';\n\nconst SYSTEM_TABLE_BOOTSTRAP_LOCK_SQL =\n \"SELECT pg_advisory_xact_lock(hashtext('smrt'), hashtext('system-tables'))\";\n\nconst logger = createLogger({ level: 'info' });\n\ntype DatabaseWithConfig = DatabaseInterface & {\n config?: {\n type?: string;\n url?: string;\n };\n type?: string;\n};\n\ntype TransactionCapableDatabase = DatabaseInterface & {\n transaction?: <T>(\n this: DatabaseInterface,\n callback: (tx: DatabaseInterface) => Promise<T>,\n ) => Promise<T>;\n};\n\ninterface ResolvedAiUsageConfig {\n enabled: boolean;\n persist: boolean;\n estimateCosts: boolean;\n costRates?: Record<string, { input: number; output: number }>;\n handlers: AiUsageHandler[];\n}\n\ntype AiUsageFilterOptions = Pick<\n AiUsageListOptions,\n | 'since'\n | 'until'\n | 'provider'\n | 'model'\n | 'operation'\n | 'className'\n | 'tenantId'\n>;\n\ninterface AiUsageWhereClause {\n conditions: string[];\n params: unknown[];\n nextParamIndex: number;\n}\n\nfunction firstString(...candidates: unknown[]): string | undefined {\n return candidates.find((candidate): candidate is string => {\n return typeof candidate === 'string';\n });\n}\n\nfunction firstNumber(...candidates: unknown[]): number | undefined {\n return candidates.find((candidate): candidate is number => {\n return typeof candidate === 'number';\n });\n}\n\nfunction getDatabaseUrl(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseWithConfig;\n return db.url || dbWithConfig.config?.url || '';\n}\n\nfunction getDatabaseTypeHint(\n config: DatabaseConfig | undefined,\n): string | undefined {\n if (!config || typeof config === 'string') {\n return undefined;\n }\n\n const configWithType = config as DatabaseWithConfig & {\n client?: unknown;\n };\n\n if (typeof configWithType.type === 'string') {\n return configWithType.type;\n }\n\n if (typeof configWithType.config?.type === 'string') {\n return configWithType.config.type;\n }\n\n if ('query' in configWithType && typeof configWithType.query === 'function') {\n return undefined;\n }\n\n if ('client' in configWithType && configWithType.client) {\n return 'postgres';\n }\n\n return undefined;\n}\n\nfunction normalizeIncomingAiUsageTokens(\n value: unknown,\n): AiTokenUsage | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n const usage = value as Record<string, unknown>;\n const promptTokens =\n typeof usage.promptTokens === 'number'\n ? usage.promptTokens\n : typeof usage.inputTokens === 'number'\n ? usage.inputTokens\n : undefined;\n const completionTokens =\n typeof usage.completionTokens === 'number'\n ? usage.completionTokens\n : typeof usage.outputTokens === 'number'\n ? usage.outputTokens\n : undefined;\n const totalTokens =\n typeof usage.totalTokens === 'number'\n ? usage.totalTokens\n : promptTokens !== undefined || completionTokens !== undefined\n ? (promptTokens ?? 0) + (completionTokens ?? 0)\n : undefined;\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction hydratePersistedAiUsageTokens(row: {\n prompt_tokens?: unknown;\n completion_tokens?: unknown;\n total_tokens?: unknown;\n}): AiTokenUsage | undefined {\n const promptTokens =\n typeof row.prompt_tokens === 'number'\n ? row.prompt_tokens\n : row.prompt_tokens === null || row.prompt_tokens === undefined\n ? undefined\n : Number(row.prompt_tokens);\n const completionTokens =\n typeof row.completion_tokens === 'number'\n ? row.completion_tokens\n : row.completion_tokens === null || row.completion_tokens === undefined\n ? undefined\n : Number(row.completion_tokens);\n const totalTokens =\n typeof row.total_tokens === 'number'\n ? row.total_tokens\n : row.total_tokens === null || row.total_tokens === undefined\n ? undefined\n : Number(row.total_tokens);\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction normalizeAiUsageTags(\n value: unknown,\n): Record<string, string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const tags: Record<string, string> = {};\n for (const [key, tagValue] of Object.entries(value)) {\n if (tagValue === undefined || tagValue === null) continue;\n tags[key] = String(tagValue);\n }\n\n return Object.keys(tags).length > 0 ? tags : undefined;\n}\n\nfunction parseAiUsageTags(value: unknown): Record<string, string> | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n try {\n return normalizeAiUsageTags(JSON.parse(value));\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeAiUsageTimestamp(value: unknown): Date {\n if (value instanceof Date) {\n return value;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n return date;\n }\n }\n\n return new Date();\n}\n\nfunction getQueryRows(\n result: Awaited<ReturnType<DatabaseInterface['query']>>,\n): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : ((result as { rows?: Record<string, unknown>[] }).rows ?? []);\n}\n\nfunction buildAiUsageWhereClause(\n options: AiUsageFilterOptions,\n): AiUsageWhereClause {\n const conditions: string[] = [];\n const params: unknown[] = [];\n let nextParamIndex = 1;\n\n if (options.since) {\n conditions.push(`created_at >= $${nextParamIndex++}`);\n params.push(options.since.toISOString());\n }\n\n if (options.until) {\n conditions.push(`created_at <= $${nextParamIndex++}`);\n params.push(options.until.toISOString());\n }\n\n if (options.provider) {\n conditions.push(`provider = $${nextParamIndex++}`);\n params.push(options.provider);\n }\n\n if (options.model) {\n conditions.push(`model = $${nextParamIndex++}`);\n params.push(options.model);\n }\n\n if (options.operation) {\n conditions.push(`operation = $${nextParamIndex++}`);\n params.push(options.operation);\n }\n\n if (options.className) {\n conditions.push(`class_name = $${nextParamIndex++}`);\n params.push(options.className);\n }\n\n if (options.tenantId === null) {\n conditions.push(`tenant_id IS NULL`);\n } else if (options.tenantId) {\n conditions.push(`tenant_id = $${nextParamIndex++}`);\n params.push(options.tenantId);\n }\n\n return {\n conditions,\n params,\n nextParamIndex,\n };\n}\n\n/**\n * Configuration options for the SmrtClass\n */\nexport interface SmrtClassOptions {\n /**\n * Optional custom class name override\n */\n _className?: string;\n\n /**\n * Database configuration - unified approach matching @happyvertical/sql\n *\n * Supports three formats:\n * - String shortcut: 'products.db' (auto-detects database type)\n * - Config object: { type: 'sqlite', url: 'products.db' }\n * - DatabaseInterface instance: await getDatabase(...)\n *\n * @see DatabaseConfig for type definition\n */\n db?: DatabaseConfig;\n\n /**\n * Alias for db option - for backward compatibility with documentation\n *\n * @deprecated Use 'db' instead. This alias exists for backward compatibility.\n */\n persistence?: DatabaseConfig;\n\n /**\n * Filesystem adapter configuration options\n */\n fs?: FilesystemAdapterOptions;\n\n /**\n * AI client configuration options or instance\n */\n ai?: AIClientOptions | AIClient;\n\n /**\n * AI usage tracking configuration (overrides global defaults)\n */\n usage?: AiUsageConfig;\n\n /**\n * Logging configuration (overrides global default)\n */\n logging?: LoggerConfig;\n\n /**\n * Metrics configuration (overrides global default)\n */\n metrics?: MetricsConfig;\n\n /**\n * Pub/Sub configuration (overrides global default)\n */\n pubsub?: PubSubConfig;\n\n /**\n * Sanitization configuration (overrides global default)\n */\n sanitization?: import('./config.js').GlobalSignalConfig['sanitization'];\n\n /**\n * Custom signal configuration (overrides global default)\n */\n signals?: {\n /** Shared signal bus instance */\n bus?: SignalBus;\n /** Additional custom adapters */\n adapters?: SignalAdapter[];\n };\n\n /**\n * Internal flag to reuse an already initialized DatabaseInterface instance.\n *\n * Skips database resolution and system-table setup during lightweight hydration.\n * @internal\n */\n _reuseInitializedDb?: boolean;\n\n /**\n * Internal flag to defer runtime-only services such as signals and AI setup.\n *\n * Lightweight hydration paths set this so plain query reads avoid per-row\n * runtime bootstrap costs.\n * @internal\n */\n _deferRuntimeInitialization?: boolean;\n}\n\n/**\n * Foundation class providing core functionality for the SMRT framework\n *\n * SmrtClass provides unified access to database, filesystem, and AI client\n * interfaces. It serves as the foundation for all other classes in the\n * SMRT framework.\n */\nexport class SmrtClass {\n /**\n * AI client instance for interacting with AI models\n */\n protected _ai!: AIClient;\n\n /**\n * Filesystem adapter for file operations\n */\n protected _fs!: FilesystemAdapter;\n\n /**\n * Database interface for data persistence\n */\n protected _db!: DatabaseInterface;\n private _dbEngineHint?: string;\n\n /**\n * Class name used for identification\n */\n protected _className!: string;\n\n /**\n * Signal bus for method execution tracking\n */\n protected _signalBus?: SignalBus;\n\n /**\n * Adapters registered by this instance (for cleanup)\n */\n private _registeredAdapters: SignalAdapter[] = [];\n\n /**\n * In-memory AI usage collector for quick inspection.\n */\n private _aiUsageCollector?: AiUsageCollector;\n\n /**\n * Registered AI usage handlers for this instance.\n */\n private _aiUsageHandlers: AiUsageHandler[] = [];\n\n /**\n * Tracks whether optional runtime services (signals, AI, fs) are ready.\n */\n private _runtimeServicesInitialized = false;\n\n /**\n * Shared in-flight runtime initialization promise for single-flight setup.\n */\n private _runtimeServicesInitPromise?: Promise<void>;\n\n /**\n * Configuration options provided to the class\n */\n public options: SmrtClassOptions;\n\n /**\n * Track which databases have had system tables initialized\n * - WeakSet for :memory: databases (URL not unique, track by instance)\n * - Set<string> for all others (URL is unique identifier)\n */\n private static _systemTablesInitialized = new WeakSet<DatabaseInterface>();\n private static _systemTablesInitializedByUrl = new Set<string>();\n\n /**\n * Creates a new SmrtClass instance\n *\n * @param options - Configuration options for database, filesystem, and AI clients\n */\n constructor(options: SmrtClassOptions = {}) {\n this.options = options;\n this._className = this.constructor.name;\n }\n\n /**\n * Determines whether this class requires a database to function\n *\n * Override this method in subclasses that require database access\n * to enable early validation during initialization.\n *\n * @returns True if database is required, false otherwise\n * @example\n * ```typescript\n * class MyDataModel extends SmrtClass {\n * protected requiresDatabase(): boolean {\n * return true; // This class needs database access\n * }\n * }\n * ```\n */\n protected requiresDatabase(): boolean {\n return false; // Base class doesn't require database by default\n }\n\n /**\n * Initializes database, filesystem, and AI client connections\n *\n * This method sets up all required services based on the provided options.\n * It should be called before using any of the service interfaces.\n *\n * @returns Promise that resolves to this instance for chaining\n * @throws {Error} If database is required but not provided in options\n */\n protected async initialize(): Promise<this> {\n await this.initializeCoreResources();\n\n if (!this.options._deferRuntimeInitialization) {\n await this.initializeRuntimeServices();\n }\n\n return this;\n }\n\n /**\n * Initialize core resources required for ORM behavior.\n *\n * This setup is shared by both full runtime initialization and lightweight\n * query hydration. Hydrated objects reuse an existing DB connection and skip\n * repeated system-table checks.\n */\n protected async initializeCoreResources(): Promise<void> {\n // Framework init hook for the change feed (#1758): make sure the writer\n // interceptor observes every save/delete before any instance can write.\n // Idempotent and cheap when already registered.\n registerChangeFeedWriter();\n\n // Map persistence to db for backward compatibility\n if (this.options.persistence && !this.options.db) {\n this.options.db = this.options.persistence;\n }\n\n // Validate database configuration if required\n if (this.requiresDatabase() && !this.options.db) {\n throw new Error(\n `${this._className} requires a database configuration. ` +\n `Please provide 'db' in options: { db: { url: '...' } } or { db: 'database.db' }`,\n );\n }\n\n if (this.options.db) {\n this._dbEngineHint = getDatabaseTypeHint(this.options.db);\n\n if (\n this.options._reuseInitializedDb &&\n typeof this.options.db === 'object' &&\n 'query' in this.options.db\n ) {\n this._db = this.options.db as DatabaseInterface;\n this.options.db = this._db;\n } else {\n // Handle four db config formats (in implementation order):\n // 1. String URL: 'products.db' (shortcut)\n // 2. DatabaseInterface instance: already initialized db (has 'query' method)\n // 3. Config with client: { type: 'postgres', client: pgPool } (SvelteKit pattern)\n // 4. Config object: { type: 'sqlite', url: 'products.db' }\n if (typeof this.options.db === 'string') {\n // Format 1: String shortcut - let getDatabase auto-detect type from URL\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const isMemoryDb = this.options.db === ':memory:';\n this._db = await getDatabase({\n url: this.options.db,\n ...(isMemoryDb ? {} : { dbid: `smrt:${this.options.db}` }),\n });\n } else if ('query' in this.options.db) {\n // Format 2: Already a DatabaseInterface instance - return as-is\n this._db = this.options.db as DatabaseInterface;\n } else if ('client' in this.options.db && this.options.db.client) {\n // Format 3: Config with pre-created client (e.g., from SvelteKit's $env-based connection)\n // Pass the client to getDatabase which will use it instead of creating a new connection\n const dbConfig = this.options.db as {\n type?: string;\n client: unknown;\n url?: string;\n };\n // `client` is a runtime-only property the postgres adapter reads\n // but the public `getDatabase` option union does not model, so the\n // literal is routed through the function's own parameter type.\n this._db = await getDatabase({\n type: dbConfig.type || 'postgres',\n client: dbConfig.client,\n url: dbConfig.url,\n } as unknown as Parameters<typeof getDatabase>[0]);\n } else {\n // Format 4: Config object - pass to getDatabase (handles all types uniformly)\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const dbConfig = this.options.db as { url?: string; type?: string };\n const dbUrl = dbConfig.url || 'memory';\n const isMemoryDb = dbUrl === ':memory:' || dbUrl === 'memory';\n // The loose config-object variant of `DatabaseConfig` carries an\n // index signature that the closed `getDatabase` option union does\n // not accept structurally, so route through its parameter type.\n this._db = await getDatabase({\n ...this.options.db,\n ...(isMemoryDb ? {} : { dbid: `smrt:${dbUrl}` }),\n } as unknown as Parameters<typeof getDatabase>[0]);\n }\n\n /**\n * INTENTIONAL MUTATION: After resolving the database config,\n * we replace options.db with the actual DatabaseInterface instance.\n * This enables child objects to share the same connection via:\n *\n * const child = new ChildObject({ db: parent.options.db });\n *\n * Without this, passing this.options to getCollection() would use the config object\n * which causes a NEW db instance to be created, losing data isolation.\n *\n * See issue #567 for context on why this pattern is necessary.\n */\n this.options.db = this._db;\n\n await this.ensureSystemTables();\n }\n }\n }\n\n /**\n * Initialize optional runtime services that are not required for plain ORM reads.\n */\n protected async initializeRuntimeServices(): Promise<void> {\n if (this._runtimeServicesInitialized) {\n return;\n }\n\n if (!this._runtimeServicesInitPromise) {\n this._runtimeServicesInitPromise = (async () => {\n if (this.options.fs && !this._fs) {\n this._fs = await FilesystemAdapter.create(this.options.fs);\n }\n\n // Initialize AI client with environment variable support\n // Priority: instance options > env vars > global config > defaults\n const globalConfig = config.toJSON();\n const usageConfig = this.mergeAiUsageConfig(globalConfig);\n this.initializeAiUsageHandlers(usageConfig);\n\n if (\n !this._ai &&\n (this.options.ai || globalConfig.ai || process.env.SMRT_AI_PROVIDER)\n ) {\n // Check if options.ai is already a client-like object with embed method\n // This allows passing mock AI clients for testing\n const aiOption = this.options.ai as\n | Record<string, unknown>\n | undefined;\n if (\n aiOption &&\n typeof aiOption === 'object' &&\n typeof aiOption.embed === 'function' &&\n !aiOption.provider\n ) {\n this._ai = aiOption as unknown as AIClient;\n } else {\n // CC-8 follow-up: ideally this would route through\n // `@happyvertical/smrt-config` for sanitization parity (e.g. via\n // `getPackageConfig('ai', ...)`), but smrt-config currently only\n // merges file-based config + runtime overrides — it does NOT\n // read from `process.env` with a typed prefix/schema. Until\n // smrt-config grows an env-loader (or wraps `loadEnvConfig`),\n // we continue to use the underlying utility directly. Tracked\n // alongside the CC-8 audit on issue #1199.\n const { loadEnvConfig } = await import('@happyvertical/utils');\n\n // Start with global defaults\n const baseConfig = globalConfig.ai || {};\n\n // Merge with instance options (takes priority over global)\n const userConfig = { ...baseConfig, ...this.options.ai };\n\n // Load environment variables and merge (user options take priority).\n // `AIConfig` carries an index signature, so provider-specific keys\n // (`type`, `onUsage`, `defaultModel`, …) read back as `unknown`.\n const aiConfig = loadEnvConfig<AIConfig>(userConfig, {\n packageName: 'ai',\n prefix: 'SMRT',\n schema: {\n provider: 'string',\n model: 'string',\n apiKey: 'string',\n timeout: 'number',\n maxRetries: 'number',\n temperature: 'number',\n maxTokens: 'number',\n },\n });\n\n const existingOnUsage =\n aiConfig.onUsage ??\n (userConfig as Record<string, unknown>).onUsage ??\n undefined;\n aiConfig.onUsage = async (event: unknown) => {\n if (typeof existingOnUsage === 'function') {\n await (existingOnUsage as (usageEvent: unknown) => unknown)(\n event,\n );\n }\n await this.handleAiUsageCallback(event, aiConfig, usageConfig);\n };\n\n // Only initialize if we have a provider configured\n if (aiConfig.provider || aiConfig.type || aiConfig.apiKey) {\n // Use getAI() factory to support all AI providers (OpenAI, Anthropic, Gemini, etc.)\n // getAI() returns AIInterface, which we narrow to AIClient for\n // backward compatibility. The index-signature `AIConfig` is routed\n // through getAI's own parameter type rather than the closed union.\n this._ai = (await getAI(\n aiConfig as unknown as Parameters<typeof getAI>[0],\n )) as unknown as AIClient;\n }\n }\n }\n\n await this.initializeSignals();\n this._runtimeServicesInitialized = true;\n })();\n }\n\n try {\n await this._runtimeServicesInitPromise;\n } finally {\n this._runtimeServicesInitPromise = undefined;\n }\n }\n\n /**\n * Ensure deferred runtime services are ready before using them.\n */\n protected async ensureRuntimeServicesInitialized(): Promise<void> {\n if (!this._runtimeServicesInitialized) {\n await this.initializeRuntimeServices();\n }\n }\n\n /**\n * Resolve the AI client, initializing deferred runtime services on demand.\n */\n protected async getAiClient(): Promise<AIClient> {\n await this.ensureRuntimeServicesInitialized();\n\n if (!this._ai) {\n throw new Error(\n `${this._className} does not have an AI client configured. ` +\n `Provide 'ai' in options or configure a global SMRT AI provider.`,\n );\n }\n\n return this._ai;\n }\n\n /**\n * Resolve the AI client if one is configured, otherwise return undefined.\n */\n protected async getOptionalAiClient(): Promise<AIClient | undefined> {\n await this.ensureRuntimeServicesInitialized();\n return this._ai;\n }\n\n private async isSystemSchemaVersionApplied(\n db: DatabaseInterface,\n version: string,\n ): Promise<boolean> {\n const engine = detectEngine(getDatabaseUrl(db), this._dbEngineHint);\n\n if (\n engine === 'postgres' &&\n !(await tableExists(db, '_smrt_migrations', this._dbEngineHint))\n ) {\n return false;\n }\n\n try {\n const versionParam = engine === 'postgres' ? '$1' : '?';\n const rows = await db.query(\n `SELECT 1 FROM _smrt_migrations WHERE version = ${versionParam} LIMIT 1`,\n version,\n );\n return getQueryRows(rows).length > 0;\n } catch (error) {\n if (engine === 'postgres') {\n throw error;\n }\n\n return false;\n }\n }\n\n /**\n * Ensure SMRT system tables exist in the database\n *\n * System tables use _smrt_ prefix and store framework metadata:\n * - _smrt_contexts: Context memory storage for remembered patterns\n * - _smrt_migrations: Schema version tracking\n * - _smrt_registry: Object registry persistence\n * - _smrt_signals: Signal history/audit log\n *\n * This method is idempotent and safe to call multiple times.\n * Tables are only created once per database connection.\n */\n private async ensureSystemTables(): Promise<void> {\n if (!this._db) return;\n\n const dbUrl = getDatabaseUrl(this._db);\n\n // Some databases share URLs but are different instances:\n // - :memory: databases (SQLite/DuckDB in-memory)\n // - JSON databases (may have undefined or shared URLs)\n // - Any database with undefined URL\n // Use WeakSet for instance tracking, Set<string> for URL tracking\n const dbConstructorName = this._db.constructor?.name || '';\n const isMemoryDb = dbUrl === ':memory:';\n const isJsonDb = dbConstructorName.toLowerCase().includes('json');\n const hasUndefinedUrl = !dbUrl;\n const useInstanceTracking = isMemoryDb || isJsonDb || hasUndefinedUrl;\n\n if (useInstanceTracking) {\n // Check WeakSet for databases that may share URLs (track by instance)\n if (SmrtClass._systemTablesInitialized.has(this._db)) {\n return;\n }\n } else {\n // Check Set<string> for URL-based databases (track by URL)\n if (SmrtClass._systemTablesInitializedByUrl.has(dbUrl)) {\n return;\n }\n }\n\n try {\n await this.withSystemTableBootstrapLock((db) =>\n this.bootstrapSystemTables(db),\n );\n await this.ensureNativeVectorStorage();\n\n // Mark as initialized using appropriate tracking mechanism\n if (useInstanceTracking) {\n SmrtClass._systemTablesInitialized.add(this._db);\n } else {\n SmrtClass._systemTablesInitializedByUrl.add(dbUrl);\n }\n } catch (error) {\n // DO NOT SWALLOW ERRORS - fail loudly so we know what's wrong\n const dbInfo = this._db.constructor?.name || 'unknown database';\n throw new Error(\n `Failed to create system tables for ${dbInfo}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n private async withSystemTableBootstrapLock<T>(\n callback: (db: DatabaseInterface) => Promise<T>,\n ): Promise<T> {\n if (!this._db) {\n throw new Error('Database is not initialized');\n }\n\n const beginTransaction = this._db.beginTransaction;\n const transaction = (this._db as TransactionCapableDatabase).transaction;\n const shouldUsePostgresLock =\n detectEngine(getDatabaseUrl(this._db), this._dbEngineHint) === 'postgres';\n\n if (!shouldUsePostgresLock) {\n return callback(this._db);\n }\n\n if (typeof beginTransaction === 'function') {\n const tx = await beginTransaction.call(this._db);\n if (!tx) {\n throw new Error('Database transaction could not be started');\n }\n\n try {\n await tx.query(SYSTEM_TABLE_BOOTSTRAP_LOCK_SQL);\n const result = await callback(tx);\n await tx.commit();\n return result;\n } catch (error) {\n await this.rollbackSystemTableBootstrap(tx);\n throw error;\n }\n }\n\n if (typeof transaction === 'function') {\n const runInTransaction = transaction.bind(this._db) as <R>(\n callback: (tx: DatabaseInterface) => Promise<R>,\n ) => Promise<R>;\n\n return runInTransaction(async (tx) => {\n await tx.query(SYSTEM_TABLE_BOOTSTRAP_LOCK_SQL);\n return callback(tx);\n });\n }\n\n throw new Error(\n 'Postgres system table bootstrap requires a transaction-capable database adapter',\n );\n }\n\n private async rollbackSystemTableBootstrap(\n tx: TransactionHandle,\n ): Promise<void> {\n try {\n if (typeof tx.isActive !== 'function' || tx.isActive()) {\n await tx.rollback();\n }\n } catch (rollbackError) {\n logger.warn(\n `[smrt] Failed to rollback system table bootstrap transaction: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`,\n );\n }\n }\n\n private async bootstrapSystemTables(db: DatabaseInterface): Promise<void> {\n // Fast path: check if system tables already exist by querying _smrt_migrations.\n // This avoids 29 sequential DDL round-trips on high-latency connections\n // (e.g. remote Postgres over Tailscale where each round-trip is ~650ms).\n // For Postgres, this runs after acquiring the advisory lock so concurrent\n // initializers can observe a completed bootstrap and skip replaying DDL.\n const version = SMRT_SCHEMA_VERSION;\n if (await this.isSystemSchemaVersionApplied(db, version)) {\n return;\n }\n\n // Older installs can have a subset of system columns already created.\n // Upgrade those tables before replaying idempotent DDL so index creation\n // does not fail on missing legacy columns.\n await ensureLegacySystemTableCompatibility(db, this._dbEngineHint);\n\n // Create all system tables\n // Split multi-statement SQL into individual statements to avoid race conditions\n // Each ALL_SYSTEM_TABLES entry contains CREATE TABLE + CREATE INDEX statements\n const allStatements: string[] = [];\n for (const multiStatementSQL of ALL_SYSTEM_TABLES) {\n // Split on semicolon, filter out empty statements\n const statements = multiStatementSQL\n .split(';')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n allStatements.push(...statements);\n }\n\n // Use db.query() for system tables — they use CREATE TABLE/INDEX IF NOT EXISTS\n // which databases handle natively in a single round-trip. The syncSchema()\n // approach does per-column existence checks (multiple round-trips per table)\n // which is unnecessary for framework-owned system tables and extremely slow\n // on high-latency connections (e.g. remote postgres over Tailscale).\n for (const statement of allStatements) {\n await db.query(statement);\n }\n\n // Record current schema version\n // Use ON CONFLICT for DuckDB compatibility (not INSERT OR IGNORE)\n const id = crypto.randomUUID();\n const description = 'Initial SMRT system tables';\n await db.execute`\n INSERT INTO _smrt_migrations (id, version, description)\n VALUES (${id}, ${version}, ${description})\n ON CONFLICT(version) DO NOTHING\n `;\n }\n\n private async ensureNativeVectorStorage(): Promise<void> {\n if (!this._db) {\n return;\n }\n\n try {\n const { ObjectRegistry } = await import('./registry.js');\n const embeddingConfig = ObjectRegistry.getProjectEmbeddingConfig();\n if (embeddingConfig?.storage === 'native') {\n const { EmbeddingStorage } = await import('./embeddings/storage.js');\n const vector = this._db.vector;\n if (vector) {\n const dimensions = embeddingConfig.dimensions || 768;\n await EmbeddingStorage.ensureVectorStorage(\n this._db,\n dimensions,\n vector,\n );\n } else {\n logger.warn(\n '[smrt] Embedding storage set to \"native\" but database has no vector capability. Falling back to JSON storage.',\n );\n }\n }\n } catch (error) {\n // Don't fail system table initialization for vector setup errors\n logger.warn(\n `[smrt] Failed to initialize vector storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n /**\n * Access system tables through standard database interface\n * System tables use _smrt_ prefix to avoid conflicts with user tables\n */\n protected get systemDb(): DatabaseInterface {\n return this._db;\n }\n\n /**\n * Initialize signal bus and adapters\n *\n * Merges global configuration with instance-specific overrides.\n * Registers built-in and custom adapters based on configuration.\n */\n private async initializeSignals(): Promise<void> {\n const globalConfig = config.toJSON();\n const effectiveConfig = this.mergeSignalConfig(globalConfig);\n\n // If a shared bus is provided, always use it (don't create new adapters)\n if (this.options.signals?.bus) {\n this._signalBus = this.options.signals.bus;\n return;\n }\n\n // Otherwise, check if we should initialize signals based on config\n if (!this.shouldInitializeSignals(effectiveConfig)) {\n return;\n }\n\n this._signalBus = new SignalBus({\n sanitization: effectiveConfig.sanitization,\n });\n await this.registerAdapters(effectiveConfig);\n }\n\n /**\n * Merge global and instance signal configuration\n *\n * Instance configuration takes priority over global defaults.\n *\n * @param globalConfig - Global configuration from smrt.configure()\n * @returns Merged configuration\n */\n private mergeSignalConfig(\n globalConfig: GlobalSignalConfig,\n ): GlobalSignalConfig {\n return {\n logging: this.options.logging ?? globalConfig.logging,\n metrics: this.options.metrics ?? globalConfig.metrics,\n pubsub: this.options.pubsub ?? globalConfig.pubsub,\n usage: this.mergeAiUsageConfig(globalConfig),\n sanitization: this.options.sanitization ?? globalConfig.sanitization,\n signals: {\n bus: this.options.signals?.bus ?? globalConfig.signals?.bus,\n adapters: [\n ...(globalConfig.signals?.adapters ?? []),\n ...(this.options.signals?.adapters ?? []),\n ],\n },\n };\n }\n\n /**\n * Check if signals should be initialized\n *\n * Signals are initialized if any adapter is configured.\n *\n * @param config - Effective signal configuration\n * @returns True if signals should be initialized\n */\n private shouldInitializeSignals(config: GlobalSignalConfig): boolean {\n return !!(\n config.logging !== false ||\n config.metrics?.enabled ||\n config.pubsub?.enabled ||\n config.signals?.adapters?.length\n );\n }\n\n /**\n * Register signal adapters based on configuration\n *\n * @param config - Effective signal configuration\n */\n private async registerAdapters(config: GlobalSignalConfig): Promise<void> {\n if (!this._signalBus) return;\n\n // Logging adapter (default: enabled with console)\n if (config.logging !== false) {\n const { createLogger, LoggerAdapter } = await import(\n '@happyvertical/logger'\n );\n const logger = createLogger(config.logging ?? true);\n const adapter = new LoggerAdapter(logger);\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Metrics adapter (default: disabled)\n if (config.metrics?.enabled) {\n const { MetricsAdapter } = await import('./adapters/metrics.js');\n const adapter = new MetricsAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Pub/Sub adapter (default: disabled)\n if (config.pubsub?.enabled) {\n const { PubSubAdapter } = await import('./adapters/pubsub.js');\n const adapter = new PubSubAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Custom adapters\n if (config.signals?.adapters) {\n for (const adapter of config.signals.adapters) {\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n }\n }\n\n /**\n * Gets the filesystem adapter instance\n */\n get fs() {\n return this._fs;\n }\n\n /**\n * Gets the database interface instance\n */\n get db() {\n // Throw helpful error if database is accessed before initialization\n if (!this._db) {\n throw new Error(\n `Database accessed before initialization. ` +\n `Please call await instance.initialize() before accessing the database.`,\n );\n }\n return this._db;\n }\n\n /**\n * Gets the AI client instance\n */\n get ai() {\n return this._ai;\n }\n\n /**\n * Get the in-memory AI usage snapshot for this instance.\n */\n getAiUsageSnapshot(): AiUsageSnapshot | undefined {\n return this._aiUsageCollector?.getSnapshot();\n }\n\n /**\n * Reset the in-memory AI usage collector.\n */\n resetAiUsage(): void {\n this._aiUsageCollector?.reset();\n }\n\n /**\n * List persisted AI usage records.\n */\n async listAiUsage(\n options: AiUsageListOptions = {},\n ): Promise<SmrtAiUsageRecord[]> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n let paramIndex = params.length + 1;\n\n let sql = 'SELECT * FROM _smrt_ai_usage';\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ` ORDER BY ${\n options.orderBy === 'timestamp ASC' ? 'created_at ASC' : 'created_at DESC'\n }`;\n\n if (options.limit !== undefined) {\n sql += ` LIMIT $${paramIndex++}`;\n params.push(options.limit);\n }\n\n if (options.offset !== undefined) {\n sql += ` OFFSET $${paramIndex++}`;\n params.push(options.offset);\n }\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n\n return rows.map((row) => ({\n id: String(row.id),\n provider: String(row.provider),\n model: String(row.model),\n operation: String(row.operation),\n usage: hydratePersistedAiUsageTokens(row),\n estimatedCost:\n row.estimated_cost === null || row.estimated_cost === undefined\n ? undefined\n : Number(row.estimated_cost),\n duration: Number(row.duration ?? 0),\n className:\n row.class_name === null || row.class_name === undefined\n ? undefined\n : String(row.class_name),\n tenantId:\n row.tenant_id === undefined\n ? undefined\n : (row.tenant_id as string | null),\n tags: parseAiUsageTags(row.tags),\n timestamp: normalizeAiUsageTimestamp(row.created_at),\n }));\n }\n\n /**\n * Summarize persisted AI usage records by a grouping dimension.\n */\n async summarizeAiUsage(\n options: AiUsageSummaryOptions = {},\n ): Promise<Record<string, AiUsageStats>> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const groupBy = options.groupBy ?? 'model';\n const bucketExpression =\n groupBy === 'provider'\n ? `provider`\n : groupBy === 'model'\n ? `provider || ':' || model`\n : groupBy === 'class'\n ? `COALESCE(class_name, 'unknown')`\n : groupBy === 'tenant'\n ? `COALESCE(tenant_id, 'global')`\n : groupBy === 'operation'\n ? `operation`\n : `substr(CAST(created_at AS TEXT), 1, 10)`;\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n\n let sql = `\n SELECT ${bucketExpression} AS bucket,\n COUNT(*) AS call_count,\n COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,\n COALESCE(SUM(completion_tokens), 0) AS completion_tokens,\n COALESCE(SUM(total_tokens), 0) AS total_tokens,\n COALESCE(SUM(duration), 0) AS total_duration,\n COALESCE(SUM(estimated_cost), 0) AS estimated_cost,\n MAX(created_at) AS last_used\n FROM _smrt_ai_usage\n `;\n\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ' GROUP BY bucket ORDER BY bucket ASC';\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n const summary: Record<string, AiUsageStats> = {};\n\n for (const row of rows) {\n const bucket = String(row.bucket);\n summary[bucket] = {\n callCount: Number(row.call_count ?? 0),\n promptTokens: Number(row.prompt_tokens ?? 0),\n completionTokens: Number(row.completion_tokens ?? 0),\n totalTokens: Number(row.total_tokens ?? 0),\n totalDuration: Number(row.total_duration ?? 0),\n estimatedCost: Number(row.estimated_cost ?? 0),\n lastUsed: row.last_used ? new Date(String(row.last_used)).getTime() : 0,\n };\n }\n\n return summary;\n }\n\n /**\n * Gets the signal bus instance\n *\n * @returns Signal bus if signals are enabled, undefined otherwise\n */\n get signalBus(): SignalBus | undefined {\n return this._signalBus;\n }\n\n /**\n * Cleanup method to prevent memory leaks\n *\n * Unregisters all adapters from the signal bus that were registered\n * by this instance. Call this when the SmrtClass instance is no longer\n * needed to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const product = new Product({ name: 'Widget' });\n * await product.initialize();\n * // ... use product ...\n * product.destroy(); // Clean up when done\n * ```\n */\n destroy(): void {\n // Only unregister adapters if we own the bus (not shared)\n if (this._signalBus && !this.options.signals?.bus) {\n for (const adapter of this._registeredAdapters) {\n this._signalBus.unregister(adapter);\n }\n this._registeredAdapters = [];\n }\n\n // TODO: If SmrtClass grows a broader async teardown lifecycle, move\n // AI usage handler cleanup there alongside other connection-bound state.\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n }\n\n private mergeAiUsageConfig(\n globalConfig: GlobalSignalConfig,\n ): ResolvedAiUsageConfig {\n const globalUsage = globalConfig.usage ?? {};\n const instanceUsage = this.options.usage ?? {};\n\n return {\n enabled: instanceUsage.enabled ?? globalUsage.enabled ?? true,\n persist: instanceUsage.persist ?? globalUsage.persist ?? true,\n estimateCosts:\n instanceUsage.estimateCosts ?? globalUsage.estimateCosts ?? true,\n costRates: {\n ...(globalUsage.costRates ?? {}),\n ...(instanceUsage.costRates ?? {}),\n },\n handlers: [\n ...(globalUsage.handlers ?? []),\n ...(instanceUsage.handlers ?? []),\n ],\n };\n }\n\n private initializeAiUsageHandlers(config: ResolvedAiUsageConfig): void {\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n\n if (!config.enabled) {\n return;\n }\n\n this._aiUsageCollector = new AiUsageCollector();\n this._aiUsageHandlers.push(this._aiUsageCollector);\n\n if (config.persist && this._db) {\n this._aiUsageHandlers.push(new AiUsagePersistenceHandler(this._db));\n }\n\n this._aiUsageHandlers.push(...config.handlers);\n }\n\n private async handleAiUsageCallback(\n event: unknown,\n aiConfig: Record<string, unknown>,\n usageConfig: ResolvedAiUsageConfig,\n ): Promise<void> {\n if (!usageConfig.enabled || this._aiUsageHandlers.length === 0) {\n return;\n }\n\n const normalizedEvent = this.normalizeAiUsageEvent(event, aiConfig);\n if (!normalizedEvent) {\n return;\n }\n\n if (usageConfig.estimateCosts) {\n normalizedEvent.estimatedCost = estimateAiUsageCost(\n normalizedEvent.provider,\n normalizedEvent.model,\n normalizedEvent.usage,\n usageConfig.costRates,\n );\n }\n\n const results = await Promise.allSettled(\n this._aiUsageHandlers.map((handler) => handler.handle(normalizedEvent)),\n );\n\n for (const result of results) {\n if (result.status === 'rejected') {\n logger.warn(\n `[smrt] AI usage handler failed for ${normalizedEvent.provider}:${normalizedEvent.model}: ${\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason)\n }`,\n );\n }\n }\n }\n\n private normalizeAiUsageEvent(\n event: unknown,\n aiConfig: Record<string, unknown>,\n ): SmrtAiUsageEvent | undefined {\n const raw = (event ?? {}) as Record<string, unknown>;\n const provider = firstString(\n raw.provider,\n raw.type,\n aiConfig.provider,\n aiConfig.type,\n );\n const model = firstString(\n raw.model,\n raw.defaultModel,\n aiConfig.model,\n aiConfig.defaultModel,\n );\n const operation =\n firstString(raw.operation, raw.kind, raw.method) ?? 'unknown';\n const usage =\n normalizeIncomingAiUsageTokens(raw.usage) ??\n normalizeIncomingAiUsageTokens(raw.tokenUsage) ??\n normalizeIncomingAiUsageTokens({\n promptTokens: raw.promptTokens,\n completionTokens: raw.completionTokens,\n totalTokens: raw.totalTokens,\n });\n\n if (!provider || !model) {\n return undefined;\n }\n\n const duration =\n firstNumber(raw.duration, raw.durationMs, raw.latency) ?? 0;\n const tenantId =\n 'tenantId' in this &&\n (this as { tenantId?: string | null }).tenantId !== undefined\n ? ((this as { tenantId?: string | null }).tenantId ?? null)\n : undefined;\n\n return {\n provider,\n model,\n operation,\n usage,\n duration,\n timestamp: normalizeAiUsageTimestamp(raw.timestamp),\n tags: normalizeAiUsageTags(raw.tags),\n className: this._className,\n tenantId,\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA4CA,IAAM,kCACJ;AAEF,IAAM,SAAS,aAAa,EAAE,OAAO,OAAO,CAAC;AA0C7C,SAAS,YAAY,GAAG,YAA2C;CACjE,OAAO,WAAW,MAAM,cAAmC;EACzD,OAAO,OAAO,cAAc;CAC9B,CAAC;AACH;AAEA,SAAS,YAAY,GAAG,YAA2C;CACjE,OAAO,WAAW,MAAM,cAAmC;EACzD,OAAO,OAAO,cAAc;CAC9B,CAAC;AACH;AAEA,SAAS,eAAe,IAA+B;CACrD,MAAM,eAAe;CACrB,OAAO,GAAG,OAAO,aAAa,QAAQ,OAAO;AAC/C;AAEA,SAAS,oBACP,QACoB;CACpB,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B;CAGF,MAAM,iBAAiB;CAIvB,IAAI,OAAO,eAAe,SAAS,UACjC,OAAO,eAAe;CAGxB,IAAI,OAAO,eAAe,QAAQ,SAAS,UACzC,OAAO,eAAe,OAAO;CAG/B,IAAI,WAAW,kBAAkB,OAAO,eAAe,UAAU,YAC/D;CAGF,IAAI,YAAY,kBAAkB,eAAe,QAC/C,OAAO;AAIX;AAEA,SAAS,+BACP,OAC0B;CAC1B,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B;CAGF,MAAM,QAAQ;CACd,MAAM,eACJ,OAAO,MAAM,iBAAiB,WAC1B,MAAM,eACN,OAAO,MAAM,gBAAgB,WAC3B,MAAM,cACN,KAAA;CACR,MAAM,mBACJ,OAAO,MAAM,qBAAqB,WAC9B,MAAM,mBACN,OAAO,MAAM,iBAAiB,WAC5B,MAAM,eACN,KAAA;CACR,MAAM,cACJ,OAAO,MAAM,gBAAgB,WACzB,MAAM,cACN,iBAAiB,KAAA,KAAa,qBAAqB,KAAA,KAChD,gBAAgB,MAAM,oBAAoB,KAC3C,KAAA;CAER,IACE,iBAAiB,KAAA,KACjB,qBAAqB,KAAA,KACrB,gBAAgB,KAAA,GAEhB;CAGF,OAAO;EACL;EACA;EACA;CACF;AACF;AAEA,SAAS,8BAA8B,KAIV;CAC3B,MAAM,eACJ,OAAO,IAAI,kBAAkB,WACzB,IAAI,gBACJ,IAAI,kBAAkB,QAAQ,IAAI,kBAAkB,KAAA,IAClD,KAAA,IACA,OAAO,IAAI,aAAa;CAChC,MAAM,mBACJ,OAAO,IAAI,sBAAsB,WAC7B,IAAI,oBACJ,IAAI,sBAAsB,QAAQ,IAAI,sBAAsB,KAAA,IAC1D,KAAA,IACA,OAAO,IAAI,iBAAiB;CACpC,MAAM,cACJ,OAAO,IAAI,iBAAiB,WACxB,IAAI,eACJ,IAAI,iBAAiB,QAAQ,IAAI,iBAAiB,KAAA,IAChD,KAAA,IACA,OAAO,IAAI,YAAY;CAE/B,IACE,iBAAiB,KAAA,KACjB,qBAAqB,KAAA,KACrB,gBAAgB,KAAA,GAEhB;CAGF,OAAO;EACL;EACA;EACA;CACF;AACF;AAEA,SAAS,qBACP,OACoC;CACpC,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAC5D;CAGF,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,KAAK,GAAG;EACnD,IAAI,aAAa,KAAA,KAAa,aAAa,MAAM;EACjD,KAAK,OAAO,OAAO,QAAQ;CAC7B;CAEA,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,OAAO,KAAA;AAC/C;AAEA,SAAS,iBAAiB,OAAoD;CAC5E,IAAI,OAAO,UAAU,UACnB;CAGF,IAAI;EACF,OAAO,qBAAqB,KAAK,MAAM,KAAK,CAAC;CAC/C,QAAQ;EACN;CACF;AACF;AAEA,SAAS,0BAA0B,OAAsB;CACvD,IAAI,iBAAiB,MACnB,OAAO;CAGT,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;EAC1D,MAAM,OAAO,IAAI,KAAK,KAAK;EAC3B,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC9B,OAAO;CAEX;CAEA,uBAAO,IAAI,KAAK;AAClB;AAEA,SAAS,aACP,QAC2B;CAC3B,OAAO,MAAM,QAAQ,MAAM,IACtB,SACC,OAAgD,QAAQ,CAAC;AACjE;AAEA,SAAS,wBACP,SACoB;CACpB,MAAM,aAAuB,CAAC;CAC9B,MAAM,SAAoB,CAAC;CAC3B,IAAI,iBAAiB;CAErB,IAAI,QAAQ,OAAO;EACjB,WAAW,KAAK,kBAAkB,kBAAkB;EACpD,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;CACzC;CAEA,IAAI,QAAQ,OAAO;EACjB,WAAW,KAAK,kBAAkB,kBAAkB;EACpD,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;CACzC;CAEA,IAAI,QAAQ,UAAU;EACpB,WAAW,KAAK,eAAe,kBAAkB;EACjD,OAAO,KAAK,QAAQ,QAAQ;CAC9B;CAEA,IAAI,QAAQ,OAAO;EACjB,WAAW,KAAK,YAAY,kBAAkB;EAC9C,OAAO,KAAK,QAAQ,KAAK;CAC3B;CAEA,IAAI,QAAQ,WAAW;EACrB,WAAW,KAAK,gBAAgB,kBAAkB;EAClD,OAAO,KAAK,QAAQ,SAAS;CAC/B;CAEA,IAAI,QAAQ,WAAW;EACrB,WAAW,KAAK,iBAAiB,kBAAkB;EACnD,OAAO,KAAK,QAAQ,SAAS;CAC/B;CAEA,IAAI,QAAQ,aAAa,MACvB,WAAW,KAAK,mBAAmB;MAC9B,IAAI,QAAQ,UAAU;EAC3B,WAAW,KAAK,gBAAgB,kBAAkB;EAClD,OAAO,KAAK,QAAQ,QAAQ;CAC9B;CAEA,OAAO;EACL;EACA;EACA;CACF;AACF;;;;;;;;AAoGA,IAAa,YAAb,MAAa,UAAU;;;;CAIrB;;;;CAKA;;;;CAKA;CACA;;;;CAKA;;;;CAKA;;;;CAKA,sBAA+C,CAAC;;;;CAKhD;;;;CAKA,mBAA6C,CAAC;;;;CAK9C,8BAAsC;;;;CAKtC;;;;CAKA;;;;;;CAOA,OAAe,2CAA2B,IAAI,QAA2B;CACzE,OAAe,gDAAgC,IAAI,IAAY;;;;;;CAO/D,YAAY,UAA4B,CAAC,GAAG;EAC1C,KAAK,UAAU;EACf,KAAK,aAAa,KAAK,YAAY;CACrC;;;;;;;;;;;;;;;;;CAkBA,mBAAsC;EACpC,OAAO;CACT;;;;;;;;;;CAWA,MAAgB,aAA4B;EAC1C,MAAM,KAAK,wBAAwB;EAEnC,IAAI,CAAC,KAAK,QAAQ,6BAChB,MAAM,KAAK,0BAA0B;EAGvC,OAAO;CACT;;;;;;;;CASA,MAAgB,0BAAyC;EAIvD,yBAAyB;EAGzB,IAAI,KAAK,QAAQ,eAAe,CAAC,KAAK,QAAQ,IAC5C,KAAK,QAAQ,KAAK,KAAK,QAAQ;EAIjC,IAAI,KAAK,iBAAiB,KAAK,CAAC,KAAK,QAAQ,IAC3C,MAAM,IAAI,MACR,GAAG,KAAK,WAAW,oHAErB;EAGF,IAAI,KAAK,QAAQ,IAAI;GACnB,KAAK,gBAAgB,oBAAoB,KAAK,QAAQ,EAAE;GAExD,IACE,KAAK,QAAQ,uBACb,OAAO,KAAK,QAAQ,OAAO,YAC3B,WAAW,KAAK,QAAQ,IACxB;IACA,KAAK,MAAM,KAAK,QAAQ;IACxB,KAAK,QAAQ,KAAK,KAAK;GACzB,OAAO;IAML,IAAI,OAAO,KAAK,QAAQ,OAAO,UAAU;KAIvC,MAAM,aAAa,KAAK,QAAQ,OAAO;KACvC,KAAK,MAAM,MAAM,YAAY;MAC3B,KAAK,KAAK,QAAQ;MAClB,GAAI,aAAa,CAAC,IAAI,EAAE,MAAM,QAAQ,KAAK,QAAQ,KAAK;KAC1D,CAAC;IACH,OAAO,IAAI,WAAW,KAAK,QAAQ,IAEjC,KAAK,MAAM,KAAK,QAAQ;SACnB,IAAI,YAAY,KAAK,QAAQ,MAAM,KAAK,QAAQ,GAAG,QAAQ;KAGhE,MAAM,WAAW,KAAK,QAAQ;KAQ9B,KAAK,MAAM,MAAM,YAAY;MAC3B,MAAM,SAAS,QAAQ;MACvB,QAAQ,SAAS;MACjB,KAAK,SAAS;KAChB,CAAiD;IACnD,OAAO;KAKL,MAAM,QADW,KAAK,QAAQ,GACP,OAAO;KAC9B,MAAM,aAAa,UAAU,cAAc,UAAU;KAIrD,KAAK,MAAM,MAAM,YAAY;MAC3B,GAAG,KAAK,QAAQ;MAChB,GAAI,aAAa,CAAC,IAAI,EAAE,MAAM,QAAQ,QAAQ;KAChD,CAAiD;IACnD;;;;;;;;;;;;;IAcA,KAAK,QAAQ,KAAK,KAAK;IAEvB,MAAM,KAAK,mBAAmB;GAChC;EACF;CACF;;;;CAKA,MAAgB,4BAA2C;EACzD,IAAI,KAAK,6BACP;EAGF,IAAI,CAAC,KAAK,6BACR,KAAK,+BAA+B,YAAY;GAC9C,IAAI,KAAK,QAAQ,MAAM,CAAC,KAAK,KAC3B,KAAK,MAAM,MAAM,kBAAkB,OAAO,KAAK,QAAQ,EAAE;GAK3D,MAAM,eAAe,OAAO,OAAO;GACnC,MAAM,cAAc,KAAK,mBAAmB,YAAY;GACxD,KAAK,0BAA0B,WAAW;GAE1C,IACE,CAAC,KAAK,QACL,KAAK,QAAQ,MAAM,aAAa,MAAM,QAAQ,IAAI,mBACnD;IAGA,MAAM,WAAW,KAAK,QAAQ;IAG9B,IACE,YACA,OAAO,aAAa,YACpB,OAAO,SAAS,UAAU,cAC1B,CAAC,SAAS,UAEV,KAAK,MAAM;SACN;KASL,MAAM,EAAE,kBAAkB,MAAM,OAAO;KAMvC,MAAM,aAAa;MAAE,GAHF,aAAa,MAAM,CAAC;MAGH,GAAG,KAAK,QAAQ;KAAG;KAKvD,MAAM,WAAW,cAAwB,YAAY;MACnD,aAAa;MACb,QAAQ;MACR,QAAQ;OACN,UAAU;OACV,OAAO;OACP,QAAQ;OACR,SAAS;OACT,YAAY;OACZ,aAAa;OACb,WAAW;MACb;KACF,CAAC;KAED,MAAM,kBACJ,SAAS,WACR,WAAuC,WACxC,KAAA;KACF,SAAS,UAAU,OAAO,UAAmB;MAC3C,IAAI,OAAO,oBAAoB,YAC7B,MAAO,gBACL,KACF;MAEF,MAAM,KAAK,sBAAsB,OAAO,UAAU,WAAW;KAC/D;KAGA,IAAI,SAAS,YAAY,SAAS,QAAQ,SAAS,QAKjD,KAAK,MAAO,MAAM,MAChB,QACF;IAEJ;GACF;GAEA,MAAM,KAAK,kBAAkB;GAC7B,KAAK,8BAA8B;EACrC,EAAA,CAAG;EAGL,IAAI;GACF,MAAM,KAAK;EACb,UAAU;GACR,KAAK,8BAA8B,KAAA;EACrC;CACF;;;;CAKA,MAAgB,mCAAkD;EAChE,IAAI,CAAC,KAAK,6BACR,MAAM,KAAK,0BAA0B;CAEzC;;;;CAKA,MAAgB,cAAiC;EAC/C,MAAM,KAAK,iCAAiC;EAE5C,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MACR,GAAG,KAAK,WAAW,wGAErB;EAGF,OAAO,KAAK;CACd;;;;CAKA,MAAgB,sBAAqD;EACnE,MAAM,KAAK,iCAAiC;EAC5C,OAAO,KAAK;CACd;CAEA,MAAc,6BACZ,IACA,SACkB;EAClB,MAAM,SAAS,aAAa,eAAe,EAAE,GAAG,KAAK,aAAa;EAElE,IACE,WAAW,cACX,CAAE,MAAM,YAAY,IAAI,oBAAoB,KAAK,aAAa,GAE9D,OAAO;EAGT,IAAI;GACF,MAAM,eAAe,WAAW,aAAa,OAAO;GAKpD,OAAO,aAAa,MAJD,GAAG,MACpB,kDAAkD,aAAa,WAC/D,OACF,CACwB,CAAC,CAAC,SAAS;EACrC,SAAS,OAAO;GACd,IAAI,WAAW,YACb,MAAM;GAGR,OAAO;EACT;CACF;;;;;;;;;;;;;CAcA,MAAc,qBAAoC;EAChD,IAAI,CAAC,KAAK,KAAK;EAEf,MAAM,QAAQ,eAAe,KAAK,GAAG;EAOrC,MAAM,oBAAoB,KAAK,IAAI,aAAa,QAAQ;EACxD,MAAM,aAAa,UAAU;EAC7B,MAAM,WAAW,kBAAkB,YAAY,CAAC,CAAC,SAAS,MAAM;EAEhE,MAAM,sBAAsB,cAAc,YAAY,CAD7B;EAGzB,IAAI;OAEE,UAAU,yBAAyB,IAAI,KAAK,GAAG,GACjD;EAAA,OAIF,IAAI,UAAU,8BAA8B,IAAI,KAAK,GACnD;EAIJ,IAAI;GACF,MAAM,KAAK,8BAA8B,OACvC,KAAK,sBAAsB,EAAE,CAC/B;GACA,MAAM,KAAK,0BAA0B;GAGrC,IAAI,qBACF,UAAU,yBAAyB,IAAI,KAAK,GAAG;QAE/C,UAAU,8BAA8B,IAAI,KAAK;EAErD,SAAS,OAAO;GAEd,MAAM,SAAS,KAAK,IAAI,aAAa,QAAQ;GAC7C,MAAM,IAAI,MACR,sCAAsC,OAAO,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACtG,EAAE,OAAO,MAAM,CACjB;EACF;CACF;CAEA,MAAc,6BACZ,UACY;EACZ,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MAAM,6BAA6B;EAG/C,MAAM,mBAAmB,KAAK,IAAI;EAClC,MAAM,cAAe,KAAK,IAAmC;EAI7D,IAAI,EAFF,aAAa,eAAe,KAAK,GAAG,GAAG,KAAK,aAAa,MAAM,aAG/D,OAAO,SAAS,KAAK,GAAG;EAG1B,IAAI,OAAO,qBAAqB,YAAY;GAC1C,MAAM,KAAK,MAAM,iBAAiB,KAAK,KAAK,GAAG;GAC/C,IAAI,CAAC,IACH,MAAM,IAAI,MAAM,2CAA2C;GAG7D,IAAI;IACF,MAAM,GAAG,MAAM,+BAA+B;IAC9C,MAAM,SAAS,MAAM,SAAS,EAAE;IAChC,MAAM,GAAG,OAAO;IAChB,OAAO;GACT,SAAS,OAAO;IACd,MAAM,KAAK,6BAA6B,EAAE;IAC1C,MAAM;GACR;EACF;EAEA,IAAI,OAAO,gBAAgB,YAKzB,OAJyB,YAAY,KAAK,KAAK,GAIxC,CAAA,CAAiB,OAAO,OAAO;GACpC,MAAM,GAAG,MAAM,+BAA+B;GAC9C,OAAO,SAAS,EAAE;EACpB,CAAC;EAGH,MAAM,IAAI,MACR,iFACF;CACF;CAEA,MAAc,6BACZ,IACe;EACf,IAAI;GACF,IAAI,OAAO,GAAG,aAAa,cAAc,GAAG,SAAS,GACnD,MAAM,GAAG,SAAS;EAEtB,SAAS,eAAe;GACtB,OAAO,KACL,iEAAiE,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,GAChJ;EACF;CACF;CAEA,MAAc,sBAAsB,IAAsC;EAMxE,MAAM,UAAU;EAChB,IAAI,MAAM,KAAK,6BAA6B,IAAI,OAAO,GACrD;EAMF,MAAM,qCAAqC,IAAI,KAAK,aAAa;EAKjE,MAAM,gBAA0B,CAAC;EACjC,KAAK,MAAM,qBAAqB,mBAAmB;GAEjD,MAAM,aAAa,kBAChB,MAAM,GAAG,CAAC,CACV,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CACpB,QAAQ,MAAM,EAAE,SAAS,CAAC;GAC7B,cAAc,KAAK,GAAG,UAAU;EAClC;EAOA,KAAK,MAAM,aAAa,eACtB,MAAM,GAAG,MAAM,SAAS;EAK1B,MAAM,KAAK,OAAO,WAAW;EAE7B,MAAM,GAAG,OAAO;;gBAEJ,GAAG,IAAI,QAAQ,IAAI,6BAAY;;;CAG7C;CAEA,MAAc,4BAA2C;EACvD,IAAI,CAAC,KAAK,KACR;EAGF,IAAI;GACF,MAAM,EAAE,mBAAmB,MAAM,OAAO;GACxC,MAAM,kBAAkB,eAAe,0BAA0B;GACjE,IAAI,iBAAiB,YAAY,UAAU;IACzC,MAAM,EAAE,qBAAqB,MAAM,OAAO;IAC1C,MAAM,SAAS,KAAK,IAAI;IACxB,IAAI,QAAQ;KACV,MAAM,aAAa,gBAAgB,cAAc;KACjD,MAAM,iBAAiB,oBACrB,KAAK,KACL,YACA,MACF;IACF,OACE,OAAO,KACL,iHACF;GAEJ;EACF,SAAS,OAAO;GAEd,OAAO,KACL,+CAA+C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACtG;EACF;CACF;;;;;CAMA,IAAc,WAA8B;EAC1C,OAAO,KAAK;CACd;;;;;;;CAQA,MAAc,oBAAmC;EAC/C,MAAM,eAAe,OAAO,OAAO;EACnC,MAAM,kBAAkB,KAAK,kBAAkB,YAAY;EAG3D,IAAI,KAAK,QAAQ,SAAS,KAAK;GAC7B,KAAK,aAAa,KAAK,QAAQ,QAAQ;GACvC;EACF;EAGA,IAAI,CAAC,KAAK,wBAAwB,eAAe,GAC/C;EAGF,KAAK,aAAa,IAAI,UAAU,EAC9B,cAAc,gBAAgB,aAChC,CAAC;EACD,MAAM,KAAK,iBAAiB,eAAe;CAC7C;;;;;;;;;CAUA,kBACE,cACoB;EACpB,OAAO;GACL,SAAS,KAAK,QAAQ,WAAW,aAAa;GAC9C,SAAS,KAAK,QAAQ,WAAW,aAAa;GAC9C,QAAQ,KAAK,QAAQ,UAAU,aAAa;GAC5C,OAAO,KAAK,mBAAmB,YAAY;GAC3C,cAAc,KAAK,QAAQ,gBAAgB,aAAa;GACxD,SAAS;IACP,KAAK,KAAK,QAAQ,SAAS,OAAO,aAAa,SAAS;IACxD,UAAU,CACR,GAAI,aAAa,SAAS,YAAY,CAAC,GACvC,GAAI,KAAK,QAAQ,SAAS,YAAY,CAAC,CACzC;GACF;EACF;CACF;;;;;;;;;CAUA,wBAAgC,QAAqC;EACnE,OAAO,CAAC,EACN,OAAO,YAAY,SACnB,OAAO,SAAS,WAChB,OAAO,QAAQ,WACf,OAAO,SAAS,UAAU;CAE9B;;;;;;CAOA,MAAc,iBAAiB,QAA2C;EACxE,IAAI,CAAC,KAAK,YAAY;EAGtB,IAAI,OAAO,YAAY,OAAO;GAC5B,MAAM,EAAE,cAAc,kBAAkB,MAAM,OAC5C;GAGF,MAAM,UAAU,IAAI,cADL,aAAa,OAAO,WAAW,IACZ,CAAM;GACxC,KAAK,WAAW,SAAS,OAAO;GAChC,KAAK,oBAAoB,KAAK,OAAO;EACvC;EAGA,IAAI,OAAO,SAAS,SAAS;GAC3B,MAAM,EAAE,mBAAmB,MAAM,OAAO;GACxC,MAAM,UAAU,IAAI,eAAe;GACnC,KAAK,WAAW,SAAS,OAAO;GAChC,KAAK,oBAAoB,KAAK,OAAO;EACvC;EAGA,IAAI,OAAO,QAAQ,SAAS;GAC1B,MAAM,EAAE,kBAAkB,MAAM,OAAO;GACvC,MAAM,UAAU,IAAI,cAAc;GAClC,KAAK,WAAW,SAAS,OAAO;GAChC,KAAK,oBAAoB,KAAK,OAAO;EACvC;EAGA,IAAI,OAAO,SAAS,UAClB,KAAK,MAAM,WAAW,OAAO,QAAQ,UAAU;GAC7C,KAAK,WAAW,SAAS,OAAO;GAChC,KAAK,oBAAoB,KAAK,OAAO;EACvC;CAEJ;;;;CAKA,IAAI,KAAK;EACP,OAAO,KAAK;CACd;;;;CAKA,IAAI,KAAK;EAEP,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MACR,iHAEF;EAEF,OAAO,KAAK;CACd;;;;CAKA,IAAI,KAAK;EACP,OAAO,KAAK;CACd;;;;CAKA,qBAAkD;EAChD,OAAO,KAAK,mBAAmB,YAAY;CAC7C;;;;CAKA,eAAqB;EACnB,KAAK,mBAAmB,MAAM;CAChC;;;;CAKA,MAAM,YACJ,UAA8B,CAAC,GACD;EAC9B,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MACR,8GAEF;EAGF,MAAM,EAAE,YAAY,WAAW,wBAAwB,OAAO;EAC9D,IAAI,aAAa,OAAO,SAAS;EAEjC,IAAI,MAAM;EACV,IAAI,WAAW,SAAS,GACtB,OAAO,UAAU,WAAW,KAAK,OAAO;EAG1C,OAAO,aACL,QAAQ,YAAY,kBAAkB,mBAAmB;EAG3D,IAAI,QAAQ,UAAU,KAAA,GAAW;GAC/B,OAAO,WAAW;GAClB,OAAO,KAAK,QAAQ,KAAK;EAC3B;EAEA,IAAI,QAAQ,WAAW,KAAA,GAAW;GAChC,OAAO,YAAY;GACnB,OAAO,KAAK,QAAQ,MAAM;EAC5B;EAIA,OAFa,aAAa,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG,MAAM,CAEtD,CAAA,CAAK,KAAK,SAAS;GACxB,IAAI,OAAO,IAAI,EAAE;GACjB,UAAU,OAAO,IAAI,QAAQ;GAC7B,OAAO,OAAO,IAAI,KAAK;GACvB,WAAW,OAAO,IAAI,SAAS;GAC/B,OAAO,8BAA8B,GAAG;GACxC,eACE,IAAI,mBAAmB,QAAQ,IAAI,mBAAmB,KAAA,IAClD,KAAA,IACA,OAAO,IAAI,cAAc;GAC/B,UAAU,OAAO,IAAI,YAAY,CAAC;GAClC,WACE,IAAI,eAAe,QAAQ,IAAI,eAAe,KAAA,IAC1C,KAAA,IACA,OAAO,IAAI,UAAU;GAC3B,UACE,IAAI,cAAc,KAAA,IACd,KAAA,IACC,IAAI;GACX,MAAM,iBAAiB,IAAI,IAAI;GAC/B,WAAW,0BAA0B,IAAI,UAAU;EACrD,EAAE;CACJ;;;;CAKA,MAAM,iBACJ,UAAiC,CAAC,GACK;EACvC,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MACR,8GAEF;EAGF,MAAM,UAAU,QAAQ,WAAW;EACnC,MAAM,mBACJ,YAAY,aACR,aACA,YAAY,UACV,6BACA,YAAY,UACV,oCACA,YAAY,WACV,kCACA,YAAY,cACV,cACA;EAEd,MAAM,EAAE,YAAY,WAAW,wBAAwB,OAAO;EAE9D,IAAI,MAAM;eACC,iBAAiB;;;;;;;;;;EAW5B,IAAI,WAAW,SAAS,GACtB,OAAO,UAAU,WAAW,KAAK,OAAO;EAG1C,OAAO;EAEP,MAAM,OAAO,aAAa,MAAM,KAAK,IAAI,MAAM,KAAK,GAAG,MAAM,CAAC;EAC9D,MAAM,UAAwC,CAAC;EAE/C,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,SAAS,OAAO,IAAI,MAAM;GAChC,QAAQ,UAAU;IAChB,WAAW,OAAO,IAAI,cAAc,CAAC;IACrC,cAAc,OAAO,IAAI,iBAAiB,CAAC;IAC3C,kBAAkB,OAAO,IAAI,qBAAqB,CAAC;IACnD,aAAa,OAAO,IAAI,gBAAgB,CAAC;IACzC,eAAe,OAAO,IAAI,kBAAkB,CAAC;IAC7C,eAAe,OAAO,IAAI,kBAAkB,CAAC;IAC7C,UAAU,IAAI,YAAY,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,QAAQ,IAAI;GACxE;EACF;EAEA,OAAO;CACT;;;;;;CAOA,IAAI,YAAmC;EACrC,OAAO,KAAK;CACd;;;;;;;;;;;;;;;;CAiBA,UAAgB;EAEd,IAAI,KAAK,cAAc,CAAC,KAAK,QAAQ,SAAS,KAAK;GACjD,KAAK,MAAM,WAAW,KAAK,qBACzB,KAAK,WAAW,WAAW,OAAO;GAEpC,KAAK,sBAAsB,CAAC;EAC9B;EAIA,KAAK,oBAAoB,KAAA;EACzB,KAAK,mBAAmB,CAAC;CAC3B;CAEA,mBACE,cACuB;EACvB,MAAM,cAAc,aAAa,SAAS,CAAC;EAC3C,MAAM,gBAAgB,KAAK,QAAQ,SAAS,CAAC;EAE7C,OAAO;GACL,SAAS,cAAc,WAAW,YAAY,WAAW;GACzD,SAAS,cAAc,WAAW,YAAY,WAAW;GACzD,eACE,cAAc,iBAAiB,YAAY,iBAAiB;GAC9D,WAAW;IACT,GAAI,YAAY,aAAa,CAAC;IAC9B,GAAI,cAAc,aAAa,CAAC;GAClC;GACA,UAAU,CACR,GAAI,YAAY,YAAY,CAAC,GAC7B,GAAI,cAAc,YAAY,CAAC,CACjC;EACF;CACF;CAEA,0BAAkC,QAAqC;EACrE,KAAK,oBAAoB,KAAA;EACzB,KAAK,mBAAmB,CAAC;EAEzB,IAAI,CAAC,OAAO,SACV;EAGF,KAAK,oBAAoB,IAAI,iBAAiB;EAC9C,KAAK,iBAAiB,KAAK,KAAK,iBAAiB;EAEjD,IAAI,OAAO,WAAW,KAAK,KACzB,KAAK,iBAAiB,KAAK,IAAI,0BAA0B,KAAK,GAAG,CAAC;EAGpE,KAAK,iBAAiB,KAAK,GAAG,OAAO,QAAQ;CAC/C;CAEA,MAAc,sBACZ,OACA,UACA,aACe;EACf,IAAI,CAAC,YAAY,WAAW,KAAK,iBAAiB,WAAW,GAC3D;EAGF,MAAM,kBAAkB,KAAK,sBAAsB,OAAO,QAAQ;EAClE,IAAI,CAAC,iBACH;EAGF,IAAI,YAAY,eACd,gBAAgB,gBAAgB,oBAC9B,gBAAgB,UAChB,gBAAgB,OAChB,gBAAgB,OAChB,YAAY,SACd;EAGF,MAAM,UAAU,MAAM,QAAQ,WAC5B,KAAK,iBAAiB,KAAK,YAAY,QAAQ,OAAO,eAAe,CAAC,CACxE;EAEA,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,WAAW,YACpB,OAAO,KACL,sCAAsC,gBAAgB,SAAS,GAAG,gBAAgB,MAAM,IACtF,OAAO,kBAAkB,QACrB,OAAO,OAAO,UACd,OAAO,OAAO,MAAM,GAE5B;CAGN;CAEA,sBACE,OACA,UAC8B;EAC9B,MAAM,MAAO,SAAS,CAAC;EACvB,MAAM,WAAW,YACf,IAAI,UACJ,IAAI,MACJ,SAAS,UACT,SAAS,IACX;EACA,MAAM,QAAQ,YACZ,IAAI,OACJ,IAAI,cACJ,SAAS,OACT,SAAS,YACX;EACA,MAAM,YACJ,YAAY,IAAI,WAAW,IAAI,MAAM,IAAI,MAAM,KAAK;EACtD,MAAM,QACJ,+BAA+B,IAAI,KAAK,KACxC,+BAA+B,IAAI,UAAU,KAC7C,+BAA+B;GAC7B,cAAc,IAAI;GAClB,kBAAkB,IAAI;GACtB,aAAa,IAAI;EACnB,CAAC;EAEH,IAAI,CAAC,YAAY,CAAC,OAChB;EAGF,MAAM,WACJ,YAAY,IAAI,UAAU,IAAI,YAAY,IAAI,OAAO,KAAK;EAC5D,MAAM,WACJ,cAAc,QACb,KAAsC,aAAa,KAAA,IAC9C,KAAsC,YAAY,OACpD,KAAA;EAEN,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,WAAW,0BAA0B,IAAI,SAAS;GAClD,MAAM,qBAAqB,IAAI,IAAI;GACnC,WAAW,KAAK;GAChB;EACF;CACF;AACF"}
@@ -1,8 +1,8 @@
1
- import { SmrtClass } from "./class.js";
2
1
  import { buildQueryCacheKey, ensureCacheInvalidationListener, getCacheGeneration, getCachedRows, invalidateCollectionCache, registerCrossProcessCacheInterest, resolveDbCacheKey, setCachedRows } from "./collection-cache.js";
2
+ import { GlobalInterceptors, createInterceptorContext } from "./interceptors.js";
3
+ import { SmrtClass } from "./class.js";
3
4
  import { EmbeddingProvider } from "./embeddings/provider.js";
4
5
  import { EmbeddingStorage } from "./embeddings/storage.js";
5
- import { GlobalInterceptors, createInterceptorContext } from "./interceptors.js";
6
6
  import { classnameToTablename, toSnakeCase } from "./utils/naming.js";
7
7
  import { verifyPersistenceTable } from "./schema/table-verifier.js";
8
8
  import { fieldsFromClass, formatDataJs, toCamelCase } from "./utils.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAsDnC,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAUD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAiGtE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAuDnC,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAiBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAiGtE"}
@@ -1,3 +1,4 @@
1
+ import { CLIENT_FETCH_RUNTIME } from "../generated-client-runtime.js";
1
2
  import { generateDeclarations } from "../prebuild/index.js";
2
3
  import * as fs from "node:fs";
3
4
  import * as path from "node:path";
@@ -7,11 +8,11 @@ import * as path from "node:path";
7
8
  * Solves virtual module resolution in downstream projects
8
9
  */
9
10
  var VIRTUAL_MODULES = {
10
- "@smrt/routes": "smrt:routes",
11
- "@smrt/client": "smrt:client",
12
- "@smrt/mcp": "smrt:mcp",
13
- "@smrt/types": "smrt:types",
14
- "@smrt/manifest": "smrt:manifest"
11
+ "@smrt/routes": "smrt-consumer:routes",
12
+ "@smrt/client": "smrt-consumer:client",
13
+ "@smrt/mcp": "smrt-consumer:mcp",
14
+ "@smrt/types": "smrt-consumer:types",
15
+ "@smrt/manifest": "smrt-consumer:manifest"
15
16
  };
16
17
  /**
17
18
  * Consumer plugin for projects that use SMRT packages
@@ -62,11 +63,11 @@ function smrtConsumer(options = {}) {
62
63
  objects: {}
63
64
  };
64
65
  switch (cleanId) {
65
- case "smrt:routes": return generateFallbackRoutesModule();
66
- case "smrt:client": return generateFallbackClientModule(typeManifest);
67
- case "smrt:mcp": return generateFallbackMcpModule();
68
- case "smrt:types": return generateFallbackTypesModule(typeManifest);
69
- case "smrt:manifest": return generateFallbackManifestModule(typeManifest);
66
+ case "smrt-consumer:routes": return generateFallbackRoutesModule();
67
+ case "smrt-consumer:client": return generateFallbackClientModule(typeManifest);
68
+ case "smrt-consumer:mcp": return generateFallbackMcpModule();
69
+ case "smrt-consumer:types": return generateFallbackTypesModule(typeManifest);
70
+ case "smrt-consumer:manifest": return generateFallbackManifestModule(typeManifest);
70
71
  default: return null;
71
72
  }
72
73
  }
@@ -368,26 +369,30 @@ export default createClient;
368
369
  `;
369
370
  return `
370
371
  // Auto-generated API client from SMRT consumer
372
+
373
+ ${CLIENT_FETCH_RUNTIME}
374
+
371
375
  export function createClient(basePath = '/api/v1') {
372
376
  return {${objects.map(([name, obj]) => {
373
377
  const { collection } = obj;
374
378
  return `
375
- ${name}: {
376
- list: () => fetch(basePath + '/${collection}').then(r => r.json()),
377
- get: (id) => fetch(basePath + '/${collection}/' + id).then(r => r.json()),
378
- create: (data) => fetch(basePath + '/${collection}', {
379
+ ${JSON.stringify(name)}: {
380
+ list: () => __smrtFetchJson(basePath + '/${collection}', { method: 'GET' }),
381
+ get: (id) => __smrtFetchJson(basePath + '/${collection}/' + id, { method: 'GET' }),
382
+ create: (data) => __smrtFetchJson(basePath + '/${collection}', {
379
383
  method: 'POST',
380
384
  headers: { 'Content-Type': 'application/json' },
381
385
  body: JSON.stringify(data)
382
- }).then(r => r.json()),
383
- update: (id, data) => fetch(basePath + '/${collection}/' + id, {
386
+ }),
387
+ update: (id, data) => __smrtFetchJson(basePath + '/${collection}/' + id, {
384
388
  method: 'PUT',
385
389
  headers: { 'Content-Type': 'application/json' },
386
390
  body: JSON.stringify(data)
387
- }).then(r => r.json()),
388
- delete: (id) => fetch(basePath + '/${collection}/' + id, {
391
+ }),
392
+ delete: (id) => __smrtFetchOk(basePath + '/${collection}/' + id, {
389
393
  method: 'DELETE'
390
- }).then(r => r.ok)
394
+ }),
395
+ search: (query) => __smrtFetchJson(basePath + '/${collection}/search?q=' + encodeURIComponent(query), { method: 'GET' })
391
396
  }`;
392
397
  }).join(",")}
393
398
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/consumer-plugin/index.ts"],"sourcesContent":["/**\n * Vite plugin for consuming SMRT packages\n * Solves virtual module resolution in downstream projects\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Plugin } from 'vite';\nimport { generateDeclarations } from '../prebuild/index.js';\nimport type { SmartObjectManifest } from '../scanner/types.js';\n\n/**\n * Loosely-typed view of an object definition as carried by an external\n * package's static manifest. The static manifests are read from JSON at the\n * package boundary, so only the fields this plugin consumes are typed; the\n * index signature preserves any additional fields (e.g. for spreads). This is\n * a structural superset of a manifest `SmartObjectDefinition` plus the\n * consumer-only `hasCollection` marker.\n */\ninterface ConsumerObjectDefinition {\n className?: string;\n packageName?: string;\n packageVersion?: string;\n qualifiedName?: string;\n importPath?: string;\n exportName?: string;\n collectionExportName?: string;\n hasCollection?: boolean;\n collection?: string;\n extends?: string;\n extendsQualified?: string;\n extendsTypeArg?: string;\n [key: string]: unknown;\n}\n\n/**\n * Aggregated manifest assembled by the consumer plugin from one or more\n * external package manifests. Loosely typed because the inputs originate from\n * JSON read at the package boundary.\n */\ninterface ConsumerManifest {\n version: string;\n timestamp: number;\n packageName?: string;\n packageVersion?: string;\n objects: Record<string, ConsumerObjectDefinition>;\n}\n\n/**\n * Minimal structural shape of a parsed `package.json` consumed here (name,\n * version, and the export map used to derive import paths). The index\n * signature keeps the remaining fields accessible.\n */\ninterface ConsumerPackageJson {\n name?: string;\n version?: string;\n main?: string;\n exports?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface SmrtConsumerOptions {\n /** SMRT packages to scan (e.g., ['@my-org/products', '@my-org/content']) */\n packages?: string[];\n /** Generate TypeScript declarations */\n generateTypes?: boolean;\n /** Output directory for generated types */\n typesDir?: string;\n /** Project root path */\n projectRoot?: string;\n /** SvelteKit integration mode */\n svelteKit?: boolean;\n /** Use static types only (for federation builds) */\n staticTypes?: boolean;\n /** Disable file scanning */\n disableScanning?: boolean;\n}\n\nconst VIRTUAL_MODULES = {\n '@smrt/routes': 'smrt:routes',\n '@smrt/client': 'smrt:client',\n '@smrt/mcp': 'smrt:mcp',\n '@smrt/types': 'smrt:types',\n '@smrt/manifest': 'smrt:manifest',\n};\n\n/**\n * Consumer plugin for projects that use SMRT packages\n */\nexport function smrtConsumer(options: SmrtConsumerOptions = {}): Plugin {\n const {\n packages = [],\n generateTypes = true,\n typesDir = 'src/types/smrt-generated',\n projectRoot = process.cwd(),\n disableScanning = false,\n } = options;\n\n let smrtPackages: string[] = [];\n let typeManifest: ConsumerManifest | null = null;\n let typesGenerated = false;\n\n return {\n name: 'smrt-consumer',\n\n async buildStart() {\n console.log('[smrt:consumer] Initializing SMRT consumer plugin');\n\n // Discover SMRT packages if not explicitly specified\n if (packages.length === 0 && !disableScanning) {\n smrtPackages = await discoverSmrtPackages(projectRoot);\n } else {\n smrtPackages = packages;\n }\n\n if (smrtPackages.length > 0) {\n console.log(\n `[smrt:consumer] Found SMRT packages: ${smrtPackages.join(', ')}`,\n );\n\n // Aggregate type manifests from discovered packages\n typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot);\n\n // Save aggregated manifest for CLI discovery\n await saveAggregatedManifest(typeManifest, projectRoot);\n\n // Generate registration file for CLI class loading\n await generateRegistrationFile(typeManifest, projectRoot);\n\n // Generate types if requested\n if (generateTypes && !typesGenerated) {\n await generateProjectTypes(typeManifest, typesDir, projectRoot);\n typesGenerated = true;\n }\n } else {\n console.log('[smrt:consumer] No SMRT packages found');\n typeManifest = { version: '1.0.0', timestamp: Date.now(), objects: {} };\n }\n },\n\n resolveId(id, _importer) {\n // Resolve virtual modules to generated type declarations\n if (id in VIRTUAL_MODULES) {\n const typeFileName = getTypeFileName(id);\n const typePath = path.join(projectRoot, typesDir, typeFileName);\n\n // If types file exists, resolve to it\n if (fs.existsSync(typePath)) {\n return typePath;\n }\n\n // Otherwise use virtual module ID for runtime resolution\n return `\\0${VIRTUAL_MODULES[id as keyof typeof VIRTUAL_MODULES]}`;\n }\n return null;\n },\n\n async load(id) {\n // Handle virtual modules if types aren't available\n const cleanId = id.startsWith('\\0') ? id.slice(1) : id;\n\n if (!typeManifest) {\n typeManifest = { version: '1.0.0', timestamp: Date.now(), objects: {} };\n }\n\n switch (cleanId) {\n case 'smrt:routes':\n return generateFallbackRoutesModule();\n\n case 'smrt:client':\n return generateFallbackClientModule(typeManifest);\n\n case 'smrt:mcp':\n return generateFallbackMcpModule();\n\n case 'smrt:types':\n return generateFallbackTypesModule(typeManifest);\n\n case 'smrt:manifest':\n return generateFallbackManifestModule(typeManifest);\n\n default:\n return null;\n }\n },\n };\n}\n\n/**\n * Discover SMRT packages from a consumer app's dependencies.\n *\n * Intentional split (#1579): this **consumer-plugin** path is async and\n * resolves SMRT packages from the downstream app's `package.json` dependency\n * names (`@have/`/`smrt` heuristic + `hasSmrtManifest` probe) inside the Vite\n * consumer plugin. It is deliberately separate from the build-time\n * `discoverSmrtPackages()` in `src/manifest/discover-smrt-packages.ts` — a\n * synchronous, lockfile-cached `node_modules` manifest scan used for manifest\n * generation. Different inputs, contexts, and lifecycles, not duplicated logic.\n */\nasync function discoverSmrtPackages(projectRoot: string): Promise<string[]> {\n const packages: string[] = [];\n const nodeModulesPath = path.join(projectRoot, 'node_modules');\n\n if (!fs.existsSync(nodeModulesPath)) {\n return packages;\n }\n\n try {\n // Check package.json for workspace dependencies\n const packageJsonPath = path.join(projectRoot, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n const allDeps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n ...packageJson.peerDependencies,\n };\n\n // Look for packages that likely contain SMRT objects\n for (const [name, version] of Object.entries(allDeps)) {\n if (\n typeof version === 'string' &&\n (name.includes('smrt') ||\n name.includes('@have/') ||\n (await hasSmrtManifest(nodeModulesPath, name)))\n ) {\n packages.push(name);\n }\n }\n }\n } catch (error) {\n console.warn('[smrt:consumer] Error discovering packages:', error);\n }\n\n return packages;\n}\n\n/**\n * Check if a package has SMRT manifest\n */\nasync function hasSmrtManifest(\n nodeModulesPath: string,\n packageName: string,\n): Promise<boolean> {\n const packagePath = path.join(nodeModulesPath, packageName);\n const manifestPath = path.join(\n packagePath,\n 'dist',\n 'manifest',\n 'static-manifest.js',\n );\n return fs.existsSync(manifestPath);\n}\n\n/**\n * Aggregate type manifests from multiple packages\n */\nasync function aggregateTypeManifests(\n packages: string[],\n projectRoot: string,\n): Promise<ConsumerManifest> {\n const aggregatedManifest: ConsumerManifest = {\n version: '1.0.0',\n timestamp: Date.now(),\n objects: {},\n };\n\n for (const packageName of packages) {\n try {\n const packageDir = path.join(projectRoot, 'node_modules', packageName);\n\n // Load package.json for version and export information\n const packageJsonPath = path.join(packageDir, 'package.json');\n let packageJson: ConsumerPackageJson;\n try {\n const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');\n packageJson = JSON.parse(packageJsonContent) as ConsumerPackageJson;\n } catch {\n console.warn(\n `[smrt:consumer] Could not read package.json for ${packageName}`,\n );\n continue;\n }\n\n // Try multiple manifest locations\n const manifestCandidates = [\n path.join(packageDir, 'dist', 'manifest', 'static-manifest.js'),\n path.join(packageDir, 'dist', 'manifest.json'),\n path.join(packageDir, 'manifest.json'),\n ];\n\n for (const manifestPath of manifestCandidates) {\n if (fs.existsSync(manifestPath)) {\n // Import or read the manifest\n let manifest: Partial<ConsumerManifest> | undefined;\n if (manifestPath.endsWith('.js')) {\n const manifestModule = await import(manifestPath);\n manifest = manifestModule.staticManifest || manifestModule.default;\n } else {\n const manifestContent = fs.readFileSync(manifestPath, 'utf-8');\n manifest = JSON.parse(manifestContent) as Partial<ConsumerManifest>;\n }\n\n if (manifest?.objects) {\n console.log(\n `[smrt:consumer] Loaded manifest from ${packageName} (${Object.keys(manifest.objects).length} objects)`,\n );\n\n // ENHANCED: Preserve package metadata for each object\n for (const [objectName, objectDef] of Object.entries(\n manifest.objects,\n )) {\n const def = objectDef;\n\n aggregatedManifest.objects[objectName] = {\n ...def,\n // Ensure package metadata is preserved/set\n packageName:\n def.packageName || manifest.packageName || packageName,\n packageVersion:\n def.packageVersion ||\n manifest.packageVersion ||\n packageJson.version,\n // Add fallback import paths if missing\n importPath: def.importPath || determineImportPath(packageJson),\n exportName: def.exportName || def.className || objectName,\n collectionExportName:\n def.collectionExportName ||\n `${def.className || objectName}Collection`,\n };\n }\n\n break; // Use first found manifest for this package\n }\n }\n }\n } catch (error) {\n console.warn(\n `[smrt:consumer] Error loading manifest from ${packageName}:`,\n error,\n );\n }\n }\n\n return aggregatedManifest;\n}\n\n/**\n * Determine import path from package.json\n */\nfunction determineImportPath(packageJson: ConsumerPackageJson): string {\n const packageName = packageJson.name;\n\n if (!packageName) {\n throw new Error('Package name not found in package.json');\n }\n\n // Strategy 1: Check for specific exports\n if (packageJson.exports) {\n // Check for objects export\n if (packageJson.exports['./objects']) {\n return `${packageName}/objects`;\n }\n\n // Check for main export\n const mainExport = packageJson.exports['.'];\n if (mainExport) {\n // Handle conditional exports\n if (typeof mainExport === 'object' && mainExport !== null) {\n const conditional = mainExport as Record<string, unknown>;\n if (conditional.import) {\n return packageName;\n }\n if (conditional.default) {\n return packageName;\n }\n }\n return packageName;\n }\n }\n\n // Strategy 2: Check main field\n if (packageJson.main) {\n return packageName;\n }\n\n // Strategy 3: Fallback to package name\n return packageName;\n}\n\n/**\n * Save aggregated manifest to .smrt/manifest.json for CLI discovery.\n *\n * Merge-preserving: `smrtPlugin()` writes the project's own scanned objects\n * to the same file (`writeLocalManifest`, issue #963), and both writes happen\n * in parallel `buildStart` hooks — so a plain overwrite here would clobber\n * the local objects whenever this plugin's write lands last (issue #1760\n * review). Local field metadata would then silently vanish from CLI schema\n * commands and from server runtimes that seed `.smrt/manifest.json`, dropping\n * domain columns on write. This function therefore only ADDS/refreshes the\n * external-package entries it owns and preserves everything else already in\n * the file (including the top-level `packageName` the local write sets).\n */\nasync function saveAggregatedManifest(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const manifestPath = path.join(smrtDir, 'manifest.json');\n\n try {\n // Create .smrt directory if it doesn't exist\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Merge with whatever is on disk: existing entries (typically the local\n // project's objects written by smrtPlugin) are preserved; aggregated\n // external entries win for the qualified names this plugin owns.\n let merged: ConsumerManifest = manifest;\n if (fs.existsSync(manifestPath)) {\n try {\n const existing = JSON.parse(\n fs.readFileSync(manifestPath, 'utf-8'),\n ) as Partial<ConsumerManifest>;\n if (existing && typeof existing.objects === 'object') {\n merged = {\n ...existing,\n ...manifest,\n // The aggregated manifest carries no packageName; keep the local\n // project's (used as the manifest cache key at runtime).\n ...(existing.packageName\n ? { packageName: existing.packageName }\n : {}),\n objects: { ...existing.objects, ...manifest.objects },\n };\n }\n } catch {\n // Unreadable/corrupt existing file — fall back to a plain write.\n }\n }\n\n // Write manifest\n fs.writeFileSync(manifestPath, JSON.stringify(merged, null, 2), 'utf-8');\n\n console.log(\n `[smrt:consumer] Saved aggregated manifest to .smrt/manifest.json (${Object.keys(merged.objects).length} objects)`,\n );\n } catch (error) {\n console.warn('[smrt:consumer] Failed to save aggregated manifest:', error);\n }\n}\n\n/**\n * Generate registration file for CLI class loading\n *\n * Creates .smrt/register.js with static imports and registrations\n * for all external SMRT objects discovered during build.\n */\nasync function generateRegistrationFile(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const registerPath = path.join(smrtDir, 'register.js');\n\n // Build import statements and registrations\n const imports: string[] = [];\n const registrations: string[] = [];\n let importedEntryCount = 0;\n let registeredObjectCount = 0;\n\n const manifestObjects = manifest.objects;\n const manifestObjectLookup = new Map<string, ConsumerObjectDefinition>();\n for (const [key, def] of Object.entries(manifestObjects)) {\n const candidate = def;\n const lookupKeys = [\n key,\n key.includes(':') ? key.split(':').pop() : undefined,\n candidate.qualifiedName,\n candidate.className,\n candidate.exportName,\n ];\n\n for (const lookupKey of lookupKeys) {\n if (lookupKey && !manifestObjectLookup.has(lookupKey)) {\n manifestObjectLookup.set(lookupKey, candidate);\n }\n }\n }\n\n const collectionClassMemo = new WeakMap<object, boolean>();\n\n const isCollectionClass = (\n def: ConsumerObjectDefinition | undefined,\n seen = new Set<string>(),\n ): boolean => {\n if (!def || typeof def !== 'object') {\n return false;\n }\n\n const cached = collectionClassMemo.get(def);\n if (cached !== undefined) {\n return cached;\n }\n\n if (\n def?.extends === 'SmrtCollection' ||\n def?.extendsTypeArg !== undefined\n ) {\n collectionClassMemo.set(def, true);\n return true;\n }\n\n const parentName = def?.extendsQualified || def?.extends;\n if (!parentName || seen.has(parentName)) {\n collectionClassMemo.set(def, false);\n return false;\n }\n seen.add(parentName);\n\n const parentDef = manifestObjectLookup.get(parentName);\n const isCollection = parentDef ? isCollectionClass(parentDef, seen) : false;\n collectionClassMemo.set(def, isCollection);\n\n return isCollection;\n };\n\n for (const [objectName, objectDef] of Object.entries(manifestObjects)) {\n const def = objectDef;\n\n // Skip local objects (they're imported from local entry point)\n if (!def.packageName || def.packageName === manifest.packageName) {\n continue;\n }\n\n const importPath = def.importPath || def.packageName;\n const exportName = def.exportName || def.className || objectName;\n const collectionExportName = def.collectionExportName;\n const hasCollection = def.hasCollection; // Check if collection class actually exists\n const tableName = def.collection || objectName.toLowerCase();\n\n // Generate import statement\n // Only import collection if it exists (hasCollection is truthy)\n if (hasCollection && collectionExportName) {\n imports.push(\n `import { ${exportName}, ${collectionExportName} } from '${importPath}';`,\n );\n } else {\n imports.push(`import { ${exportName} } from '${importPath}';`);\n }\n importedEntryCount++;\n\n if (isCollectionClass(def)) {\n continue;\n }\n\n // Generate registration calls\n // The import above already triggers the @smrt() decorator which registers the class\n // properly with its simple name and qualified name. We call register() again with\n // an empty config just to ensure the class is registered (in case it lacks a decorator).\n // Do NOT pass { name: qualifiedName } as that creates a separate registry entry.\n registrations.push(\n `ObjectRegistry.register(${exportName}, { name: ${JSON.stringify(exportName)}, packageName: ${JSON.stringify(def.packageName)} });`,\n );\n\n // Only register collection if it exists\n if (hasCollection && collectionExportName) {\n registrations.push(\n `ObjectRegistry.registerCollection('${tableName}', ${collectionExportName});`,\n );\n }\n\n registeredObjectCount++;\n }\n\n // Skip generation if no external entries\n if (importedEntryCount === 0) {\n console.log('[smrt:consumer] No external entries - skipping register.js');\n return;\n }\n\n const registeredObjectLabel =\n registeredObjectCount === 1 ? 'object' : 'objects';\n\n // Generate file content\n const content = `/**\n * Auto-generated by @happyvertical/smrt-core/consumer-plugin\n * DO NOT EDIT - This file is regenerated on every build\n *\n * Registers SMRT objects from external packages for CLI discovery.\n * Generated at: ${new Date().toISOString()}\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n${imports.join('\\n')}\n\n// Register all objects (executed during module evaluation)\n${registrations.join('\\n')}\n\nexport function registerAll() {\n // Objects are already registered during module evaluation\n console.log('[smrt:register] Registered ${registeredObjectCount} external ${registeredObjectLabel}');\n}\n`;\n\n // Create .smrt directory if needed\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Write registration file\n fs.writeFileSync(registerPath, content, 'utf-8');\n\n console.log(\n `[smrt:consumer] Generated .smrt/register.js with ${importedEntryCount} external entries (${registeredObjectCount} registered ${registeredObjectLabel})`,\n );\n}\n\n/**\n * Generate project-specific types\n */\nasync function generateProjectTypes(\n typeManifest: ConsumerManifest,\n typesDir: string,\n projectRoot: string,\n): Promise<void> {\n if (!typeManifest || Object.keys(typeManifest.objects).length === 0) {\n console.log(\n '[smrt:consumer] No SMRT objects found, skipping type generation',\n );\n return;\n }\n\n await generateDeclarations({\n // The aggregated manifest is a runtime SMRT manifest assembled from external\n // package manifests; it is intentionally typed loosely at the JSON boundary,\n // so narrow it to the declaration generator's strict manifest shape here.\n manifest: typeManifest as unknown as SmartObjectManifest,\n outDir: typesDir,\n projectRoot,\n includeVirtualModules: true,\n includeObjectTypes: true,\n });\n\n console.log(\n `[smrt:consumer] Generated types for ${Object.keys(typeManifest.objects).length} objects`,\n );\n}\n\n/**\n * Get type file name for virtual module\n */\nfunction getTypeFileName(virtualModule: string): string {\n const moduleMap: Record<string, string> = {\n '@smrt/routes': 'smrt-routes.d.ts',\n '@smrt/client': 'smrt-client.d.ts',\n '@smrt/mcp': 'smrt-mcp.d.ts',\n '@smrt/types': 'smrt-types.d.ts',\n '@smrt/manifest': 'smrt-manifest.d.ts',\n };\n return moduleMap[virtualModule] || 'smrt-unknown.d.ts';\n}\n\n/**\n * Fallback modules for when types aren't available\n */\nfunction generateFallbackRoutesModule(): string {\n return `\n// Fallback routes module\nexport function setupRoutes(app) {\n console.warn('[smrt:consumer] No routes available - SMRT packages may not be properly configured');\n}\nexport default setupRoutes;\n`;\n}\n\nfunction generateFallbackClientModule(manifest: ConsumerManifest): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `\n// Fallback client module\nexport function createClient(basePath = '/api/v1') {\n console.warn('[smrt:consumer] No API client available - SMRT packages may not be properly configured');\n return {};\n}\nexport default createClient;\n`;\n }\n\n // Generate basic client from manifest\n const clientMethods = objects\n .map(([name, obj]) => {\n const { collection } = obj;\n return `\n ${name}: {\n list: () => fetch(basePath + '/${collection}').then(r => r.json()),\n get: (id) => fetch(basePath + '/${collection}/' + id).then(r => r.json()),\n create: (data) => fetch(basePath + '/${collection}', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }).then(r => r.json()),\n update: (id, data) => fetch(basePath + '/${collection}/' + id, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }).then(r => r.json()),\n delete: (id) => fetch(basePath + '/${collection}/' + id, {\n method: 'DELETE'\n }).then(r => r.ok)\n }`;\n })\n .join(',');\n\n return `\n// Auto-generated API client from SMRT consumer\nexport function createClient(basePath = '/api/v1') {\n return {${clientMethods}\n };\n}\nexport default createClient;\n`;\n}\n\nfunction generateFallbackMcpModule(): string {\n return `\n// Fallback MCP module\nexport const tools = [];\nexport function createMCPServer() {\n console.warn('[smrt:consumer] No MCP tools available - SMRT packages may not be properly configured');\n return { name: 'smrt-consumer', version: '1.0.0', tools: [] };\n}\nexport default createMCPServer;\n`;\n}\n\nfunction generateFallbackTypesModule(manifest: ConsumerManifest): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `// No types available`;\n }\n\n // Generate basic interfaces\n const interfaces = objects.map(([_name, obj]) => {\n return `export interface ${obj.className}Data {\n id?: string;\n created_at?: string;\n updated_at?: string;\n [key: string]: any;\n}`;\n });\n\n return interfaces.join('\\n\\n');\n}\n\nfunction generateFallbackManifestModule(manifest: ConsumerManifest): string {\n return `\n// Auto-generated manifest from SMRT consumer\nexport const manifest = ${JSON.stringify(manifest, null, 2)};\nexport default manifest;\n`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/consumer-plugin/index.ts"],"sourcesContent":["/**\n * Vite plugin for consuming SMRT packages\n * Solves virtual module resolution in downstream projects\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Plugin } from 'vite';\nimport { CLIENT_FETCH_RUNTIME } from '../generated-client-runtime.js';\nimport { generateDeclarations } from '../prebuild/index.js';\nimport type { SmartObjectManifest } from '../scanner/types.js';\n\n/**\n * Loosely-typed view of an object definition as carried by an external\n * package's static manifest. The static manifests are read from JSON at the\n * package boundary, so only the fields this plugin consumes are typed; the\n * index signature preserves any additional fields (e.g. for spreads). This is\n * a structural superset of a manifest `SmartObjectDefinition` plus the\n * consumer-only `hasCollection` marker.\n */\ninterface ConsumerObjectDefinition {\n className?: string;\n packageName?: string;\n packageVersion?: string;\n qualifiedName?: string;\n importPath?: string;\n exportName?: string;\n collectionExportName?: string;\n hasCollection?: boolean;\n collection?: string;\n extends?: string;\n extendsQualified?: string;\n extendsTypeArg?: string;\n [key: string]: unknown;\n}\n\n/**\n * Aggregated manifest assembled by the consumer plugin from one or more\n * external package manifests. Loosely typed because the inputs originate from\n * JSON read at the package boundary.\n */\ninterface ConsumerManifest {\n version: string;\n timestamp: number;\n packageName?: string;\n packageVersion?: string;\n objects: Record<string, ConsumerObjectDefinition>;\n}\n\n/**\n * Minimal structural shape of a parsed `package.json` consumed here (name,\n * version, and the export map used to derive import paths). The index\n * signature keeps the remaining fields accessible.\n */\ninterface ConsumerPackageJson {\n name?: string;\n version?: string;\n main?: string;\n exports?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface SmrtConsumerOptions {\n /** SMRT packages to scan (e.g., ['@my-org/products', '@my-org/content']) */\n packages?: string[];\n /** Generate TypeScript declarations */\n generateTypes?: boolean;\n /** Output directory for generated types */\n typesDir?: string;\n /** Project root path */\n projectRoot?: string;\n /** SvelteKit integration mode */\n svelteKit?: boolean;\n /** Use static types only (for federation builds) */\n staticTypes?: boolean;\n /** Disable file scanning */\n disableScanning?: boolean;\n}\n\n// Distinct resolved ids per plugin (#1795). smrtPlugin resolves\n// `@happyvertical/smrt-virt-*` to `\\0smrt:*`; if this consumer plugin also\n// resolved its `@smrt/*` specifiers to `\\0smrt:*` the two virtual modules would\n// share a rollup id, and in standalone/federation builds the consumer's\n// fallback `load` would non-deterministically win and shadow smrtPlugin's real\n// module. Namespacing the consumer ids (`\\0smrt-consumer:*`) keeps them\n// separate so each plugin only ever loads its own module.\nconst VIRTUAL_MODULES = {\n '@smrt/routes': 'smrt-consumer:routes',\n '@smrt/client': 'smrt-consumer:client',\n '@smrt/mcp': 'smrt-consumer:mcp',\n '@smrt/types': 'smrt-consumer:types',\n '@smrt/manifest': 'smrt-consumer:manifest',\n};\n\n/**\n * Consumer plugin for projects that use SMRT packages\n */\nexport function smrtConsumer(options: SmrtConsumerOptions = {}): Plugin {\n const {\n packages = [],\n generateTypes = true,\n typesDir = 'src/types/smrt-generated',\n projectRoot = process.cwd(),\n disableScanning = false,\n } = options;\n\n let smrtPackages: string[] = [];\n let typeManifest: ConsumerManifest | null = null;\n let typesGenerated = false;\n\n return {\n name: 'smrt-consumer',\n\n async buildStart() {\n console.log('[smrt:consumer] Initializing SMRT consumer plugin');\n\n // Discover SMRT packages if not explicitly specified\n if (packages.length === 0 && !disableScanning) {\n smrtPackages = await discoverSmrtPackages(projectRoot);\n } else {\n smrtPackages = packages;\n }\n\n if (smrtPackages.length > 0) {\n console.log(\n `[smrt:consumer] Found SMRT packages: ${smrtPackages.join(', ')}`,\n );\n\n // Aggregate type manifests from discovered packages\n typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot);\n\n // Save aggregated manifest for CLI discovery\n await saveAggregatedManifest(typeManifest, projectRoot);\n\n // Generate registration file for CLI class loading\n await generateRegistrationFile(typeManifest, projectRoot);\n\n // Generate types if requested\n if (generateTypes && !typesGenerated) {\n await generateProjectTypes(typeManifest, typesDir, projectRoot);\n typesGenerated = true;\n }\n } else {\n console.log('[smrt:consumer] No SMRT packages found');\n typeManifest = { version: '1.0.0', timestamp: Date.now(), objects: {} };\n }\n },\n\n resolveId(id, _importer) {\n // Resolve virtual modules to generated type declarations\n if (id in VIRTUAL_MODULES) {\n const typeFileName = getTypeFileName(id);\n const typePath = path.join(projectRoot, typesDir, typeFileName);\n\n // If types file exists, resolve to it\n if (fs.existsSync(typePath)) {\n return typePath;\n }\n\n // Otherwise use virtual module ID for runtime resolution\n return `\\0${VIRTUAL_MODULES[id as keyof typeof VIRTUAL_MODULES]}`;\n }\n return null;\n },\n\n async load(id) {\n // Handle virtual modules if types aren't available\n const cleanId = id.startsWith('\\0') ? id.slice(1) : id;\n\n if (!typeManifest) {\n typeManifest = { version: '1.0.0', timestamp: Date.now(), objects: {} };\n }\n\n switch (cleanId) {\n case 'smrt-consumer:routes':\n return generateFallbackRoutesModule();\n\n case 'smrt-consumer:client':\n return generateFallbackClientModule(typeManifest);\n\n case 'smrt-consumer:mcp':\n return generateFallbackMcpModule();\n\n case 'smrt-consumer:types':\n return generateFallbackTypesModule(typeManifest);\n\n case 'smrt-consumer:manifest':\n return generateFallbackManifestModule(typeManifest);\n\n default:\n return null;\n }\n },\n };\n}\n\n/**\n * Discover SMRT packages from a consumer app's dependencies.\n *\n * Intentional split (#1579): this **consumer-plugin** path is async and\n * resolves SMRT packages from the downstream app's `package.json` dependency\n * names (`@have/`/`smrt` heuristic + `hasSmrtManifest` probe) inside the Vite\n * consumer plugin. It is deliberately separate from the build-time\n * `discoverSmrtPackages()` in `src/manifest/discover-smrt-packages.ts` — a\n * synchronous, lockfile-cached `node_modules` manifest scan used for manifest\n * generation. Different inputs, contexts, and lifecycles, not duplicated logic.\n */\nasync function discoverSmrtPackages(projectRoot: string): Promise<string[]> {\n const packages: string[] = [];\n const nodeModulesPath = path.join(projectRoot, 'node_modules');\n\n if (!fs.existsSync(nodeModulesPath)) {\n return packages;\n }\n\n try {\n // Check package.json for workspace dependencies\n const packageJsonPath = path.join(projectRoot, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n const allDeps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n ...packageJson.peerDependencies,\n };\n\n // Look for packages that likely contain SMRT objects\n for (const [name, version] of Object.entries(allDeps)) {\n if (\n typeof version === 'string' &&\n (name.includes('smrt') ||\n name.includes('@have/') ||\n (await hasSmrtManifest(nodeModulesPath, name)))\n ) {\n packages.push(name);\n }\n }\n }\n } catch (error) {\n console.warn('[smrt:consumer] Error discovering packages:', error);\n }\n\n return packages;\n}\n\n/**\n * Check if a package has SMRT manifest\n */\nasync function hasSmrtManifest(\n nodeModulesPath: string,\n packageName: string,\n): Promise<boolean> {\n const packagePath = path.join(nodeModulesPath, packageName);\n const manifestPath = path.join(\n packagePath,\n 'dist',\n 'manifest',\n 'static-manifest.js',\n );\n return fs.existsSync(manifestPath);\n}\n\n/**\n * Aggregate type manifests from multiple packages\n */\nasync function aggregateTypeManifests(\n packages: string[],\n projectRoot: string,\n): Promise<ConsumerManifest> {\n const aggregatedManifest: ConsumerManifest = {\n version: '1.0.0',\n timestamp: Date.now(),\n objects: {},\n };\n\n for (const packageName of packages) {\n try {\n const packageDir = path.join(projectRoot, 'node_modules', packageName);\n\n // Load package.json for version and export information\n const packageJsonPath = path.join(packageDir, 'package.json');\n let packageJson: ConsumerPackageJson;\n try {\n const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');\n packageJson = JSON.parse(packageJsonContent) as ConsumerPackageJson;\n } catch {\n console.warn(\n `[smrt:consumer] Could not read package.json for ${packageName}`,\n );\n continue;\n }\n\n // Try multiple manifest locations\n const manifestCandidates = [\n path.join(packageDir, 'dist', 'manifest', 'static-manifest.js'),\n path.join(packageDir, 'dist', 'manifest.json'),\n path.join(packageDir, 'manifest.json'),\n ];\n\n for (const manifestPath of manifestCandidates) {\n if (fs.existsSync(manifestPath)) {\n // Import or read the manifest\n let manifest: Partial<ConsumerManifest> | undefined;\n if (manifestPath.endsWith('.js')) {\n const manifestModule = await import(manifestPath);\n manifest = manifestModule.staticManifest || manifestModule.default;\n } else {\n const manifestContent = fs.readFileSync(manifestPath, 'utf-8');\n manifest = JSON.parse(manifestContent) as Partial<ConsumerManifest>;\n }\n\n if (manifest?.objects) {\n console.log(\n `[smrt:consumer] Loaded manifest from ${packageName} (${Object.keys(manifest.objects).length} objects)`,\n );\n\n // ENHANCED: Preserve package metadata for each object\n for (const [objectName, objectDef] of Object.entries(\n manifest.objects,\n )) {\n const def = objectDef;\n\n aggregatedManifest.objects[objectName] = {\n ...def,\n // Ensure package metadata is preserved/set\n packageName:\n def.packageName || manifest.packageName || packageName,\n packageVersion:\n def.packageVersion ||\n manifest.packageVersion ||\n packageJson.version,\n // Add fallback import paths if missing\n importPath: def.importPath || determineImportPath(packageJson),\n exportName: def.exportName || def.className || objectName,\n collectionExportName:\n def.collectionExportName ||\n `${def.className || objectName}Collection`,\n };\n }\n\n break; // Use first found manifest for this package\n }\n }\n }\n } catch (error) {\n console.warn(\n `[smrt:consumer] Error loading manifest from ${packageName}:`,\n error,\n );\n }\n }\n\n return aggregatedManifest;\n}\n\n/**\n * Determine import path from package.json\n */\nfunction determineImportPath(packageJson: ConsumerPackageJson): string {\n const packageName = packageJson.name;\n\n if (!packageName) {\n throw new Error('Package name not found in package.json');\n }\n\n // Strategy 1: Check for specific exports\n if (packageJson.exports) {\n // Check for objects export\n if (packageJson.exports['./objects']) {\n return `${packageName}/objects`;\n }\n\n // Check for main export\n const mainExport = packageJson.exports['.'];\n if (mainExport) {\n // Handle conditional exports\n if (typeof mainExport === 'object' && mainExport !== null) {\n const conditional = mainExport as Record<string, unknown>;\n if (conditional.import) {\n return packageName;\n }\n if (conditional.default) {\n return packageName;\n }\n }\n return packageName;\n }\n }\n\n // Strategy 2: Check main field\n if (packageJson.main) {\n return packageName;\n }\n\n // Strategy 3: Fallback to package name\n return packageName;\n}\n\n/**\n * Save aggregated manifest to .smrt/manifest.json for CLI discovery.\n *\n * Merge-preserving: `smrtPlugin()` writes the project's own scanned objects\n * to the same file (`writeLocalManifest`, issue #963), and both writes happen\n * in parallel `buildStart` hooks — so a plain overwrite here would clobber\n * the local objects whenever this plugin's write lands last (issue #1760\n * review). Local field metadata would then silently vanish from CLI schema\n * commands and from server runtimes that seed `.smrt/manifest.json`, dropping\n * domain columns on write. This function therefore only ADDS/refreshes the\n * external-package entries it owns and preserves everything else already in\n * the file (including the top-level `packageName` the local write sets).\n */\nasync function saveAggregatedManifest(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const manifestPath = path.join(smrtDir, 'manifest.json');\n\n try {\n // Create .smrt directory if it doesn't exist\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Merge with whatever is on disk: existing entries (typically the local\n // project's objects written by smrtPlugin) are preserved; aggregated\n // external entries win for the qualified names this plugin owns.\n let merged: ConsumerManifest = manifest;\n if (fs.existsSync(manifestPath)) {\n try {\n const existing = JSON.parse(\n fs.readFileSync(manifestPath, 'utf-8'),\n ) as Partial<ConsumerManifest>;\n if (existing && typeof existing.objects === 'object') {\n merged = {\n ...existing,\n ...manifest,\n // The aggregated manifest carries no packageName; keep the local\n // project's (used as the manifest cache key at runtime).\n ...(existing.packageName\n ? { packageName: existing.packageName }\n : {}),\n objects: { ...existing.objects, ...manifest.objects },\n };\n }\n } catch {\n // Unreadable/corrupt existing file — fall back to a plain write.\n }\n }\n\n // Write manifest\n fs.writeFileSync(manifestPath, JSON.stringify(merged, null, 2), 'utf-8');\n\n console.log(\n `[smrt:consumer] Saved aggregated manifest to .smrt/manifest.json (${Object.keys(merged.objects).length} objects)`,\n );\n } catch (error) {\n console.warn('[smrt:consumer] Failed to save aggregated manifest:', error);\n }\n}\n\n/**\n * Generate registration file for CLI class loading\n *\n * Creates .smrt/register.js with static imports and registrations\n * for all external SMRT objects discovered during build.\n */\nasync function generateRegistrationFile(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const registerPath = path.join(smrtDir, 'register.js');\n\n // Build import statements and registrations\n const imports: string[] = [];\n const registrations: string[] = [];\n let importedEntryCount = 0;\n let registeredObjectCount = 0;\n\n const manifestObjects = manifest.objects;\n const manifestObjectLookup = new Map<string, ConsumerObjectDefinition>();\n for (const [key, def] of Object.entries(manifestObjects)) {\n const candidate = def;\n const lookupKeys = [\n key,\n key.includes(':') ? key.split(':').pop() : undefined,\n candidate.qualifiedName,\n candidate.className,\n candidate.exportName,\n ];\n\n for (const lookupKey of lookupKeys) {\n if (lookupKey && !manifestObjectLookup.has(lookupKey)) {\n manifestObjectLookup.set(lookupKey, candidate);\n }\n }\n }\n\n const collectionClassMemo = new WeakMap<object, boolean>();\n\n const isCollectionClass = (\n def: ConsumerObjectDefinition | undefined,\n seen = new Set<string>(),\n ): boolean => {\n if (!def || typeof def !== 'object') {\n return false;\n }\n\n const cached = collectionClassMemo.get(def);\n if (cached !== undefined) {\n return cached;\n }\n\n if (\n def?.extends === 'SmrtCollection' ||\n def?.extendsTypeArg !== undefined\n ) {\n collectionClassMemo.set(def, true);\n return true;\n }\n\n const parentName = def?.extendsQualified || def?.extends;\n if (!parentName || seen.has(parentName)) {\n collectionClassMemo.set(def, false);\n return false;\n }\n seen.add(parentName);\n\n const parentDef = manifestObjectLookup.get(parentName);\n const isCollection = parentDef ? isCollectionClass(parentDef, seen) : false;\n collectionClassMemo.set(def, isCollection);\n\n return isCollection;\n };\n\n for (const [objectName, objectDef] of Object.entries(manifestObjects)) {\n const def = objectDef;\n\n // Skip local objects (they're imported from local entry point)\n if (!def.packageName || def.packageName === manifest.packageName) {\n continue;\n }\n\n const importPath = def.importPath || def.packageName;\n const exportName = def.exportName || def.className || objectName;\n const collectionExportName = def.collectionExportName;\n const hasCollection = def.hasCollection; // Check if collection class actually exists\n const tableName = def.collection || objectName.toLowerCase();\n\n // Generate import statement\n // Only import collection if it exists (hasCollection is truthy)\n if (hasCollection && collectionExportName) {\n imports.push(\n `import { ${exportName}, ${collectionExportName} } from '${importPath}';`,\n );\n } else {\n imports.push(`import { ${exportName} } from '${importPath}';`);\n }\n importedEntryCount++;\n\n if (isCollectionClass(def)) {\n continue;\n }\n\n // Generate registration calls\n // The import above already triggers the @smrt() decorator which registers the class\n // properly with its simple name and qualified name. We call register() again with\n // an empty config just to ensure the class is registered (in case it lacks a decorator).\n // Do NOT pass { name: qualifiedName } as that creates a separate registry entry.\n registrations.push(\n `ObjectRegistry.register(${exportName}, { name: ${JSON.stringify(exportName)}, packageName: ${JSON.stringify(def.packageName)} });`,\n );\n\n // Only register collection if it exists\n if (hasCollection && collectionExportName) {\n registrations.push(\n `ObjectRegistry.registerCollection('${tableName}', ${collectionExportName});`,\n );\n }\n\n registeredObjectCount++;\n }\n\n // Skip generation if no external entries\n if (importedEntryCount === 0) {\n console.log('[smrt:consumer] No external entries - skipping register.js');\n return;\n }\n\n const registeredObjectLabel =\n registeredObjectCount === 1 ? 'object' : 'objects';\n\n // Generate file content\n const content = `/**\n * Auto-generated by @happyvertical/smrt-core/consumer-plugin\n * DO NOT EDIT - This file is regenerated on every build\n *\n * Registers SMRT objects from external packages for CLI discovery.\n * Generated at: ${new Date().toISOString()}\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n${imports.join('\\n')}\n\n// Register all objects (executed during module evaluation)\n${registrations.join('\\n')}\n\nexport function registerAll() {\n // Objects are already registered during module evaluation\n console.log('[smrt:register] Registered ${registeredObjectCount} external ${registeredObjectLabel}');\n}\n`;\n\n // Create .smrt directory if needed\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Write registration file\n fs.writeFileSync(registerPath, content, 'utf-8');\n\n console.log(\n `[smrt:consumer] Generated .smrt/register.js with ${importedEntryCount} external entries (${registeredObjectCount} registered ${registeredObjectLabel})`,\n );\n}\n\n/**\n * Generate project-specific types\n */\nasync function generateProjectTypes(\n typeManifest: ConsumerManifest,\n typesDir: string,\n projectRoot: string,\n): Promise<void> {\n if (!typeManifest || Object.keys(typeManifest.objects).length === 0) {\n console.log(\n '[smrt:consumer] No SMRT objects found, skipping type generation',\n );\n return;\n }\n\n await generateDeclarations({\n // The aggregated manifest is a runtime SMRT manifest assembled from external\n // package manifests; it is intentionally typed loosely at the JSON boundary,\n // so narrow it to the declaration generator's strict manifest shape here.\n manifest: typeManifest as unknown as SmartObjectManifest,\n outDir: typesDir,\n projectRoot,\n includeVirtualModules: true,\n includeObjectTypes: true,\n });\n\n console.log(\n `[smrt:consumer] Generated types for ${Object.keys(typeManifest.objects).length} objects`,\n );\n}\n\n/**\n * Get type file name for virtual module\n */\nfunction getTypeFileName(virtualModule: string): string {\n const moduleMap: Record<string, string> = {\n '@smrt/routes': 'smrt-routes.d.ts',\n '@smrt/client': 'smrt-client.d.ts',\n '@smrt/mcp': 'smrt-mcp.d.ts',\n '@smrt/types': 'smrt-types.d.ts',\n '@smrt/manifest': 'smrt-manifest.d.ts',\n };\n return moduleMap[virtualModule] || 'smrt-unknown.d.ts';\n}\n\n/**\n * Fallback modules for when types aren't available\n */\nfunction generateFallbackRoutesModule(): string {\n return `\n// Fallback routes module\nexport function setupRoutes(app) {\n console.warn('[smrt:consumer] No routes available - SMRT packages may not be properly configured');\n}\nexport default setupRoutes;\n`;\n}\n\nfunction generateFallbackClientModule(manifest: ConsumerManifest): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `\n// Fallback client module\nexport function createClient(basePath = '/api/v1') {\n console.warn('[smrt:consumer] No API client available - SMRT packages may not be properly configured');\n return {};\n}\nexport default createClient;\n`;\n }\n\n // Generate basic client from manifest.\n //\n // Object keys are QUOTED via JSON.stringify (#1795): a manifest key can be a\n // package-qualified name like `@happyvertical/smrt-assets:AssetAssociation`,\n // which is not a valid bare object-literal key and produced a build-breaking\n // syntax error in the emitted module. Fetchers reject on !response.ok (#1796)\n // via the shared __smrtFetchJson/__smrtFetchOk runtime.\n const clientMethods = objects\n .map(([name, obj]) => {\n const { collection } = obj;\n return `\n ${JSON.stringify(name)}: {\n list: () => __smrtFetchJson(basePath + '/${collection}', { method: 'GET' }),\n get: (id) => __smrtFetchJson(basePath + '/${collection}/' + id, { method: 'GET' }),\n create: (data) => __smrtFetchJson(basePath + '/${collection}', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }),\n update: (id, data) => __smrtFetchJson(basePath + '/${collection}/' + id, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data)\n }),\n delete: (id) => __smrtFetchOk(basePath + '/${collection}/' + id, {\n method: 'DELETE'\n }),\n search: (query) => __smrtFetchJson(basePath + '/${collection}/search?q=' + encodeURIComponent(query), { method: 'GET' })\n }`;\n })\n .join(',');\n\n return `\n// Auto-generated API client from SMRT consumer\n\n${CLIENT_FETCH_RUNTIME}\n\nexport function createClient(basePath = '/api/v1') {\n return {${clientMethods}\n };\n}\nexport default createClient;\n`;\n}\n\nfunction generateFallbackMcpModule(): string {\n return `\n// Fallback MCP module\nexport const tools = [];\nexport function createMCPServer() {\n console.warn('[smrt:consumer] No MCP tools available - SMRT packages may not be properly configured');\n return { name: 'smrt-consumer', version: '1.0.0', tools: [] };\n}\nexport default createMCPServer;\n`;\n}\n\nfunction generateFallbackTypesModule(manifest: ConsumerManifest): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `// No types available`;\n }\n\n // Generate basic interfaces\n const interfaces = objects.map(([_name, obj]) => {\n return `export interface ${obj.className}Data {\n id?: string;\n created_at?: string;\n updated_at?: string;\n [key: string]: any;\n}`;\n });\n\n return interfaces.join('\\n\\n');\n}\n\nfunction generateFallbackManifestModule(manifest: ConsumerManifest): string {\n return `\n// Auto-generated manifest from SMRT consumer\nexport const manifest = ${JSON.stringify(manifest, null, 2)};\nexport default manifest;\n`;\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { ensureDispatchSubscriptionsSystemTableCompatibility, ensureDispatchSystemTableCompatibility } from "../system/compatibility.js";
1
+ import { resolveDispatchTenantId, resolveDispatchTenantScope } from "./tenant-resolver.js";
2
2
  import { CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE, CREATE_SMRT_DISPATCH_TABLE } from "../system/schema.js";
3
+ import { ensureDispatchSubscriptionsSystemTableCompatibility, ensureDispatchSystemTableCompatibility } from "../system/compatibility.js";
3
4
  import { Dispatch } from "./models/Dispatch.js";
4
5
  import { DispatchCollection } from "./collections/Dispatches.js";
5
6
  import { DispatchSubscription } from "./models/DispatchSubscription.js";
6
7
  import { DispatchSubscriptionCollection } from "./collections/DispatchSubscriptions.js";
7
- import { resolveDispatchTenantId, resolveDispatchTenantScope } from "./tenant-resolver.js";
8
8
  import { createLogger } from "@happyvertical/logger";
9
9
  import { getDatabase } from "@happyvertical/sql";
10
10
  //#region src/dispatch/bus.ts
@@ -1,7 +1,7 @@
1
+ import { isTenantScopedClassResolved, resolveDispatchTenantId, resolveDispatchTenantScope, setDispatchTenantResolver, setTenantScopedClassResolver } from "./tenant-resolver.js";
1
2
  import { Dispatch } from "./models/Dispatch.js";
2
3
  import { DispatchCollection } from "./collections/Dispatches.js";
3
4
  import { DispatchSubscription } from "./models/DispatchSubscription.js";
4
5
  import { DispatchSubscriptionCollection } from "./collections/DispatchSubscriptions.js";
5
- import { isTenantScopedClassResolved, resolveDispatchTenantId, resolveDispatchTenantScope, setDispatchTenantResolver, setTenantScopedClassResolver } from "./tenant-resolver.js";
6
6
  import { DispatchBus, createDispatchBus } from "./bus.js";
7
7
  export { Dispatch, DispatchBus, DispatchCollection, DispatchSubscription, DispatchSubscriptionCollection, createDispatchBus, isTenantScopedClassResolved, resolveDispatchTenantId, resolveDispatchTenantScope, setDispatchTenantResolver, setTenantScopedClassResolver };