@typesql/kysely 0.0.3 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @typesql/kysely
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Queries run from the `sql` bound to your client: `createSql<DB>(client)` then `sql("…").execute()` (or `executeTakeFirst`, `executeTakeFirstOrThrow`, `executeCount`), and `sql.transaction(async (tx) => …)` for transactions, nested with savepoints; the `query`, `one`, `maybeOne`, `many`, `execute`, `transaction` and `RowCountError` exports of `@typesql/pg` and `@typesql/postgres`, `query` and `execute` of `@typesql/kysely`, and `queryRaw` and `executeRaw` of `@typesql/prisma` are removed.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies:
12
+ - @typesql/core@0.1.0
13
+
14
+ ## 0.0.4
15
+
16
+ ### Patch Changes
17
+
18
+ - Queries have `executeCount()`, which resolves to the affected row count: an `Executor` may resolve to `{ rows, rowCount }` instead of the rows, as the `executor()` of `@typesql/pg`, `@typesql/postgres` and `@typesql/kysely` now do.
19
+
20
+ - `createSql(executor, { rowKeys: "camelCase" })` (and the adapters' `createSql(client, { rowKeys: "camelCase" })`) camelCases the top-level row keys the way Kysely's `CamelCasePlugin` does, in the row types and in the rows `execute*` and the `query`/`one`/`maybeOne`/`many` helpers return, leaving JSON values untouched.
21
+
22
+ - `execute(db, q)` returns the number of rows for statements Kysely reports no `numAffectedRows` for (`SELECT`), as `@typesql/pg` does, instead of 0.
23
+
24
+ - Parameters accept `null` wherever Postgres does: comparisons (`col = $1`, `IN`, `ANY`, `LIKE`), `LIMIT`/`OFFSET`, casts in comparisons and function arguments (`coalesce($1::text, 'x')`), and array elements (`(number | null)[]`); assigning `null` to a `NOT NULL` column is still an error.
25
+
26
+ - Updated dependencies:
27
+ - @typesql/core@0.0.4
28
+
3
29
  ## 0.0.3
4
30
 
5
31
  ### Patch Changes
package/README.md CHANGED
@@ -10,10 +10,10 @@ pnpm add @typesql/core @typesql/kysely kysely
10
10
  ```
11
11
 
12
12
  ```ts
13
- import { createSql, query } from "@typesql/kysely";
13
+ import { createSql } from "@typesql/kysely";
14
14
  import type { DB } from "./db"; // kysely-codegen output
15
15
 
16
- const sql = createSql<DB>();
16
+ const sql = createSql<DB>(db); // db: your Kysely<DB> instance, or a Transaction<DB>
17
17
 
18
18
  const q = sql(
19
19
  `SELECT u.id, u.email, p.title, p.views
@@ -21,19 +21,25 @@ const q = sql(
21
21
  WHERE u.id = $1 AND p.published = $2`,
22
22
  1, true,
23
23
  );
24
- // ^? Query<{ id: number; email: string; title: string; views: string }, [number, boolean]>
24
+ // ^? Query<{ id: number; email: string; title: string; views: string }, [number | null, boolean | null]>
25
25
 
26
- const rows = await query(db, q); // db: Kysely<DB> or a Transaction<DB>
26
+ const rows = await q.execute();
27
+
28
+ await sql.transaction(async (tx) => {
29
+ await tx("UPDATE posts SET published = true WHERE author_id = $1", 1).execute();
30
+ });
27
31
  ```
28
32
 
29
33
  ## API
30
34
 
31
35
  | Export | What it is |
32
36
  |---|---|
33
- | `createSql<DB, TM = DefaultTypeMap, Overrides = {}, Options = {}>()` | core's `createSql<FromKysely<DB, Overrides, Options>, TM>()` |
34
- | `FromKysely<DB, Overrides = {}, Options = {}>` | the converted schema type, for use with core's `createSql<Schema, TM>()` or the adapters' `createSql<Schema>()` |
35
- | `query(db, q)` | runs a typesql `Query` through Kysely, `Promise<Row[]>` |
36
- | `execute(db, q)` | runs a statement, `Promise<number>` (`numAffectedRows`, or 0) |
37
+ | `createSql<DB, TM = DefaultTypeMap, Overrides = {}, Options = {}>(db, options?)` | core's `createSql<FromKysely<DB, Overrides, Options>, TM>()` with the queries running on `db`; `{ rowKeys: "camelCase" }` gives `CamelCasePlugin`-style row keys |
38
+ | `q.execute()`, `q.executeTakeFirst()`, `q.executeTakeFirstOrThrow()`, `q.executeCount()` | `Promise<Row[]>`, `Promise<Row \| undefined>`, `Promise<Row>` (`NoResultError` on 0 rows), `Promise<number>` (`numAffectedRows`; for other statements the number of rows returned, 0 for DDL) |
39
+ | `sql.transaction(fn)` | `db.transaction().execute(...)` with `tx` bound to the transaction; on a `Transaction`, and for `tx.transaction(fn)`, a savepoint |
40
+ | `FromKysely<DB, Overrides = {}, Options = {}>` | the converted schema type, for use with core's `createSql<Schema, TM>()` or the adapters' `createSql<Schema>(client)` |
41
+ | `KyselySql<DB, TM, Overrides, Options, Keys>` | the type `createSql` returns |
42
+ | `executor(db)` | an `Executor` for `q.execute(executor)` and core's `createSql(executor)`, also bypassing plugins |
37
43
  | `InferColumn<T>` | the descriptor inferred for one Kysely column type (handy to check a mapping) |
38
44
  | `SnakeCase<S>` | `CamelCasePlugin`'s default camelCase → snake_case mapping, at the type level |
39
45
  | `KyselyOverrides`, `TableOverride`, `ColumnOverride`, `KyselyOptions`, `KyselyExecutor` | parameter types |
@@ -47,15 +53,16 @@ import type { PgTypeMap } from "@typesql/pg";
47
53
  import { createSql, type FromKysely } from "@typesql/kysely";
48
54
  import { createSql as createCoreSql } from "@typesql/core";
49
55
 
50
- const sql = createSql<DB, PgTypeMap>();
51
- const same = createCoreSql<FromKysely<DB>, PgTypeMap>();
56
+ const sql = createSql<DB, PgTypeMap>(db);
57
+ const same = createCoreSql<FromKysely<DB>, PgTypeMap>(); // types only, no client
52
58
  ```
53
59
 
54
- `query` and `execute` send `CompiledQuery.raw(q.text, q.values)` through
60
+ Queries are sent as `CompiledQuery.raw(q.text, q.values)` through
55
61
  `db.withoutPlugins().executeQuery(...)`. Plugins are bypassed on purpose: typesql's row types
56
62
  describe what Postgres returns (`created_at`), and a result transformer such as
57
- `CamelCasePlugin` would rename the keys. Logging, the dialect and the connection pool are
58
- unaffected. Any `sql(...)` result can also be run with `@typesql/pg` directly on the pool.
63
+ `CamelCasePlugin` would rename the keys (use `{ rowKeys: "camelCase" }` for camelCase rows).
64
+ Logging, the dialect and the connection pool are unaffected. Inside a Kysely transaction,
65
+ `createSql<DB>(trx)` binds a `sql` to it, so builder and SQL queries share the transaction.
59
66
 
60
67
  ## How columns are converted
61
68
 
@@ -103,7 +110,7 @@ const sql = createSql<DB, DefaultTypeMap, {
103
110
  "auth.users": { id: "uuid" };
104
111
  active_users: { $view: true }; // a view: INSERT/UPDATE/DELETE are rejected
105
112
  $types: { mood: Mood; "auth.role": AuthRole }; // enum names, see below
106
- }>();
113
+ }>(db);
107
114
  ```
108
115
 
109
116
  A descriptor may set any of `type`, `nullable` and `hasDefault`; the others stay inferred.
@@ -135,7 +142,7 @@ under `$types` and every column whose values are **exactly** that union gets the
135
142
  ```ts
136
143
  import type { Mood, AuthRole } from "./db"; // kysely-codegen exports one alias per enum
137
144
 
138
- createSql<DB, DefaultTypeMap, { $types: { mood: Mood; "auth.role": AuthRole } }>();
145
+ createSql<DB, DefaultTypeMap, { $types: { mood: Mood; "auth.role": AuthRole } }>(db);
139
146
  ```
140
147
 
141
148
  If two `$types` entries have the same values the column keeps its derived name; set the column's
@@ -151,14 +158,24 @@ back to the database's snake_case names the way `CamelCasePlugin` does by defaul
151
158
  `created_at`, `userID` → `user_id`), so the SQL you write and the rows you get use the real names:
152
159
 
153
160
  ```ts
154
- const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>();
161
+ const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>(db);
155
162
  const q = sql("SELECT author_id, created_at FROM blog_posts WHERE id = $1", 1);
156
- // ^? Query<{ author_id: number; created_at: Date }, [number]>
163
+ // ^? Query<{ author_id: number; created_at: Date }, [number | null]>
157
164
  ```
158
165
 
159
166
  Overrides still use the interface's (camelCase) keys. The plugin's non-default options
160
167
  (`upperCase`, `underscoreBeforeDigits`, `underscoreBetweenUppercaseLetters`) are not modelled.
161
168
 
169
+ For camelCase **rows**, add `{ rowKeys: "camelCase" }`. The top-level keys are renamed with
170
+ `CamelCasePlugin`'s default snake_case → camelCase mapping, in the row types and at runtime.
171
+ The plugin itself stays bypassed, so keys inside JSON values are left alone:
172
+
173
+ ```ts
174
+ const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>(db, { rowKeys: "camelCase" });
175
+ const post = await sql("SELECT author_id, created_at FROM blog_posts WHERE id = $1", 1).executeTakeFirstOrThrow();
176
+ // ^? { authorId: number; createdAt: Date }
177
+ ```
178
+
162
179
  ## Limitations
163
180
 
164
181
  - **The TS → Postgres mapping is lossy.** `number`, `string` and `Date` each stand for several
package/dist/client.d.ts CHANGED
@@ -1,21 +1,25 @@
1
1
  import { CompiledQuery, type QueryResult } from "kysely";
2
- import type { Executor, Query, RowType } from "@typesql/core";
2
+ import { type Executor, type SqlDriver } from "@typesql/core";
3
3
  /**
4
- * What the helpers use from a `Kysely<DB>` or `Transaction<DB>`. Queries run through
4
+ * What the adapter uses from a `Kysely<DB>` or `Transaction<DB>`. Queries run through
5
5
  * `withoutPlugins()`: typesql's row types describe what the driver returns, so result
6
- * transformers such as `CamelCasePlugin` must not rename the columns.
6
+ * transformers such as `CamelCasePlugin` must not rename the columns (`createSql(db,
7
+ * { rowKeys: "camelCase" })` renames the top-level keys instead, in the types too).
7
8
  */
8
9
  export interface KyselyExecutor {
9
10
  withoutPlugins(): {
10
11
  executeQuery<R>(query: CompiledQuery<R>): Promise<QueryResult<R>>;
11
12
  };
13
+ transaction(): {
14
+ execute<T>(fn: (trx: KyselyExecutor) => Promise<T>): Promise<T>;
15
+ };
16
+ readonly isTransaction: boolean;
12
17
  }
13
- type AnyQuery = Query<unknown, readonly unknown[]>;
14
- /** An `Executor` for core's `q.execute()`: runs queries on a Kysely instance (or transaction), without its plugins. */
18
+ /**
19
+ * An `Executor` for core's `q.execute()`: runs queries on a Kysely instance (or transaction),
20
+ * without its plugins, and resolves to `{ rows, rowCount }` so `q.executeCount()` works.
21
+ */
15
22
  export declare function executor(db: KyselyExecutor): Executor;
16
- /** Run a typesql query on a Kysely instance (or transaction) and return its rows, typed from the query. */
17
- export declare function query<Q extends AnyQuery>(db: KyselyExecutor, q: Q): Promise<RowType<Q>[]>;
18
- /** Run a statement for its side effect and return the affected row count (`numAffectedRows`, or 0). */
19
- export declare function execute(db: KyselyExecutor, q: AnyQuery): Promise<number>;
20
- export {};
23
+ /** How `createSql(db)` runs queries and transactions. */
24
+ export declare const driver: SqlDriver<KyselyExecutor>;
21
25
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,IAAI;QAAE,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,CAAC;CACzF;AAED,KAAK,QAAQ,GAAG,KAAK,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC,CAAC;AAMnD,uHAAuH;AACvH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,cAAc,GAAG,QAAQ,CAErD;AAED,2GAA2G;AAC3G,wBAAsB,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAG/F;AAED,uGAAuG;AACvG,wBAAsB,OAAO,CAAC,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAG9E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,IAAI;QAAE,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,CAAC;IACxF,WAAW,IAAI;QAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAC;IACnF,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAcD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,cAAc,GAAG,QAAQ,CAKrD;AAWD,yDAAyD;AACzD,eAAO,MAAM,MAAM,EAAE,SAAS,CAAC,cAAc,CAA6B,CAAC"}
package/dist/client.js CHANGED
@@ -1,19 +1,34 @@
1
1
  import { CompiledQuery } from "kysely";
2
+ import { runTransaction } from "@typesql/core";
2
3
  function run(db, text, values) {
3
4
  return db.withoutPlugins().executeQuery(CompiledQuery.raw(text, Array.from(values)));
4
5
  }
5
- /** An `Executor` for core's `q.execute()`: runs queries on a Kysely instance (or transaction), without its plugins. */
6
- export function executor(db) {
7
- return async (text, values) => (await run(db, text, values)).rows;
6
+ /**
7
+ * Kysely reports `numAffectedRows` for INSERT/UPDATE/DELETE/MERGE only; for other statements the
8
+ * count is the rows returned (SELECT), or 0 (DDL), as Postgres' command tag says.
9
+ */
10
+ function rowCount(result) {
11
+ return result.numAffectedRows === undefined ? result.rows.length : Number(result.numAffectedRows);
8
12
  }
9
- /** Run a typesql query on a Kysely instance (or transaction) and return its rows, typed from the query. */
10
- export async function query(db, q) {
11
- const result = await run(db, q.text, q.values);
12
- return result.rows;
13
+ /**
14
+ * An `Executor` for core's `q.execute()`: runs queries on a Kysely instance (or transaction),
15
+ * without its plugins, and resolves to `{ rows, rowCount }` so `q.executeCount()` works.
16
+ */
17
+ export function executor(db) {
18
+ return async (text, values) => {
19
+ const result = await run(db, text, values);
20
+ return { rows: result.rows, rowCount: rowCount(result) };
21
+ };
13
22
  }
14
- /** Run a statement for its side effect and return the affected row count (`numAffectedRows`, or 0). */
15
- export async function execute(db, q) {
16
- const result = await run(db, q.text, q.values);
17
- return Number(result.numAffectedRows ?? 0n);
23
+ /**
24
+ * Run `fn` inside `db.transaction().execute()`. A Kysely `Transaction` cannot start another one,
25
+ * so inside one `fn` runs in a savepoint.
26
+ */
27
+ function transaction(db, depth, fn) {
28
+ if (!db.isTransaction)
29
+ return db.transaction().execute(fn);
30
+ return runTransaction((statement) => run(db, statement, []), () => fn(db), { savepoint: `typesql_${depth + 1}` });
18
31
  }
32
+ /** How `createSql(db)` runs queries and transactions. */
33
+ export const driver = { executor, transaction };
19
34
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAoB,MAAM,QAAQ,CAAC;AAczD,SAAS,GAAG,CAAI,EAAkB,EAAE,IAAY,EAAE,MAA0B;IAC1E,OAAO,EAAE,CAAC,cAAc,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAqB,CAAC,CAAC;AAC3G,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,QAAQ,CAAC,EAAkB;IACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,CAAU,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7E,CAAC;AAED,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,KAAK,CAAqB,EAAkB,EAAE,CAAI;IACtE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,uGAAuG;AACvG,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAkB,EAAE,CAAW;IAC3D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,MAAM,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAoB,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,cAAc,EAAiC,MAAM,eAAe,CAAC;AAc9E,SAAS,GAAG,CAAI,EAAkB,EAAE,IAAY,EAAE,MAA0B;IAC1E,OAAO,EAAE,CAAC,cAAc,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAqB,CAAC,CAAC;AAC3G,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,MAA4B;IAC5C,OAAO,MAAM,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;AACpG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAkB;IACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QAC5B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAU,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3D,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAI,EAAkB,EAAE,KAAa,EAAE,EAAuC;IAChG,IAAI,CAAC,EAAE,CAAC,aAAa;QAAE,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3D,OAAO,cAAc,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,WAAW,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;AACpH,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,MAAM,GAA8B,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,18 +1,30 @@
1
- import { type DefaultTypeMap, type Sql } from "@typesql/core";
1
+ import { type DefaultTypeMap, type RowKeys, type TransactionalSql } from "@typesql/core";
2
2
  import { type KyselyExecutor } from "./client.js";
3
3
  import type { FromKysely, KyselyOptions, KyselyOverrides } from "./from-kysely.js";
4
- export { NoResultError, type Executor } from "@typesql/core";
4
+ export { NoResultError, type Executor, type TransactionalSql } from "@typesql/core";
5
5
  export type { FromKysely, KyselyOverrides, KyselyOptions, TableOverride, ColumnOverride, InferColumn, SnakeCase } from "./from-kysely.js";
6
- export { query, execute, executor, type KyselyExecutor } from "./client.js";
6
+ export { executor, type KyselyExecutor } from "./client.js";
7
+ /** What `createSql<DB>(db)` returns: core's `Sql` over `FromKysely<DB, ...>`, with `transaction(fn)`. */
8
+ export type KyselySql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}, Keys extends RowKeys = "preserve"> = TransactionalSql<FromKysely<DB, Overrides, Options>, TM, Keys>;
7
9
  /**
8
10
  * `createSql` from core with a Kysely `DB` interface as the schema:
9
- * `createSql<DB>()` is `createSql<FromKysely<DB>, DefaultTypeMap>()`.
11
+ * `createSql<DB>(db)` is `createSql<FromKysely<DB>, DefaultTypeMap>()` bound to `db`.
10
12
  *
11
- * const sql = createSql<DB>();
12
- * const sql = createSql<DB, PgTypeMap, { users: { id: "int8" } }>();
13
- * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>();
13
+ * const sql = createSql<DB>(db);
14
+ * const sql = createSql<DB, PgTypeMap, { users: { id: "int8" } }>(db);
15
+ * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>(db);
14
16
  *
15
- * With a `db` (a `Kysely` instance or transaction), the queries' `execute*` methods run on it.
17
+ * The queries' `execute*` methods run on `db` (a `Kysely` instance or transaction), bypassing
18
+ * its plugins, and `sql.transaction(fn)` runs `fn` with a `sql` bound to one transaction.
19
+ * `{ rowKeys: "camelCase" }` camelCases the rows' top-level keys as `CamelCasePlugin` does, in
20
+ * the types and at runtime, without touching JSON values:
21
+ *
22
+ * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>(db, { rowKeys: "camelCase" });
16
23
  */
17
- export declare function createSql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}>(db?: KyselyExecutor): Sql<FromKysely<DB, Overrides, Options>, TM>;
24
+ export declare function createSql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}>(db: KyselyExecutor | undefined, options: {
25
+ readonly rowKeys: "camelCase";
26
+ }): KyselySql<DB, TM, Overrides, Options, "camelCase">;
27
+ export declare function createSql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}>(db?: KyselyExecutor, options?: {
28
+ readonly rowKeys?: "preserve";
29
+ }): KyselySql<DB, TM, Overrides, Options>;
18
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnF,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1I,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,EAAE,EACF,EAAE,GAAG,cAAc,EACnB,SAAS,SAAS,eAAe,GAAG,EAAE,EACtC,OAAO,SAAS,aAAa,GAAG,EAAE,EAClC,EAAE,CAAC,EAAE,cAAc,GAAG,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAElE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,cAAc,EAAE,KAAK,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClI,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnF,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpF,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1I,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5D,yGAAyG;AACzG,MAAM,MAAM,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,cAAc,EAAE,SAAS,SAAS,eAAe,GAAG,EAAE,EAAE,OAAO,SAAS,aAAa,GAAG,EAAE,EAAE,IAAI,SAAS,OAAO,GAAG,UAAU,IAC1J,gBAAgB,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;AAEjE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,cAAc,EAAE,SAAS,SAAS,eAAe,GAAG,EAAE,EAAE,OAAO,SAAS,aAAa,GAAG,EAAE,EAC3H,EAAE,EAAE,cAAc,GAAG,SAAS,EAC9B,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GACzC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;AACtD,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,cAAc,EAAE,SAAS,SAAS,eAAe,GAAG,EAAE,EAAE,OAAO,SAAS,aAAa,GAAG,EAAE,EAC3H,EAAE,CAAC,EAAE,cAAc,EACnB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;CAAE,GAC1C,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -1,18 +1,8 @@
1
- import { createSql as createCoreSql } from "@typesql/core";
2
- import { executor } from "./client.js";
1
+ import { createTransactionalSql } from "@typesql/core";
2
+ import { driver } from "./client.js";
3
3
  export { NoResultError } from "@typesql/core";
4
- export { query, execute, executor } from "./client.js";
5
- /**
6
- * `createSql` from core with a Kysely `DB` interface as the schema:
7
- * `createSql<DB>()` is `createSql<FromKysely<DB>, DefaultTypeMap>()`.
8
- *
9
- * const sql = createSql<DB>();
10
- * const sql = createSql<DB, PgTypeMap, { users: { id: "int8" } }>();
11
- * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>();
12
- *
13
- * With a `db` (a `Kysely` instance or transaction), the queries' `execute*` methods run on it.
14
- */
15
- export function createSql(db) {
16
- return createCoreSql(db && executor(db));
4
+ export { executor } from "./client.js";
5
+ export function createSql(db, options) {
6
+ return createTransactionalSql(db, driver, options);
17
7
  }
18
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,IAAI,aAAa,EAAiC,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,QAAQ,EAAuB,MAAM,aAAa,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAiB,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAuB,MAAM,aAAa,CAAC;AAE5E;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAKvB,EAAmB;IACnB,OAAO,aAAa,CAAyC,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AACnF,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAA6E,MAAM,eAAe,CAAC;AAClI,OAAO,EAAE,MAAM,EAAuB,MAAM,aAAa,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAwC,MAAM,eAAe,CAAC;AAEpF,OAAO,EAAE,QAAQ,EAAuB,MAAM,aAAa,CAAC;AA6B5D,MAAM,UAAU,SAAS,CACvB,EAAmB,EACnB,OAAoB;IAEpB,OAAO,sBAAsB,CAAyD,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7G,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typesql/kysely",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "description": "Use Kysely database interfaces (kysely-codegen, prisma-kysely) as a typesql schema.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,13 +28,15 @@
28
28
  "access": "public"
29
29
  },
30
30
  "dependencies": {
31
- "@typesql/core": "0.0.3"
31
+ "@typesql/core": "0.1.0"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "kysely": ">=0.27"
35
35
  },
36
36
  "devDependencies": {
37
+ "@types/pg": "^8.23.1",
37
38
  "kysely": "^0.29.6",
39
+ "pg": "^8.23.1",
38
40
  "typescript": "7.0.2",
39
41
  "vitest": "^5.0.4"
40
42
  },
package/src/client.ts CHANGED
@@ -1,34 +1,49 @@
1
1
  import { CompiledQuery, type QueryResult } from "kysely";
2
- import type { Executor, Query, RowType } from "@typesql/core";
2
+ import { runTransaction, type Executor, type SqlDriver } from "@typesql/core";
3
3
 
4
4
  /**
5
- * What the helpers use from a `Kysely<DB>` or `Transaction<DB>`. Queries run through
5
+ * What the adapter uses from a `Kysely<DB>` or `Transaction<DB>`. Queries run through
6
6
  * `withoutPlugins()`: typesql's row types describe what the driver returns, so result
7
- * transformers such as `CamelCasePlugin` must not rename the columns.
7
+ * transformers such as `CamelCasePlugin` must not rename the columns (`createSql(db,
8
+ * { rowKeys: "camelCase" })` renames the top-level keys instead, in the types too).
8
9
  */
9
10
  export interface KyselyExecutor {
10
11
  withoutPlugins(): { executeQuery<R>(query: CompiledQuery<R>): Promise<QueryResult<R>> };
12
+ transaction(): { execute<T>(fn: (trx: KyselyExecutor) => Promise<T>): Promise<T> };
13
+ readonly isTransaction: boolean;
11
14
  }
12
15
 
13
- type AnyQuery = Query<unknown, readonly unknown[]>;
14
-
15
16
  function run<R>(db: KyselyExecutor, text: string, values: readonly unknown[]): Promise<QueryResult<R>> {
16
17
  return db.withoutPlugins().executeQuery(CompiledQuery.raw(text, Array.from(values)) as CompiledQuery<R>);
17
18
  }
18
19
 
19
- /** An `Executor` for core's `q.execute()`: runs queries on a Kysely instance (or transaction), without its plugins. */
20
- export function executor(db: KyselyExecutor): Executor {
21
- return async (text, values) => (await run<unknown>(db, text, values)).rows;
20
+ /**
21
+ * Kysely reports `numAffectedRows` for INSERT/UPDATE/DELETE/MERGE only; for other statements the
22
+ * count is the rows returned (SELECT), or 0 (DDL), as Postgres' command tag says.
23
+ */
24
+ function rowCount(result: QueryResult<unknown>): number {
25
+ return result.numAffectedRows === undefined ? result.rows.length : Number(result.numAffectedRows);
22
26
  }
23
27
 
24
- /** Run a typesql query on a Kysely instance (or transaction) and return its rows, typed from the query. */
25
- export async function query<Q extends AnyQuery>(db: KyselyExecutor, q: Q): Promise<RowType<Q>[]> {
26
- const result = await run<RowType<Q>>(db, q.text, q.values);
27
- return result.rows;
28
+ /**
29
+ * An `Executor` for core's `q.execute()`: runs queries on a Kysely instance (or transaction),
30
+ * without its plugins, and resolves to `{ rows, rowCount }` so `q.executeCount()` works.
31
+ */
32
+ export function executor(db: KyselyExecutor): Executor {
33
+ return async (text, values) => {
34
+ const result = await run<unknown>(db, text, values);
35
+ return { rows: result.rows, rowCount: rowCount(result) };
36
+ };
28
37
  }
29
38
 
30
- /** Run a statement for its side effect and return the affected row count (`numAffectedRows`, or 0). */
31
- export async function execute(db: KyselyExecutor, q: AnyQuery): Promise<number> {
32
- const result = await run<unknown>(db, q.text, q.values);
33
- return Number(result.numAffectedRows ?? 0n);
39
+ /**
40
+ * Run `fn` inside `db.transaction().execute()`. A Kysely `Transaction` cannot start another one,
41
+ * so inside one `fn` runs in a savepoint.
42
+ */
43
+ function transaction<T>(db: KyselyExecutor, depth: number, fn: (trx: KyselyExecutor) => Promise<T>): Promise<T> {
44
+ if (!db.isTransaction) return db.transaction().execute(fn);
45
+ return runTransaction((statement) => run(db, statement, []), () => fn(db), { savepoint: `typesql_${depth + 1}` });
34
46
  }
47
+
48
+ /** How `createSql(db)` runs queries and transactions. */
49
+ export const driver: SqlDriver<KyselyExecutor> = { executor, transaction };
package/src/index.ts CHANGED
@@ -1,26 +1,41 @@
1
- import { createSql as createCoreSql, type DefaultTypeMap, type Sql } from "@typesql/core";
2
- import { executor, type KyselyExecutor } from "./client.js";
1
+ import { createTransactionalSql, type DefaultTypeMap, type RowKeys, type SqlOptions, type TransactionalSql } from "@typesql/core";
2
+ import { driver, type KyselyExecutor } from "./client.js";
3
3
  import type { FromKysely, KyselyOptions, KyselyOverrides } from "./from-kysely.js";
4
4
 
5
- export { NoResultError, type Executor } from "@typesql/core";
5
+ export { NoResultError, type Executor, type TransactionalSql } from "@typesql/core";
6
6
  export type { FromKysely, KyselyOverrides, KyselyOptions, TableOverride, ColumnOverride, InferColumn, SnakeCase } from "./from-kysely.js";
7
- export { query, execute, executor, type KyselyExecutor } from "./client.js";
7
+ export { executor, type KyselyExecutor } from "./client.js";
8
+
9
+ /** What `createSql<DB>(db)` returns: core's `Sql` over `FromKysely<DB, ...>`, with `transaction(fn)`. */
10
+ export type KyselySql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}, Keys extends RowKeys = "preserve"> =
11
+ TransactionalSql<FromKysely<DB, Overrides, Options>, TM, Keys>;
8
12
 
9
13
  /**
10
14
  * `createSql` from core with a Kysely `DB` interface as the schema:
11
- * `createSql<DB>()` is `createSql<FromKysely<DB>, DefaultTypeMap>()`.
15
+ * `createSql<DB>(db)` is `createSql<FromKysely<DB>, DefaultTypeMap>()` bound to `db`.
16
+ *
17
+ * const sql = createSql<DB>(db);
18
+ * const sql = createSql<DB, PgTypeMap, { users: { id: "int8" } }>(db);
19
+ * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>(db);
12
20
  *
13
- * const sql = createSql<DB>();
14
- * const sql = createSql<DB, PgTypeMap, { users: { id: "int8" } }>();
15
- * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>();
21
+ * The queries' `execute*` methods run on `db` (a `Kysely` instance or transaction), bypassing
22
+ * its plugins, and `sql.transaction(fn)` runs `fn` with a `sql` bound to one transaction.
23
+ * `{ rowKeys: "camelCase" }` camelCases the rows' top-level keys as `CamelCasePlugin` does, in
24
+ * the types and at runtime, without touching JSON values:
16
25
  *
17
- * With a `db` (a `Kysely` instance or transaction), the queries' `execute*` methods run on it.
26
+ * const sql = createSql<DB, DefaultTypeMap, {}, { camelCase: true }>(db, { rowKeys: "camelCase" });
18
27
  */
19
- export function createSql<
20
- DB,
21
- TM = DefaultTypeMap,
22
- Overrides extends KyselyOverrides = {},
23
- Options extends KyselyOptions = {},
24
- >(db?: KyselyExecutor): Sql<FromKysely<DB, Overrides, Options>, TM> {
25
- return createCoreSql<FromKysely<DB, Overrides, Options>, TM>(db && executor(db));
28
+ export function createSql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}>(
29
+ db: KyselyExecutor | undefined,
30
+ options: { readonly rowKeys: "camelCase" },
31
+ ): KyselySql<DB, TM, Overrides, Options, "camelCase">;
32
+ export function createSql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}>(
33
+ db?: KyselyExecutor,
34
+ options?: { readonly rowKeys?: "preserve" },
35
+ ): KyselySql<DB, TM, Overrides, Options>;
36
+ export function createSql<DB, TM = DefaultTypeMap, Overrides extends KyselyOverrides = {}, Options extends KyselyOptions = {}>(
37
+ db?: KyselyExecutor,
38
+ options?: SqlOptions,
39
+ ): KyselySql<DB, TM, Overrides, Options, RowKeys> {
40
+ return createTransactionalSql<FromKysely<DB, Overrides, Options>, TM, KyselyExecutor>(db, driver, options);
26
41
  }