@fragno-dev/db 0.0.1 → 0.1.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/.turbo/turbo-build.log +137 -13
- package/.turbo/turbo-test.log +36 -0
- package/CHANGELOG.md +7 -0
- package/dist/adapters/adapters.d.ts +18 -0
- package/dist/adapters/adapters.d.ts.map +1 -0
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +21 -0
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -0
- package/dist/adapters/drizzle/drizzle-adapter.js +62 -0
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -0
- package/dist/adapters/drizzle/drizzle-query.d.ts +17 -0
- package/dist/adapters/drizzle/drizzle-query.d.ts.map +1 -0
- package/dist/adapters/drizzle/drizzle-query.js +139 -0
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -0
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +9 -0
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -0
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +300 -0
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -0
- package/dist/adapters/drizzle/drizzle-uow-decoder.js +82 -0
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -0
- package/dist/adapters/drizzle/drizzle-uow-executor.js +125 -0
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -0
- package/dist/adapters/drizzle/generate.js +273 -0
- package/dist/adapters/drizzle/generate.js.map +1 -0
- package/dist/adapters/drizzle/join-column-utils.js +28 -0
- package/dist/adapters/drizzle/join-column-utils.js.map +1 -0
- package/dist/adapters/drizzle/shared.js +11 -0
- package/dist/adapters/drizzle/shared.js.map +1 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts +23 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-adapter.js +119 -0
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -0
- package/dist/adapters/kysely/kysely-query-builder.js +306 -0
- package/dist/adapters/kysely/kysely-query-builder.js.map +1 -0
- package/dist/adapters/kysely/kysely-query-compiler.js +67 -0
- package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -0
- package/dist/adapters/kysely/kysely-query.js +158 -0
- package/dist/adapters/kysely/kysely-query.js.map +1 -0
- package/dist/adapters/kysely/kysely-uow-compiler.js +139 -0
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -0
- package/dist/adapters/kysely/kysely-uow-executor.js +89 -0
- package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -0
- package/dist/adapters/kysely/migration/execute.js +176 -0
- package/dist/adapters/kysely/migration/execute.js.map +1 -0
- package/dist/fragment.d.ts +54 -0
- package/dist/fragment.d.ts.map +1 -0
- package/dist/fragment.js +92 -0
- package/dist/fragment.js.map +1 -0
- package/dist/id.d.ts +2 -0
- package/dist/migration-engine/auto-from-schema.js +116 -0
- package/dist/migration-engine/auto-from-schema.js.map +1 -0
- package/dist/migration-engine/create.d.ts +41 -0
- package/dist/migration-engine/create.d.ts.map +1 -0
- package/dist/migration-engine/create.js +58 -0
- package/dist/migration-engine/create.js.map +1 -0
- package/dist/migration-engine/shared.d.ts +90 -0
- package/dist/migration-engine/shared.d.ts.map +1 -0
- package/dist/migration-engine/shared.js +8 -0
- package/dist/migration-engine/shared.js.map +1 -0
- package/dist/mod.d.ts +55 -2
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +111 -2
- package/dist/mod.js.map +1 -1
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/column-builder.js +108 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/column-builder.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/column.js +55 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/column.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/entity.js +18 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/entity.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/columns/common.js +183 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/columns/common.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/columns/enum.js +58 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/columns/enum.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/foreign-keys.js +68 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/foreign-keys.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/unique-constraint.js +56 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/unique-constraint.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/utils/array.js +65 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/pg-core/utils/array.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/expressions/conditions.js +81 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/expressions/conditions.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/expressions/select.js +13 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/expressions/select.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/functions/aggregate.js +10 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/functions/aggregate.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/sql.js +372 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/sql/sql.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/subquery.js +23 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/subquery.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/table.js +62 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/table.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/table.utils.js +6 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/table.utils.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/tracing-utils.js +8 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/tracing-utils.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/tracing.js +8 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/tracing.js.map +1 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/view-common.js +6 -0
- package/dist/node_modules/.bun/drizzle-orm@0.44.6_4fae081eecb963e2/node_modules/drizzle-orm/view-common.js.map +1 -0
- package/dist/query/condition-builder.d.ts +41 -0
- package/dist/query/condition-builder.d.ts.map +1 -0
- package/dist/query/condition-builder.js +93 -0
- package/dist/query/condition-builder.js.map +1 -0
- package/dist/query/cursor.d.ts +88 -0
- package/dist/query/cursor.d.ts.map +1 -0
- package/dist/query/cursor.js +103 -0
- package/dist/query/cursor.js.map +1 -0
- package/dist/query/orm/orm.d.ts +18 -0
- package/dist/query/orm/orm.d.ts.map +1 -0
- package/dist/query/orm/orm.js +48 -0
- package/dist/query/orm/orm.js.map +1 -0
- package/dist/query/query.d.ts +79 -0
- package/dist/query/query.d.ts.map +1 -0
- package/dist/query/query.js +1 -0
- package/dist/query/result-transform.js +155 -0
- package/dist/query/result-transform.js.map +1 -0
- package/dist/query/unit-of-work.d.ts +435 -0
- package/dist/query/unit-of-work.d.ts.map +1 -0
- package/dist/query/unit-of-work.js +549 -0
- package/dist/query/unit-of-work.js.map +1 -0
- package/dist/schema/create.d.ts +273 -116
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +410 -222
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/serialize.js +101 -0
- package/dist/schema/serialize.js.map +1 -0
- package/dist/schema-generator/schema-generator.d.ts +15 -0
- package/dist/schema-generator/schema-generator.d.ts.map +1 -0
- package/dist/shared/providers.d.ts +6 -0
- package/dist/shared/providers.d.ts.map +1 -0
- package/dist/util/import-generator.js +26 -0
- package/dist/util/import-generator.js.map +1 -0
- package/dist/util/parse.js +15 -0
- package/dist/util/parse.js.map +1 -0
- package/dist/util/types.d.ts +8 -0
- package/dist/util/types.d.ts.map +1 -0
- package/package.json +63 -2
- package/src/adapters/adapters.ts +22 -0
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +433 -0
- package/src/adapters/drizzle/drizzle-adapter.test.ts +122 -0
- package/src/adapters/drizzle/drizzle-adapter.ts +118 -0
- package/src/adapters/drizzle/drizzle-query.ts +234 -0
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +1084 -0
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +546 -0
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +165 -0
- package/src/adapters/drizzle/drizzle-uow-executor.ts +213 -0
- package/src/adapters/drizzle/generate.test.ts +643 -0
- package/src/adapters/drizzle/generate.ts +481 -0
- package/src/adapters/drizzle/join-column-utils.test.ts +79 -0
- package/src/adapters/drizzle/join-column-utils.ts +39 -0
- package/src/adapters/drizzle/migrate-drizzle.test.ts +226 -0
- package/src/adapters/drizzle/shared.ts +22 -0
- package/src/adapters/drizzle/test-utils.ts +56 -0
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +789 -0
- package/src/adapters/kysely/kysely-adapter.ts +196 -0
- package/src/adapters/kysely/kysely-query-builder.test.ts +1344 -0
- package/src/adapters/kysely/kysely-query-builder.ts +611 -0
- package/src/adapters/kysely/kysely-query-compiler.ts +124 -0
- package/src/adapters/kysely/kysely-query.ts +254 -0
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +916 -0
- package/src/adapters/kysely/kysely-uow-compiler.ts +271 -0
- package/src/adapters/kysely/kysely-uow-executor.ts +149 -0
- package/src/adapters/kysely/kysely-uow-joins.test.ts +811 -0
- package/src/adapters/kysely/migration/execute-mysql.test.ts +1173 -0
- package/src/adapters/kysely/migration/execute-postgres.test.ts +2657 -0
- package/src/adapters/kysely/migration/execute.ts +382 -0
- package/src/adapters/kysely/migration/kysely-migrator.test.ts +197 -0
- package/src/fragment.test.ts +287 -0
- package/src/fragment.ts +198 -0
- package/src/migration-engine/auto-from-schema.test.ts +118 -58
- package/src/migration-engine/auto-from-schema.ts +103 -32
- package/src/migration-engine/create.test.ts +34 -46
- package/src/migration-engine/create.ts +41 -26
- package/src/migration-engine/shared.ts +26 -6
- package/src/mod.ts +197 -1
- package/src/query/condition-builder.test.ts +379 -0
- package/src/query/condition-builder.ts +294 -0
- package/src/query/cursor.test.ts +296 -0
- package/src/query/cursor.ts +147 -0
- package/src/query/orm/orm.ts +92 -0
- package/src/query/query-type.test.ts +429 -0
- package/src/query/query.ts +200 -0
- package/src/query/result-transform.test.ts +795 -0
- package/src/query/result-transform.ts +247 -0
- package/src/query/unit-of-work-types.test.ts +192 -0
- package/src/query/unit-of-work.test.ts +947 -0
- package/src/query/unit-of-work.ts +1199 -0
- package/src/schema/create.test.ts +653 -110
- package/src/schema/create.ts +708 -337
- package/src/schema/serialize.test.ts +559 -0
- package/src/schema/serialize.ts +359 -0
- package/src/schema-generator/schema-generator.ts +12 -0
- package/src/shared/config.ts +0 -8
- package/src/util/import-generator.ts +28 -0
- package/src/util/parse.ts +16 -0
- package/src/util/types.ts +4 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +11 -1
- package/vitest.config.ts +3 -0
- /package/dist/{cuid.js → id.js} +0 -0
- /package/src/{cuid.ts → id.ts} +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { decodeCursor, serializeCursorValues } from "../../query/cursor.js";
|
|
2
|
+
import { buildCondition } from "../../query/condition-builder.js";
|
|
3
|
+
import { createKyselyQueryBuilder } from "./kysely-query-builder.js";
|
|
4
|
+
import { createKyselyQueryCompiler } from "./kysely-query-compiler.js";
|
|
5
|
+
|
|
6
|
+
//#region src/adapters/kysely/kysely-uow-compiler.ts
|
|
7
|
+
/**
|
|
8
|
+
* Create a Kysely-specific Unit of Work compiler
|
|
9
|
+
*
|
|
10
|
+
* This compiler translates UOW operations into Kysely CompiledQuery objects
|
|
11
|
+
* that can be executed as a batch/transaction.
|
|
12
|
+
*
|
|
13
|
+
* @param schema - The database schema
|
|
14
|
+
* @param config - Kysely configuration
|
|
15
|
+
* @returns A UOWCompiler instance for Kysely
|
|
16
|
+
*/
|
|
17
|
+
function createKyselyUOWCompiler(schema, config) {
|
|
18
|
+
const queryCompiler = createKyselyQueryCompiler(schema, config);
|
|
19
|
+
const queryBuilder = createKyselyQueryBuilder(config.db, config.provider);
|
|
20
|
+
const { provider } = config;
|
|
21
|
+
function toTable(name) {
|
|
22
|
+
const table = schema.tables[name];
|
|
23
|
+
if (!table) throw new Error(`Invalid table name ${name}.`);
|
|
24
|
+
return table;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
compileRetrievalOperation(op) {
|
|
28
|
+
switch (op.type) {
|
|
29
|
+
case "count": return queryCompiler.count(op.table.name, { where: op.options.where });
|
|
30
|
+
case "find": {
|
|
31
|
+
const { useIndex: _useIndex, orderByIndex, joins: join, after, before, pageSize,...findManyOptions } = op.options;
|
|
32
|
+
let indexColumns = [];
|
|
33
|
+
let orderDirection = "asc";
|
|
34
|
+
if (orderByIndex) {
|
|
35
|
+
const index = op.table.indexes[orderByIndex.indexName];
|
|
36
|
+
orderDirection = orderByIndex.direction;
|
|
37
|
+
if (!index) if (orderByIndex.indexName === "_primary") indexColumns = [op.table.getIdColumn()];
|
|
38
|
+
else throw new Error(`Index "${orderByIndex.indexName}" not found on table "${op.table.name}"`);
|
|
39
|
+
else indexColumns = index.columns;
|
|
40
|
+
}
|
|
41
|
+
let orderBy;
|
|
42
|
+
if (indexColumns.length > 0) orderBy = indexColumns.map((col) => [col, orderDirection]);
|
|
43
|
+
let cursorCondition;
|
|
44
|
+
if ((after || before) && indexColumns.length > 0) {
|
|
45
|
+
const serializedValues = serializeCursorValues(decodeCursor(after || before), indexColumns, provider);
|
|
46
|
+
const isAfter = !!after;
|
|
47
|
+
const useGreaterThan = isAfter && orderDirection === "asc" || !isAfter && orderDirection === "desc";
|
|
48
|
+
if (indexColumns.length === 1) {
|
|
49
|
+
const col = indexColumns[0];
|
|
50
|
+
const val = serializedValues[col.ormName];
|
|
51
|
+
cursorCondition = {
|
|
52
|
+
type: "compare",
|
|
53
|
+
a: col,
|
|
54
|
+
operator: useGreaterThan ? ">" : "<",
|
|
55
|
+
b: val
|
|
56
|
+
};
|
|
57
|
+
} else throw new Error("Multi-column cursor pagination is not yet supported in Kysely Unit of Work implementation");
|
|
58
|
+
}
|
|
59
|
+
let combinedWhere;
|
|
60
|
+
if (findManyOptions.where) {
|
|
61
|
+
const whereResult = buildCondition(op.table.columns, findManyOptions.where);
|
|
62
|
+
if (whereResult === true) combinedWhere = void 0;
|
|
63
|
+
else if (whereResult === false) return null;
|
|
64
|
+
else combinedWhere = whereResult;
|
|
65
|
+
}
|
|
66
|
+
if (cursorCondition) if (combinedWhere) combinedWhere = {
|
|
67
|
+
type: "and",
|
|
68
|
+
items: [combinedWhere, cursorCondition]
|
|
69
|
+
};
|
|
70
|
+
else combinedWhere = cursorCondition;
|
|
71
|
+
if (join && join.length > 0) return queryBuilder.findMany(op.table, {
|
|
72
|
+
select: findManyOptions.select ?? true,
|
|
73
|
+
where: combinedWhere,
|
|
74
|
+
orderBy,
|
|
75
|
+
limit: pageSize,
|
|
76
|
+
join
|
|
77
|
+
});
|
|
78
|
+
return queryCompiler.findMany(op.table.name, {
|
|
79
|
+
...findManyOptions,
|
|
80
|
+
where: combinedWhere ? () => combinedWhere : void 0,
|
|
81
|
+
orderBy: orderBy?.map(([col, dir]) => [col.ormName, dir]),
|
|
82
|
+
limit: pageSize
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
compileMutationOperation(op) {
|
|
88
|
+
switch (op.type) {
|
|
89
|
+
case "create": return {
|
|
90
|
+
query: queryCompiler.create(op.table, op.values),
|
|
91
|
+
expectedAffectedRows: null
|
|
92
|
+
};
|
|
93
|
+
case "update": {
|
|
94
|
+
const table = toTable(op.table);
|
|
95
|
+
const idColumn = table.getIdColumn();
|
|
96
|
+
const versionColumn = table.getVersionColumn();
|
|
97
|
+
const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
|
|
98
|
+
const versionToCheck = getVersionToCheck(op.id, op.checkVersion);
|
|
99
|
+
const whereClause = versionToCheck !== void 0 ? () => buildCondition(table.columns, (eb) => eb.and(eb(idColumn.ormName, "=", externalId), eb(versionColumn.ormName, "=", versionToCheck))) : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, "=", externalId));
|
|
100
|
+
const query = queryCompiler.updateMany(op.table, {
|
|
101
|
+
where: whereClause,
|
|
102
|
+
set: op.set
|
|
103
|
+
});
|
|
104
|
+
return query ? {
|
|
105
|
+
query,
|
|
106
|
+
expectedAffectedRows: op.checkVersion ? 1 : null
|
|
107
|
+
} : null;
|
|
108
|
+
}
|
|
109
|
+
case "delete": {
|
|
110
|
+
const table = toTable(op.table);
|
|
111
|
+
const idColumn = table.getIdColumn();
|
|
112
|
+
const versionColumn = table.getVersionColumn();
|
|
113
|
+
const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
|
|
114
|
+
const versionToCheck = getVersionToCheck(op.id, op.checkVersion);
|
|
115
|
+
const whereClause = versionToCheck !== void 0 ? () => buildCondition(table.columns, (eb) => eb.and(eb(idColumn.ormName, "=", externalId), eb(versionColumn.ormName, "=", versionToCheck))) : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, "=", externalId));
|
|
116
|
+
const query = queryCompiler.deleteMany(op.table, { where: whereClause });
|
|
117
|
+
return query ? {
|
|
118
|
+
query,
|
|
119
|
+
expectedAffectedRows: op.checkVersion ? 1 : null
|
|
120
|
+
} : null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get the version to check for a given ID and checkVersion flag.
|
|
128
|
+
* @returns The version to check or undefined if no check is required.
|
|
129
|
+
* @throws Error if the ID is a string and checkVersion is true.
|
|
130
|
+
*/
|
|
131
|
+
function getVersionToCheck(id, checkVersion) {
|
|
132
|
+
if (!checkVersion) return;
|
|
133
|
+
if (typeof id === "string") throw new Error(`Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`);
|
|
134
|
+
return id.version;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
export { createKyselyUOWCompiler };
|
|
139
|
+
//# sourceMappingURL=kysely-uow-compiler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-uow-compiler.js","names":["indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: [AnyColumn, \"asc\" | \"desc\"][] | undefined","cursorCondition: Condition | undefined","combinedWhere: Condition | undefined"],"sources":["../../../src/adapters/kysely/kysely-uow-compiler.ts"],"sourcesContent":["import type { CompiledQuery } from \"kysely\";\nimport type { AnyColumn, AnySchema, FragnoId } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport type { KyselyConfig } from \"./kysely-adapter\";\nimport { createKyselyQueryCompiler } from \"./kysely-query-compiler\";\nimport { createKyselyQueryBuilder } from \"./kysely-query-builder\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { AnySelectClause } from \"../../query/query\";\n\n/**\n * Create a Kysely-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Kysely CompiledQuery objects\n * that can be executed as a batch/transaction.\n *\n * @param schema - The database schema\n * @param config - Kysely configuration\n * @returns A UOWCompiler instance for Kysely\n */\nexport function createKyselyUOWCompiler<TSchema extends AnySchema>(\n schema: TSchema,\n config: KyselyConfig,\n): UOWCompiler<TSchema, CompiledQuery> {\n const queryCompiler = createKyselyQueryCompiler(schema, config);\n const queryBuilder = createKyselyQueryBuilder(config.db, config.provider);\n const { provider } = config;\n\n function toTable(name: unknown) {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<TSchema>): CompiledQuery | null {\n switch (op.type) {\n case \"count\": {\n return queryCompiler.count(op.table.name, {\n where: op.options.where,\n });\n }\n\n case \"find\": {\n // Map UOW FindOptions to query compiler's FindManyOptions\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins: join,\n after,\n before,\n pageSize,\n ...findManyOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to internal ID column\n // (which is the actual primary key and maintains insertion order)\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n // Order by all columns in the index with the specified direction\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: [AnyColumn, \"asc\" | \"desc\"][] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => [col, orderDirection]);\n }\n\n // Handle cursor pagination - build a cursor condition\n let cursorCondition: Condition | undefined;\n\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n const cursorData = decodeCursor(cursor!);\n const serializedValues = serializeCursorValues(cursorData, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = indexColumns[0]!;\n const val = serializedValues[col.ormName];\n const operator = useGreaterThan ? \">\" : \"<\";\n cursorCondition = {\n type: \"compare\",\n a: col,\n operator,\n b: val,\n };\n } else {\n // Multi-column tuple comparison - not yet supported for Kysely\n throw new Error(\n \"Multi-column cursor pagination is not yet supported in Kysely Unit of Work implementation\",\n );\n }\n }\n\n // Combine user where clause with cursor condition\n let combinedWhere: Condition | undefined;\n if (findManyOptions.where) {\n const whereResult = buildCondition(op.table.columns, findManyOptions.where);\n if (whereResult === true) {\n combinedWhere = undefined;\n } else if (whereResult === false) {\n return null;\n } else {\n combinedWhere = whereResult;\n }\n }\n\n if (cursorCondition) {\n if (combinedWhere) {\n combinedWhere = {\n type: \"and\",\n items: [combinedWhere, cursorCondition],\n };\n } else {\n combinedWhere = cursorCondition;\n }\n }\n\n // When we have joins or need to bypass buildFindOptions, use queryBuilder directly\n if (join && join.length > 0) {\n return queryBuilder.findMany(op.table, {\n // Safe cast: select from UOW matches SimplifyFindOptions requirement\n select: (findManyOptions.select ?? true) as AnySelectClause,\n where: combinedWhere,\n orderBy,\n limit: pageSize,\n join,\n });\n }\n\n return queryCompiler.findMany(op.table.name, {\n ...findManyOptions,\n where: combinedWhere ? () => combinedWhere! : undefined,\n orderBy: orderBy?.map(([col, dir]) => [col.ormName, dir]),\n limit: pageSize,\n });\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<TSchema>,\n ): CompiledMutation<CompiledQuery> | null {\n switch (op.type) {\n case \"create\":\n // queryCompiler.create() calls encodeValues() which handles runtime defaults\n return {\n query: queryCompiler.create(op.table, op.values),\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n\n case \"update\": {\n const table = toTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const whereClause =\n versionToCheck !== undefined\n ? () =>\n buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n const query = queryCompiler.updateMany(op.table, {\n where: whereClause,\n set: op.set,\n });\n\n return query\n ? {\n query,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n }\n : null;\n }\n\n case \"delete\": {\n const table = toTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n\n // Extract external ID based on whether op.id is FragnoId or string\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const whereClause =\n versionToCheck !== undefined\n ? () =>\n buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n const query = queryCompiler.deleteMany(op.table, {\n where: whereClause,\n });\n\n return query\n ? {\n query,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n }\n : null;\n }\n }\n },\n };\n}\n\n/**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\nfunction getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//#region src/adapters/kysely/kysely-uow-executor.ts
|
|
2
|
+
function getAffectedRows(result) {
|
|
3
|
+
const affectedRows = result.numAffectedRows ?? result.numChangedRows ?? ("affectedRows" in result && (typeof result["affectedRows"] === "number" || typeof result["affectedRows"] === "bigint") ? result["affectedRows"] : void 0);
|
|
4
|
+
if (affectedRows === void 0) throw new Error("No affected rows found");
|
|
5
|
+
if (affectedRows > Number.MAX_SAFE_INTEGER) throw new Error(`affectedRows BigInt value ${affectedRows.toString()} exceeds JS safe integer range`);
|
|
6
|
+
return Number(affectedRows);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Execute the retrieval phase of a Unit of Work using Kysely
|
|
10
|
+
*
|
|
11
|
+
* All retrieval queries are executed inside a single transaction to ensure
|
|
12
|
+
* snapshot isolation - all reads see a consistent view of the database.
|
|
13
|
+
*
|
|
14
|
+
* @param kysely - The Kysely database instance
|
|
15
|
+
* @param retrievalBatch - Array of compiled retrieval queries
|
|
16
|
+
* @returns Array of query results matching the retrieval operations order
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const retrievalResults = await executeKyselyRetrievalPhase(kysely, compiled.retrievalBatch);
|
|
21
|
+
* const [users, posts] = retrievalResults;
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
async function executeKyselyRetrievalPhase(kysely, retrievalBatch) {
|
|
25
|
+
if (retrievalBatch.length === 0) return [];
|
|
26
|
+
const retrievalResults = [];
|
|
27
|
+
await kysely.transaction().execute(async (tx) => {
|
|
28
|
+
for (const query of retrievalBatch) {
|
|
29
|
+
const result = await tx.executeQuery(query);
|
|
30
|
+
retrievalResults.push(result.rows);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return retrievalResults;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Execute the mutation phase of a Unit of Work using Kysely
|
|
37
|
+
*
|
|
38
|
+
* All mutation queries are executed in a transaction with optimistic locking.
|
|
39
|
+
* If any version check fails, the entire transaction is rolled back and
|
|
40
|
+
* success=false is returned.
|
|
41
|
+
*
|
|
42
|
+
* @param kysely - The Kysely database instance
|
|
43
|
+
* @param mutationBatch - Array of compiled mutation queries with expected affected rows
|
|
44
|
+
* @returns Object with success flag and internal IDs from create operations
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const { success, createdInternalIds } = await executeKyselyMutationPhase(kysely, compiled.mutationBatch);
|
|
49
|
+
* if (!success) {
|
|
50
|
+
* console.log("Version conflict detected, retrying...");
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
async function executeKyselyMutationPhase(kysely, mutationBatch) {
|
|
55
|
+
if (mutationBatch.length === 0) return {
|
|
56
|
+
success: true,
|
|
57
|
+
createdInternalIds: []
|
|
58
|
+
};
|
|
59
|
+
const createdInternalIds = [];
|
|
60
|
+
try {
|
|
61
|
+
await kysely.transaction().execute(async (tx) => {
|
|
62
|
+
for (const compiledMutation of mutationBatch) {
|
|
63
|
+
const result = await tx.executeQuery(compiledMutation.query);
|
|
64
|
+
if (compiledMutation.expectedAffectedRows === null) if (Array.isArray(result.rows) && result.rows.length > 0) {
|
|
65
|
+
const row = result.rows[0];
|
|
66
|
+
if ("_internalId" in row || "_internal_id" in row) {
|
|
67
|
+
const internalId = row["_internalId"] ?? row["_internal_id"];
|
|
68
|
+
createdInternalIds.push(internalId);
|
|
69
|
+
} else createdInternalIds.push(null);
|
|
70
|
+
} else createdInternalIds.push(null);
|
|
71
|
+
else {
|
|
72
|
+
const affectedRows = getAffectedRows(result);
|
|
73
|
+
if (affectedRows !== compiledMutation.expectedAffectedRows) throw new Error(`Version conflict: expected ${compiledMutation.expectedAffectedRows} rows affected, but got ${affectedRows}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
createdInternalIds
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error instanceof Error && error.message.includes("Version conflict")) return { success: false };
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
export { executeKyselyMutationPhase, executeKyselyRetrievalPhase };
|
|
89
|
+
//# sourceMappingURL=kysely-uow-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-uow-executor.js","names":["retrievalResults: unknown[]","createdInternalIds: (bigint | null)[]"],"sources":["../../../src/adapters/kysely/kysely-uow-executor.ts"],"sourcesContent":["import type { Kysely, QueryResult } from \"kysely\";\nimport type { CompiledMutation, MutationResult } from \"../../query/unit-of-work\";\n\nfunction getAffectedRows(result: QueryResult<unknown>): number {\n const affectedRows =\n result.numAffectedRows ??\n result.numChangedRows ??\n // PGLite returns `affectedRows` instead of `numAffectedRows` or `numChangedRows`\n (\"affectedRows\" in result &&\n (typeof result[\"affectedRows\"] === \"number\" || typeof result[\"affectedRows\"] === \"bigint\")\n ? result[\"affectedRows\"]\n : undefined);\n\n if (affectedRows === undefined) {\n throw new Error(\"No affected rows found\");\n }\n\n if (affectedRows > Number.MAX_SAFE_INTEGER) {\n throw new Error(\n `affectedRows BigInt value ${affectedRows.toString()} exceeds JS safe integer range`,\n );\n }\n\n return Number(affectedRows);\n}\n\n/**\n * Execute the retrieval phase of a Unit of Work using Kysely\n *\n * All retrieval queries are executed inside a single transaction to ensure\n * snapshot isolation - all reads see a consistent view of the database.\n *\n * @param kysely - The Kysely database instance\n * @param retrievalBatch - Array of compiled retrieval queries\n * @returns Array of query results matching the retrieval operations order\n *\n * @example\n * ```ts\n * const retrievalResults = await executeKyselyRetrievalPhase(kysely, compiled.retrievalBatch);\n * const [users, posts] = retrievalResults;\n * ```\n */\nexport async function executeKyselyRetrievalPhase(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n kysely: Kysely<any>,\n retrievalBatch: (Kysely<unknown>[\"executeQuery\"] extends (query: infer Q) => unknown\n ? Q\n : never)[],\n): Promise<unknown[]> {\n // If no retrieval operations, return empty array immediately\n if (retrievalBatch.length === 0) {\n return [];\n }\n\n const retrievalResults: unknown[] = [];\n\n // Execute all retrieval queries inside a transaction for snapshot isolation\n await kysely.transaction().execute(async (tx) => {\n for (const query of retrievalBatch) {\n const result = await tx.executeQuery(query);\n retrievalResults.push(result.rows);\n }\n });\n\n return retrievalResults;\n}\n\n/**\n * Execute the mutation phase of a Unit of Work using Kysely\n *\n * All mutation queries are executed in a transaction with optimistic locking.\n * If any version check fails, the entire transaction is rolled back and\n * success=false is returned.\n *\n * @param kysely - The Kysely database instance\n * @param mutationBatch - Array of compiled mutation queries with expected affected rows\n * @returns Object with success flag and internal IDs from create operations\n *\n * @example\n * ```ts\n * const { success, createdInternalIds } = await executeKyselyMutationPhase(kysely, compiled.mutationBatch);\n * if (!success) {\n * console.log(\"Version conflict detected, retrying...\");\n * }\n * ```\n */\nexport async function executeKyselyMutationPhase(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n kysely: Kysely<any>,\n mutationBatch: CompiledMutation<\n Kysely<unknown>[\"executeQuery\"] extends (query: infer Q) => unknown ? Q : never\n >[],\n): Promise<MutationResult> {\n // If there are no mutations, return success immediately\n if (mutationBatch.length === 0) {\n return { success: true, createdInternalIds: [] };\n }\n\n const createdInternalIds: (bigint | null)[] = [];\n\n // Execute mutation batch in a transaction\n try {\n await kysely.transaction().execute(async (tx) => {\n for (const compiledMutation of mutationBatch) {\n const result = await tx.executeQuery(compiledMutation.query);\n\n // For creates (expectedAffectedRows === null), try to extract internal ID\n if (compiledMutation.expectedAffectedRows === null) {\n // Check if result has rows (RETURNING clause supported)\n if (Array.isArray(result.rows) && result.rows.length > 0) {\n const row = result.rows[0] as Record<string, unknown>;\n if (\"_internalId\" in row || \"_internal_id\" in row) {\n const internalId = (row[\"_internalId\"] ?? row[\"_internal_id\"]) as bigint;\n createdInternalIds.push(internalId);\n } else {\n // RETURNING supported but _internalId not found\n createdInternalIds.push(null);\n }\n } else {\n // No RETURNING support (e.g., MySQL)\n createdInternalIds.push(null);\n }\n } else {\n // Check affected rows for updates/deletes\n const affectedRows = getAffectedRows(result);\n\n if (affectedRows !== compiledMutation.expectedAffectedRows) {\n // Version conflict detected - the UPDATE/DELETE didn't affect the expected number of rows\n // This means either the row doesn't exist or the version has changed\n throw new Error(\n `Version conflict: expected ${compiledMutation.expectedAffectedRows} rows affected, but got ${affectedRows}`,\n );\n }\n }\n }\n });\n\n return { success: true, createdInternalIds };\n } catch (error) {\n // Transaction failed - could be version conflict or other constraint violation\n // Return success=false to indicate the UOW should be retried\n if (error instanceof Error && error.message.includes(\"Version conflict\")) {\n return { success: false };\n }\n\n // Other database errors should be thrown\n throw error;\n }\n}\n"],"mappings":";AAGA,SAAS,gBAAgB,QAAsC;CAC7D,MAAM,eACJ,OAAO,mBACP,OAAO,mBAEN,kBAAkB,WAClB,OAAO,OAAO,oBAAoB,YAAY,OAAO,OAAO,oBAAoB,YAC7E,OAAO,kBACP;AAEN,KAAI,iBAAiB,OACnB,OAAM,IAAI,MAAM,yBAAyB;AAG3C,KAAI,eAAe,OAAO,iBACxB,OAAM,IAAI,MACR,6BAA6B,aAAa,UAAU,CAAC,gCACtD;AAGH,QAAO,OAAO,aAAa;;;;;;;;;;;;;;;;;;AAmB7B,eAAsB,4BAEpB,QACA,gBAGoB;AAEpB,KAAI,eAAe,WAAW,EAC5B,QAAO,EAAE;CAGX,MAAMA,mBAA8B,EAAE;AAGtC,OAAM,OAAO,aAAa,CAAC,QAAQ,OAAO,OAAO;AAC/C,OAAK,MAAM,SAAS,gBAAgB;GAClC,MAAM,SAAS,MAAM,GAAG,aAAa,MAAM;AAC3C,oBAAiB,KAAK,OAAO,KAAK;;GAEpC;AAEF,QAAO;;;;;;;;;;;;;;;;;;;;;AAsBT,eAAsB,2BAEpB,QACA,eAGyB;AAEzB,KAAI,cAAc,WAAW,EAC3B,QAAO;EAAE,SAAS;EAAM,oBAAoB,EAAE;EAAE;CAGlD,MAAMC,qBAAwC,EAAE;AAGhD,KAAI;AACF,QAAM,OAAO,aAAa,CAAC,QAAQ,OAAO,OAAO;AAC/C,QAAK,MAAM,oBAAoB,eAAe;IAC5C,MAAM,SAAS,MAAM,GAAG,aAAa,iBAAiB,MAAM;AAG5D,QAAI,iBAAiB,yBAAyB,KAE5C,KAAI,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,KAAK,SAAS,GAAG;KACxD,MAAM,MAAM,OAAO,KAAK;AACxB,SAAI,iBAAiB,OAAO,kBAAkB,KAAK;MACjD,MAAM,aAAc,IAAI,kBAAkB,IAAI;AAC9C,yBAAmB,KAAK,WAAW;WAGnC,oBAAmB,KAAK,KAAK;UAI/B,oBAAmB,KAAK,KAAK;SAE1B;KAEL,MAAM,eAAe,gBAAgB,OAAO;AAE5C,SAAI,iBAAiB,iBAAiB,qBAGpC,OAAM,IAAI,MACR,8BAA8B,iBAAiB,qBAAqB,0BAA0B,eAC/F;;;IAIP;AAEF,SAAO;GAAE,SAAS;GAAM;GAAoB;UACrC,OAAO;AAGd,MAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,mBAAmB,CACtE,QAAO,EAAE,SAAS,OAAO;AAI3B,QAAM"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { schemaToDBType } from "../../../schema/serialize.js";
|
|
2
|
+
import { isUpdated } from "../../../migration-engine/shared.js";
|
|
3
|
+
import { sql } from "kysely";
|
|
4
|
+
|
|
5
|
+
//#region src/adapters/kysely/migration/execute.ts
|
|
6
|
+
const errors = {
|
|
7
|
+
IdColumnUpdate: "ID columns cannot be updated. Not every database supports updating primary keys and often requires workarounds.",
|
|
8
|
+
SQLiteUpdateColumn: "SQLite doesn't support updating columns. Recreate the table instead.",
|
|
9
|
+
SQLiteUpdateForeignKeys: "SQLite doesn't support modifying foreign keys directly. Use `recreate-table` instead."
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Returns the appropriate foreign key action based on the provider.
|
|
13
|
+
* MSSQL doesn't support RESTRICT, so we use NO ACTION (functionally equivalent).
|
|
14
|
+
*/
|
|
15
|
+
function getForeignKeyAction(provider) {
|
|
16
|
+
return provider === "mssql" ? "no action" : "restrict";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generates MSSQL default constraint name following the DF_tableName_columnName pattern.
|
|
20
|
+
*/
|
|
21
|
+
function getMssqlDefaultConstraintName(tableName, columnName) {
|
|
22
|
+
return `DF_${tableName}_${columnName}`;
|
|
23
|
+
}
|
|
24
|
+
function createUniqueIndex(db, name, tableName, cols, provider) {
|
|
25
|
+
const query = db.schema.createIndex(name).on(tableName).columns(cols).unique();
|
|
26
|
+
if (provider === "mssql") return query.where((b) => {
|
|
27
|
+
return b.and(cols.map((col) => b(col, "is not", null)));
|
|
28
|
+
});
|
|
29
|
+
return query;
|
|
30
|
+
}
|
|
31
|
+
function dropUniqueIndex(db, name, tableName, provider) {
|
|
32
|
+
let query = db.schema.dropIndex(name).ifExists();
|
|
33
|
+
if (provider === "cockroachdb") query = query.cascade();
|
|
34
|
+
if (provider === "mssql") query = query.on(tableName);
|
|
35
|
+
return query;
|
|
36
|
+
}
|
|
37
|
+
function executeColumn(tableName, operation, config) {
|
|
38
|
+
const { db, provider } = config;
|
|
39
|
+
const next = () => db.schema.alterTable(tableName);
|
|
40
|
+
const results = [];
|
|
41
|
+
switch (operation.type) {
|
|
42
|
+
case "rename-column":
|
|
43
|
+
results.push(next().renameColumn(operation.from, operation.to));
|
|
44
|
+
return results;
|
|
45
|
+
case "drop-column":
|
|
46
|
+
results.push(next().dropColumn(operation.name));
|
|
47
|
+
return results;
|
|
48
|
+
case "create-column": {
|
|
49
|
+
const col = operation.value;
|
|
50
|
+
results.push(next().addColumn(col.name, sql.raw(schemaToDBType(col, provider)), getColumnBuilderCallback(col, provider)));
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
case "update-column": {
|
|
54
|
+
const col = operation.value;
|
|
55
|
+
if (col.role === "external-id" || col.role === "internal-id") throw new Error(errors.IdColumnUpdate);
|
|
56
|
+
if (provider === "sqlite") throw new Error(errors.SQLiteUpdateColumn);
|
|
57
|
+
if (!isUpdated(operation)) return results;
|
|
58
|
+
if (provider === "mysql") {
|
|
59
|
+
results.push(next().modifyColumn(operation.name, sql.raw(schemaToDBType(col, provider)), getColumnBuilderCallback(col, provider)));
|
|
60
|
+
return results;
|
|
61
|
+
}
|
|
62
|
+
const mssqlRecreateDefaultConstraint = operation.updateDataType || operation.updateDefault;
|
|
63
|
+
if (provider === "mssql" && mssqlRecreateDefaultConstraint) results.push(rawToNode(db, mssqlDropDefaultConstraint(tableName, col.name)));
|
|
64
|
+
if (operation.updateDataType) {
|
|
65
|
+
const dbType = sql.raw(schemaToDBType(col, provider));
|
|
66
|
+
if (provider === "postgresql" || provider === "cockroachdb") results.push(rawToNode(db, sql`ALTER TABLE ${sql.ref(tableName)} ALTER COLUMN ${sql.ref(operation.name)} TYPE ${dbType} USING (${sql.ref(operation.name)}::${dbType})`));
|
|
67
|
+
else results.push(next().alterColumn(operation.name, (b) => b.setDataType(dbType)));
|
|
68
|
+
}
|
|
69
|
+
if (operation.updateNullable) results.push(next().alterColumn(operation.name, (build) => col.isNullable ? build.dropNotNull() : build.setNotNull()));
|
|
70
|
+
if (provider === "mssql" && mssqlRecreateDefaultConstraint) {
|
|
71
|
+
const defaultValue = defaultValueToDB(col, provider);
|
|
72
|
+
if (defaultValue) {
|
|
73
|
+
const constraintName = getMssqlDefaultConstraintName(tableName, col.name);
|
|
74
|
+
results.push(rawToNode(db, sql`ALTER TABLE ${sql.ref(tableName)} ADD CONSTRAINT ${sql.ref(constraintName)} DEFAULT ${defaultValue} FOR ${sql.ref(col.name)}`));
|
|
75
|
+
}
|
|
76
|
+
} else if (operation.updateDefault) {
|
|
77
|
+
const defaultValue = defaultValueToDB(col, provider);
|
|
78
|
+
results.push(next().alterColumn(operation.name, (build) => {
|
|
79
|
+
if (!defaultValue) return build.dropDefault();
|
|
80
|
+
return build.setDefault(defaultValue);
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function execute(operation, config, onCustomNode) {
|
|
88
|
+
const { db, provider } = config;
|
|
89
|
+
function createTable(tableName, columns) {
|
|
90
|
+
let builder = db.schema.createTable(tableName);
|
|
91
|
+
for (const columnInfo of columns) builder = builder.addColumn(columnInfo.name, sql.raw(schemaToDBType(columnInfo, provider)), getColumnBuilderCallback(columnInfo, provider));
|
|
92
|
+
return builder;
|
|
93
|
+
}
|
|
94
|
+
switch (operation.type) {
|
|
95
|
+
case "create-table": return createTable(operation.name, operation.columns);
|
|
96
|
+
case "rename-table":
|
|
97
|
+
if (provider === "mssql") return rawToNode(db, sql`EXEC sp_rename ${sql.lit(operation.from)}, ${sql.lit(operation.to)}`);
|
|
98
|
+
return db.schema.alterTable(operation.from).renameTo(operation.to);
|
|
99
|
+
case "alter-table": {
|
|
100
|
+
const results = [];
|
|
101
|
+
for (const op of operation.value) results.push(...executeColumn(operation.name, op, config));
|
|
102
|
+
return results;
|
|
103
|
+
}
|
|
104
|
+
case "drop-table": return db.schema.dropTable(operation.name);
|
|
105
|
+
case "custom": return onCustomNode(operation);
|
|
106
|
+
case "add-foreign-key": {
|
|
107
|
+
if (provider === "sqlite") throw new Error(errors.SQLiteUpdateForeignKeys);
|
|
108
|
+
const { table, value } = operation;
|
|
109
|
+
const action = getForeignKeyAction(provider);
|
|
110
|
+
return db.schema.alterTable(table).addForeignKeyConstraint(value.name, value.columns, value.referencedTable, value.referencedColumns, (b) => b.onUpdate(action).onDelete(action));
|
|
111
|
+
}
|
|
112
|
+
case "drop-foreign-key": {
|
|
113
|
+
if (provider === "sqlite") throw new Error(errors.SQLiteUpdateForeignKeys);
|
|
114
|
+
const { table, name } = operation;
|
|
115
|
+
let query = db.schema.alterTable(table).dropConstraint(name);
|
|
116
|
+
if (provider !== "mysql") query = query.ifExists();
|
|
117
|
+
return query;
|
|
118
|
+
}
|
|
119
|
+
case "add-index":
|
|
120
|
+
if (operation.unique) return createUniqueIndex(db, operation.name, operation.table, operation.columns, provider);
|
|
121
|
+
return db.schema.createIndex(operation.name).on(operation.table).columns(operation.columns);
|
|
122
|
+
case "drop-index": return dropUniqueIndex(db, operation.name, operation.table, provider);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function getColumnBuilderCallback(col, provider) {
|
|
126
|
+
return (build) => {
|
|
127
|
+
if (!col.isNullable) build = build.notNull();
|
|
128
|
+
if (col.role === "internal-id") {
|
|
129
|
+
build = build.primaryKey();
|
|
130
|
+
if (provider === "postgresql" || provider === "cockroachdb") {} else if (provider === "mysql") build = build.autoIncrement();
|
|
131
|
+
else if (provider === "sqlite") build = build.autoIncrement();
|
|
132
|
+
else if (provider === "mssql") build = build.identity();
|
|
133
|
+
}
|
|
134
|
+
if (col.role === "external-id") build = build.unique();
|
|
135
|
+
const defaultValue = defaultValueToDB(col, provider);
|
|
136
|
+
if (defaultValue) build = build.defaultTo(defaultValue);
|
|
137
|
+
return build;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function rawToNode(db, raw) {
|
|
141
|
+
return {
|
|
142
|
+
compile() {
|
|
143
|
+
return raw.compile(db);
|
|
144
|
+
},
|
|
145
|
+
execute() {
|
|
146
|
+
return raw.execute(db);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function mssqlDropDefaultConstraint(tableName, columnName) {
|
|
151
|
+
return sql`
|
|
152
|
+
DECLARE @ConstraintName NVARCHAR(200);
|
|
153
|
+
|
|
154
|
+
SELECT @ConstraintName = dc.name
|
|
155
|
+
FROM sys.default_constraints dc
|
|
156
|
+
JOIN sys.columns c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id
|
|
157
|
+
JOIN sys.tables t ON t.object_id = c.object_id
|
|
158
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
159
|
+
WHERE s.name = 'dbo' AND t.name = ${sql.lit(tableName)} AND c.name = ${sql.lit(columnName)};
|
|
160
|
+
|
|
161
|
+
IF @ConstraintName IS NOT NULL
|
|
162
|
+
BEGIN
|
|
163
|
+
EXEC('ALTER TABLE [dbo].[' + ${sql.lit(tableName)} + '] DROP CONSTRAINT [' + @ConstraintName + ']');
|
|
164
|
+
END`;
|
|
165
|
+
}
|
|
166
|
+
function defaultValueToDB(column, provider) {
|
|
167
|
+
const value = column.default;
|
|
168
|
+
if (!value) return;
|
|
169
|
+
if (provider === "mysql" && column.type === "string") return;
|
|
170
|
+
if ("runtime" in value && value.runtime === "now") return sql`CURRENT_TIMESTAMP`;
|
|
171
|
+
if ("value" in value && value.value !== void 0) return sql.lit(value.value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
//#endregion
|
|
175
|
+
export { execute };
|
|
176
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.js","names":["results: ExecuteNode[]"],"sources":["../../../../src/adapters/kysely/migration/execute.ts"],"sourcesContent":["import {\n type ColumnBuilderCallback,\n type Compilable,\n type CreateTableBuilder,\n type Kysely,\n type RawBuilder,\n sql,\n} from \"kysely\";\nimport {\n type CustomOperation,\n isUpdated,\n type ColumnOperation,\n type MigrationOperation,\n type ColumnInfo,\n} from \"../../../migration-engine/shared\";\nimport type { SQLProvider } from \"../../../shared/providers\";\nimport { schemaToDBType } from \"../../../schema/serialize\";\nimport type { KyselyConfig } from \"../kysely-adapter\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\nexport type ExecuteNode = Compilable & {\n execute(): Promise<unknown>;\n};\n\nconst errors = {\n IdColumnUpdate:\n \"ID columns cannot be updated. Not every database supports updating primary keys and often requires workarounds.\",\n SQLiteUpdateColumn: \"SQLite doesn't support updating columns. Recreate the table instead.\",\n SQLiteUpdateForeignKeys:\n \"SQLite doesn't support modifying foreign keys directly. Use `recreate-table` instead.\",\n} as const;\n\n/**\n * Returns the appropriate foreign key action based on the provider.\n * MSSQL doesn't support RESTRICT, so we use NO ACTION (functionally equivalent).\n */\nfunction getForeignKeyAction(provider: SQLProvider): \"restrict\" | \"no action\" {\n return provider === \"mssql\" ? \"no action\" : \"restrict\";\n}\n\n/**\n * Generates MSSQL default constraint name following the DF_tableName_columnName pattern.\n */\nfunction getMssqlDefaultConstraintName(tableName: string, columnName: string): string {\n const MSSQL_DEFAULT_CONSTRAINT_PREFIX = \"DF\" as const;\n return `${MSSQL_DEFAULT_CONSTRAINT_PREFIX}_${tableName}_${columnName}`;\n}\n\nfunction createUniqueIndex(\n db: KyselyAny,\n name: string,\n tableName: string,\n cols: string[],\n provider: SQLProvider,\n) {\n const query = db.schema.createIndex(name).on(tableName).columns(cols).unique();\n\n if (provider === \"mssql\") {\n // MSSQL: ignore null values in unique indexes by default\n return query.where((b) => {\n return b.and(cols.map((col) => b(col, \"is not\", null)));\n });\n }\n\n return query;\n}\n\nfunction dropUniqueIndex(db: KyselyAny, name: string, tableName: string, provider: SQLProvider) {\n let query = db.schema.dropIndex(name).ifExists();\n\n if (provider === \"cockroachdb\") {\n query = query.cascade();\n }\n\n if (provider === \"mssql\") {\n query = query.on(tableName);\n }\n\n return query;\n}\n\nfunction executeColumn(\n tableName: string,\n operation: ColumnOperation,\n config: KyselyConfig,\n): ExecuteNode[] {\n const { db, provider } = config;\n const next = () => db.schema.alterTable(tableName);\n const results: ExecuteNode[] = [];\n\n switch (operation.type) {\n case \"rename-column\":\n results.push(next().renameColumn(operation.from, operation.to));\n return results;\n\n case \"drop-column\":\n results.push(next().dropColumn(operation.name));\n\n return results;\n case \"create-column\": {\n const col = operation.value;\n\n results.push(\n next().addColumn(\n col.name,\n sql.raw(schemaToDBType(col, provider)),\n getColumnBuilderCallback(col, provider),\n ),\n );\n\n return results;\n }\n case \"update-column\": {\n const col = operation.value;\n\n if (col.role === \"external-id\" || col.role === \"internal-id\") {\n throw new Error(errors.IdColumnUpdate);\n }\n if (provider === \"sqlite\") {\n throw new Error(errors.SQLiteUpdateColumn);\n }\n\n if (!isUpdated(operation)) {\n return results;\n }\n if (provider === \"mysql\") {\n results.push(\n next().modifyColumn(\n operation.name,\n sql.raw(schemaToDBType(col, provider)),\n getColumnBuilderCallback(col, provider),\n ),\n );\n return results;\n }\n\n // MSSQL requires dropping and recreating default constraints when changing data type or default value\n const mssqlRecreateDefaultConstraint = operation.updateDataType || operation.updateDefault;\n\n if (provider === \"mssql\" && mssqlRecreateDefaultConstraint) {\n results.push(rawToNode(db, mssqlDropDefaultConstraint(tableName, col.name)));\n }\n\n // TODO: We should maybe do some of these operations in a single query\n\n if (operation.updateDataType) {\n const dbType = sql.raw(schemaToDBType(col, provider));\n\n if (provider === \"postgresql\" || provider === \"cockroachdb\") {\n // PostgreSQL/CockroachDB: Use explicit USING clause for type conversion\n results.push(\n rawToNode(\n db,\n sql`ALTER TABLE ${sql.ref(tableName)} ALTER COLUMN ${sql.ref(operation.name)} TYPE ${dbType} USING (${sql.ref(operation.name)}::${dbType})`,\n ),\n );\n } else {\n results.push(next().alterColumn(operation.name, (b) => b.setDataType(dbType)));\n }\n }\n\n if (operation.updateNullable) {\n results.push(\n next().alterColumn(operation.name, (build) =>\n col.isNullable ? build.dropNotNull() : build.setNotNull(),\n ),\n );\n }\n\n if (provider === \"mssql\" && mssqlRecreateDefaultConstraint) {\n const defaultValue = defaultValueToDB(col, provider);\n\n if (defaultValue) {\n const constraintName = getMssqlDefaultConstraintName(tableName, col.name);\n\n results.push(\n rawToNode(\n db,\n sql`ALTER TABLE ${sql.ref(tableName)} ADD CONSTRAINT ${sql.ref(constraintName)} DEFAULT ${defaultValue} FOR ${sql.ref(col.name)}`,\n ),\n );\n }\n } else if (operation.updateDefault) {\n const defaultValue = defaultValueToDB(col, provider);\n\n results.push(\n next().alterColumn(operation.name, (build) => {\n if (!defaultValue) {\n return build.dropDefault();\n }\n return build.setDefault(defaultValue);\n }),\n );\n }\n\n return results;\n }\n }\n}\n\nexport function execute(\n operation: MigrationOperation,\n config: KyselyConfig,\n onCustomNode: (op: CustomOperation) => ExecuteNode | ExecuteNode[],\n): ExecuteNode | ExecuteNode[] {\n const { db, provider } = config;\n\n function createTable(tableName: string, columns: ColumnInfo[]) {\n let builder = db.schema.createTable(tableName) as CreateTableBuilder<string, string>;\n\n // Add columns from the column info array\n for (const columnInfo of columns) {\n builder = builder.addColumn(\n columnInfo.name,\n sql.raw(schemaToDBType(columnInfo, provider)),\n getColumnBuilderCallback(columnInfo, provider),\n );\n }\n\n return builder;\n }\n\n switch (operation.type) {\n case \"create-table\":\n return createTable(operation.name, operation.columns);\n case \"rename-table\":\n if (provider === \"mssql\") {\n return rawToNode(\n db,\n sql`EXEC sp_rename ${sql.lit(operation.from)}, ${sql.lit(operation.to)}`,\n );\n }\n\n return db.schema.alterTable(operation.from).renameTo(operation.to);\n case \"alter-table\": {\n const results: ExecuteNode[] = [];\n\n for (const op of operation.value) {\n results.push(...executeColumn(operation.name, op, config));\n }\n\n return results;\n }\n case \"drop-table\":\n return db.schema.dropTable(operation.name);\n case \"custom\":\n return onCustomNode(operation);\n case \"add-foreign-key\": {\n if (provider === \"sqlite\") {\n throw new Error(errors.SQLiteUpdateForeignKeys);\n }\n\n const { table, value } = operation;\n const action = getForeignKeyAction(provider);\n\n return db.schema\n .alterTable(table)\n .addForeignKeyConstraint(\n value.name,\n value.columns,\n value.referencedTable,\n value.referencedColumns,\n (b) => b.onUpdate(action).onDelete(action),\n );\n }\n case \"drop-foreign-key\": {\n if (provider === \"sqlite\") {\n throw new Error(errors.SQLiteUpdateForeignKeys);\n }\n\n const { table, name } = operation;\n let query = db.schema.alterTable(table).dropConstraint(name);\n\n // MySQL doesn't support IF EXISTS for dropping constraints\n if (provider !== \"mysql\") {\n query = query.ifExists();\n }\n\n return query;\n }\n case \"add-index\": {\n if (operation.unique) {\n return createUniqueIndex(db, operation.name, operation.table, operation.columns, provider);\n }\n return db.schema.createIndex(operation.name).on(operation.table).columns(operation.columns);\n }\n case \"drop-index\": {\n return dropUniqueIndex(db, operation.name, operation.table, provider);\n }\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction getColumnBuilderCallback(col: ColumnInfo, provider: SQLProvider): ColumnBuilderCallback {\n return (build) => {\n if (!col.isNullable) {\n build = build.notNull();\n }\n\n // Internal ID is the primary key with auto-increment\n if (col.role === \"internal-id\") {\n build = build.primaryKey();\n // Auto-increment for internal ID\n if (provider === \"postgresql\" || provider === \"cockroachdb\") {\n // SERIAL/BIGSERIAL handles auto-increment\n // Already handled in schemaToDBType\n } else if (provider === \"mysql\") {\n build = build.autoIncrement();\n } else if (provider === \"sqlite\") {\n build = build.autoIncrement();\n } else if (provider === \"mssql\") {\n build = build.identity();\n }\n }\n\n // External ID must be unique\n if (col.role === \"external-id\") {\n build = build.unique();\n }\n\n const defaultValue = defaultValueToDB(col, provider);\n if (defaultValue) {\n build = build.defaultTo(defaultValue);\n }\n return build;\n };\n}\n\nfunction rawToNode(db: KyselyAny, raw: RawBuilder<unknown>): ExecuteNode {\n return {\n compile() {\n return raw.compile(db);\n },\n execute() {\n return raw.execute(db);\n },\n };\n}\n\nfunction mssqlDropDefaultConstraint(tableName: string, columnName: string) {\n return sql`\nDECLARE @ConstraintName NVARCHAR(200);\n\nSELECT @ConstraintName = dc.name\nFROM sys.default_constraints dc\nJOIN sys.columns c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id\nJOIN sys.tables t ON t.object_id = c.object_id\nJOIN sys.schemas s ON t.schema_id = s.schema_id\nWHERE s.name = 'dbo' AND t.name = ${sql.lit(tableName)} AND c.name = ${sql.lit(columnName)};\n\nIF @ConstraintName IS NOT NULL\nBEGIN\n EXEC('ALTER TABLE [dbo].[' + ${sql.lit(tableName)} + '] DROP CONSTRAINT [' + @ConstraintName + ']');\nEND`;\n}\n\nfunction defaultValueToDB(column: ColumnInfo, provider: SQLProvider) {\n const value = column.default;\n if (!value) {\n return undefined;\n }\n\n // MySQL doesn't support default values for TEXT columns\n if (provider === \"mysql\" && column.type === \"string\") {\n return undefined;\n }\n\n if (\"runtime\" in value && value.runtime === \"now\") {\n return sql`CURRENT_TIMESTAMP`;\n }\n\n if (\"value\" in value && value.value !== undefined) {\n return sql.lit(value.value);\n }\n\n return undefined;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { AnySchema } from "./schema/create.js";
|
|
2
|
+
import { AbstractQuery } from "./query/query.js";
|
|
3
|
+
import { DatabaseAdapter } from "./adapters/adapters.js";
|
|
4
|
+
import { FragmentDefinition, FragnoPublicConfig } from "@fragno-dev/core";
|
|
5
|
+
|
|
6
|
+
//#region src/fragment.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extended FragnoPublicConfig that includes a database adapter.
|
|
10
|
+
* Use this type when creating fragments with database support.
|
|
11
|
+
*/
|
|
12
|
+
type FragnoPublicConfigWithDatabase = FragnoPublicConfig & {
|
|
13
|
+
databaseAdapter: DatabaseAdapter<any>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Additional context provided to database fragments containing the database adapter and ORM instance.
|
|
17
|
+
*/
|
|
18
|
+
type DatabaseFragmentContext<TSchema extends AnySchema> = {
|
|
19
|
+
databaseAdapter: DatabaseAdapter<any>;
|
|
20
|
+
orm: AbstractQuery<TSchema>;
|
|
21
|
+
};
|
|
22
|
+
declare class DatabaseFragmentBuilder<const TSchema extends AnySchema, const TConfig, const TDeps = {}, const TServices extends Record<string, unknown> = {}> {
|
|
23
|
+
#private;
|
|
24
|
+
constructor(options: {
|
|
25
|
+
name: string;
|
|
26
|
+
schema?: TSchema;
|
|
27
|
+
namespace?: string;
|
|
28
|
+
dependencies?: (context: {
|
|
29
|
+
config: TConfig;
|
|
30
|
+
fragnoConfig: FragnoPublicConfig;
|
|
31
|
+
} & DatabaseFragmentContext<TSchema>) => TDeps;
|
|
32
|
+
services?: (context: {
|
|
33
|
+
config: TConfig;
|
|
34
|
+
fragnoConfig: FragnoPublicConfig;
|
|
35
|
+
deps: TDeps;
|
|
36
|
+
} & DatabaseFragmentContext<TSchema>) => TServices;
|
|
37
|
+
});
|
|
38
|
+
get $requiredOptions(): FragnoPublicConfigWithDatabase;
|
|
39
|
+
get definition(): FragmentDefinition<TConfig, TDeps, TServices>;
|
|
40
|
+
withDatabase<TNewSchema extends AnySchema>(schema: TNewSchema, namespace?: string): DatabaseFragmentBuilder<TNewSchema, TConfig, TDeps, TServices>;
|
|
41
|
+
withDependencies<TNewDeps>(fn: (context: {
|
|
42
|
+
config: TConfig;
|
|
43
|
+
fragnoConfig: FragnoPublicConfig;
|
|
44
|
+
} & DatabaseFragmentContext<TSchema>) => TNewDeps): DatabaseFragmentBuilder<TSchema, TConfig, TNewDeps, {}>;
|
|
45
|
+
withServices<TNewServices extends Record<string, unknown>>(fn: (context: {
|
|
46
|
+
config: TConfig;
|
|
47
|
+
fragnoConfig: FragnoPublicConfig;
|
|
48
|
+
deps: TDeps;
|
|
49
|
+
} & DatabaseFragmentContext<TSchema>) => TNewServices): DatabaseFragmentBuilder<TSchema, TConfig, TDeps, TNewServices>;
|
|
50
|
+
}
|
|
51
|
+
declare function defineFragmentWithDatabase<TConfig = {}>(name: string): DatabaseFragmentBuilder<never, TConfig, {}, {}>;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { DatabaseFragmentBuilder, DatabaseFragmentContext, FragnoPublicConfigWithDatabase, defineFragmentWithDatabase };
|
|
54
|
+
//# sourceMappingURL=fragment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragment.d.ts","names":[],"sources":["../src/fragment.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AASA;AAQA;AAAoD,KARxC,8BAAA,GAAiC,kBAQO,GAAA;EAEjC,eAAA,EARA,eAQA,CAAA,GAAA,CAAA;CACE;;;AAGrB;AACwB,KAPZ,uBAOY,CAAA,gBAP4B,SAO5B,CAAA,GAAA;EAGE,eAAA,EARP,eAQO,CAAA,GAAA,CAAA;EAqBb,GAAA,EA5BN,aA4BM,CA5BQ,OA4BR,CAAA;CAIG;AACM,cA9BT,uBA8BS,CAAA,sBA7BE,SA6BF,EAAA,aAAA,EAAA,cAAA,CAAA,CAAA,EAAA,wBA1BI,MA0BJ,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA;EACY,CAAA,OAAA;EAAxB,WAAA,CAAA,OAAA,EAAA;IACD,IAAA,EAAA,MAAA;IAGO,MAAA,CAAA,EAVH,OAUG;IACM,SAAA,CAAA,EAAA,MAAA;IACR,YAAA,CAAA,EAAA,CAAA,OAAA,EAAA;MACoB,MAAA,EATlB,OASkB;MAAxB,YAAA,EARY,kBAQZ;IACD,CAAA,GARC,uBAQD,CARyB,OAQzB,CAAA,EAAA,GAPA,KAOA;IASiB,QAAA,CAAA,EAAA,CAAA,OAAA,EAAA;MAIa,MAAA,EAjBvB,OAiBuB;MAAS,YAAA,EAhB1B,kBAgB0B;MAAO,IAAA,EAfzC,KAeyC;IAAnC,CAAA,GAdV,uBAcU,CAdc,OAcd,CAAA,EAAA,GAbX,SAaW;EAoDc,CAAA;EACtB,IAAA,gBAAA,CAAA,CAAA,EAzDc,8BAyDd;EAEiB,IAAA,UAAA,CAAA,CAAA,EAvDT,kBAuDS,CAvDU,OAuDV,EAvDmB,KAuDnB,EAvD0B,SAuD1B,CAAA;EAAY,YAAA,CAAA,mBAHP,SAGO,CAAA,CAAA,MAAA,EAF7B,UAE6B,EAAA,SAAA,CAAA,EAAA,MAAA,CAAA,EAApC,uBAAoC,CAAZ,UAAY,EAAA,OAAA,EAAS,KAAT,EAAgB,SAAhB,CAAA;EAAS,gBAAA,CAAA,QAAA,CAAA,CAAA,EAAA,EAAA,CAAA,OAAA,EAAA;IAAO,MAAA,EA4BzC,OA5ByC;IAApD,YAAA,EA6BiB,kBA7BjB;EA4BW,CAAA,GAEN,uBAFM,CAEkB,OAFlB,CAAA,EAAA,GAGP,QAHO,CAAA,EAIX,uBAJW,CAIa,OAJb,EAIsB,OAJtB,EAI+B,QAJ/B,EAAA,CAAA,CAAA,CAAA;EACM,YAAA,CAAA,qBAac,MAbd,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,EAAA,EAAA,CAAA,OAAA,EAAA;IACY,MAAA,EAelB,OAfkB;IAAxB,YAAA,EAgBY,kBAhBZ;IACD,IAAA,EAgBK,KAhBL;EACoB,CAAA,GAgBnB,uBAhBmB,CAgBK,OAhBL,CAAA,EAAA,GAiBpB,YAjBoB,CAAA,EAkBxB,uBAlBwB,CAkBA,OAlBA,EAkBS,OAlBT,EAkBkB,KAlBlB,EAkByB,YAlBzB,CAAA;;AAAkB,iBA6B/B,0BA7B+B,CAAA,UAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EA+B5C,uBA/B4C,CAAA,KAAA,EA+Bb,OA/Ba,EAAA,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA"}
|
package/dist/fragment.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//#region src/fragment.ts
|
|
2
|
+
var DatabaseFragmentBuilder = class DatabaseFragmentBuilder {
|
|
3
|
+
#name;
|
|
4
|
+
#schema;
|
|
5
|
+
#namespace;
|
|
6
|
+
#dependencies;
|
|
7
|
+
#services;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.#name = options.name;
|
|
10
|
+
this.#schema = options.schema;
|
|
11
|
+
this.#namespace = options.namespace;
|
|
12
|
+
this.#dependencies = options.dependencies;
|
|
13
|
+
this.#services = options.services;
|
|
14
|
+
}
|
|
15
|
+
get $requiredOptions() {
|
|
16
|
+
throw new Error("Type only method. Do not call.");
|
|
17
|
+
}
|
|
18
|
+
get definition() {
|
|
19
|
+
const schema = this.#schema;
|
|
20
|
+
const namespace = this.#namespace ?? "";
|
|
21
|
+
const name = this.#name;
|
|
22
|
+
const dependencies = this.#dependencies;
|
|
23
|
+
const services = this.#services;
|
|
24
|
+
return {
|
|
25
|
+
name,
|
|
26
|
+
dependencies: (config, options) => {
|
|
27
|
+
const dbContext = this.#createDatabaseContext(options, schema, namespace, name);
|
|
28
|
+
return dependencies?.({
|
|
29
|
+
config,
|
|
30
|
+
fragnoConfig: options,
|
|
31
|
+
...dbContext
|
|
32
|
+
}) ?? {};
|
|
33
|
+
},
|
|
34
|
+
services: (config, options, deps) => {
|
|
35
|
+
const dbContext = this.#createDatabaseContext(options, schema, namespace, name);
|
|
36
|
+
return services?.({
|
|
37
|
+
config,
|
|
38
|
+
fragnoConfig: options,
|
|
39
|
+
deps,
|
|
40
|
+
...dbContext
|
|
41
|
+
}) ?? {};
|
|
42
|
+
},
|
|
43
|
+
additionalContext: {
|
|
44
|
+
databaseSchema: schema,
|
|
45
|
+
databaseNamespace: namespace
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
#createDatabaseContext(options, schema, namespace, name) {
|
|
50
|
+
const databaseAdapter = options.databaseAdapter;
|
|
51
|
+
if (!databaseAdapter) throw new Error(`Fragment '${name}' requires a database adapter in options.databaseAdapter`);
|
|
52
|
+
if (!schema) throw new Error(`Fragment '${name}' requires a schema. Use withDatabase() to provide one.`);
|
|
53
|
+
return {
|
|
54
|
+
databaseAdapter,
|
|
55
|
+
orm: databaseAdapter.createQueryEngine(schema, namespace)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
withDatabase(schema, namespace) {
|
|
59
|
+
return new DatabaseFragmentBuilder({
|
|
60
|
+
name: this.#name,
|
|
61
|
+
schema,
|
|
62
|
+
namespace: namespace ?? this.#name + "-db",
|
|
63
|
+
dependencies: this.#dependencies,
|
|
64
|
+
services: this.#services
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
withDependencies(fn) {
|
|
68
|
+
return new DatabaseFragmentBuilder({
|
|
69
|
+
name: this.#name,
|
|
70
|
+
schema: this.#schema,
|
|
71
|
+
namespace: this.#namespace,
|
|
72
|
+
dependencies: fn,
|
|
73
|
+
services: void 0
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
withServices(fn) {
|
|
77
|
+
return new DatabaseFragmentBuilder({
|
|
78
|
+
name: this.#name,
|
|
79
|
+
schema: this.#schema,
|
|
80
|
+
namespace: this.#namespace,
|
|
81
|
+
dependencies: this.#dependencies,
|
|
82
|
+
services: fn
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
function defineFragmentWithDatabase(name) {
|
|
87
|
+
return new DatabaseFragmentBuilder({ name });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { DatabaseFragmentBuilder, defineFragmentWithDatabase };
|
|
92
|
+
//# sourceMappingURL=fragment.js.map
|