@happyvertical/smrt-core 0.40.15 → 0.40.17

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 (96) hide show
  1. package/AGENTS.md +5 -0
  2. package/dist/browser.js +2 -2
  3. package/dist/change-feed.d.ts.map +1 -1
  4. package/dist/change-feed.js +21 -4
  5. package/dist/change-feed.js.map +1 -1
  6. package/dist/class.d.ts.map +1 -1
  7. package/dist/class.js +5 -3
  8. package/dist/class.js.map +1 -1
  9. package/dist/collection.d.ts +10 -0
  10. package/dist/collection.d.ts.map +1 -1
  11. package/dist/collection.js +16 -2
  12. package/dist/collection.js.map +1 -1
  13. package/dist/dispatch/bus.d.ts.map +1 -1
  14. package/dist/dispatch/bus.js +11 -6
  15. package/dist/dispatch/bus.js.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/manifest/static-manifest.js +2 -2
  18. package/dist/manifest/static-manifest.js.map +1 -1
  19. package/dist/manifest/store.js +1 -1
  20. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  21. package/dist/manifest/test-manifest-stub.js +384 -2
  22. package/dist/manifest/test-manifest-stub.js.map +1 -1
  23. package/dist/manifest.json +2 -2
  24. package/dist/migrations/backfill-tracker.d.ts +1 -0
  25. package/dist/migrations/backfill-tracker.d.ts.map +1 -1
  26. package/dist/migrations/backfill-tracker.js +9 -3
  27. package/dist/migrations/backfill-tracker.js.map +1 -1
  28. package/dist/migrations/differ.d.ts +18 -0
  29. package/dist/migrations/differ.d.ts.map +1 -1
  30. package/dist/migrations/differ.js +28 -5
  31. package/dist/migrations/differ.js.map +1 -1
  32. package/dist/migrations/generator.d.ts +8 -0
  33. package/dist/migrations/generator.d.ts.map +1 -1
  34. package/dist/migrations/generator.js +5 -1
  35. package/dist/migrations/generator.js.map +1 -1
  36. package/dist/migrations/orchestrate.d.ts +10 -0
  37. package/dist/migrations/orchestrate.d.ts.map +1 -1
  38. package/dist/migrations/orchestrate.js +8 -2
  39. package/dist/migrations/orchestrate.js.map +1 -1
  40. package/dist/migrations/tracker.d.ts.map +1 -1
  41. package/dist/migrations/tracker.js +4 -2
  42. package/dist/migrations/tracker.js.map +1 -1
  43. package/dist/registry/schema-builder.d.ts +13 -6
  44. package/dist/registry/schema-builder.d.ts.map +1 -1
  45. package/dist/registry/schema-builder.js +54 -15
  46. package/dist/registry/schema-builder.js.map +1 -1
  47. package/dist/registry.d.ts +3 -2
  48. package/dist/registry.d.ts.map +1 -1
  49. package/dist/registry.js +4 -4
  50. package/dist/registry.js.map +1 -1
  51. package/dist/schema/code-generator.js +123 -0
  52. package/dist/schema/code-generator.js.map +1 -0
  53. package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
  54. package/dist/schema/ddl/base-strategy.js +2 -2
  55. package/dist/schema/ddl/base-strategy.js.map +1 -1
  56. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  57. package/dist/schema/ddl/duckdb-strategy.js +2 -3
  58. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  59. package/dist/schema/ddl/index.js +26 -2
  60. package/dist/schema/ddl/index.js.map +1 -1
  61. package/dist/schema/ddl/materialize-manifest.d.ts +36 -0
  62. package/dist/schema/ddl/materialize-manifest.d.ts.map +1 -0
  63. package/dist/schema/ddl/materialize-manifest.js +361 -0
  64. package/dist/schema/ddl/materialize-manifest.js.map +1 -0
  65. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  66. package/dist/schema/ddl/postgres-strategy.js +1 -1
  67. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  68. package/dist/schema/index.js +12 -0
  69. package/dist/schema/override-system.js +205 -0
  70. package/dist/schema/override-system.js.map +1 -0
  71. package/dist/schema/schema-aggregator.d.ts.map +1 -1
  72. package/dist/schema/schema-aggregator.js +261 -0
  73. package/dist/schema/schema-aggregator.js.map +1 -0
  74. package/dist/schema/schema-manager.js +11 -1
  75. package/dist/schema/schema-manager.js.map +1 -1
  76. package/dist/schema/utils.d.ts +1 -0
  77. package/dist/schema/utils.d.ts.map +1 -1
  78. package/dist/schema/utils.js +2 -1
  79. package/dist/schema/utils.js.map +1 -1
  80. package/dist/schema.d.ts +2 -0
  81. package/dist/schema.d.ts.map +1 -0
  82. package/dist/schema.js +13 -0
  83. package/dist/smrt-knowledge.json +7 -6
  84. package/dist/system/compatibility.d.ts +25 -0
  85. package/dist/system/compatibility.d.ts.map +1 -1
  86. package/dist/system/compatibility.js +61 -1
  87. package/dist/system/compatibility.js.map +1 -1
  88. package/dist/system/index.js +2 -2
  89. package/dist/system/schema.d.ts +17 -9
  90. package/dist/system/schema.d.ts.map +1 -1
  91. package/dist/system/schema.js +25 -11
  92. package/dist/system/schema.js.map +1 -1
  93. package/dist/testing/database.d.ts.map +1 -1
  94. package/dist/testing/database.js +5 -2
  95. package/dist/testing/database.js.map +1 -1
  96. package/package.json +10 -5
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../src/system/schema.ts"],"sourcesContent":["/**\n * SMRT System Tables Schema\n *\n * System tables use _smrt_ prefix to avoid conflicts with user tables.\n * All system tables are created in the same database as user data.\n */\n\n/**\n * Context memory storage\n * Stores remembered context (learned strategies, patterns, selectors) for reuse\n */\nexport const CREATE_SMRT_CONTEXTS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_contexts (\n id TEXT PRIMARY KEY,\n owner_class TEXT NOT NULL,\n owner_id TEXT NOT NULL,\n scope TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT,\n metadata TEXT,\n version INTEGER DEFAULT 1,\n confidence REAL DEFAULT 1.0,\n success_count INTEGER DEFAULT 0,\n failure_count INTEGER DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n last_used_at TIMESTAMP,\n expires_at TIMESTAMP,\n UNIQUE(owner_class, owner_id, scope, key, version)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_owner\n ON _smrt_contexts(owner_class, owner_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_scope\n ON _smrt_contexts(scope);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_confidence\n ON _smrt_contexts(confidence);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_last_used\n ON _smrt_contexts(last_used_at);\n`;\n\n/**\n * Schema version tracking\n * Records which SMRT framework versions have been applied\n */\nexport const CREATE_SMRT_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_migrations (\n id TEXT PRIMARY KEY,\n version TEXT NOT NULL UNIQUE,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n checksum TEXT\n);\n`;\n\n/**\n * Schema migration tracking\n * Tracks applied schema migrations for idempotency, audit, and rollback\n */\nexport const CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_schema_migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n version TEXT NOT NULL,\n checksum TEXT NOT NULL,\n applied_checksum TEXT,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n execution_time_ms INTEGER,\n package_name TEXT,\n source_file TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n error_message TEXT,\n attempts INTEGER DEFAULT 0,\n is_reversible INTEGER DEFAULT 1,\n rolled_back_at TIMESTAMP,\n applied_by TEXT,\n batch INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_status\n ON _smrt_schema_migrations(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_applied_at\n ON _smrt_schema_migrations(applied_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_batch\n ON _smrt_schema_migrations(batch);\n`;\n\n/**\n * Runtime object registry persistence\n * Stores metadata about registered SMRT objects for introspection\n */\nexport const CREATE_SMRT_REGISTRY_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_registry (\n class_name TEXT PRIMARY KEY,\n schema_version TEXT,\n fields TEXT,\n relationships TEXT,\n config TEXT,\n manifest TEXT,\n last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n`;\n\n/**\n * Signal history/audit log\n * Optional persistence of signals for debugging and auditing\n */\nexport const CREATE_SMRT_SIGNALS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_signals (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source_class TEXT,\n source_id TEXT,\n target_class TEXT,\n target_id TEXT,\n payload TEXT,\n timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_source\n ON _smrt_signals(source_class, source_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_type\n ON _smrt_signals(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_timestamp\n ON _smrt_signals(timestamp);\n`;\n\n/**\n * Embedding storage for semantic search\n * Stores embedding vectors for SMRT objects to enable vector similarity search\n */\nexport const CREATE_SMRT_EMBEDDINGS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_embeddings (\n id TEXT PRIMARY KEY,\n object_class TEXT NOT NULL,\n object_id TEXT NOT NULL,\n field_name TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n embedding TEXT NOT NULL,\n model TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n provider TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(object_class, object_id, field_name, model)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_object\n ON _smrt_embeddings(object_class, object_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_class\n ON _smrt_embeddings(object_class);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_hash\n ON _smrt_embeddings(content_hash);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_model\n ON _smrt_embeddings(model);\n`;\n\n/**\n * Dispatch queue for inter-agent communication\n * Stores dispatch messages for asynchronous agent-to-agent signaling\n */\nexport const CREATE_SMRT_DISPATCH_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source TEXT NOT NULL,\n source_id TEXT,\n payload TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n attempts INTEGER DEFAULT 0,\n last_error TEXT,\n processed_at TIMESTAMP,\n processed_by TEXT,\n target_subscriber TEXT,\n correlation_id TEXT,\n tenant_id TEXT,\n metadata TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_status\n ON _smrt_dispatch(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_tenant_id\n ON _smrt_dispatch(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_type\n ON _smrt_dispatch(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_source\n ON _smrt_dispatch(source);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_created\n ON _smrt_dispatch(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_target\n ON _smrt_dispatch(target_subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_correlation\n ON _smrt_dispatch(correlation_id);\n`;\n\n/**\n * Dispatch subscriptions for persistent handlers\n * Stores subscriptions to dispatch types for agent processing\n */\nexport const CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch_subscriptions (\n id TEXT PRIMARY KEY,\n signal_type TEXT NOT NULL,\n subscriber TEXT NOT NULL,\n handler TEXT NOT NULL DEFAULT 'handleDispatch',\n delivery TEXT NOT NULL DEFAULT 'compete',\n enabled INTEGER DEFAULT 1,\n tenant_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Subscription identity is tenant-scoped (S5 #1398): the same\n-- (signal_type, subscriber) pair may exist independently in different tenants,\n-- so tenant B can no longer overwrite/delete/enable tenant A's subscription.\n-- A named UNIQUE index (rather than an inline UNIQUE constraint) is used so the\n-- compatibility migration can additively reshape existing tables. NULL tenant_id\n-- (global subscriptions) is deduped at the application layer by the NULL-aware\n-- upsert in @happyvertical/sql.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_smrt_dispatch_subs_tenant_signal_subscriber\n ON _smrt_dispatch_subscriptions(tenant_id, signal_type, subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_subscriber\n ON _smrt_dispatch_subscriptions(subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_tenant_id\n ON _smrt_dispatch_subscriptions(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_signal_type\n ON _smrt_dispatch_subscriptions(signal_type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_enabled\n ON _smrt_dispatch_subscriptions(enabled);\n`;\n\n/**\n * AI usage telemetry storage\n * Stores normalized AI usage records for reporting and billing hooks\n */\nexport const CREATE_SMRT_AI_USAGE_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_ai_usage (\n id TEXT PRIMARY KEY,\n provider TEXT NOT NULL,\n model TEXT NOT NULL,\n operation TEXT NOT NULL,\n prompt_tokens INTEGER,\n completion_tokens INTEGER,\n total_tokens INTEGER,\n estimated_cost REAL,\n duration INTEGER NOT NULL,\n class_name TEXT,\n tenant_id TEXT,\n tags TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_created\n ON _smrt_ai_usage(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_class\n ON _smrt_ai_usage(class_name);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_tenant\n ON _smrt_ai_usage(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_provider_model\n ON _smrt_ai_usage(provider, model);\n`;\n\n/**\n * Append-only change feed (issue #1758)\n *\n * One row per framework save/delete: monotonic per-database sequence,\n * table, row id, operation (create/update/delete — deletes are tombstones),\n * tenant, timestamp. Written by the change-feed interceptor and read\n * through `getChangesSince()`.\n *\n * `seq` is deliberately a plain BIGINT PRIMARY KEY rather than a native\n * AUTOINCREMENT/identity/serial column: the appender allocates\n * `COALESCE(MAX(seq), 0) + 1` inside the INSERT (with a conflict retry),\n * which keeps committed sequences contiguous so commit order equals\n * sequence order on every engine. Native identity columns allocate before\n * commit, so under concurrent writers on MVCC engines a reader could\n * observe seq N+1 while seq N is still uncommitted and advance its cursor\n * past it — breaking the feed's no-missed-changes cursor guarantee. The\n * plain column also keeps this DDL portable across SQLite, Postgres and\n * DuckDB with no per-engine branching. See `change-feed.ts`.\n *\n * `row_id` is nullable: manual bumps (`bumpChangeFeed`) may record a\n * table-level change without a specific row.\n */\nexport const CREATE_SMRT_CHANGES_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_changes (\n seq BIGINT PRIMARY KEY,\n table_name TEXT NOT NULL,\n row_id TEXT,\n operation TEXT NOT NULL,\n tenant_id TEXT,\n created_at TIMESTAMP NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_table_seq\n ON _smrt_changes(table_name, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_tenant_seq\n ON _smrt_changes(tenant_id, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_created_at\n ON _smrt_changes(created_at);\n`;\n\n/** PostgreSQL helper used to isolate best-effort feed appends (#2026). */\nexport const POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME = '_smrt_append_change';\n\n/** Exact PostgreSQL identity used for catalog lookup of the append helper. */\nexport const POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY = `${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME}(text,text,text,text,timestamp without time zone)`;\n\n/**\n * PostgreSQL-only change-feed append function.\n *\n * A PL/pgSQL block with an EXCEPTION handler runs its body in an internal\n * subtransaction. Returning SQLSTATE as data lets the caller log or retry a\n * failed best-effort append without leaving its surrounding transaction in\n * PostgreSQL's aborted (25P02) state. This statement contains dollar-quoted\n * semicolons, so callers must execute it whole rather than adding it to\n * {@link ALL_SYSTEM_TABLES}, whose portable DDL entries are semicolon-split.\n */\nexport const CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION = `\nCREATE OR REPLACE FUNCTION ${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME}(\n p_table_name TEXT,\n p_row_id TEXT,\n p_operation TEXT,\n p_tenant_id TEXT,\n p_created_at TIMESTAMP\n)\nRETURNS TABLE(\n allocated_seq BIGINT,\n error_code TEXT,\n error_message TEXT\n)\nLANGUAGE plpgsql\nSECURITY INVOKER\nAS $smrt_change_feed$\nDECLARE\n v_seq BIGINT;\n v_error_code TEXT;\n v_error_message TEXT;\nBEGIN\n BEGIN\n INSERT INTO _smrt_changes (\n seq,\n table_name,\n row_id,\n operation,\n tenant_id,\n created_at\n )\n SELECT\n COALESCE(MAX(changes.seq), 0) + 1,\n p_table_name,\n p_row_id,\n p_operation,\n p_tenant_id,\n p_created_at\n FROM _smrt_changes AS changes\n RETURNING _smrt_changes.seq INTO v_seq;\n\n RETURN QUERY SELECT v_seq, NULL::TEXT, NULL::TEXT;\n EXCEPTION WHEN query_canceled OR assert_failure OR OTHERS THEN\n GET STACKED DIAGNOSTICS\n v_error_code = RETURNED_SQLSTATE,\n v_error_message = MESSAGE_TEXT;\n RETURN QUERY SELECT NULL::BIGINT, v_error_code, v_error_message;\n END;\nEND;\n$smrt_change_feed$;\n`;\n\n/**\n * Serialize PostgreSQL helper replacement inside one server-side statement.\n *\n * The transaction-scoped advisory lock prevents concurrent bootstraps from\n * racing on PostgreSQL's `pg_proc` uniqueness constraint. The nested dollar\n * quote keeps the complete function DDL atomic from the client's perspective.\n */\nexport const REPLACE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION = `\nDO $smrt_replace_change_feed$\nBEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\nEND;\n$smrt_replace_change_feed$;\n`;\n\n/**\n * Install the PostgreSQL helper only when missing, serialized server-side.\n *\n * A client-side catalog probe remains the fast path for already-initialized\n * read handles. This guarded statement is the cold-path race boundary: both\n * the advisory lock and the post-lock catalog check run before function DDL.\n */\nexport const ENSURE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION = `\nDO $smrt_ensure_change_feed$\nBEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n IF to_regprocedure('${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}') IS NULL THEN\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\n END IF;\nEND;\n$smrt_ensure_change_feed$;\n`;\n\nconst POSTGRES_CHANGE_FEED_SCHEMA_DDL = CREATE_SMRT_CHANGES_TABLE.split(';')\n .map((statement) => statement.trim())\n .filter((statement) => statement.length > 0)\n .map(\n (statement) => `EXECUTE $smrt_change_feed_schema_ddl$\n${statement}\n$smrt_change_feed_schema_ddl$;`,\n )\n .join('\\n');\n\n/**\n * Install the complete PostgreSQL change-feed schema under the bootstrap lock.\n *\n * The lock is deliberately acquired before table or index DDL. Framework\n * bootstrap uses the same ordering, so a raw-handle cold start cannot retain\n * catalog locks while waiting behind a framework bootstrap transaction.\n */\nexport const ENSURE_POSTGRES_CHANGE_FEED_SCHEMA = `\nDO $smrt_ensure_change_feed_schema$\nBEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n${POSTGRES_CHANGE_FEED_SCHEMA_DDL}\n IF to_regprocedure('${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}') IS NULL THEN\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\n END IF;\nEND;\n$smrt_ensure_change_feed_schema$;\n`;\n\n/**\n * Data backfill tracking\n *\n * Distinct from `_smrt_schema_migrations` — backfills are app-specific\n * data corrections (slug rewrites, model splits, lookup-table seeds) that\n * don't have schema diffs or rollback semantics. Apps register backfills\n * by name and the tracker handles idempotency.\n */\nexport const CREATE_SMRT_BACKFILLS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_backfills (\n name TEXT PRIMARY KEY,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n package_name TEXT\n);\n`;\n\n/**\n * All system table creation statements\n */\nexport const ALL_SYSTEM_TABLES = [\n CREATE_SMRT_CONTEXTS_TABLE,\n CREATE_SMRT_MIGRATIONS_TABLE,\n CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE,\n CREATE_SMRT_BACKFILLS_TABLE,\n CREATE_SMRT_REGISTRY_TABLE,\n CREATE_SMRT_SIGNALS_TABLE,\n CREATE_SMRT_EMBEDDINGS_TABLE,\n CREATE_SMRT_DISPATCH_TABLE,\n CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE,\n CREATE_SMRT_AI_USAGE_TABLE,\n CREATE_SMRT_CHANGES_TABLE,\n];\n\n/**\n * Current SMRT system schema version\n */\nexport const SMRT_SCHEMA_VERSION = '1.8.0';\n"],"mappings":";;;;;;;;;;;AAWA,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqC1C,IAAa,+BAA+B;;;;;;;;;;;;;AAc5C,IAAa,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnD,IAAa,6BAA6B;;;;;;;;;;;;;;;AAgB1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;AA0BzC,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC5C,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8C1C,IAAa,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCxD,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoD1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;AAqBzC,IAAa,4CAA4C;;AAGzD,IAAa,gDAAgD,GAAG,0CAA0C;;;;;;;;;;;AAY1G,IAAa,8CAA8C;6BAC9B,0CAA0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDvE,IAAa,+CAA+C;;;;;;;;EAQ1D,4CAA4C,KAAK,EAAE;;;;;;;;;;;;AAarD,IAAa,8CAA8C;;;;;;;wBAOnC,8CAA8C;;EAEpE,4CAA4C,KAAK,EAAE;;;;;;;;;;;;;AAwBrD,IAAa,qCAAqC;;;;;;;EAjBV,0BAA0B,MAAM,GAAG,CAAC,CACzE,KAAK,cAAc,UAAU,KAAK,CAAC,CAAC,CACpC,QAAQ,cAAc,UAAU,SAAS,CAAC,CAAC,CAC3C,KACE,cAAc;EACjB,UAAU;+BAEV,CAAC,CACA,KAAK,IAgBN,EAAgC;wBACV,8CAA8C;;EAEpE,4CAA4C,KAAK,EAAE;;;;;;;;;;;;;;AAerD,IAAa,8BAA8B;;;;;;;;;;;AAY3C,IAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;AAKA,IAAa,sBAAsB"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../src/system/schema.ts"],"sourcesContent":["/**\n * SMRT System Tables Schema\n *\n * System tables use _smrt_ prefix to avoid conflicts with user tables.\n * All system tables are created in the same database as user data.\n */\n\nimport type { DatabaseEngine } from '../schema/ddl/types.js';\n\n/**\n * Context memory storage\n * Stores remembered context (learned strategies, patterns, selectors) for reuse\n */\nexport const CREATE_SMRT_CONTEXTS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_contexts (\n id TEXT PRIMARY KEY,\n owner_class TEXT NOT NULL,\n owner_id TEXT NOT NULL,\n scope TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT,\n metadata TEXT,\n version INTEGER DEFAULT 1,\n confidence REAL DEFAULT 1.0,\n success_count INTEGER DEFAULT 0,\n failure_count INTEGER DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n last_used_at TIMESTAMP,\n expires_at TIMESTAMP,\n UNIQUE(owner_class, owner_id, scope, key, version)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_owner\n ON _smrt_contexts(owner_class, owner_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_scope\n ON _smrt_contexts(scope);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_confidence\n ON _smrt_contexts(confidence);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_last_used\n ON _smrt_contexts(last_used_at);\n`;\n\n/**\n * Schema version tracking\n * Records which SMRT framework versions have been applied\n */\nexport const CREATE_SMRT_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_migrations (\n id TEXT PRIMARY KEY,\n version TEXT NOT NULL UNIQUE,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n checksum TEXT\n);\n`;\n\n/**\n * Schema migration tracking\n * Tracks applied schema migrations for idempotency, audit, and rollback\n */\nexport const CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_schema_migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n version TEXT NOT NULL,\n checksum TEXT NOT NULL,\n applied_checksum TEXT,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n execution_time_ms INTEGER,\n package_name TEXT,\n source_file TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n error_message TEXT,\n attempts INTEGER DEFAULT 0,\n is_reversible INTEGER DEFAULT 1,\n rolled_back_at TIMESTAMP,\n applied_by TEXT,\n batch INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_status\n ON _smrt_schema_migrations(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_applied_at\n ON _smrt_schema_migrations(applied_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_batch\n ON _smrt_schema_migrations(batch);\n`;\n\n/**\n * Runtime object registry persistence\n * Stores metadata about registered SMRT objects for introspection\n */\nexport const CREATE_SMRT_REGISTRY_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_registry (\n class_name TEXT PRIMARY KEY,\n schema_version TEXT,\n fields TEXT,\n relationships TEXT,\n config TEXT,\n manifest TEXT,\n last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n`;\n\n/**\n * Signal history/audit log\n * Optional persistence of signals for debugging and auditing\n */\nexport const CREATE_SMRT_SIGNALS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_signals (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source_class TEXT,\n source_id TEXT,\n target_class TEXT,\n target_id TEXT,\n payload TEXT,\n timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_source\n ON _smrt_signals(source_class, source_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_type\n ON _smrt_signals(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_timestamp\n ON _smrt_signals(timestamp);\n`;\n\n/**\n * Embedding storage for semantic search\n * Stores embedding vectors for SMRT objects to enable vector similarity search\n */\nexport const CREATE_SMRT_EMBEDDINGS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_embeddings (\n id TEXT PRIMARY KEY,\n object_class TEXT NOT NULL,\n object_id TEXT NOT NULL,\n field_name TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n embedding TEXT NOT NULL,\n model TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n provider TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(object_class, object_id, field_name, model)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_object\n ON _smrt_embeddings(object_class, object_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_class\n ON _smrt_embeddings(object_class);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_hash\n ON _smrt_embeddings(content_hash);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_model\n ON _smrt_embeddings(model);\n`;\n\n/**\n * Dispatch queue for inter-agent communication\n * Stores dispatch messages for asynchronous agent-to-agent signaling\n */\nexport const CREATE_SMRT_DISPATCH_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source TEXT NOT NULL,\n source_id TEXT,\n payload TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n attempts INTEGER DEFAULT 0,\n last_error TEXT,\n processed_at TIMESTAMP,\n processed_by TEXT,\n target_subscriber TEXT,\n correlation_id TEXT,\n tenant_id TEXT,\n metadata TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_status\n ON _smrt_dispatch(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_tenant_id\n ON _smrt_dispatch(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_type\n ON _smrt_dispatch(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_source\n ON _smrt_dispatch(source);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_created\n ON _smrt_dispatch(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_target\n ON _smrt_dispatch(target_subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_correlation\n ON _smrt_dispatch(correlation_id);\n`;\n\n/**\n * Dispatch subscriptions for persistent handlers\n * Stores subscriptions to dispatch types for agent processing\n */\nexport const CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch_subscriptions (\n id TEXT PRIMARY KEY,\n signal_type TEXT NOT NULL,\n subscriber TEXT NOT NULL,\n handler TEXT NOT NULL DEFAULT 'handleDispatch',\n delivery TEXT NOT NULL DEFAULT 'compete',\n enabled INTEGER DEFAULT 1,\n tenant_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Subscription identity is tenant-scoped (S5 #1398): the same\n-- (signal_type, subscriber) pair may exist independently in different tenants,\n-- so tenant B can no longer overwrite/delete/enable tenant A's subscription.\n-- A named UNIQUE index (rather than an inline UNIQUE constraint) is used so the\n-- compatibility migration can additively reshape existing tables. NULL tenant_id\n-- (global subscriptions) is deduped at the application layer by the NULL-aware\n-- upsert in @happyvertical/sql.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_smrt_dispatch_subs_tenant_signal_subscriber\n ON _smrt_dispatch_subscriptions(tenant_id, signal_type, subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_subscriber\n ON _smrt_dispatch_subscriptions(subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_tenant_id\n ON _smrt_dispatch_subscriptions(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_signal_type\n ON _smrt_dispatch_subscriptions(signal_type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_enabled\n ON _smrt_dispatch_subscriptions(enabled);\n`;\n\n/**\n * AI usage telemetry storage\n * Stores normalized AI usage records for reporting and billing hooks\n */\nexport const CREATE_SMRT_AI_USAGE_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_ai_usage (\n id TEXT PRIMARY KEY,\n provider TEXT NOT NULL,\n model TEXT NOT NULL,\n operation TEXT NOT NULL,\n prompt_tokens INTEGER,\n completion_tokens INTEGER,\n total_tokens INTEGER,\n estimated_cost REAL,\n duration INTEGER NOT NULL,\n class_name TEXT,\n tenant_id TEXT,\n tags TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_created\n ON _smrt_ai_usage(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_class\n ON _smrt_ai_usage(class_name);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_tenant\n ON _smrt_ai_usage(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_provider_model\n ON _smrt_ai_usage(provider, model);\n`;\n\n/**\n * Append-only change feed (issue #1758)\n *\n * One row per framework save/delete: monotonic per-database sequence,\n * table, row id, operation (create/update/delete — deletes are tombstones),\n * tenant, timestamp. Written by the change-feed interceptor and read\n * through `getChangesSince()`.\n *\n * `seq` is deliberately a plain BIGINT PRIMARY KEY rather than a native\n * AUTOINCREMENT/identity/serial column: the appender allocates\n * `COALESCE(MAX(seq), 0) + 1` inside the INSERT (with a conflict retry),\n * which keeps committed sequences contiguous so commit order equals\n * sequence order on every engine. Native identity columns allocate before\n * commit, so under concurrent writers on MVCC engines a reader could\n * observe seq N+1 while seq N is still uncommitted and advance its cursor\n * past it — breaking the feed's no-missed-changes cursor guarantee. The\n * plain column also keeps this DDL portable across SQLite, Postgres and\n * DuckDB with no per-engine branching. See `change-feed.ts`.\n *\n * `row_id` is nullable: manual bumps (`bumpChangeFeed`) may record a\n * table-level change without a specific row.\n */\nexport const CREATE_SMRT_CHANGES_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_changes (\n seq BIGINT PRIMARY KEY,\n table_name TEXT NOT NULL,\n row_id TEXT,\n operation TEXT NOT NULL,\n tenant_id TEXT,\n created_at TIMESTAMP NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_table_seq\n ON _smrt_changes(table_name, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_tenant_seq\n ON _smrt_changes(tenant_id, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_created_at\n ON _smrt_changes(created_at);\n`;\n\n/** PostgreSQL materialization of the portable change-feed table DDL. */\nexport const CREATE_POSTGRES_SMRT_CHANGES_TABLE =\n CREATE_SMRT_CHANGES_TABLE.replace(/\\bTIMESTAMP\\b/g, 'TIMESTAMPTZ');\n\n/** PostgreSQL helper used to isolate best-effort feed appends (#2026). */\nexport const POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME = '_smrt_append_change';\n\n/** Legacy helper identity used before PostgreSQL Date values became instants. */\nexport const LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY = `${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME}(text,text,text,text,timestamp without time zone)`;\n\n/** Exact PostgreSQL identity used for catalog lookup of the append helper. */\nexport const POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY = `${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME}(text,text,text,text,timestamp with time zone)`;\n\n/**\n * PostgreSQL-only change-feed append function.\n *\n * A PL/pgSQL block with an EXCEPTION handler runs its body in an internal\n * subtransaction. Returning SQLSTATE as data lets the caller log or retry a\n * failed best-effort append without leaving its surrounding transaction in\n * PostgreSQL's aborted (25P02) state. This statement contains dollar-quoted\n * semicolons, so callers must execute it whole rather than adding it to\n * {@link ALL_SYSTEM_TABLES}, whose portable DDL entries are semicolon-split.\n */\nexport const CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION = `\nCREATE OR REPLACE FUNCTION ${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME}(\n p_table_name TEXT,\n p_row_id TEXT,\n p_operation TEXT,\n p_tenant_id TEXT,\n p_created_at TIMESTAMPTZ\n)\nRETURNS TABLE(\n allocated_seq BIGINT,\n error_code TEXT,\n error_message TEXT\n)\nLANGUAGE plpgsql\nSECURITY INVOKER\nAS $smrt_change_feed$\nDECLARE\n v_seq BIGINT;\n v_error_code TEXT;\n v_error_message TEXT;\nBEGIN\n BEGIN\n INSERT INTO _smrt_changes (\n seq,\n table_name,\n row_id,\n operation,\n tenant_id,\n created_at\n )\n SELECT\n COALESCE(MAX(changes.seq), 0) + 1,\n p_table_name,\n p_row_id,\n p_operation,\n p_tenant_id,\n p_created_at\n FROM _smrt_changes AS changes\n RETURNING _smrt_changes.seq INTO v_seq;\n\n RETURN QUERY SELECT v_seq, NULL::TEXT, NULL::TEXT;\n EXCEPTION WHEN query_canceled OR assert_failure OR OTHERS THEN\n GET STACKED DIAGNOSTICS\n v_error_code = RETURNED_SQLSTATE,\n v_error_message = MESSAGE_TEXT;\n RETURN QUERY SELECT NULL::BIGINT, v_error_code, v_error_message;\n END;\nEND;\n$smrt_change_feed$;\n`;\n\n/**\n * Serialize PostgreSQL helper replacement inside one server-side statement.\n *\n * The transaction-scoped advisory lock prevents concurrent bootstraps from\n * racing on PostgreSQL's `pg_proc` uniqueness constraint. The nested dollar\n * quote keeps the complete function DDL atomic from the client's perspective.\n */\nexport const REPLACE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION = `\nDO $smrt_replace_change_feed$\nBEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\nEND;\n$smrt_replace_change_feed$;\n`;\n\n/**\n * Install the PostgreSQL helper only when missing, serialized server-side.\n *\n * A client-side catalog probe remains the fast path for already-initialized\n * read handles. This guarded statement is the cold-path race boundary: both\n * the advisory lock and the post-lock catalog check run before function DDL.\n */\nexport const ENSURE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION = `\nDO $smrt_ensure_change_feed$\nBEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\n IF to_regprocedure('${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}') IS NULL THEN\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\n END IF;\nEND;\n$smrt_ensure_change_feed$;\n`;\n\nconst POSTGRES_CHANGE_FEED_SCHEMA_DDL =\n CREATE_POSTGRES_SMRT_CHANGES_TABLE.split(';')\n .map((statement) => statement.trim())\n .filter((statement) => statement.length > 0)\n .map(\n (statement) => `EXECUTE $smrt_change_feed_schema_ddl$\n${statement}\n$smrt_change_feed_schema_ddl$;`,\n )\n .join('\\n');\n\n/**\n * Install the complete PostgreSQL change-feed schema under the bootstrap lock.\n *\n * The lock is deliberately acquired before table or index DDL. Framework\n * bootstrap uses the same ordering, so a raw-handle cold start cannot retain\n * catalog locks while waiting behind a framework bootstrap transaction.\n */\nexport const ENSURE_POSTGRES_CHANGE_FEED_SCHEMA = `\nDO $smrt_ensure_change_feed_schema$\nBEGIN\n PERFORM pg_advisory_xact_lock(\n hashtext('smrt'),\n hashtext('system-tables')\n );\n${POSTGRES_CHANGE_FEED_SCHEMA_DDL}\n DROP FUNCTION IF EXISTS ${LEGACY_POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY};\n IF to_regprocedure('${POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY}') IS NULL THEN\n EXECUTE $smrt_change_feed_ddl$\n${CREATE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION.trim()}\n$smrt_change_feed_ddl$;\n END IF;\nEND;\n$smrt_ensure_change_feed_schema$;\n`;\n\n/**\n * Data backfill tracking\n *\n * Distinct from `_smrt_schema_migrations` — backfills are app-specific\n * data corrections (slug rewrites, model splits, lookup-table seeds) that\n * don't have schema diffs or rollback semantics. Apps register backfills\n * by name and the tracker handles idempotency.\n */\nexport const CREATE_SMRT_BACKFILLS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_backfills (\n name TEXT PRIMARY KEY,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n package_name TEXT\n);\n`;\n\n/**\n * All system table creation statements\n */\nexport const ALL_SYSTEM_TABLES = [\n CREATE_SMRT_CONTEXTS_TABLE,\n CREATE_SMRT_MIGRATIONS_TABLE,\n CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE,\n CREATE_SMRT_BACKFILLS_TABLE,\n CREATE_SMRT_REGISTRY_TABLE,\n CREATE_SMRT_SIGNALS_TABLE,\n CREATE_SMRT_EMBEDDINGS_TABLE,\n CREATE_SMRT_DISPATCH_TABLE,\n CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE,\n CREATE_SMRT_AI_USAGE_TABLE,\n CREATE_SMRT_CHANGES_TABLE,\n];\n\n/**\n * Materialize the portable system-table schema for a target engine.\n *\n * PostgreSQL must use TIMESTAMPTZ because every system Date is an instant.\n * Other engines keep the established portable TIMESTAMP declarations.\n */\nexport function getSystemTableDDL(engine: DatabaseEngine): string[] {\n return ALL_SYSTEM_TABLES.map((ddl) =>\n getSystemTableDDLForEngine(ddl, engine),\n );\n}\n\n/** Materialize one portable system-table statement for a target engine. */\nexport function getSystemTableDDLForEngine(\n ddl: string,\n engine: DatabaseEngine,\n): string {\n return engine === 'postgres'\n ? ddl.replace(/\\bTIMESTAMP\\b/g, 'TIMESTAMPTZ')\n : ddl;\n}\n\n/**\n * Current SMRT system schema version\n */\nexport const SMRT_SCHEMA_VERSION = '1.9.0';\n"],"mappings":";;;;;AAaA,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqC1C,IAAa,+BAA+B;;;;;;;;;;;;;AAc5C,IAAa,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnD,IAAa,6BAA6B;;;;;;;;;;;;;;;AAgB1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;AA0BzC,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC5C,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8C1C,IAAa,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCxD,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoD1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;AAqBzC,IAAa,qCACX,0BAA0B,QAAQ,kBAAkB,aAAa;;AAGnE,IAAa,4CAA4C;;AAGzD,IAAa,uDAAuD,GAAG,0CAA0C;;AAGjH,IAAa,gDAAgD,GAAG,0CAA0C;;;;;;;;;;;AAY1G,IAAa,8CAA8C;6BAC9B,0CAA0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDvE,IAAa,+CAA+C;;;;;;;4BAOhC,qDAAqD;;EAE/E,4CAA4C,KAAK,EAAE;;;;;;;;;;;;AAarD,IAAa,8CAA8C;;;;;;;4BAO/B,qDAAqD;wBACzD,8CAA8C;;EAEpE,4CAA4C,KAAK,EAAE;;;;;;;;;;;;;AAyBrD,IAAa,qCAAqC;;;;;;;EAjBhD,mCAAmC,MAAM,GAAG,CAAC,CAC1C,KAAK,cAAc,UAAU,KAAK,CAAC,CAAC,CACpC,QAAQ,cAAc,UAAU,SAAS,CAAC,CAAC,CAC3C,KACE,cAAc;EACnB,UAAU;+BAER,CAAC,CACA,KAAK,IAgBR,EAAgC;4BACN,qDAAqD;wBACzD,8CAA8C;;EAEpE,4CAA4C,KAAK,EAAE;;;;;;;;;;;;;;AAerD,IAAa,8BAA8B;;;;;;;;;;;AAY3C,IAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAQA,SAAgB,kBAAkB,QAAkC;CAClE,OAAO,kBAAkB,KAAK,QAC5B,2BAA2B,KAAK,MAAM,CACxC;AACF;;AAGA,SAAgB,2BACd,KACA,QACQ;CACR,OAAO,WAAW,aACd,IAAI,QAAQ,kBAAkB,aAAa,IAC3C;AACN;;;;AAKA,IAAa,sBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/testing/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAiJ5D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAEpC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,eAAe,CACnC,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAwH5B"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/testing/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAkJ5D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAEpC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,eAAe,CACnC,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAwH5B"}
@@ -1,4 +1,5 @@
1
- import { ALL_SYSTEM_TABLES } from "../system/schema.js";
1
+ import { detectEngine } from "../schema/ddl/index.js";
2
+ import { getSystemTableDDL } from "../system/schema.js";
2
3
  import { ensurePostgresChangeFeedAppendFunction } from "../change-feed.js";
3
4
  import { ensureLegacySystemTableCompatibility } from "../system/compatibility.js";
4
5
  import { SchemaGenerator } from "../schema/generator.js";
@@ -166,8 +167,10 @@ async function getTestDatabase(options = {}) {
166
167
  */
167
168
  async function initializeSystemTables(db) {
168
169
  await ensureLegacySystemTableCompatibility(db);
170
+ const configuredDb = db;
171
+ const engine = detectEngine(db.url || configuredDb.config?.url || "", configuredDb.type || configuredDb.config?.type);
169
172
  const allStatements = [];
170
- for (const multiStatementSQL of ALL_SYSTEM_TABLES) {
173
+ for (const multiStatementSQL of getSystemTableDDL(engine)) {
171
174
  const statements = multiStatementSQL.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
172
175
  allStatements.push(...statements);
173
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","names":[],"sources":["../../src/testing/database.ts"],"sourcesContent":["/**\n * Test database utilities for SMRT framework\n *\n * Provides `getTestDatabase()` which creates an in-memory database with all\n * registered SMRT object schemas. Uses the same schema generation logic as\n * the migration system to ensure consistency between test and production.\n *\n * @example\n * ```typescript\n * import { getTestDatabase } from '@happyvertical/smrt-core/testing';\n *\n * beforeEach(async () => {\n * const db = await getTestDatabase();\n * collection = await MyCollection.create({ db });\n * });\n * ```\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { getDatabase } from '@happyvertical/sql';\nimport { ensurePostgresChangeFeedAppendFunction } from '../change-feed.js';\nimport {\n type CollectionRegistrationLookup,\n isCollectionRegistration,\n resolveCollectionItemClassName,\n resolveRelatedRegistration,\n} from '../registry/collection-resolution.js';\nimport { ObjectRegistry } from '../registry.js';\nimport { SchemaGenerator } from '../schema/generator.js';\nimport { ensureLegacySystemTableCompatibility } from '../system/compatibility.js';\nimport { ALL_SYSTEM_TABLES } from '../system/schema.js';\n\ntype TestDatabaseConnectionOptions = Parameters<typeof getDatabase>[0] & {\n __smrtSkipVitestSchemaPreparation?: boolean;\n};\n\nfunction resolveSTIBaseRegistration(className: string, stiBaseName: string) {\n const registered = ObjectRegistry.getClass(className);\n\n if (stiBaseName.includes(':')) {\n return ObjectRegistry.getClass(stiBaseName);\n }\n\n if (registered?.packageName) {\n const samePackageBase = ObjectRegistry.getClassInPackage(\n registered.packageName,\n stiBaseName,\n );\n if (samePackageBase) {\n return samePackageBase;\n }\n }\n\n return ObjectRegistry.getClass(stiBaseName);\n}\n\nfunction resolveSTIBaseLookupName(\n className: string,\n stiBaseName: string,\n): string {\n const stiBase = resolveSTIBaseRegistration(className, stiBaseName);\n return stiBase?.qualifiedName || stiBase?.name || stiBaseName;\n}\n\nfunction isSTIChild(className: string): boolean {\n const stiBaseName = ObjectRegistry.getSTIBase(className);\n if (!stiBaseName) {\n return false;\n }\n\n const registered = ObjectRegistry.getClass(className);\n const stiBase = resolveSTIBaseRegistration(className, stiBaseName);\n\n if (registered && stiBase) {\n return registered !== stiBase;\n }\n\n return stiBaseName !== className;\n}\n\ntype RegisteredSchemaClass = NonNullable<\n ReturnType<typeof ObjectRegistry.getClass>\n>;\n\nconst collectionRegistrationLookup: CollectionRegistrationLookup = {\n findClass: (className) => ObjectRegistry.getClass(className),\n findClassInPackage: (packageName, className) =>\n ObjectRegistry.getClassInPackage(packageName, className),\n getInheritanceChain: (className) =>\n ObjectRegistry.getInheritanceChain(className),\n};\n\nfunction resolveCollectionSchemaClassName(\n className: string,\n registered: RegisteredSchemaClass,\n): string {\n const itemClassName = resolveCollectionItemClassName(\n className,\n registered,\n collectionRegistrationLookup,\n );\n if (itemClassName) {\n const itemRegistration = resolveRelatedRegistration(\n itemClassName,\n registered,\n collectionRegistrationLookup,\n );\n const itemLookupName =\n itemRegistration?.qualifiedName ||\n itemRegistration?.name ||\n itemClassName;\n const stiBase = ObjectRegistry.getSTIBase(itemLookupName);\n return stiBase\n ? resolveSTIBaseLookupName(itemLookupName, stiBase)\n : itemLookupName;\n }\n\n const tableName =\n registered.schema?.tableName ||\n registered.config.tableName ||\n ObjectRegistry.getTableName(className);\n if (!tableName) {\n return className;\n }\n\n const collectionPackage = registered.packageName;\n\n for (const candidate of ObjectRegistry.getAllClasses().values()) {\n if (\n candidate === registered ||\n isCollectionRegistration(\n candidate.qualifiedName || candidate.name,\n candidate,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n const candidateTableName =\n candidate.schema?.tableName || candidate.config.tableName;\n if (candidateTableName !== tableName) {\n continue;\n }\n\n if (\n collectionPackage &&\n candidate.packageName &&\n candidate.packageName !== collectionPackage\n ) {\n continue;\n }\n\n const candidateLookupName = candidate.qualifiedName || candidate.name;\n const stiBase = ObjectRegistry.getSTIBase(candidateLookupName);\n return stiBase\n ? resolveSTIBaseLookupName(candidateLookupName, stiBase)\n : candidateLookupName;\n }\n\n return className;\n}\n\n/**\n * Options for creating a test database\n */\nexport interface TestDatabaseOptions {\n /**\n * Database type (default: 'sqlite')\n * - 'sqlite': SQLite database\n * - 'json': JSON adapter (stores data as JSON files with DuckDB for querying)\n * - 'duckdb': Native DuckDB database\n */\n type?: 'sqlite' | 'json' | 'duckdb';\n\n /**\n * Database URL (default: ':memory:')\n * Use ':memory:' for in-memory databases (fastest for tests)\n * Or provide a file path for persistent test databases\n */\n url?: string;\n\n /**\n * Pre-existing database to initialize schemas in.\n * If provided, `type` and `url` are ignored.\n */\n db?: DatabaseInterface;\n\n /**\n * Specific classes to setup schemas for.\n * If not provided, sets up schemas for all registered classes.\n */\n classes?: string[];\n\n /**\n * Whether to include system tables (default: true)\n * System tables include _smrt_contexts, _smrt_migrations, etc.\n */\n includeSystemTables?: boolean;\n}\n\nfunction resolveRequestedSchemaClassName(className: string): string {\n const registered = ObjectRegistry.getClass(className);\n if (!registered) {\n return className;\n }\n\n if (\n !isCollectionRegistration(\n className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n const stiBase = ObjectRegistry.getSTIBase(className);\n return stiBase ? resolveSTIBaseLookupName(className, stiBase) : className;\n }\n\n return resolveCollectionSchemaClassName(className, registered);\n}\n\nfunction resolveRequestedSchemaClassNames(classNames: string[]): string[] {\n const resolved: string[] = [];\n const seen = new Set<string>();\n\n for (const className of classNames) {\n const schemaClassName = resolveRequestedSchemaClassName(className);\n if (seen.has(schemaClassName)) {\n continue;\n }\n\n seen.add(schemaClassName);\n resolved.push(schemaClassName);\n }\n\n return resolved;\n}\n\n/**\n * Creates an in-memory test database with schemas pre-created\n *\n * This utility is designed for testing. It creates an in-memory database\n * and initializes all registered SMRT object schemas using the **same\n * schema generation logic** as the production migration system.\n *\n * **Key features:**\n * - Uses `SchemaGenerator.generateSQL()` - the single source of truth for DDL\n * - Handles STI (Single Table Inheritance) correctly\n * - Creates system tables for framework functionality\n * - Safe for parallel test execution (each call creates isolated instance)\n *\n * @param options - Configuration options\n * @returns Promise resolving to configured DatabaseInterface\n *\n * @example\n * ```typescript\n * // Basic usage - all registered schemas\n * const db = await getTestDatabase();\n *\n * // Specific classes only\n * const db = await getTestDatabase({ classes: ['Council', 'Meeting'] });\n *\n * // JSON adapter instead of SQLite\n * const db = await getTestDatabase({ type: 'json' });\n *\n * // Native DuckDB adapter\n * const db = await getTestDatabase({ type: 'duckdb', url: ':memory:' });\n *\n * // File-based database for persistent tests\n * const db = await getTestDatabase({ type: 'sqlite', url: '/tmp/test.db' });\n *\n * // Initialize schemas in an existing database\n * const myDb = await getDatabase({ type: 'sqlite', url: ':memory:' });\n * await getTestDatabase({ db: myDb });\n *\n * // Skip system tables (rare use case)\n * const db = await getTestDatabase({ includeSystemTables: false });\n * ```\n */\nexport async function getTestDatabase(\n options: TestDatabaseOptions = {},\n): Promise<DatabaseInterface> {\n const {\n type = 'sqlite',\n url = ':memory:',\n db: existingDb,\n classes,\n includeSystemTables = true,\n } = options;\n\n // Use existing database or create new one\n const db =\n existingDb ??\n (await getDatabase({\n type,\n url,\n __smrtSkipVitestSchemaPreparation: true,\n } as TestDatabaseConnectionOptions));\n\n // Initialize system tables (same as production)\n if (includeSystemTables) {\n await initializeSystemTables(db);\n }\n\n // Get class names to setup\n const classNames = resolveRequestedSchemaClassNames(\n classes ?? ObjectRegistry.getQualifiedClassNames(),\n );\n\n // Skip if no classes registered\n if (classNames.length === 0) {\n return db;\n }\n\n // Use the same schema generation as production\n const schemaGenerator = new SchemaGenerator();\n const ddlEngine =\n type === 'json' ||\n typeof (db as { exportTable?: unknown }).exportTable === 'function'\n ? 'json'\n : type === 'duckdb'\n ? 'duckdb'\n : 'sqlite';\n\n // Track created tables to avoid duplicates (STI base classes)\n const createdTables = new Set<string>();\n\n for (const className of classNames) {\n // R11: the registration carries idType (native uuid vs text); read it\n // below when building runtimeSchemaConfig.\n const registered = ObjectRegistry.getClass(className);\n // Skip STI children - their schema is part of the base class table.\n // main (#1324): isSTIChild() compares RegisteredClass *identity* rather\n // than raw strings, so it stays correct under R5-canon (getSTIBase returns\n // qualified names) while also handling collection/override registrations.\n if (isSTIChild(className)) {\n continue;\n }\n\n const tableName = ObjectRegistry.getTableName(className);\n if (!tableName || createdTables.has(tableName)) {\n continue;\n }\n\n const fields = await ObjectRegistry.getAllFields(className);\n const strategy = ObjectRegistry.getTableStrategy(className);\n const runtimeSchemaConfig = {\n conflictColumns: ObjectRegistry.getConflictColumns(className),\n idType: registered?.config.idType,\n registry: ObjectRegistry,\n };\n\n // Generate schema using SchemaGenerator (same as migrations)\n const schema =\n strategy === 'sti'\n ? await schemaGenerator.generateSTISchemaFromRegistry(\n className,\n tableName,\n fields,\n runtimeSchemaConfig,\n )\n : schemaGenerator.generateSchemaFromRegistry(\n className,\n tableName,\n fields,\n runtimeSchemaConfig,\n );\n\n // Generate DDL using generateSQL() - the single source of truth\n const ddl = schemaGenerator.generateSQL(schema, ddlEngine);\n\n try {\n await db.query(ddl);\n createdTables.add(tableName);\n\n // Create indexes (use DDL strategy so jsonPath / where / etc. render)\n const ddlStrategy = (\n await import('../schema/ddl/index.js')\n ).getDDLStrategy(ddlEngine);\n const indexStatements = ddlStrategy.generateIndexes(schema);\n for (const indexSQL of indexStatements) {\n try {\n await db.query(indexSQL);\n } catch (indexError) {\n // Log but don't fail on index creation errors\n // Some indexes may fail if columns don't exist (STI meta fields)\n console.warn(\n `[getTestDatabase] Warning: Failed to create index: ${indexError instanceof Error ? indexError.message : String(indexError)} (SQL: ${indexSQL})`,\n );\n }\n }\n } catch (error) {\n // Provide helpful error message for table creation failures\n throw new Error(\n `[getTestDatabase] Failed to create table '${tableName}' for class '${className}': ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n return db;\n}\n\n/**\n * Initialize SMRT system tables in a database\n *\n * System tables use _smrt_ prefix and store framework metadata.\n * All statements use `IF NOT EXISTS` for idempotency.\n *\n * @param db - Database interface to initialize\n */\nasync function initializeSystemTables(db: DatabaseInterface): Promise<void> {\n await ensureLegacySystemTableCompatibility(db);\n\n // Split multi-statement SQL into individual statements\n const allStatements: string[] = [];\n for (const multiStatementSQL of ALL_SYSTEM_TABLES) {\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() — system tables use CREATE TABLE/INDEX IF NOT EXISTS\n // which databases handle natively without per-column existence checks.\n for (const statement of allStatements) {\n await db.query(statement);\n }\n\n // PostgreSQL keeps the best-effort append boundary in a function that must\n // be executed whole rather than included in semicolon-split portable DDL.\n await ensurePostgresChangeFeedAppendFunction(db);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"database.js","names":[],"sources":["../../src/testing/database.ts"],"sourcesContent":["/**\n * Test database utilities for SMRT framework\n *\n * Provides `getTestDatabase()` which creates an in-memory database with all\n * registered SMRT object schemas. Uses the same schema generation logic as\n * the migration system to ensure consistency between test and production.\n *\n * @example\n * ```typescript\n * import { getTestDatabase } from '@happyvertical/smrt-core/testing';\n *\n * beforeEach(async () => {\n * const db = await getTestDatabase();\n * collection = await MyCollection.create({ db });\n * });\n * ```\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { getDatabase } from '@happyvertical/sql';\nimport { ensurePostgresChangeFeedAppendFunction } from '../change-feed.js';\nimport {\n type CollectionRegistrationLookup,\n isCollectionRegistration,\n resolveCollectionItemClassName,\n resolveRelatedRegistration,\n} from '../registry/collection-resolution.js';\nimport { ObjectRegistry } from '../registry.js';\nimport { detectEngine } from '../schema/ddl/index.js';\nimport { SchemaGenerator } from '../schema/generator.js';\nimport { ensureLegacySystemTableCompatibility } from '../system/compatibility.js';\nimport { getSystemTableDDL } from '../system/schema.js';\n\ntype TestDatabaseConnectionOptions = Parameters<typeof getDatabase>[0] & {\n __smrtSkipVitestSchemaPreparation?: boolean;\n};\n\nfunction resolveSTIBaseRegistration(className: string, stiBaseName: string) {\n const registered = ObjectRegistry.getClass(className);\n\n if (stiBaseName.includes(':')) {\n return ObjectRegistry.getClass(stiBaseName);\n }\n\n if (registered?.packageName) {\n const samePackageBase = ObjectRegistry.getClassInPackage(\n registered.packageName,\n stiBaseName,\n );\n if (samePackageBase) {\n return samePackageBase;\n }\n }\n\n return ObjectRegistry.getClass(stiBaseName);\n}\n\nfunction resolveSTIBaseLookupName(\n className: string,\n stiBaseName: string,\n): string {\n const stiBase = resolveSTIBaseRegistration(className, stiBaseName);\n return stiBase?.qualifiedName || stiBase?.name || stiBaseName;\n}\n\nfunction isSTIChild(className: string): boolean {\n const stiBaseName = ObjectRegistry.getSTIBase(className);\n if (!stiBaseName) {\n return false;\n }\n\n const registered = ObjectRegistry.getClass(className);\n const stiBase = resolveSTIBaseRegistration(className, stiBaseName);\n\n if (registered && stiBase) {\n return registered !== stiBase;\n }\n\n return stiBaseName !== className;\n}\n\ntype RegisteredSchemaClass = NonNullable<\n ReturnType<typeof ObjectRegistry.getClass>\n>;\n\nconst collectionRegistrationLookup: CollectionRegistrationLookup = {\n findClass: (className) => ObjectRegistry.getClass(className),\n findClassInPackage: (packageName, className) =>\n ObjectRegistry.getClassInPackage(packageName, className),\n getInheritanceChain: (className) =>\n ObjectRegistry.getInheritanceChain(className),\n};\n\nfunction resolveCollectionSchemaClassName(\n className: string,\n registered: RegisteredSchemaClass,\n): string {\n const itemClassName = resolveCollectionItemClassName(\n className,\n registered,\n collectionRegistrationLookup,\n );\n if (itemClassName) {\n const itemRegistration = resolveRelatedRegistration(\n itemClassName,\n registered,\n collectionRegistrationLookup,\n );\n const itemLookupName =\n itemRegistration?.qualifiedName ||\n itemRegistration?.name ||\n itemClassName;\n const stiBase = ObjectRegistry.getSTIBase(itemLookupName);\n return stiBase\n ? resolveSTIBaseLookupName(itemLookupName, stiBase)\n : itemLookupName;\n }\n\n const tableName =\n registered.schema?.tableName ||\n registered.config.tableName ||\n ObjectRegistry.getTableName(className);\n if (!tableName) {\n return className;\n }\n\n const collectionPackage = registered.packageName;\n\n for (const candidate of ObjectRegistry.getAllClasses().values()) {\n if (\n candidate === registered ||\n isCollectionRegistration(\n candidate.qualifiedName || candidate.name,\n candidate,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n const candidateTableName =\n candidate.schema?.tableName || candidate.config.tableName;\n if (candidateTableName !== tableName) {\n continue;\n }\n\n if (\n collectionPackage &&\n candidate.packageName &&\n candidate.packageName !== collectionPackage\n ) {\n continue;\n }\n\n const candidateLookupName = candidate.qualifiedName || candidate.name;\n const stiBase = ObjectRegistry.getSTIBase(candidateLookupName);\n return stiBase\n ? resolveSTIBaseLookupName(candidateLookupName, stiBase)\n : candidateLookupName;\n }\n\n return className;\n}\n\n/**\n * Options for creating a test database\n */\nexport interface TestDatabaseOptions {\n /**\n * Database type (default: 'sqlite')\n * - 'sqlite': SQLite database\n * - 'json': JSON adapter (stores data as JSON files with DuckDB for querying)\n * - 'duckdb': Native DuckDB database\n */\n type?: 'sqlite' | 'json' | 'duckdb';\n\n /**\n * Database URL (default: ':memory:')\n * Use ':memory:' for in-memory databases (fastest for tests)\n * Or provide a file path for persistent test databases\n */\n url?: string;\n\n /**\n * Pre-existing database to initialize schemas in.\n * If provided, `type` and `url` are ignored.\n */\n db?: DatabaseInterface;\n\n /**\n * Specific classes to setup schemas for.\n * If not provided, sets up schemas for all registered classes.\n */\n classes?: string[];\n\n /**\n * Whether to include system tables (default: true)\n * System tables include _smrt_contexts, _smrt_migrations, etc.\n */\n includeSystemTables?: boolean;\n}\n\nfunction resolveRequestedSchemaClassName(className: string): string {\n const registered = ObjectRegistry.getClass(className);\n if (!registered) {\n return className;\n }\n\n if (\n !isCollectionRegistration(\n className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n const stiBase = ObjectRegistry.getSTIBase(className);\n return stiBase ? resolveSTIBaseLookupName(className, stiBase) : className;\n }\n\n return resolveCollectionSchemaClassName(className, registered);\n}\n\nfunction resolveRequestedSchemaClassNames(classNames: string[]): string[] {\n const resolved: string[] = [];\n const seen = new Set<string>();\n\n for (const className of classNames) {\n const schemaClassName = resolveRequestedSchemaClassName(className);\n if (seen.has(schemaClassName)) {\n continue;\n }\n\n seen.add(schemaClassName);\n resolved.push(schemaClassName);\n }\n\n return resolved;\n}\n\n/**\n * Creates an in-memory test database with schemas pre-created\n *\n * This utility is designed for testing. It creates an in-memory database\n * and initializes all registered SMRT object schemas using the **same\n * schema generation logic** as the production migration system.\n *\n * **Key features:**\n * - Uses `SchemaGenerator.generateSQL()` - the single source of truth for DDL\n * - Handles STI (Single Table Inheritance) correctly\n * - Creates system tables for framework functionality\n * - Safe for parallel test execution (each call creates isolated instance)\n *\n * @param options - Configuration options\n * @returns Promise resolving to configured DatabaseInterface\n *\n * @example\n * ```typescript\n * // Basic usage - all registered schemas\n * const db = await getTestDatabase();\n *\n * // Specific classes only\n * const db = await getTestDatabase({ classes: ['Council', 'Meeting'] });\n *\n * // JSON adapter instead of SQLite\n * const db = await getTestDatabase({ type: 'json' });\n *\n * // Native DuckDB adapter\n * const db = await getTestDatabase({ type: 'duckdb', url: ':memory:' });\n *\n * // File-based database for persistent tests\n * const db = await getTestDatabase({ type: 'sqlite', url: '/tmp/test.db' });\n *\n * // Initialize schemas in an existing database\n * const myDb = await getDatabase({ type: 'sqlite', url: ':memory:' });\n * await getTestDatabase({ db: myDb });\n *\n * // Skip system tables (rare use case)\n * const db = await getTestDatabase({ includeSystemTables: false });\n * ```\n */\nexport async function getTestDatabase(\n options: TestDatabaseOptions = {},\n): Promise<DatabaseInterface> {\n const {\n type = 'sqlite',\n url = ':memory:',\n db: existingDb,\n classes,\n includeSystemTables = true,\n } = options;\n\n // Use existing database or create new one\n const db =\n existingDb ??\n (await getDatabase({\n type,\n url,\n __smrtSkipVitestSchemaPreparation: true,\n } as TestDatabaseConnectionOptions));\n\n // Initialize system tables (same as production)\n if (includeSystemTables) {\n await initializeSystemTables(db);\n }\n\n // Get class names to setup\n const classNames = resolveRequestedSchemaClassNames(\n classes ?? ObjectRegistry.getQualifiedClassNames(),\n );\n\n // Skip if no classes registered\n if (classNames.length === 0) {\n return db;\n }\n\n // Use the same schema generation as production\n const schemaGenerator = new SchemaGenerator();\n const ddlEngine =\n type === 'json' ||\n typeof (db as { exportTable?: unknown }).exportTable === 'function'\n ? 'json'\n : type === 'duckdb'\n ? 'duckdb'\n : 'sqlite';\n\n // Track created tables to avoid duplicates (STI base classes)\n const createdTables = new Set<string>();\n\n for (const className of classNames) {\n // R11: the registration carries idType (native uuid vs text); read it\n // below when building runtimeSchemaConfig.\n const registered = ObjectRegistry.getClass(className);\n // Skip STI children - their schema is part of the base class table.\n // main (#1324): isSTIChild() compares RegisteredClass *identity* rather\n // than raw strings, so it stays correct under R5-canon (getSTIBase returns\n // qualified names) while also handling collection/override registrations.\n if (isSTIChild(className)) {\n continue;\n }\n\n const tableName = ObjectRegistry.getTableName(className);\n if (!tableName || createdTables.has(tableName)) {\n continue;\n }\n\n const fields = await ObjectRegistry.getAllFields(className);\n const strategy = ObjectRegistry.getTableStrategy(className);\n const runtimeSchemaConfig = {\n conflictColumns: ObjectRegistry.getConflictColumns(className),\n idType: registered?.config.idType,\n registry: ObjectRegistry,\n };\n\n // Generate schema using SchemaGenerator (same as migrations)\n const schema =\n strategy === 'sti'\n ? await schemaGenerator.generateSTISchemaFromRegistry(\n className,\n tableName,\n fields,\n runtimeSchemaConfig,\n )\n : schemaGenerator.generateSchemaFromRegistry(\n className,\n tableName,\n fields,\n runtimeSchemaConfig,\n );\n\n // Generate DDL using generateSQL() - the single source of truth\n const ddl = schemaGenerator.generateSQL(schema, ddlEngine);\n\n try {\n await db.query(ddl);\n createdTables.add(tableName);\n\n // Create indexes (use DDL strategy so jsonPath / where / etc. render)\n const ddlStrategy = (\n await import('../schema/ddl/index.js')\n ).getDDLStrategy(ddlEngine);\n const indexStatements = ddlStrategy.generateIndexes(schema);\n for (const indexSQL of indexStatements) {\n try {\n await db.query(indexSQL);\n } catch (indexError) {\n // Log but don't fail on index creation errors\n // Some indexes may fail if columns don't exist (STI meta fields)\n console.warn(\n `[getTestDatabase] Warning: Failed to create index: ${indexError instanceof Error ? indexError.message : String(indexError)} (SQL: ${indexSQL})`,\n );\n }\n }\n } catch (error) {\n // Provide helpful error message for table creation failures\n throw new Error(\n `[getTestDatabase] Failed to create table '${tableName}' for class '${className}': ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n return db;\n}\n\n/**\n * Initialize SMRT system tables in a database\n *\n * System tables use _smrt_ prefix and store framework metadata.\n * All statements use `IF NOT EXISTS` for idempotency.\n *\n * @param db - Database interface to initialize\n */\nasync function initializeSystemTables(db: DatabaseInterface): Promise<void> {\n await ensureLegacySystemTableCompatibility(db);\n\n const configuredDb = db as DatabaseInterface & {\n config?: { type?: string; url?: string };\n type?: string;\n };\n const engine = detectEngine(\n db.url || configuredDb.config?.url || '',\n configuredDb.type || configuredDb.config?.type,\n );\n\n // Split multi-statement SQL into individual statements\n const allStatements: string[] = [];\n for (const multiStatementSQL of getSystemTableDDL(engine)) {\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() — system tables use CREATE TABLE/INDEX IF NOT EXISTS\n // which databases handle natively without per-column existence checks.\n for (const statement of allStatements) {\n await db.query(statement);\n }\n\n // PostgreSQL keeps the best-effort append boundary in a function that must\n // be executed whole rather than included in semicolon-split portable DDL.\n await ensurePostgresChangeFeedAppendFunction(db);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-core",
3
- "version": "0.40.15",
3
+ "version": "0.40.17",
4
4
  "description": "Core AI agent framework with standardized collections, object-relational mapping, and code generators",
5
5
  "author": "HappyVertical",
6
6
  "type": "module",
@@ -35,6 +35,11 @@
35
35
  "import": "./dist/filesystem.js",
36
36
  "default": "./dist/filesystem.js"
37
37
  },
38
+ "./schema": {
39
+ "types": "./dist/schema.d.ts",
40
+ "import": "./dist/schema.js",
41
+ "default": "./dist/schema.js"
42
+ },
38
43
  "./schema/utils": {
39
44
  "types": "./dist/schema/utils.d.ts",
40
45
  "import": "./dist/schema/utils.js",
@@ -159,9 +164,9 @@
159
164
  "tsx": "^4.23.0",
160
165
  "typescript": "5.9.3",
161
166
  "yaml": "^2.9.0",
162
- "@happyvertical/smrt-config": "0.40.15",
163
- "@happyvertical/smrt-scanner": "0.40.15",
164
- "@happyvertical/smrt-types": "0.40.15"
167
+ "@happyvertical/smrt-config": "0.40.17",
168
+ "@happyvertical/smrt-scanner": "0.40.17",
169
+ "@happyvertical/smrt-types": "0.40.17"
165
170
  },
166
171
  "peerDependencies": {
167
172
  "@huggingface/transformers": ">=3.0.0 <4.0.0",
@@ -199,7 +204,7 @@
199
204
  "generate": "node scripts/generate-manifest.js",
200
205
  "generate:test": "node scripts/generate-test-manifest.js",
201
206
  "test": "node scripts/generate-test-manifest.js && vitest run",
202
- "test:postgres": "node ../../scripts/run-with-ci-postgres.mjs -- pnpm exec vitest run src/__tests__/change-feed-concurrency.optional.test.ts",
207
+ "test:postgres": "node scripts/generate-test-manifest.js && NODE_OPTIONS=--trace-deprecation node ../../scripts/run-with-ci-postgres.mjs -- pnpm exec vitest run src/__tests__/change-feed-concurrency.optional.test.ts src/__tests__/issue-2069-postgres-date-instant.optional.test.ts src/__tests__/issue-2070-postgres-hydration.optional.test.ts",
203
208
  "test:integration": "TEST_INTEGRATION=1 vitest run src/__tests__/full-registry-integration.test.ts src/__tests__/manifest-no-leak.test.ts",
204
209
  "test:watch": "node scripts/generate-test-manifest.js && vitest",
205
210
  "build": "node scripts/generate-manifest.js && node scripts/generate-test-manifest.js && vite build && cp -r src/vite-plugin/templates dist/vite-plugin/ && cp -r scripts dist/ && cp src/manifest/static-manifest.json dist/manifest.json && cp src/manifest/smrt-knowledge.json dist/smrt-knowledge.json",