kysely 0.9.3 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/cjs/dialect/mysql/mysql-dialect.d.ts +11 -0
  2. package/dist/cjs/dialect/mysql/mysql-dialect.js +7 -0
  3. package/dist/cjs/dialect/postgres/postgres-dialect.d.ts +13 -1
  4. package/dist/cjs/dialect/postgres/postgres-dialect.js +10 -0
  5. package/dist/cjs/dialect/postgres/postgres-driver.js +2 -2
  6. package/dist/cjs/dialect/postgres/postgres-query-compiler.js +1 -1
  7. package/dist/cjs/driver/runtime-driver.d.ts +3 -2
  8. package/dist/cjs/driver/runtime-driver.js +57 -4
  9. package/dist/cjs/index.d.ts +2 -1
  10. package/dist/cjs/kysely.d.ts +31 -9
  11. package/dist/cjs/kysely.js +25 -9
  12. package/dist/cjs/migration/migration.d.ts +32 -0
  13. package/dist/cjs/operation-node/operator-node.d.ts +1 -1
  14. package/dist/cjs/operation-node/operator-node.js +1 -0
  15. package/dist/cjs/parser/join-parser.d.ts +4 -4
  16. package/dist/cjs/plugin/camel-case/camel-case-plugin.js +5 -116
  17. package/dist/cjs/plugin/camel-case/camel-case-transformer.d.ts +8 -0
  18. package/dist/cjs/plugin/camel-case/camel-case-transformer.js +19 -0
  19. package/dist/cjs/plugin/camel-case/camel-case.d.ts +15 -0
  20. package/dist/cjs/plugin/camel-case/camel-case.js +109 -0
  21. package/dist/cjs/plugin/kysely-plugin.d.ts +1 -1
  22. package/dist/cjs/query-builder/query-builder.d.ts +35 -0
  23. package/dist/cjs/query-builder/query-builder.js +37 -0
  24. package/dist/cjs/query-executor/default-query-executor.d.ts +1 -0
  25. package/dist/cjs/query-executor/default-query-executor.js +6 -0
  26. package/dist/cjs/query-executor/noop-query-executor.d.ts +1 -0
  27. package/dist/cjs/query-executor/noop-query-executor.js +3 -0
  28. package/dist/cjs/query-executor/query-executor.d.ts +9 -4
  29. package/dist/cjs/util/log.d.ts +19 -0
  30. package/dist/cjs/util/log.js +37 -0
  31. package/dist/cjs/util/random-string.js +6 -3
  32. package/dist/cjs/util/type-utils.d.ts +4 -4
  33. package/dist/esm/dialect/mysql/mysql-dialect.d.ts +11 -0
  34. package/dist/esm/dialect/mysql/mysql-dialect.js +7 -0
  35. package/dist/esm/dialect/mysql/mysql-driver.js +1 -1
  36. package/dist/esm/dialect/postgres/postgres-dialect.d.ts +13 -1
  37. package/dist/esm/dialect/postgres/postgres-dialect.js +10 -0
  38. package/dist/esm/dialect/postgres/postgres-driver.js +2 -2
  39. package/dist/esm/dialect/postgres/postgres-query-compiler.js +1 -1
  40. package/dist/esm/driver/runtime-driver.d.ts +3 -2
  41. package/dist/esm/driver/runtime-driver.js +57 -4
  42. package/dist/esm/index.d.ts +2 -1
  43. package/dist/esm/kysely.d.ts +31 -9
  44. package/dist/esm/kysely.js +25 -9
  45. package/dist/esm/migration/migration.d.ts +32 -0
  46. package/dist/esm/operation-node/operator-node.d.ts +1 -1
  47. package/dist/esm/operation-node/operator-node.js +1 -0
  48. package/dist/esm/parser/join-parser.d.ts +4 -4
  49. package/dist/esm/plugin/camel-case/camel-case-plugin.js +4 -115
  50. package/dist/esm/plugin/camel-case/camel-case-transformer.d.ts +8 -0
  51. package/dist/esm/plugin/camel-case/camel-case-transformer.js +15 -0
  52. package/dist/esm/plugin/camel-case/camel-case.d.ts +15 -0
  53. package/dist/esm/plugin/camel-case/camel-case.js +104 -0
  54. package/dist/esm/plugin/kysely-plugin.d.ts +1 -1
  55. package/dist/esm/query-builder/query-builder.d.ts +35 -0
  56. package/dist/esm/query-builder/query-builder.js +37 -0
  57. package/dist/esm/query-executor/default-query-executor.d.ts +1 -0
  58. package/dist/esm/query-executor/default-query-executor.js +6 -0
  59. package/dist/esm/query-executor/noop-query-executor.d.ts +1 -0
  60. package/dist/esm/query-executor/noop-query-executor.js +3 -0
  61. package/dist/esm/query-executor/query-executor.d.ts +9 -4
  62. package/dist/esm/util/log.d.ts +19 -0
  63. package/dist/esm/util/log.js +33 -0
  64. package/dist/esm/util/random-string.js +6 -3
  65. package/dist/esm/util/type-utils.d.ts +4 -4
  66. package/package.json +4 -1
@@ -10,6 +10,7 @@ import { DatabaseIntrospector } from './introspection/database-introspector.js';
10
10
  import { Driver, IsolationLevel } from './driver/driver.js';
11
11
  import { ParseContext } from './parser/parse-context.js';
12
12
  import { FunctionBuilder } from './query-builder/function-builder.js';
13
+ import { LogLevel } from './util/log.js';
13
14
  /**
14
15
  * The main Kysely class.
15
16
  *
@@ -127,15 +128,20 @@ export declare class Kysely<DB> extends QueryCreator<DB> {
127
128
  */
128
129
  get fn(): FunctionBuilder<DB, keyof DB>;
129
130
  /**
130
- * Starts a transaction. If the callback throws the transaction is rolled back,
131
- * otherwise it's committed.
131
+ * Creates a {@link TransactionBuilder} that can be used to run queries inside a transaction.
132
132
  *
133
- * @example
134
- * In the example below if either query fails or `someFunction` throws, both inserts
135
- * will be rolled back. Otherwise the transaction will be committed by the time the
136
- * `transaction` function returns the output value. The output value of the
137
- * `transaction` method is the value returned from the callback.
133
+ * The returned {@link TransactionBuilder} can be used to configure the transaction. The
134
+ * {@link TransactionBuilder.execute} method can then be called to run the transaction.
135
+ * {@link TransactionBuilder.execute} takes a function that is run inside the
136
+ * transaction. If the function throws, the transaction is rolled back. Otherwise
137
+ * the transaction is committed.
138
+ *
139
+ * The callback function passed to the {@link TransactionBuilder.execute | execute}
140
+ * method gets the transaction object as its only argument. The transaction is
141
+ * of type {@link Transaction} which inherits {@link Kysely}. Any query
142
+ * started through the transaction object is executed inside the transaction.
138
143
  *
144
+ * @example
139
145
  * ```ts
140
146
  * const catto = await db.transaction().execute(async (trx) => {
141
147
  * const jennifer = await trx.insertInto('person')
@@ -188,6 +194,10 @@ export declare class Kysely<DB> extends QueryCreator<DB> {
188
194
  * ```
189
195
  */
190
196
  connection(): ConnectionBuilder<DB>;
197
+ /**
198
+ * Returns a copy of this Kysely instance with the given plugin installed.
199
+ */
200
+ withPlugin(plugin: KyselyPlugin): Kysely<DB>;
191
201
  /**
192
202
  * Returns a copy of this Kysely instance without any plugins.
193
203
  */
@@ -220,8 +230,20 @@ export interface KyselyProps {
220
230
  }
221
231
  export declare function isKyselyProps(obj: unknown): obj is KyselyProps;
222
232
  export interface KyselyConfig {
223
- dialect: Dialect;
224
- plugins?: KyselyPlugin[];
233
+ readonly dialect: Dialect;
234
+ readonly plugins?: KyselyPlugin[];
235
+ /**
236
+ * A list of log levels to log.
237
+ *
238
+ * Currently there's only two levels: `query` and `error`.
239
+ * This will be expanded based on user feedback later.
240
+ *
241
+ * Log levels:
242
+ *
243
+ * - query: Log each query's SQL and duration using console.log
244
+ * - error: Log all query errors using console.error
245
+ */
246
+ readonly log?: ReadonlyArray<LogLevel>;
225
247
  }
226
248
  export declare class ConnectionBuilder<DB> {
227
249
  #private;
@@ -12,6 +12,7 @@ import { TRANSACTION_ISOLATION_LEVELS, } from './driver/driver.js';
12
12
  import { preventAwait } from './util/prevent-await.js';
13
13
  import { DefaultParseContext } from './parser/parse-context.js';
14
14
  import { FunctionBuilder } from './query-builder/function-builder.js';
15
+ import { Log } from './util/log.js';
15
16
  /**
16
17
  * The main Kysely class.
17
18
  *
@@ -57,15 +58,16 @@ export class Kysely extends QueryCreator {
57
58
  constructor(args) {
58
59
  if (isKyselyProps(args)) {
59
60
  super({ executor: args.executor, parseContext: args.parseContext });
60
- this.#props = freeze(args);
61
+ this.#props = freeze({ ...args });
61
62
  }
62
63
  else {
63
64
  const dialect = args.dialect;
64
65
  const driver = dialect.createDriver();
65
66
  const compiler = dialect.createQueryCompiler();
66
67
  const adapter = dialect.createAdapter();
68
+ const log = new Log(args.log ?? []);
67
69
  const parseContext = new DefaultParseContext(adapter);
68
- const runtimeDriver = new RuntimeDriver(driver);
70
+ const runtimeDriver = new RuntimeDriver(driver, log);
69
71
  const connectionProvider = new DefaultConnectionProvider(runtimeDriver);
70
72
  const executor = new DefaultQueryExecutor(compiler, connectionProvider, args.plugins ?? []);
71
73
  super({ executor, parseContext });
@@ -163,15 +165,20 @@ export class Kysely extends QueryCreator {
163
165
  return new FunctionBuilder({ executor: this.#props.executor });
164
166
  }
165
167
  /**
166
- * Starts a transaction. If the callback throws the transaction is rolled back,
167
- * otherwise it's committed.
168
+ * Creates a {@link TransactionBuilder} that can be used to run queries inside a transaction.
168
169
  *
169
- * @example
170
- * In the example below if either query fails or `someFunction` throws, both inserts
171
- * will be rolled back. Otherwise the transaction will be committed by the time the
172
- * `transaction` function returns the output value. The output value of the
173
- * `transaction` method is the value returned from the callback.
170
+ * The returned {@link TransactionBuilder} can be used to configure the transaction. The
171
+ * {@link TransactionBuilder.execute} method can then be called to run the transaction.
172
+ * {@link TransactionBuilder.execute} takes a function that is run inside the
173
+ * transaction. If the function throws, the transaction is rolled back. Otherwise
174
+ * the transaction is committed.
175
+ *
176
+ * The callback function passed to the {@link TransactionBuilder.execute | execute}
177
+ * method gets the transaction object as its only argument. The transaction is
178
+ * of type {@link Transaction} which inherits {@link Kysely}. Any query
179
+ * started through the transaction object is executed inside the transaction.
174
180
  *
181
+ * @example
175
182
  * ```ts
176
183
  * const catto = await db.transaction().execute(async (trx) => {
177
184
  * const jennifer = await trx.insertInto('person')
@@ -228,6 +235,15 @@ export class Kysely extends QueryCreator {
228
235
  connection() {
229
236
  return new ConnectionBuilder({ ...this.#props });
230
237
  }
238
+ /**
239
+ * Returns a copy of this Kysely instance with the given plugin installed.
240
+ */
241
+ withPlugin(plugin) {
242
+ return new Kysely({
243
+ ...this.#props,
244
+ executor: this.#props.executor.withPlugin(plugin),
245
+ });
246
+ }
231
247
  /**
232
248
  * Returns a copy of this Kysely instance without any plugins.
233
249
  */
@@ -24,6 +24,38 @@ export declare class MigrationModule {
24
24
  * path.join(__dirname, 'migrations')
25
25
  * )
26
26
  * ```
27
+ *
28
+ * In the next example, we use a record of migration objects instead of file folder path.
29
+ * The keys in the object are migration names that can be anything you want. The order of
30
+ * the migrations is determined based on the alphabetical order of the keys in the record.
31
+ * This can be useful if you are using a bundler like webpack or esbuild.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * await db.migration.migrateToLatest({
36
+ * migration1: {
37
+ * async up(db: Kysely<any>): Promise<void> {
38
+ * ...
39
+ * },
40
+ *
41
+ * async down(db: Kysely<any>): Promise<void> {
42
+ * ...
43
+ * }
44
+ * },
45
+ *
46
+ * migration2: {
47
+ * async up(db: Kysely<any>): Promise<void> {
48
+ * ...
49
+ * },
50
+ *
51
+ * async down(db: Kysely<any>): Promise<void> {
52
+ * ...
53
+ * }
54
+ * },
55
+ *
56
+ * ...
57
+ * })
58
+ * ```
27
59
  */
28
60
  migrateToLatest(migrationsFolderPath: string): Promise<void>;
29
61
  migrateToLatest(allMigrations: Record<string, Migration>): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { ArrayItemType } from '../util/type-utils.js';
2
2
  import { OperationNode } from './operation-node.js';
3
- export declare const OPERATORS: readonly ["=", "==", "!=", "<>", ">", ">=", "<", "<=", "in", "not in", "is", "is not", "like", "not like", "ilike", "not ilike", "@>", "<@", "?", "?", "?&", "!<", "!>", "<=>", "!~", "~", "~*", "!~*", "exists", "not exists"];
3
+ export declare const OPERATORS: readonly ["=", "==", "!=", "<>", ">", ">=", "<", "<=", "in", "not in", "is", "is not", "like", "not like", "ilike", "not ilike", "@>", "<@", "?", "?", "?&", "!<", "!>", "<=>", "!~", "~", "~*", "!~*", "exists", "not exists", "&&"];
4
4
  export declare type Operator = ArrayItemType<typeof OPERATORS>;
5
5
  export interface OperatorNode extends OperationNode {
6
6
  readonly kind: 'OperatorNode';
@@ -30,6 +30,7 @@ export const OPERATORS = [
30
30
  '!~*',
31
31
  'exists',
32
32
  'not exists',
33
+ '&&',
33
34
  ];
34
35
  /**
35
36
  * @internal
@@ -3,9 +3,9 @@ import { AnyColumn, AnyColumnWithTable } from '../util/type-utils.js';
3
3
  import { ExtractAliasFromTableExpression, TableExpressionDatabase } from './table-parser.js';
4
4
  import { JoinBuilder } from '../query-builder/join-builder.js';
5
5
  import { ParseContext } from './parse-context.js';
6
- export declare type JoinReferenceExpression<DB, TB extends keyof DB, F> = AnyJoinColumn<DB, TB, F> | AnyJoinColumnWithTable<DB, TB, F>;
7
- export declare type JoinCallbackExpression<DB, TB extends keyof DB, F> = (join: JoinBuilder<TableExpressionDatabase<DB, F>, TB | ExtractAliasFromTableExpression<DB, F>>) => JoinBuilder<any, any>;
8
- declare type AnyJoinColumn<DB, TB extends keyof DB, F> = AnyColumn<TableExpressionDatabase<DB, F>, TB | ExtractAliasFromTableExpression<DB, F>>;
9
- declare type AnyJoinColumnWithTable<DB, TB extends keyof DB, F> = AnyColumnWithTable<TableExpressionDatabase<DB, F>, TB | ExtractAliasFromTableExpression<DB, F>>;
6
+ export declare type JoinReferenceExpression<DB, TB extends keyof DB, TE> = AnyJoinColumn<DB, TB, TE> | AnyJoinColumnWithTable<DB, TB, TE>;
7
+ export declare type JoinCallbackExpression<DB, TB extends keyof DB, TE> = (join: JoinBuilder<TableExpressionDatabase<DB, TE>, TB | ExtractAliasFromTableExpression<DB, TE>>) => JoinBuilder<any, any>;
8
+ declare type AnyJoinColumn<DB, TB extends keyof DB, TE> = AnyColumn<TableExpressionDatabase<DB, TE>, TB | ExtractAliasFromTableExpression<DB, TE>>;
9
+ declare type AnyJoinColumnWithTable<DB, TB extends keyof DB, TE> = AnyColumnWithTable<TableExpressionDatabase<DB, TE>, TB | ExtractAliasFromTableExpression<DB, TE>>;
10
10
  export declare function parseJoin(ctx: ParseContext, joinType: JoinType, args: any[]): JoinNode;
11
11
  export {};
@@ -1,5 +1,6 @@
1
- import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
2
1
  import { isObject } from '../../util/object-utils.js';
2
+ import { SnakeCaseTransformer } from './camel-case-transformer.js';
3
+ import { createCamelCaseMapper, createSnakeCaseMapper, } from './camel-case.js';
3
4
  /**
4
5
  * A plugin that converts snake_case identifiers in the database into
5
6
  * camelCase in the javascript side.
@@ -69,8 +70,8 @@ export class CamelCasePlugin {
69
70
  #snakeCase;
70
71
  #snakeCaseTransformer;
71
72
  constructor(opt = {}) {
72
- this.#camelCase = createCamelCase(opt);
73
- this.#snakeCase = createSnakeCase(opt);
73
+ this.#camelCase = createCamelCaseMapper(opt);
74
+ this.#snakeCase = createSnakeCaseMapper(opt);
74
75
  this.#snakeCaseTransformer = new SnakeCaseTransformer(this.snakeCase.bind(this));
75
76
  }
76
77
  transformQuery(args) {
@@ -105,118 +106,6 @@ export class CamelCasePlugin {
105
106
  return this.#camelCase(str);
106
107
  }
107
108
  }
108
- class SnakeCaseTransformer extends OperationNodeTransformer {
109
- #snakeCase;
110
- constructor(snakeCase) {
111
- super();
112
- this.#snakeCase = snakeCase;
113
- }
114
- transformIdentifier(node) {
115
- node = super.transformIdentifier(node);
116
- return {
117
- ...node,
118
- identifier: this.#snakeCase(node.identifier),
119
- };
120
- }
121
- }
122
109
  function canMap(obj) {
123
110
  return isObject(obj) && !(obj instanceof Date) && !Buffer.isBuffer(obj);
124
111
  }
125
- function createSnakeCase({ upperCase = false, underscoreBeforeDigits = false, underscoreBetweenUppercaseLetters = false, } = {}) {
126
- return memoize((str) => {
127
- if (str.length === 0) {
128
- return str;
129
- }
130
- const upper = str.toUpperCase();
131
- const lower = str.toLowerCase();
132
- let out = lower[0];
133
- for (let i = 1, l = str.length; i < l; ++i) {
134
- const char = str[i];
135
- const prevChar = str[i - 1];
136
- const upperChar = upper[i];
137
- const prevUpperChar = upper[i - 1];
138
- const lowerChar = lower[i];
139
- const prevLowerChar = lower[i - 1];
140
- // If underScoreBeforeDigits is true then, well, insert an underscore
141
- // before digits :). Only the first digit gets an underscore if
142
- // there are multiple.
143
- if (underscoreBeforeDigits && isDigit(char) && !isDigit(prevChar)) {
144
- out += '_' + char;
145
- continue;
146
- }
147
- // Test if `char` is an upper-case character and that the character
148
- // actually has different upper and lower case versions.
149
- if (char === upperChar && upperChar !== lowerChar) {
150
- const prevCharacterIsUppercase = prevChar === prevUpperChar && prevUpperChar !== prevLowerChar;
151
- // If underscoreBetweenUppercaseLetters is true, we always place an underscore
152
- // before consecutive uppercase letters (e.g. "fooBAR" becomes "foo_b_a_r").
153
- // Otherwise, we don't (e.g. "fooBAR" becomes "foo_bar").
154
- if (underscoreBetweenUppercaseLetters || !prevCharacterIsUppercase) {
155
- out += '_' + lowerChar;
156
- }
157
- else {
158
- out += lowerChar;
159
- }
160
- }
161
- else {
162
- out += char;
163
- }
164
- }
165
- if (upperCase) {
166
- return out.toUpperCase();
167
- }
168
- else {
169
- return out;
170
- }
171
- });
172
- }
173
- function createCamelCase({ upperCase = false } = {}) {
174
- return memoize((str) => {
175
- if (str.length === 0) {
176
- return str;
177
- }
178
- if (upperCase && isAllUpperCaseSnakeCase(str)) {
179
- // Only convert to lower case if the string is all upper
180
- // case snake_case. This allows camelCase strings to go
181
- // through without changing.
182
- str = str.toLowerCase();
183
- }
184
- let out = str[0];
185
- for (let i = 1, l = str.length; i < l; ++i) {
186
- const char = str[i];
187
- const prevChar = str[i - 1];
188
- if (char !== '_') {
189
- if (prevChar === '_') {
190
- out += char.toUpperCase();
191
- }
192
- else {
193
- out += char;
194
- }
195
- }
196
- }
197
- return out;
198
- });
199
- }
200
- function isAllUpperCaseSnakeCase(str) {
201
- for (let i = 1, l = str.length; i < l; ++i) {
202
- const char = str[i];
203
- if (char !== '_' && char !== char.toUpperCase()) {
204
- return false;
205
- }
206
- }
207
- return true;
208
- }
209
- function isDigit(char) {
210
- return char >= '0' && char <= '9';
211
- }
212
- function memoize(func) {
213
- const cache = new Map();
214
- return (str) => {
215
- let mapped = cache.get(str);
216
- if (!mapped) {
217
- mapped = func(str);
218
- cache.set(str, mapped);
219
- }
220
- return mapped;
221
- };
222
- }
@@ -0,0 +1,8 @@
1
+ import { IdentifierNode } from '../../operation-node/identifier-node.js';
2
+ import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
3
+ import { StringMapper } from './camel-case.js';
4
+ export declare class SnakeCaseTransformer extends OperationNodeTransformer {
5
+ #private;
6
+ constructor(snakeCase: StringMapper);
7
+ protected transformIdentifier(node: IdentifierNode): IdentifierNode;
8
+ }
@@ -0,0 +1,15 @@
1
+ import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js';
2
+ export class SnakeCaseTransformer extends OperationNodeTransformer {
3
+ #snakeCase;
4
+ constructor(snakeCase) {
5
+ super();
6
+ this.#snakeCase = snakeCase;
7
+ }
8
+ transformIdentifier(node) {
9
+ node = super.transformIdentifier(node);
10
+ return {
11
+ ...node,
12
+ identifier: this.#snakeCase(node.identifier),
13
+ };
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ export declare type StringMapper = (str: string) => string;
2
+ /**
3
+ * Creates a function that transforms camel case strings to snake case.
4
+ */
5
+ export declare function createSnakeCaseMapper({ upperCase, underscoreBeforeDigits, underscoreBetweenUppercaseLetters, }?: {
6
+ upperCase?: boolean | undefined;
7
+ underscoreBeforeDigits?: boolean | undefined;
8
+ underscoreBetweenUppercaseLetters?: boolean | undefined;
9
+ }): StringMapper;
10
+ /**
11
+ * Creates a function that transforms snake case strings to camel case.
12
+ */
13
+ export declare function createCamelCaseMapper({ upperCase, }?: {
14
+ upperCase?: boolean | undefined;
15
+ }): StringMapper;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Creates a function that transforms camel case strings to snake case.
3
+ */
4
+ export function createSnakeCaseMapper({ upperCase = false, underscoreBeforeDigits = false, underscoreBetweenUppercaseLetters = false, } = {}) {
5
+ return memoize((str) => {
6
+ if (str.length === 0) {
7
+ return str;
8
+ }
9
+ const upper = str.toUpperCase();
10
+ const lower = str.toLowerCase();
11
+ let out = lower[0];
12
+ for (let i = 1, l = str.length; i < l; ++i) {
13
+ const char = str[i];
14
+ const prevChar = str[i - 1];
15
+ const upperChar = upper[i];
16
+ const prevUpperChar = upper[i - 1];
17
+ const lowerChar = lower[i];
18
+ const prevLowerChar = lower[i - 1];
19
+ // If underScoreBeforeDigits is true then, well, insert an underscore
20
+ // before digits :). Only the first digit gets an underscore if
21
+ // there are multiple.
22
+ if (underscoreBeforeDigits && isDigit(char) && !isDigit(prevChar)) {
23
+ out += '_' + char;
24
+ continue;
25
+ }
26
+ // Test if `char` is an upper-case character and that the character
27
+ // actually has different upper and lower case versions.
28
+ if (char === upperChar && upperChar !== lowerChar) {
29
+ const prevCharacterIsUppercase = prevChar === prevUpperChar && prevUpperChar !== prevLowerChar;
30
+ // If underscoreBetweenUppercaseLetters is true, we always place an underscore
31
+ // before consecutive uppercase letters (e.g. "fooBAR" becomes "foo_b_a_r").
32
+ // Otherwise, we don't (e.g. "fooBAR" becomes "foo_bar").
33
+ if (underscoreBetweenUppercaseLetters || !prevCharacterIsUppercase) {
34
+ out += '_' + lowerChar;
35
+ }
36
+ else {
37
+ out += lowerChar;
38
+ }
39
+ }
40
+ else {
41
+ out += char;
42
+ }
43
+ }
44
+ if (upperCase) {
45
+ return out.toUpperCase();
46
+ }
47
+ else {
48
+ return out;
49
+ }
50
+ });
51
+ }
52
+ /**
53
+ * Creates a function that transforms snake case strings to camel case.
54
+ */
55
+ export function createCamelCaseMapper({ upperCase = false, } = {}) {
56
+ return memoize((str) => {
57
+ if (str.length === 0) {
58
+ return str;
59
+ }
60
+ if (upperCase && isAllUpperCaseSnakeCase(str)) {
61
+ // Only convert to lower case if the string is all upper
62
+ // case snake_case. This allows camelCase strings to go
63
+ // through without changing.
64
+ str = str.toLowerCase();
65
+ }
66
+ let out = str[0];
67
+ for (let i = 1, l = str.length; i < l; ++i) {
68
+ const char = str[i];
69
+ const prevChar = str[i - 1];
70
+ if (char !== '_') {
71
+ if (prevChar === '_') {
72
+ out += char.toUpperCase();
73
+ }
74
+ else {
75
+ out += char;
76
+ }
77
+ }
78
+ }
79
+ return out;
80
+ });
81
+ }
82
+ function isAllUpperCaseSnakeCase(str) {
83
+ for (let i = 1, l = str.length; i < l; ++i) {
84
+ const char = str[i];
85
+ if (char !== '_' && char !== char.toUpperCase()) {
86
+ return false;
87
+ }
88
+ }
89
+ return true;
90
+ }
91
+ function isDigit(char) {
92
+ return char >= '0' && char <= '9';
93
+ }
94
+ function memoize(func) {
95
+ const cache = new Map();
96
+ return (str) => {
97
+ let mapped = cache.get(str);
98
+ if (!mapped) {
99
+ mapped = func(str);
100
+ cache.set(str, mapped);
101
+ }
102
+ return mapped;
103
+ };
104
+ }
@@ -21,7 +21,7 @@ export interface KyselyPlugin {
21
21
  * return args.node
22
22
  * },
23
23
  *
24
- * transformResult(args: PluginTransformResultArgs): QueryResult<AnyRow> {
24
+ * transformResult(args: PluginTransformResultArgs): QueryResult<UnknownRow> {
25
25
  * const data = this.data.get(args.queryId)
26
26
  * return args.result
27
27
  * }
@@ -1582,6 +1582,41 @@ export declare class QueryBuilder<DB, TB extends keyof DB, O = {}> implements Op
1582
1582
  * ```
1583
1583
  */
1584
1584
  unionAll(expression: UnionExpression<DB, O>): QueryBuilder<DB, TB, O>;
1585
+ /**
1586
+ * Simply calls the given function passing `this` as the only argument.
1587
+ *
1588
+ * This method can be useful when adding optional method calls:
1589
+ *
1590
+ * @example
1591
+ * ```ts
1592
+ * db.selectFrom('person')
1593
+ * .selectAll()
1594
+ * .call((qb) => {
1595
+ * if (something) {
1596
+ * return qb.where('something', '=', something)
1597
+ * } else {
1598
+ * return qb.where('somethingElse', '=', somethingElse)
1599
+ * }
1600
+ * })
1601
+ * .execute()
1602
+ * ```
1603
+ *
1604
+ * The next example uses a helper funtion `log` to log a query:
1605
+ *
1606
+ * @example
1607
+ * ```ts
1608
+ * function log<T extends AnyQueryBuilder>(qb: T): T {
1609
+ * console.log(qb.compile())
1610
+ * return qb
1611
+ * }
1612
+ *
1613
+ * db.selectFrom('person')
1614
+ * .selectAll()
1615
+ * .call(log)
1616
+ * .execute()
1617
+ * ```
1618
+ */
1619
+ call<T>(func: (qb: this) => T): T;
1585
1620
  /**
1586
1621
  * Gives an alias for the query. This method is only useful for sub queries.
1587
1622
  *
@@ -815,6 +815,43 @@ export class QueryBuilder {
815
815
  queryNode: SelectQueryNode.cloneWithUnion(this.#props.queryNode, parseUnion(expression, true)),
816
816
  });
817
817
  }
818
+ /**
819
+ * Simply calls the given function passing `this` as the only argument.
820
+ *
821
+ * This method can be useful when adding optional method calls:
822
+ *
823
+ * @example
824
+ * ```ts
825
+ * db.selectFrom('person')
826
+ * .selectAll()
827
+ * .call((qb) => {
828
+ * if (something) {
829
+ * return qb.where('something', '=', something)
830
+ * } else {
831
+ * return qb.where('somethingElse', '=', somethingElse)
832
+ * }
833
+ * })
834
+ * .execute()
835
+ * ```
836
+ *
837
+ * The next example uses a helper funtion `log` to log a query:
838
+ *
839
+ * @example
840
+ * ```ts
841
+ * function log<T extends AnyQueryBuilder>(qb: T): T {
842
+ * console.log(qb.compile())
843
+ * return qb
844
+ * }
845
+ *
846
+ * db.selectFrom('person')
847
+ * .selectAll()
848
+ * .call(log)
849
+ * .execute()
850
+ * ```
851
+ */
852
+ call(func) {
853
+ return func(this);
854
+ }
818
855
  /**
819
856
  * Gives an alias for the query. This method is only useful for sub queries.
820
857
  *
@@ -9,6 +9,7 @@ export declare class DefaultQueryExecutor extends QueryExecutor {
9
9
  constructor(compiler: QueryCompiler, connectionProvider: ConnectionProvider, plugins?: KyselyPlugin[]);
10
10
  compileQuery(node: RootOperationNode): CompiledQuery;
11
11
  protected executeQueryImpl<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>>;
12
+ withPlugin(plugin: KyselyPlugin): DefaultQueryExecutor;
12
13
  withPluginAtFront(plugin: KyselyPlugin): DefaultQueryExecutor;
13
14
  withConnectionProvider(connectionProvider: ConnectionProvider): QueryExecutor;
14
15
  withoutPlugins(): QueryExecutor;
@@ -15,6 +15,12 @@ export class DefaultQueryExecutor extends QueryExecutor {
15
15
  return connection.executeQuery(compiledQuery);
16
16
  });
17
17
  }
18
+ withPlugin(plugin) {
19
+ return new DefaultQueryExecutor(this.#compiler, this.#connectionProvider, [
20
+ ...this.plugins,
21
+ plugin,
22
+ ]);
23
+ }
18
24
  withPluginAtFront(plugin) {
19
25
  return new DefaultQueryExecutor(this.#compiler, this.#connectionProvider, [
20
26
  plugin,
@@ -11,6 +11,7 @@ export declare class NoopQueryExecutor extends QueryExecutor {
11
11
  compileQuery(): CompiledQuery;
12
12
  protected executeQueryImpl<R>(): Promise<QueryResult<R>>;
13
13
  withConnectionProvider(): QueryExecutor;
14
+ withPlugin(plugin: KyselyPlugin): NoopQueryExecutor;
14
15
  withPluginAtFront(plugin: KyselyPlugin): NoopQueryExecutor;
15
16
  withoutPlugins(): QueryExecutor;
16
17
  }
@@ -14,6 +14,9 @@ export class NoopQueryExecutor extends QueryExecutor {
14
14
  withConnectionProvider() {
15
15
  throw new Error('this query cannot have a connection provider');
16
16
  }
17
+ withPlugin(plugin) {
18
+ return new NoopQueryExecutor([...this.plugins, plugin]);
19
+ }
17
20
  withPluginAtFront(plugin) {
18
21
  return new NoopQueryExecutor([plugin, ...this.plugins]);
19
22
  }
@@ -35,15 +35,20 @@ export declare abstract class QueryExecutor {
35
35
  * Executes a compiled query.
36
36
  */
37
37
  protected abstract executeQueryImpl<R>(compiledQuery: CompiledQuery, queryId: QueryId): Promise<QueryResult<R>>;
38
+ /**
39
+ * Returns a copy of this executor with a new connection provider.
40
+ */
41
+ abstract withConnectionProvider(connectionProvider: ConnectionProvider): QueryExecutor;
38
42
  /**
39
43
  * Returns a copy of this executor with a plugin added as the
40
- * first plugin.
44
+ * last plugin.
41
45
  */
42
- abstract withPluginAtFront(plugin: KyselyPlugin): QueryExecutor;
46
+ abstract withPlugin(plugin: KyselyPlugin): QueryExecutor;
43
47
  /**
44
- * Returns a copy of this executor with a new connection provider.
48
+ * Returns a copy of this executor with a plugin added as the
49
+ * first plugin.
45
50
  */
46
- abstract withConnectionProvider(connectionProvider: ConnectionProvider): QueryExecutor;
51
+ abstract withPluginAtFront(plugin: KyselyPlugin): QueryExecutor;
47
52
  /**
48
53
  * Returns a copy of this executor without any plugins.
49
54
  */