@happyvertical/smrt-core 0.42.6 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +39 -43
- package/agents/collection-reads.md +40 -0
- package/agents/data-query.md +21 -0
- package/agents/latest-related.md +35 -0
- package/agents/memory.md +16 -0
- package/agents/schema-paths.md +29 -15
- package/dist/browser.js +2 -2
- package/dist/cascade.d.ts +7 -6
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +19 -21
- package/dist/cascade.js.map +1 -1
- package/dist/class.d.ts +17 -0
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +30 -0
- package/dist/class.js.map +1 -1
- package/dist/collection.d.ts +164 -3
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +345 -10
- package/dist/collection.js.map +1 -1
- package/dist/decorators/index.d.ts +33 -15
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +16 -11
- package/dist/decorators/index.js.map +1 -1
- package/dist/generators/rest.d.ts.map +1 -1
- package/dist/generators/rest.js +10 -2
- package/dist/generators/rest.js.map +1 -1
- package/dist/generators/tool-schema.d.ts +14 -0
- package/dist/generators/tool-schema.d.ts.map +1 -1
- package/dist/generators/tool-schema.js +13 -1
- package/dist/generators/tool-schema.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/interceptors.d.ts +2 -1
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js.map +1 -1
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +53 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +62 -1
- package/dist/migrations/differ.d.ts +2 -0
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +75 -0
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/generator.js +19 -2
- package/dist/migrations/generator.js.map +1 -1
- package/dist/migrations/orchestrate.d.ts.map +1 -1
- package/dist/migrations/orchestrate.js +4 -1
- package/dist/migrations/orchestrate.js.map +1 -1
- package/dist/object.d.ts +4 -4
- package/dist/object.js +4 -4
- package/dist/object.js.map +1 -1
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +9 -0
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +6 -0
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +78 -8
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry.d.ts +5 -7
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +6 -8
- package/dist/registry.js.map +1 -1
- package/dist/runtime/types.d.ts +2 -2
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +62 -18
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +5 -1
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/base-strategy.js +2 -0
- package/dist/schema/ddl/base-strategy.js.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.d.ts +1 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.js +17 -1
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +1 -1
- package/dist/schema/foreign-key-ddl.d.ts +9 -0
- package/dist/schema/foreign-key-ddl.d.ts.map +1 -0
- package/dist/schema/foreign-key-ddl.js +53 -0
- package/dist/schema/foreign-key-ddl.js.map +1 -0
- package/dist/schema/foreign-key-planner.d.ts +18 -0
- package/dist/schema/foreign-key-planner.d.ts.map +1 -0
- package/dist/schema/foreign-key-planner.js +109 -0
- package/dist/schema/foreign-key-planner.js.map +1 -0
- package/dist/schema/foreign-key-policy.d.ts +21 -0
- package/dist/schema/foreign-key-policy.d.ts.map +1 -0
- package/dist/schema/foreign-key-policy.js +41 -0
- package/dist/schema/foreign-key-policy.js.map +1 -0
- package/dist/schema/generator.d.ts +3 -0
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +54 -10
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +4 -1
- package/dist/schema/manifest-schema.d.ts.map +1 -1
- package/dist/schema/manifest-schema.js +20 -3
- package/dist/schema/manifest-schema.js.map +1 -1
- package/dist/schema/schema-aggregator.d.ts +7 -1
- package/dist/schema/schema-aggregator.d.ts.map +1 -1
- package/dist/schema/schema-aggregator.js +54 -9
- package/dist/schema/schema-aggregator.js.map +1 -1
- package/dist/schema/schema-manager.d.ts +1 -6
- package/dist/schema/schema-manager.d.ts.map +1 -1
- package/dist/schema/schema-manager.js +26 -30
- package/dist/schema/schema-manager.js.map +1 -1
- package/dist/schema/types.d.ts +8 -5
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +14 -1
- package/dist/schema/utils.js.map +1 -1
- package/dist/schema.js +4 -1
- package/dist/smrt-knowledge.json +63 -8
- package/dist/testing/database.d.ts +11 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +80 -9
- package/dist/testing/database.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +12 -3
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +24 -3
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts +3 -1
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +24 -4
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +4 -4
package/dist/testing/database.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { schemaForeignKeys } from "../schema/foreign-key-ddl.js";
|
|
1
2
|
import { detectEngine } from "../schema/ddl/index.js";
|
|
2
3
|
import { getSystemTableDDL } from "../system/schema.js";
|
|
3
4
|
import { ensurePostgresChangeFeedAppendFunction } from "../change-feed.js";
|
|
@@ -5,6 +6,7 @@ import { ensureLegacySystemTableCompatibility } from "../system/compatibility.js
|
|
|
5
6
|
import { SchemaGenerator } from "../schema/generator.js";
|
|
6
7
|
import { isCollectionRegistration, resolveCollectionItemClassName, resolveRelatedRegistration } from "../registry/collection-resolution.js";
|
|
7
8
|
import { ObjectRegistry } from "../registry.js";
|
|
9
|
+
import { planForeignKeyCreation } from "../schema/foreign-key-planner.js";
|
|
8
10
|
import { getDatabase } from "@happyvertical/sql";
|
|
9
11
|
//#region src/testing/database.ts
|
|
10
12
|
function resolveSTIBaseRegistration(className, stiBaseName) {
|
|
@@ -54,6 +56,29 @@ function resolveCollectionSchemaClassName(className, registered) {
|
|
|
54
56
|
}
|
|
55
57
|
return className;
|
|
56
58
|
}
|
|
59
|
+
/** Resolve the DDL dialect used when preparing an existing test database. */
|
|
60
|
+
function resolveTestDatabaseDDLEngine(type, db, inferFromDatabase = false) {
|
|
61
|
+
if (!inferFromDatabase) return type ?? "sqlite";
|
|
62
|
+
const configuredDb = db;
|
|
63
|
+
if (typeof configuredDb.inferSchemaFromJSON === "function" || typeof configuredDb.getTableLoadErrors === "function") return "json";
|
|
64
|
+
if (typeof configuredDb.exportTable === "function") {
|
|
65
|
+
if (typeof configuredDb.getTableSchema === "function" && typeof configuredDb.alterTable?.addColumn === "function") return "duckdb";
|
|
66
|
+
return "json";
|
|
67
|
+
}
|
|
68
|
+
return detectEngine(db.url || configuredDb.config?.url || "", configuredDb.type || configuredDb.config?.type);
|
|
69
|
+
}
|
|
70
|
+
function omitTestForeignKeyConstraints(schema) {
|
|
71
|
+
const foreignKeyTargets = new Set(schemaForeignKeys(schema).map((foreignKey) => foreignKey.referencesTable));
|
|
72
|
+
return {
|
|
73
|
+
...schema,
|
|
74
|
+
columns: Object.fromEntries(Object.entries(schema.columns).map(([name, column]) => [name, column.foreignKey ? {
|
|
75
|
+
...column,
|
|
76
|
+
foreignKey: void 0
|
|
77
|
+
} : column])),
|
|
78
|
+
foreignKeys: [],
|
|
79
|
+
dependencies: schema.dependencies.filter((dependency) => !foreignKeyTargets.has(dependency))
|
|
80
|
+
};
|
|
81
|
+
}
|
|
57
82
|
function resolveRequestedSchemaClassName(className) {
|
|
58
83
|
const registered = ObjectRegistry.getClass(className);
|
|
59
84
|
if (!registered) return className;
|
|
@@ -116,9 +141,9 @@ function resolveRequestedSchemaClassNames(classNames) {
|
|
|
116
141
|
* ```
|
|
117
142
|
*/
|
|
118
143
|
async function getTestDatabase(options = {}) {
|
|
119
|
-
const { type
|
|
144
|
+
const { type, url = ":memory:", db: existingDb, classes, includeSystemTables = true, omitForeignKeyConstraints = false } = options;
|
|
120
145
|
const db = existingDb ?? await getDatabase({
|
|
121
|
-
type,
|
|
146
|
+
type: type ?? "sqlite",
|
|
122
147
|
url,
|
|
123
148
|
__smrtSkipVitestSchemaPreparation: true
|
|
124
149
|
});
|
|
@@ -126,13 +151,14 @@ async function getTestDatabase(options = {}) {
|
|
|
126
151
|
const classNames = resolveRequestedSchemaClassNames(classes ?? ObjectRegistry.getQualifiedClassNames());
|
|
127
152
|
if (classNames.length === 0) return db;
|
|
128
153
|
const schemaGenerator = new SchemaGenerator();
|
|
129
|
-
const ddlEngine = type
|
|
130
|
-
const
|
|
154
|
+
const ddlEngine = resolveTestDatabaseDDLEngine(type, db, existingDb !== void 0);
|
|
155
|
+
const schemas = /* @__PURE__ */ new Map();
|
|
156
|
+
const authoritativeSchemas = ObjectRegistry.getAllSchemasAsDefinitions();
|
|
131
157
|
for (const className of classNames) {
|
|
132
158
|
const registered = ObjectRegistry.getClass(className);
|
|
133
159
|
if (isSTIChild(className)) continue;
|
|
134
160
|
const tableName = ObjectRegistry.getTableName(className);
|
|
135
|
-
if (!tableName ||
|
|
161
|
+
if (!tableName || schemas.has(tableName)) continue;
|
|
136
162
|
const fields = await ObjectRegistry.getAllFields(className);
|
|
137
163
|
const strategy = ObjectRegistry.getTableStrategy(className);
|
|
138
164
|
const runtimeSchemaConfig = {
|
|
@@ -142,20 +168,65 @@ async function getTestDatabase(options = {}) {
|
|
|
142
168
|
registry: ObjectRegistry
|
|
143
169
|
};
|
|
144
170
|
const schema = strategy === "sti" ? await schemaGenerator.generateSTISchemaFromRegistry(className, tableName, fields, runtimeSchemaConfig) : schemaGenerator.generateSchemaFromRegistry(className, tableName, fields, runtimeSchemaConfig);
|
|
171
|
+
const authoritativeSchema = authoritativeSchemas[tableName];
|
|
172
|
+
const effectiveColumns = authoritativeSchema ? Object.fromEntries(Object.entries(schema.columns).map(([name, column]) => {
|
|
173
|
+
const manifestColumn = authoritativeSchema.columns[name];
|
|
174
|
+
return [name, manifestColumn ? {
|
|
175
|
+
...column,
|
|
176
|
+
foreignKey: manifestColumn.foreignKey
|
|
177
|
+
} : column];
|
|
178
|
+
})) : schema.columns;
|
|
179
|
+
const effectiveForeignKeys = schemaForeignKeys({
|
|
180
|
+
columns: effectiveColumns,
|
|
181
|
+
foreignKeys: []
|
|
182
|
+
});
|
|
183
|
+
const effectiveSchema = {
|
|
184
|
+
...schema,
|
|
185
|
+
columns: effectiveColumns,
|
|
186
|
+
foreignKeys: effectiveForeignKeys,
|
|
187
|
+
dependencies: effectiveForeignKeys.map((foreignKey) => foreignKey.referencesTable).filter((dependency) => dependency !== tableName)
|
|
188
|
+
};
|
|
189
|
+
schemas.set(tableName, {
|
|
190
|
+
schema: omitForeignKeyConstraints ? omitTestForeignKeyConstraints(effectiveSchema) : effectiveSchema,
|
|
191
|
+
className
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const addDependencies = (schema) => {
|
|
195
|
+
for (const foreignKey of schemaForeignKeys(schema)) {
|
|
196
|
+
if (schemas.has(foreignKey.referencesTable)) continue;
|
|
197
|
+
const dependency = authoritativeSchemas[foreignKey.referencesTable];
|
|
198
|
+
if (!dependency) continue;
|
|
199
|
+
const effectiveDependency = omitForeignKeyConstraints ? omitTestForeignKeyConstraints(dependency) : dependency;
|
|
200
|
+
schemas.set(dependency.tableName, {
|
|
201
|
+
schema: effectiveDependency,
|
|
202
|
+
className: dependency.tableName
|
|
203
|
+
});
|
|
204
|
+
addDependencies(effectiveDependency);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
for (const { schema } of [...schemas.values()]) addDependencies(schema);
|
|
208
|
+
const tablePlan = planForeignKeyCreation([...schemas.values()].map(({ schema }) => schema), ddlEngine);
|
|
209
|
+
const ddlStrategy = (await import("../schema/ddl/index.js")).getDDLStrategy(ddlEngine);
|
|
210
|
+
for (const schema of tablePlan.schemas) {
|
|
211
|
+
const className = schemas.get(schema.tableName)?.className ?? "unknown";
|
|
145
212
|
const ddl = schemaGenerator.generateSQL(schema, ddlEngine);
|
|
146
213
|
try {
|
|
147
214
|
await db.query(ddl);
|
|
148
|
-
|
|
149
|
-
const indexStatements = (await import("../schema/ddl/index.js")).getDDLStrategy(ddlEngine).generateIndexes(schema);
|
|
215
|
+
const indexStatements = ddlStrategy.generateIndexes(schema);
|
|
150
216
|
for (const indexSQL of indexStatements) try {
|
|
151
217
|
await db.query(indexSQL);
|
|
152
218
|
} catch (indexError) {
|
|
153
219
|
console.warn(`[getTestDatabase] Warning: Failed to create index: ${indexError instanceof Error ? indexError.message : String(indexError)} (SQL: ${indexSQL})`);
|
|
154
220
|
}
|
|
155
221
|
} catch (error) {
|
|
156
|
-
throw new Error(`[getTestDatabase] Failed to create table '${tableName}' for class '${className}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
222
|
+
throw new Error(`[getTestDatabase] Failed to create table '${schema.tableName}' for class '${className}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
157
223
|
}
|
|
158
224
|
}
|
|
225
|
+
for (const statement of tablePlan.deferredStatements) try {
|
|
226
|
+
await db.query(statement);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
throw new Error(`[getTestDatabase] Failed to add deferred foreign-key constraint: ${error instanceof Error ? error.message : String(error)} (SQL: ${statement})`, { cause: error });
|
|
229
|
+
}
|
|
159
230
|
return db;
|
|
160
231
|
}
|
|
161
232
|
/**
|
|
@@ -179,6 +250,6 @@ async function initializeSystemTables(db) {
|
|
|
179
250
|
await ensurePostgresChangeFeedAppendFunction(db);
|
|
180
251
|
}
|
|
181
252
|
//#endregion
|
|
182
|
-
export { getTestDatabase };
|
|
253
|
+
export { getTestDatabase, resolveTestDatabaseDDLEngine };
|
|
183
254
|
|
|
184
255
|
//# sourceMappingURL=database.js.map
|
|
@@ -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 { 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 // Mirrors `schema/utils.ts`: this bag is rebuilt by hand, so every `@smrt()`\n // option the generator reads has to be listed or tests silently diverge\n // from the production schema. `indexes` (#2357) was missing from both.\n const runtimeSchemaConfig = {\n conflictColumns: ObjectRegistry.getConflictColumns(className),\n idType: registered?.config.idType,\n indexes: registered?.config.indexes,\n registry: ObjectRegistry,\n };\n\n // Generate schema through the registry paths. Production (`manifest.json`\n // and therefore `smrt db:migrate`) uses the manifest paths\n // (`generateSTISchemaFromManifest` / `generateCTISchemaFromManifest`);\n // the two families are held to the same column and index sets by\n // `src/schema/schema-path-parity.test.ts` (#2359) — do not assume they\n // agree by construction.\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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"}
|
|
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 { schemaForeignKeys } from '../schema/foreign-key-ddl.js';\nimport { planForeignKeyCreation } from '../schema/foreign-key-planner.js';\nimport { SchemaGenerator } from '../schema/generator.js';\nimport type { SchemaDefinition } from '../schema/types.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 * - 'postgres': PostgreSQL database (normally supplied through `db`)\n */\n type?: 'sqlite' | 'json' | 'duckdb' | 'postgres';\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 /**\n * Explicitly omit physical FK constraints for adapter/query tests whose\n * subject is unrelated to referential enforcement. Defaults to `false`.\n * This is required (and deliberately noisy at the call site) when a DuckDB\n * fixture uses generated `ON UPDATE CASCADE`, which DuckDB cannot enforce.\n */\n omitForeignKeyConstraints?: boolean;\n}\n\n/** Resolve the DDL dialect used when preparing an existing test database. */\nexport function resolveTestDatabaseDDLEngine(\n type: TestDatabaseOptions['type'],\n db: DatabaseInterface,\n inferFromDatabase = false,\n): 'sqlite' | 'json' | 'duckdb' | 'postgres' {\n // When this helper created the adapter, the requested type is authoritative.\n // Native DuckDB and JSON-on-DuckDB both expose exportTable(), so capability\n // inference must not override an explicit native DuckDB request.\n if (!inferFromDatabase) {\n return type ?? 'sqlite';\n }\n\n const configuredDb = db as DatabaseInterface & {\n config?: { type?: string; url?: string };\n type?: string;\n exportTable?: unknown;\n inferSchemaFromJSON?: unknown;\n getTableLoadErrors?: unknown;\n };\n\n if (\n typeof configuredDb.inferSchemaFromJSON === 'function' ||\n typeof configuredDb.getTableLoadErrors === 'function'\n ) {\n return 'json';\n }\n\n if (typeof configuredDb.exportTable === 'function') {\n // The native DuckDB adapter exposes schema evolution capabilities that the\n // JSON adapter does not. Keep exportTable-only test doubles compatible with\n // the historical JSON structural marker.\n if (\n typeof configuredDb.getTableSchema === 'function' &&\n typeof configuredDb.alterTable?.addColumn === 'function'\n ) {\n return 'duckdb';\n }\n return 'json';\n }\n\n return detectEngine(\n db.url || configuredDb.config?.url || '',\n configuredDb.type || configuredDb.config?.type,\n );\n}\n\nfunction omitTestForeignKeyConstraints(\n schema: SchemaDefinition,\n): SchemaDefinition {\n const foreignKeyTargets = new Set(\n schemaForeignKeys(schema).map((foreignKey) => foreignKey.referencesTable),\n );\n return {\n ...schema,\n columns: Object.fromEntries(\n Object.entries(schema.columns).map(([name, column]) => [\n name,\n column.foreignKey ? { ...column, foreignKey: undefined } : column,\n ]),\n ),\n foreignKeys: [],\n dependencies: schema.dependencies.filter(\n (dependency) => !foreignKeyTargets.has(dependency),\n ),\n };\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,\n url = ':memory:',\n db: existingDb,\n classes,\n includeSystemTables = true,\n omitForeignKeyConstraints = false,\n } = options;\n\n // Use existing database or create new one\n const db =\n existingDb ??\n (await getDatabase({\n type: type ?? 'sqlite',\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 = resolveTestDatabaseDDLEngine(\n type,\n db,\n existingDb !== undefined,\n );\n\n // Collect every table before executing DDL so dependency ordering and cycle\n // handling are identical to production migration/schema paths (#2413).\n const schemas = new Map<\n string,\n { schema: SchemaDefinition; className: string }\n >();\n const authoritativeSchemas = ObjectRegistry.getAllSchemasAsDefinitions();\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 || schemas.has(tableName)) {\n continue;\n }\n\n const fields = await ObjectRegistry.getAllFields(className);\n const strategy = ObjectRegistry.getTableStrategy(className);\n // Mirrors `schema/utils.ts`: this bag is rebuilt by hand, so every `@smrt()`\n // option the generator reads has to be listed or tests silently diverge\n // from the production schema. `indexes` (#2357) was missing from both.\n const runtimeSchemaConfig = {\n conflictColumns: ObjectRegistry.getConflictColumns(className),\n idType: registered?.config.idType,\n indexes: registered?.config.indexes,\n registry: ObjectRegistry,\n };\n\n // Generate schema through the registry paths. Production (`manifest.json`\n // and therefore `smrt db:migrate`) uses the manifest paths\n // (`generateSTISchemaFromManifest` / `generateCTISchemaFromManifest`);\n // the two families are held to the same column and index sets by\n // `src/schema/schema-path-parity.test.ts` (#2359) — do not assume they\n // agree by construction.\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 // Manifest registrations can carry explicit FK actions that differ from\n // decorator defaults. The deterministic merged table schema preserves\n // those authoritative actions while still backfilling runtime-only fields.\n const authoritativeSchema = authoritativeSchemas[tableName];\n const effectiveColumns = authoritativeSchema\n ? Object.fromEntries(\n Object.entries(schema.columns).map(([name, column]) => {\n const manifestColumn = authoritativeSchema.columns[name];\n return [\n name,\n manifestColumn\n ? { ...column, foreignKey: manifestColumn.foreignKey }\n : column,\n ];\n }),\n )\n : schema.columns;\n const effectiveForeignKeys = schemaForeignKeys({\n columns: effectiveColumns,\n foreignKeys: [],\n });\n const effectiveSchema: SchemaDefinition = {\n ...schema,\n columns: effectiveColumns,\n foreignKeys: effectiveForeignKeys,\n dependencies: effectiveForeignKeys\n .map((foreignKey) => foreignKey.referencesTable)\n .filter((dependency) => dependency !== tableName),\n };\n\n schemas.set(tableName, {\n schema: omitForeignKeyConstraints\n ? omitTestForeignKeyConstraints(effectiveSchema)\n : effectiveSchema,\n className,\n });\n }\n\n // Explicitly requested test classes still need their registered parents.\n // Add the authoritative dependency closure so a child-only request cannot\n // emit a physical reference to a table this helper never creates.\n const addDependencies = (schema: SchemaDefinition): void => {\n for (const foreignKey of schemaForeignKeys(schema)) {\n if (schemas.has(foreignKey.referencesTable)) continue;\n const dependency = authoritativeSchemas[foreignKey.referencesTable];\n if (!dependency) continue;\n const effectiveDependency = omitForeignKeyConstraints\n ? omitTestForeignKeyConstraints(dependency)\n : dependency;\n schemas.set(dependency.tableName, {\n schema: effectiveDependency,\n className: dependency.tableName,\n });\n addDependencies(effectiveDependency);\n }\n };\n for (const { schema } of [...schemas.values()]) addDependencies(schema);\n\n const tablePlan = planForeignKeyCreation(\n [...schemas.values()].map(({ schema }) => schema),\n ddlEngine,\n );\n const ddlStrategy = (await import('../schema/ddl/index.js')).getDDLStrategy(\n ddlEngine,\n );\n\n for (const schema of tablePlan.schemas) {\n const className = schemas.get(schema.tableName)?.className ?? 'unknown';\n const ddl = schemaGenerator.generateSQL(schema, ddlEngine);\n\n try {\n await db.query(ddl);\n\n // Create indexes (use DDL strategy so jsonPath / where / etc. render)\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 '${schema.tableName}' for class '${className}': ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n for (const statement of tablePlan.deferredStatements) {\n try {\n await db.query(statement);\n } catch (error) {\n throw new Error(\n `[getTestDatabase] Failed to add deferred foreign-key constraint: ${error instanceof Error ? error.message : String(error)} (SQL: ${statement})`,\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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAClE,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AA0B7B,OAAO,EACL,kCAAkC,EAClC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,2BAA2B,EAC3B,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC;;;;WAIG;QACH,WAAW,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC9D,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA8BD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -449,7 +449,7 @@ function smrtPlugin(options = {}) {
|
|
|
449
449
|
case "smrt:ui": return await loadDefaultUI();
|
|
450
450
|
case "smrt:index-html": return await loadDefaultHTML();
|
|
451
451
|
case "smrt:cli": return await generateCLIModule(manifest);
|
|
452
|
-
case "smrt:web": return generateWebModule(manifest);
|
|
452
|
+
case "smrt:web": return generateWebModule(manifest, { kebabRoutes: svelteKit.kebabRoutes ?? false });
|
|
453
453
|
default: return null;
|
|
454
454
|
}
|
|
455
455
|
},
|
|
@@ -641,11 +641,11 @@ export { setupRoutes as default };
|
|
|
641
641
|
* {@link buildWebCollectionDefinition} so this value emission, the matching d.ts
|
|
642
642
|
* type emission, and the #1764 shape digest cannot drift.
|
|
643
643
|
*/
|
|
644
|
-
function generateWebModule(manifest) {
|
|
644
|
+
function generateWebModule(manifest, options = {}) {
|
|
645
645
|
const definitions = {};
|
|
646
646
|
for (const entry of selectWebCollectionEntries(manifest)) definitions[entry.collection] = {
|
|
647
647
|
...buildWebCollectionDefinition(entry, manifest),
|
|
648
|
-
toolDescriptors: buildWebToolDescriptors(entry)
|
|
648
|
+
toolDescriptors: buildWebToolDescriptors(entry, options)
|
|
649
649
|
};
|
|
650
650
|
const manifestHash = computeWebManifestHash(manifest);
|
|
651
651
|
return `
|
|
@@ -1063,6 +1063,14 @@ declare module '@happyvertical/smrt-virt-web' {
|
|
|
1063
1063
|
relatedCollection: string;
|
|
1064
1064
|
}
|
|
1065
1065
|
|
|
1066
|
+
export interface WebToolRouteDescriptor {
|
|
1067
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
1068
|
+
scope: 'item' | 'collection';
|
|
1069
|
+
path: string[];
|
|
1070
|
+
parameterAliases?: Record<string, string>;
|
|
1071
|
+
optionsBag?: boolean;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1066
1074
|
/** A WebMCP/MCP tool descriptor for one collection action (#1812). */
|
|
1067
1075
|
export interface WebToolDescriptor {
|
|
1068
1076
|
action: string;
|
|
@@ -1070,6 +1078,7 @@ declare module '@happyvertical/smrt-virt-web' {
|
|
|
1070
1078
|
description: string;
|
|
1071
1079
|
inputSchema: Record<string, unknown>;
|
|
1072
1080
|
readOnly: boolean;
|
|
1081
|
+
route?: WebToolRouteDescriptor;
|
|
1073
1082
|
}
|
|
1074
1083
|
|
|
1075
1084
|
export interface SmrtWebCollectionDefinition<TData = Record<string, unknown>> {
|