@comity/sql-kysely 0.9.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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/cjs/client/create-kysely.js +29 -0
- package/dist/cjs/client/types.js +3 -0
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/internal/assert-client.js +25 -0
- package/dist/cjs/internal/map-sql-error.js +70 -0
- package/dist/cjs/query/execute.js +57 -0
- package/dist/cjs/transaction/execute.js +66 -0
- package/dist/esm/client/create-kysely.js +26 -0
- package/dist/esm/client/types.js +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/internal/assert-client.js +22 -0
- package/dist/esm/internal/map-sql-error.js +66 -0
- package/dist/esm/query/execute.js +54 -0
- package/dist/esm/transaction/execute.js +63 -0
- package/dist/types/client/create-kysely.d.ts +29 -0
- package/dist/types/client/types.d.ts +18 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/internal/assert-client.d.ts +9 -0
- package/dist/types/internal/map-sql-error.d.ts +26 -0
- package/dist/types/query/execute.d.ts +17 -0
- package/dist/types/transaction/execute.d.ts +15 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Filippo Bovo and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @comity/sql-kysely
|
|
2
|
+
|
|
3
|
+
Kysely adapter for @comity/sql.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Implements the SQL contracts from `@comity/sql` on top of Kysely. Maps Kysely operations to the SQL client and result contracts without exposing Kysely internals directly.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
This package:
|
|
16
|
+
|
|
17
|
+
- ✅ provides a Kysely-backed SQL client implementation
|
|
18
|
+
- ✅ maps Kysely errors to Comity SQL error types
|
|
19
|
+
- ✅ maps Kysely transactions to the SQL transaction contract
|
|
20
|
+
|
|
21
|
+
This package does NOT:
|
|
22
|
+
|
|
23
|
+
- ❌ define SQL contracts
|
|
24
|
+
- ❌ manage connection pools or pooling strategies
|
|
25
|
+
- ❌ expose Kysely internals through public contracts
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Public API
|
|
30
|
+
|
|
31
|
+
- `createKyselySqlClient` — factory that creates a Kysely-based SQL client
|
|
32
|
+
|
|
33
|
+
No exhaustive reference; see docs for constraints.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
38
|
+
|
|
39
|
+
- docs/overview.md
|
|
40
|
+
- docs/conventions.md
|
|
41
|
+
- docs/architecture.md
|
|
42
|
+
- docs/error-mapping.md
|
|
43
|
+
- docs/transactions.md
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Related Packages
|
|
48
|
+
|
|
49
|
+
- @comity/sql — SQL contracts and error types
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Status
|
|
54
|
+
|
|
55
|
+
Stable
|
|
56
|
+
|
|
57
|
+
_Review Completed: 2026-07-25_
|
|
58
|
+
_Reviewer: Hobiri MAGI (DeepSeek v4 Pro)_
|
|
59
|
+
_Compliance Score: 99.5% (Green)_
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createKyselySqlClient = createKyselySqlClient;
|
|
4
|
+
const execute_js_1 = require("../query/execute.js");
|
|
5
|
+
const execute_js_2 = require("../transaction/execute.js");
|
|
6
|
+
/**
|
|
7
|
+
* Create a SqlClient backed by a Kysely database instance.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam DB - Database shape used by Kysely
|
|
10
|
+
*
|
|
11
|
+
* @param options - Immutable client configuration including db and adapter name
|
|
12
|
+
*
|
|
13
|
+
* @returns SqlClient implementation that executes queries and transactions via Kysely
|
|
14
|
+
*
|
|
15
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* - Public API does not expose Kysely types
|
|
19
|
+
* - Error reasons use kebab-case domain format (sql:<error-kind>)
|
|
20
|
+
*/
|
|
21
|
+
function createKyselySqlClient(options) {
|
|
22
|
+
return {
|
|
23
|
+
/** @inheritdoc */
|
|
24
|
+
query: (q) => (0, execute_js_1.executeQuery)(options.db, q, options.adapter),
|
|
25
|
+
/** @inheritdoc */
|
|
26
|
+
begin: () => (0, execute_js_2.executeTransaction)(options.db, options.adapter),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=create-kysely.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createKyselySqlClient = void 0;
|
|
4
|
+
var create_kysely_js_1 = require("./client/create-kysely.js");
|
|
5
|
+
Object.defineProperty(exports, "createKyselySqlClient", { enumerable: true, get: function () { return create_kysely_js_1.createKyselySqlClient; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertKyselyClient = assertKyselyClient;
|
|
4
|
+
const errors_1 = require("@comity/sql/errors");
|
|
5
|
+
/**
|
|
6
|
+
* Validate the client shape and throw a structured error if invalid.
|
|
7
|
+
*
|
|
8
|
+
* @param client - Value to validate as KyselyDatabase
|
|
9
|
+
*
|
|
10
|
+
* @throws SqlError with reason "sql:invalid_configuration" if invalid
|
|
11
|
+
*/
|
|
12
|
+
function assertKyselyClient(client) {
|
|
13
|
+
if (!client ||
|
|
14
|
+
typeof client !== "object" ||
|
|
15
|
+
typeof client.executeQuery !== "function" ||
|
|
16
|
+
typeof client.transaction !== "function") {
|
|
17
|
+
throw new errors_1.SqlError("invalid_configuration", {
|
|
18
|
+
details: {
|
|
19
|
+
retriable: false,
|
|
20
|
+
expected: "KyselyDatabase",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=assert-client.js.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DRIVER_ERROR_MAPPINGS = void 0;
|
|
4
|
+
exports.mapSqlError = mapSqlError;
|
|
5
|
+
const errors_1 = require("@comity/sql/errors");
|
|
6
|
+
/**
|
|
7
|
+
* Driver-specific error code mappings.
|
|
8
|
+
*/
|
|
9
|
+
exports.DRIVER_ERROR_MAPPINGS = {
|
|
10
|
+
postgres: {
|
|
11
|
+
"57014": { reason: "cancelled", retriable: true },
|
|
12
|
+
"42601": { reason: "invalid_query", retriable: false },
|
|
13
|
+
"08006": { reason: "connection_failed", retriable: true },
|
|
14
|
+
"42883": { reason: "invalid_query", retriable: false },
|
|
15
|
+
},
|
|
16
|
+
mysql: {
|
|
17
|
+
"1317": { reason: "cancelled", retriable: true },
|
|
18
|
+
"1064": { reason: "invalid_query", retriable: false },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Maps various error types and messages into a standardized SqlError.
|
|
23
|
+
*
|
|
24
|
+
* @param cause - The original error thrown during a SQL operation
|
|
25
|
+
* @param operation - The type of SQL operation being performed
|
|
26
|
+
* @param adapter - Optional database adapter name
|
|
27
|
+
*
|
|
28
|
+
* @returns A standardized SqlError instance
|
|
29
|
+
*/
|
|
30
|
+
function mapSqlError(cause, operation, adapter) {
|
|
31
|
+
// 1. Abort / cancellation
|
|
32
|
+
if (cause instanceof DOMException && cause.name === "AbortError") {
|
|
33
|
+
return new errors_1.SqlError("cancelled", {
|
|
34
|
+
details: { operation, adapter, retriable: true },
|
|
35
|
+
cause,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// 2. Driver-specific error code mappings
|
|
39
|
+
if (adapter in exports.DRIVER_ERROR_MAPPINGS &&
|
|
40
|
+
typeof exports.DRIVER_ERROR_MAPPINGS[adapter] === "object" &&
|
|
41
|
+
typeof cause === "object" &&
|
|
42
|
+
cause &&
|
|
43
|
+
"code" in cause &&
|
|
44
|
+
typeof cause.code === "string" &&
|
|
45
|
+
cause.code in exports.DRIVER_ERROR_MAPPINGS[adapter] &&
|
|
46
|
+
exports.DRIVER_ERROR_MAPPINGS[adapter][cause.code]) {
|
|
47
|
+
const mapping = exports.DRIVER_ERROR_MAPPINGS[adapter][cause.code];
|
|
48
|
+
// Return mapped error
|
|
49
|
+
if (mapping && mapping.reason) {
|
|
50
|
+
return new errors_1.SqlError(mapping.reason, {
|
|
51
|
+
details: { operation, adapter, retriable: mapping.retriable },
|
|
52
|
+
context: { originalCode: cause.code },
|
|
53
|
+
cause,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 3. Operation-aware fallback
|
|
58
|
+
if (operation === "transaction") {
|
|
59
|
+
return new errors_1.SqlError("transaction_failed", {
|
|
60
|
+
details: { operation, adapter, retriable: false },
|
|
61
|
+
cause,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// 4. Last-resort fallback
|
|
65
|
+
return new errors_1.SqlError("query_failed", {
|
|
66
|
+
details: { operation, adapter, retriable: false },
|
|
67
|
+
cause,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=map-sql-error.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeQuery = executeQuery;
|
|
4
|
+
const errors_1 = require("@comity/sql/errors");
|
|
5
|
+
const kysely_1 = require("kysely");
|
|
6
|
+
const map_sql_error_js_1 = require("../internal/map-sql-error.js");
|
|
7
|
+
/**
|
|
8
|
+
* Execute a SQL query via Kysely and return an explicit operation result.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam DB - Database shape used by Kysely
|
|
11
|
+
* @typeParam T - Row shape produced by the query
|
|
12
|
+
*
|
|
13
|
+
* @param db - Kysely database instance
|
|
14
|
+
* @param query - Immutable SQL query payload
|
|
15
|
+
* @param adapter - Adapter name for diagnostics
|
|
16
|
+
*
|
|
17
|
+
* @returns Operation result wrapping a SqlResult<T>
|
|
18
|
+
*
|
|
19
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
20
|
+
*/
|
|
21
|
+
async function executeQuery(db, query, adapter) {
|
|
22
|
+
if (query.params && !Array.isArray(query.params)) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
error: new errors_1.SqlError("invalid_query", {
|
|
26
|
+
details: {
|
|
27
|
+
retriable: false,
|
|
28
|
+
adapter,
|
|
29
|
+
operation: "query",
|
|
30
|
+
},
|
|
31
|
+
context: {
|
|
32
|
+
violation: "named_parameters",
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const compiled = kysely_1.CompiledQuery.raw(query.statement, (query.params ?? []));
|
|
39
|
+
const result = await db.executeQuery(compiled);
|
|
40
|
+
return {
|
|
41
|
+
success: true,
|
|
42
|
+
value: {
|
|
43
|
+
rows: result.rows,
|
|
44
|
+
rowCount: typeof result.numAffectedRows === "bigint"
|
|
45
|
+
? Number(result.numAffectedRows)
|
|
46
|
+
: result.rows.length,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: (0, map_sql_error_js_1.mapSqlError)(e, "query", adapter),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeTransaction = executeTransaction;
|
|
4
|
+
const map_sql_error_js_1 = require("../internal/map-sql-error.js");
|
|
5
|
+
const execute_js_1 = require("../query/execute.js");
|
|
6
|
+
/**
|
|
7
|
+
* Begin a transaction via Kysely and return a transactional capability.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam DB - Database shape used by Kysely
|
|
10
|
+
*
|
|
11
|
+
* @param db - Kysely database instance
|
|
12
|
+
* @param adapter - Adapter name for diagnostics
|
|
13
|
+
*
|
|
14
|
+
* @returns Operation result wrapping a SqlTransaction capability
|
|
15
|
+
*
|
|
16
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
17
|
+
*/
|
|
18
|
+
async function executeTransaction(db, adapter) {
|
|
19
|
+
try {
|
|
20
|
+
let trx;
|
|
21
|
+
let resolve;
|
|
22
|
+
let reject;
|
|
23
|
+
const barrier = new Promise((res, rej) => {
|
|
24
|
+
resolve = res;
|
|
25
|
+
reject = rej;
|
|
26
|
+
});
|
|
27
|
+
const txPromise = db.transaction().execute(async (t) => {
|
|
28
|
+
trx = t;
|
|
29
|
+
await barrier;
|
|
30
|
+
});
|
|
31
|
+
const transaction = {
|
|
32
|
+
/** @inheritdoc */
|
|
33
|
+
query: (q) => (0, execute_js_1.executeQuery)(trx, q, adapter),
|
|
34
|
+
/** @inheritdoc */
|
|
35
|
+
commit: async () => {
|
|
36
|
+
try {
|
|
37
|
+
resolve();
|
|
38
|
+
await txPromise;
|
|
39
|
+
return { success: true, value: undefined };
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
return { success: false, error: (0, map_sql_error_js_1.mapSqlError)(e, "transaction", adapter) };
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
/** @inheritdoc */
|
|
46
|
+
rollback: async () => {
|
|
47
|
+
try {
|
|
48
|
+
reject(new Error("rollback"));
|
|
49
|
+
await txPromise.catch(() => { });
|
|
50
|
+
return { success: true, value: undefined };
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return { success: false, error: (0, map_sql_error_js_1.mapSqlError)(e, "transaction", adapter) };
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
value: transaction,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return { success: false, error: (0, map_sql_error_js_1.mapSqlError)(e, "transaction", adapter) };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { executeQuery } from "../query/execute.js";
|
|
2
|
+
import { executeTransaction } from "../transaction/execute.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create a SqlClient backed by a Kysely database instance.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam DB - Database shape used by Kysely
|
|
7
|
+
*
|
|
8
|
+
* @param options - Immutable client configuration including db and adapter name
|
|
9
|
+
*
|
|
10
|
+
* @returns SqlClient implementation that executes queries and transactions via Kysely
|
|
11
|
+
*
|
|
12
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* - Public API does not expose Kysely types
|
|
16
|
+
* - Error reasons use kebab-case domain format (sql:<error-kind>)
|
|
17
|
+
*/
|
|
18
|
+
export function createKyselySqlClient(options) {
|
|
19
|
+
return {
|
|
20
|
+
/** @inheritdoc */
|
|
21
|
+
query: (q) => executeQuery(options.db, q, options.adapter),
|
|
22
|
+
/** @inheritdoc */
|
|
23
|
+
begin: () => executeTransaction(options.db, options.adapter),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=create-kysely.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SqlError } from "@comity/sql/errors";
|
|
2
|
+
/**
|
|
3
|
+
* Validate the client shape and throw a structured error if invalid.
|
|
4
|
+
*
|
|
5
|
+
* @param client - Value to validate as KyselyDatabase
|
|
6
|
+
*
|
|
7
|
+
* @throws SqlError with reason "sql:invalid_configuration" if invalid
|
|
8
|
+
*/
|
|
9
|
+
export function assertKyselyClient(client) {
|
|
10
|
+
if (!client ||
|
|
11
|
+
typeof client !== "object" ||
|
|
12
|
+
typeof client.executeQuery !== "function" ||
|
|
13
|
+
typeof client.transaction !== "function") {
|
|
14
|
+
throw new SqlError("invalid_configuration", {
|
|
15
|
+
details: {
|
|
16
|
+
retriable: false,
|
|
17
|
+
expected: "KyselyDatabase",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=assert-client.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SqlError } from "@comity/sql/errors";
|
|
2
|
+
/**
|
|
3
|
+
* Driver-specific error code mappings.
|
|
4
|
+
*/
|
|
5
|
+
export const DRIVER_ERROR_MAPPINGS = {
|
|
6
|
+
postgres: {
|
|
7
|
+
"57014": { reason: "cancelled", retriable: true },
|
|
8
|
+
"42601": { reason: "invalid_query", retriable: false },
|
|
9
|
+
"08006": { reason: "connection_failed", retriable: true },
|
|
10
|
+
"42883": { reason: "invalid_query", retriable: false },
|
|
11
|
+
},
|
|
12
|
+
mysql: {
|
|
13
|
+
"1317": { reason: "cancelled", retriable: true },
|
|
14
|
+
"1064": { reason: "invalid_query", retriable: false },
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Maps various error types and messages into a standardized SqlError.
|
|
19
|
+
*
|
|
20
|
+
* @param cause - The original error thrown during a SQL operation
|
|
21
|
+
* @param operation - The type of SQL operation being performed
|
|
22
|
+
* @param adapter - Optional database adapter name
|
|
23
|
+
*
|
|
24
|
+
* @returns A standardized SqlError instance
|
|
25
|
+
*/
|
|
26
|
+
export function mapSqlError(cause, operation, adapter) {
|
|
27
|
+
// 1. Abort / cancellation
|
|
28
|
+
if (cause instanceof DOMException && cause.name === "AbortError") {
|
|
29
|
+
return new SqlError("cancelled", {
|
|
30
|
+
details: { operation, adapter, retriable: true },
|
|
31
|
+
cause,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// 2. Driver-specific error code mappings
|
|
35
|
+
if (adapter in DRIVER_ERROR_MAPPINGS &&
|
|
36
|
+
typeof DRIVER_ERROR_MAPPINGS[adapter] === "object" &&
|
|
37
|
+
typeof cause === "object" &&
|
|
38
|
+
cause &&
|
|
39
|
+
"code" in cause &&
|
|
40
|
+
typeof cause.code === "string" &&
|
|
41
|
+
cause.code in DRIVER_ERROR_MAPPINGS[adapter] &&
|
|
42
|
+
DRIVER_ERROR_MAPPINGS[adapter][cause.code]) {
|
|
43
|
+
const mapping = DRIVER_ERROR_MAPPINGS[adapter][cause.code];
|
|
44
|
+
// Return mapped error
|
|
45
|
+
if (mapping && mapping.reason) {
|
|
46
|
+
return new SqlError(mapping.reason, {
|
|
47
|
+
details: { operation, adapter, retriable: mapping.retriable },
|
|
48
|
+
context: { originalCode: cause.code },
|
|
49
|
+
cause,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// 3. Operation-aware fallback
|
|
54
|
+
if (operation === "transaction") {
|
|
55
|
+
return new SqlError("transaction_failed", {
|
|
56
|
+
details: { operation, adapter, retriable: false },
|
|
57
|
+
cause,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// 4. Last-resort fallback
|
|
61
|
+
return new SqlError("query_failed", {
|
|
62
|
+
details: { operation, adapter, retriable: false },
|
|
63
|
+
cause,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=map-sql-error.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SqlError } from "@comity/sql/errors";
|
|
2
|
+
import { CompiledQuery } from "kysely";
|
|
3
|
+
import { mapSqlError } from "../internal/map-sql-error.js";
|
|
4
|
+
/**
|
|
5
|
+
* Execute a SQL query via Kysely and return an explicit operation result.
|
|
6
|
+
*
|
|
7
|
+
* @typeParam DB - Database shape used by Kysely
|
|
8
|
+
* @typeParam T - Row shape produced by the query
|
|
9
|
+
*
|
|
10
|
+
* @param db - Kysely database instance
|
|
11
|
+
* @param query - Immutable SQL query payload
|
|
12
|
+
* @param adapter - Adapter name for diagnostics
|
|
13
|
+
*
|
|
14
|
+
* @returns Operation result wrapping a SqlResult<T>
|
|
15
|
+
*
|
|
16
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
17
|
+
*/
|
|
18
|
+
export async function executeQuery(db, query, adapter) {
|
|
19
|
+
if (query.params && !Array.isArray(query.params)) {
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
error: new SqlError("invalid_query", {
|
|
23
|
+
details: {
|
|
24
|
+
retriable: false,
|
|
25
|
+
adapter,
|
|
26
|
+
operation: "query",
|
|
27
|
+
},
|
|
28
|
+
context: {
|
|
29
|
+
violation: "named_parameters",
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const compiled = CompiledQuery.raw(query.statement, (query.params ?? []));
|
|
36
|
+
const result = await db.executeQuery(compiled);
|
|
37
|
+
return {
|
|
38
|
+
success: true,
|
|
39
|
+
value: {
|
|
40
|
+
rows: result.rows,
|
|
41
|
+
rowCount: typeof result.numAffectedRows === "bigint"
|
|
42
|
+
? Number(result.numAffectedRows)
|
|
43
|
+
: result.rows.length,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: mapSqlError(e, "query", adapter),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { mapSqlError } from "../internal/map-sql-error.js";
|
|
2
|
+
import { executeQuery } from "../query/execute.js";
|
|
3
|
+
/**
|
|
4
|
+
* Begin a transaction via Kysely and return a transactional capability.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam DB - Database shape used by Kysely
|
|
7
|
+
*
|
|
8
|
+
* @param db - Kysely database instance
|
|
9
|
+
* @param adapter - Adapter name for diagnostics
|
|
10
|
+
*
|
|
11
|
+
* @returns Operation result wrapping a SqlTransaction capability
|
|
12
|
+
*
|
|
13
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
14
|
+
*/
|
|
15
|
+
export async function executeTransaction(db, adapter) {
|
|
16
|
+
try {
|
|
17
|
+
let trx;
|
|
18
|
+
let resolve;
|
|
19
|
+
let reject;
|
|
20
|
+
const barrier = new Promise((res, rej) => {
|
|
21
|
+
resolve = res;
|
|
22
|
+
reject = rej;
|
|
23
|
+
});
|
|
24
|
+
const txPromise = db.transaction().execute(async (t) => {
|
|
25
|
+
trx = t;
|
|
26
|
+
await barrier;
|
|
27
|
+
});
|
|
28
|
+
const transaction = {
|
|
29
|
+
/** @inheritdoc */
|
|
30
|
+
query: (q) => executeQuery(trx, q, adapter),
|
|
31
|
+
/** @inheritdoc */
|
|
32
|
+
commit: async () => {
|
|
33
|
+
try {
|
|
34
|
+
resolve();
|
|
35
|
+
await txPromise;
|
|
36
|
+
return { success: true, value: undefined };
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
return { success: false, error: mapSqlError(e, "transaction", adapter) };
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
/** @inheritdoc */
|
|
43
|
+
rollback: async () => {
|
|
44
|
+
try {
|
|
45
|
+
reject(new Error("rollback"));
|
|
46
|
+
await txPromise.catch(() => { });
|
|
47
|
+
return { success: true, value: undefined };
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return { success: false, error: mapSqlError(e, "transaction", adapter) };
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
value: transaction,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return { success: false, error: mapSqlError(e, "transaction", adapter) };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=execute.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SqlClient, SqlClientOptions } from "@comity/sql";
|
|
2
|
+
import type { Kysely } from "kysely";
|
|
3
|
+
/**
|
|
4
|
+
* Kysely client factory options.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam DB - Database shape used by Kysely
|
|
7
|
+
*/
|
|
8
|
+
export interface KyselySqlClientOptions<DB> extends SqlClientOptions {
|
|
9
|
+
/** Kysely database instance */
|
|
10
|
+
readonly db: Kysely<DB>;
|
|
11
|
+
/** Adapter name for diagnostics */
|
|
12
|
+
readonly adapter: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a SqlClient backed by a Kysely database instance.
|
|
16
|
+
*
|
|
17
|
+
* @typeParam DB - Database shape used by Kysely
|
|
18
|
+
*
|
|
19
|
+
* @param options - Immutable client configuration including db and adapter name
|
|
20
|
+
*
|
|
21
|
+
* @returns SqlClient implementation that executes queries and transactions via Kysely
|
|
22
|
+
*
|
|
23
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* - Public API does not expose Kysely types
|
|
27
|
+
* - Error reasons use kebab-case domain format (sql:<error-kind>)
|
|
28
|
+
*/
|
|
29
|
+
export declare function createKyselySqlClient<DB = unknown>(options: KyselySqlClientOptions<DB>): SqlClient;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Compilable } from "kysely";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal Kysely database interface used by this adapter.
|
|
4
|
+
*/
|
|
5
|
+
export interface KyselyDatabase {
|
|
6
|
+
/** Execute a compiled query and return rows with optional affected count */
|
|
7
|
+
executeQuery<T = unknown>(query: Compilable): Promise<{
|
|
8
|
+
/** Result rows */
|
|
9
|
+
rows: T[];
|
|
10
|
+
/** Optional affected rows count as bigint */
|
|
11
|
+
numAffectedRows?: bigint;
|
|
12
|
+
}>;
|
|
13
|
+
/** Start a transaction and execute the provided function within it */
|
|
14
|
+
transaction(): {
|
|
15
|
+
/** Execute transactional work and resolve with the function's result */
|
|
16
|
+
execute<T>(fn: (trx: KyselyDatabase) => Promise<T>): Promise<T>;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createKyselySqlClient } from "./client/create-kysely.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { KyselyDatabase } from "../client/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate the client shape and throw a structured error if invalid.
|
|
4
|
+
*
|
|
5
|
+
* @param client - Value to validate as KyselyDatabase
|
|
6
|
+
*
|
|
7
|
+
* @throws SqlError with reason "sql:invalid_configuration" if invalid
|
|
8
|
+
*/
|
|
9
|
+
export declare function assertKyselyClient(client: unknown): asserts client is KyselyDatabase;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SqlErrorReason } from "@comity/sql/errors";
|
|
2
|
+
import { SqlError } from "@comity/sql/errors";
|
|
3
|
+
/**
|
|
4
|
+
* Internal type for driver-specific error code mappings.
|
|
5
|
+
*/
|
|
6
|
+
type ErrorDetails = {
|
|
7
|
+
/** Error reason */
|
|
8
|
+
reason: SqlErrorReason;
|
|
9
|
+
/** Indicates if the error is retriable */
|
|
10
|
+
retriable: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Driver-specific error code mappings.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DRIVER_ERROR_MAPPINGS: Record<string, Record<string, ErrorDetails>>;
|
|
16
|
+
/**
|
|
17
|
+
* Maps various error types and messages into a standardized SqlError.
|
|
18
|
+
*
|
|
19
|
+
* @param cause - The original error thrown during a SQL operation
|
|
20
|
+
* @param operation - The type of SQL operation being performed
|
|
21
|
+
* @param adapter - Optional database adapter name
|
|
22
|
+
*
|
|
23
|
+
* @returns A standardized SqlError instance
|
|
24
|
+
*/
|
|
25
|
+
export declare function mapSqlError(cause: unknown, operation: "connect" | "query" | "transaction", adapter: string): SqlError;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SqlOperationResult, SqlQuery, SqlResult } from "@comity/sql";
|
|
2
|
+
import type { Kysely } from "kysely";
|
|
3
|
+
/**
|
|
4
|
+
* Execute a SQL query via Kysely and return an explicit operation result.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam DB - Database shape used by Kysely
|
|
7
|
+
* @typeParam T - Row shape produced by the query
|
|
8
|
+
*
|
|
9
|
+
* @param db - Kysely database instance
|
|
10
|
+
* @param query - Immutable SQL query payload
|
|
11
|
+
* @param adapter - Adapter name for diagnostics
|
|
12
|
+
*
|
|
13
|
+
* @returns Operation result wrapping a SqlResult<T>
|
|
14
|
+
*
|
|
15
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
16
|
+
*/
|
|
17
|
+
export declare function executeQuery<DB, T>(db: Kysely<DB>, query: SqlQuery, adapter: string): Promise<SqlOperationResult<SqlResult<T>>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SqlOperationResult, SqlTransaction } from "@comity/sql";
|
|
2
|
+
import type { Kysely } from "kysely";
|
|
3
|
+
/**
|
|
4
|
+
* Begin a transaction via Kysely and return a transactional capability.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam DB - Database shape used by Kysely
|
|
7
|
+
*
|
|
8
|
+
* @param db - Kysely database instance
|
|
9
|
+
* @param adapter - Adapter name for diagnostics
|
|
10
|
+
*
|
|
11
|
+
* @returns Operation result wrapping a SqlTransaction capability
|
|
12
|
+
*
|
|
13
|
+
* @throws Errors are not thrown; failures are returned via SqlOperationResult
|
|
14
|
+
*/
|
|
15
|
+
export declare function executeTransaction<DB>(db: Kysely<DB>, adapter: string): Promise<SqlOperationResult<SqlTransaction>>;
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@comity/sql-kysely",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Kysely ORM adapter for @comity/sql contracts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"author": "Filippo Bovo <hello@filippobovo.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"comity": {
|
|
10
|
+
"layer": "technology-adapter",
|
|
11
|
+
"implements": "@comity/sql"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/comityjs/framework#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/comityjs/framework.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/comityjs/framework/issues"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=24.0.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"comity",
|
|
26
|
+
"adapter",
|
|
27
|
+
"kysely",
|
|
28
|
+
"sql",
|
|
29
|
+
"typescript"
|
|
30
|
+
],
|
|
31
|
+
"files": [
|
|
32
|
+
"./dist",
|
|
33
|
+
"!./dist/**/*.map"
|
|
34
|
+
],
|
|
35
|
+
"main": "./dist/cjs/index.js",
|
|
36
|
+
"module": "./dist/esm/index.js",
|
|
37
|
+
"types": "./dist/types/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./dist/types/index.d.ts",
|
|
42
|
+
"default": "./dist/esm/index.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./dist/types/index.d.ts",
|
|
46
|
+
"default": "./dist/cjs/index.js"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"./package.json": "./package.json"
|
|
50
|
+
},
|
|
51
|
+
"typesVersions": {
|
|
52
|
+
"*": {}
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"registry": "https://registry.npmjs.org",
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"sideEffects": false,
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"kysely": "^0.28.0",
|
|
61
|
+
"@comity/sql": "0.9.0"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@comity/sql": "0.9.0"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@types/node": "^24.13.4",
|
|
68
|
+
"kysely": "^0.28.17",
|
|
69
|
+
"typescript": "^5.9.3"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "node ../../scripts/build.mjs",
|
|
73
|
+
"dev": "node ../../scripts/build.mjs --watch",
|
|
74
|
+
"test": "vitest run --coverage",
|
|
75
|
+
"type-check": "tsc -p tsconfig.json --noEmit",
|
|
76
|
+
"lint": "eslint --ext .ts src"
|
|
77
|
+
}
|
|
78
|
+
}
|