kysely-rizzolver 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/dist/cjs/fetch-result-factory.d.ts +52 -0
  4. package/dist/cjs/fetch-result-factory.js +88 -0
  5. package/dist/cjs/fetch-result.d.ts +111 -0
  6. package/dist/cjs/fetch-result.js +117 -0
  7. package/dist/cjs/index.d.ts +10 -0
  8. package/dist/cjs/index.js +21 -0
  9. package/dist/cjs/kysely-rizzolver.d.ts +138 -0
  10. package/dist/cjs/kysely-rizzolver.js +147 -0
  11. package/dist/cjs/model-collection.d.ts +26 -0
  12. package/dist/cjs/model-collection.js +24 -0
  13. package/dist/cjs/query-builder.d.ts +156 -0
  14. package/dist/cjs/query-builder.js +92 -0
  15. package/dist/cjs/selector.d.ts +72 -0
  16. package/dist/cjs/selector.js +45 -0
  17. package/dist/cjs/type-helpers.d.ts +11 -0
  18. package/dist/cjs/type-helpers.js +2 -0
  19. package/dist/esm/fetch-result-factory.js +85 -0
  20. package/dist/esm/fetch-result.js +105 -0
  21. package/dist/esm/index.js +5 -0
  22. package/dist/esm/kysely-rizzolver.js +143 -0
  23. package/dist/esm/model-collection.js +21 -0
  24. package/dist/esm/package.json +1 -0
  25. package/dist/esm/query-builder.js +89 -0
  26. package/dist/esm/selector.js +42 -0
  27. package/dist/esm/type-helpers.js +1 -0
  28. package/dist/types/fetch-result-factory.d.ts +52 -0
  29. package/dist/types/fetch-result.d.ts +111 -0
  30. package/dist/types/index.d.ts +10 -0
  31. package/dist/types/kysely-rizzolver.d.ts +138 -0
  32. package/dist/types/model-collection.d.ts +26 -0
  33. package/dist/types/query-builder.d.ts +156 -0
  34. package/dist/types/selector.d.ts +72 -0
  35. package/dist/types/type-helpers.d.ts +11 -0
  36. package/package.json +32 -0
@@ -0,0 +1,111 @@
1
+ import type { Selectable } from 'kysely';
2
+ import type { ModelCollection } from './model-collection.js';
3
+ /**
4
+ * A {@link FetchResult} is a result of a fetch operation. It can be one of
5
+ * three types:
6
+ * - {@link FetchOneResult} - A result of a fetch operation that is expected to
7
+ * return up to one row,
8
+ * - {@link FetchOneXResult} - A result of a fetch operation that is expected to
9
+ * return exactly one row,
10
+ * - {@link FetchSomeResult} - A result of a fetch operation that is expected to
11
+ * return any number of rows.
12
+ */
13
+ export type FetchResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>> = FetchOneResult<DB, T, R> | FetchOneXResult<DB, T, R> | FetchSomeResult<DB, T, R>;
14
+ /**
15
+ * A {@link FetchOneResult} is a result of a fetch operation that is expected to
16
+ * return up to one row.
17
+ */
18
+ export type FetchOneResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>> | undefined> = {
19
+ fetchType: 'fetchOne';
20
+ table: T;
21
+ result: R | undefined;
22
+ models?: ModelCollection<DB>;
23
+ /**
24
+ * Returns this result as a {@link FetchOneXResult}.
25
+ *
26
+ * @throws If the result is null or undefined.
27
+ */
28
+ asFetchOneX(): FetchOneXResult<DB, T, R & {}>;
29
+ };
30
+ /**
31
+ * A {@link FetchOneXResult} is a result of a fetch operation that is expected
32
+ * to return exactly one row.
33
+ */
34
+ export type FetchOneXResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>> = {
35
+ fetchType: 'fetchOne';
36
+ table: T;
37
+ result: R;
38
+ models?: ModelCollection<DB>;
39
+ /**
40
+ * Returns self. This is a no-op, but it's here to make it possible to
41
+ * cast this object back to a {@link FetchOneXResult}.
42
+ */
43
+ asFetchOneX(): FetchOneXResult<DB, T, R>;
44
+ };
45
+ /**
46
+ * A {@link FetchSomeResult} is a result of a fetch operation that is expected
47
+ * to return any number of rows.
48
+ */
49
+ export type FetchSomeResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>> = {
50
+ fetchType: 'fetchSome';
51
+ table: T;
52
+ result: R[];
53
+ models?: ModelCollection<DB>;
54
+ };
55
+ /**
56
+ * Used to type juggle between {@link FetchResult} and its subtypes.
57
+ */
58
+ export type AsFetchOneResult<T extends FetchResult<any, string, Selectable<any>>> = T extends FetchResult<infer DB, infer T, infer R> ? FetchOneResult<DB, T, R> : never;
59
+ /**
60
+ * Used to type juggle between {@link FetchResult} and its subtypes.
61
+ */
62
+ export type AsFetchOneXResult<T extends FetchResult<any, string, Selectable<any>>> = T extends FetchResult<infer DB, infer T, infer R> ? FetchOneXResult<DB, T, R> : never;
63
+ /**
64
+ * Used to type juggle between {@link FetchResult} and its subtypes.
65
+ */
66
+ export type AsFetchSomeResult<T extends FetchResult<any, string, Selectable<any>>> = T extends FetchResult<infer DB, infer T, infer R> ? FetchSomeResult<DB, T, R> : never;
67
+ /**
68
+ * Creates a new {@link FetchOneResult} instance.
69
+ */
70
+ export declare function newFetchOneResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(table: T, result: R | undefined, models?: ModelCollection<DB>): FetchOneResult<DB, T, R>;
71
+ /**
72
+ * Creates a new {@link FetchOneXResult} instance.
73
+ *
74
+ * Note: it may be counterintuitive, but this function accepts `undefined` as
75
+ * input. I found it is way more convenient to assert the type once in this
76
+ * funciton rather than in every caller.
77
+ */
78
+ export declare function newFetchOneXResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(table: T, result: R | undefined, models?: ModelCollection<DB>): FetchOneXResult<DB, T, R>;
79
+ /**
80
+ * Creates a new {@link FetchSomeResult} instance.
81
+ */
82
+ export declare function newFetchSomeResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(table: T, result: R[], models?: ModelCollection<DB>): FetchSomeResult<DB, T, R>;
83
+ export declare function isFetchResult(result: unknown): result is FetchResult<any, any, any>;
84
+ /**
85
+ * Checks if `value` is a {@link FetchOneResult}.
86
+ */
87
+ export declare function isFetchOneResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(value: FetchOneResult<DB, T, R> | FetchOneXResult<DB, T, R>): value is typeof value;
88
+ export declare function isFetchOneResult(value: unknown): value is FetchOneResult<any, string, Selectable<any>>;
89
+ /**
90
+ * Checks if `value` is a {@link FetchOneXResult}.
91
+ */
92
+ export declare function isFetchOneXResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(value: FetchOneResult<DB, T, R>): value is FetchOneXResult<DB, T, R>;
93
+ export declare function isFetchOneXResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(value: FetchOneXResult<DB, T, R>): value is typeof value;
94
+ export declare function isFetchOneXResult(value: unknown): value is FetchOneXResult<any, string, Selectable<any>>;
95
+ /**
96
+ * Checks if `value` is a {@link FetchSomeResult}.
97
+ */
98
+ export declare function isFetchSomeResult<DB, T extends keyof DB & string, R extends Partial<Selectable<DB[T]>>>(value: FetchSomeResult<DB, T, R>): value is typeof value;
99
+ export declare function isFetchSomeResult(value: unknown): value is FetchSomeResult<any, string, Selectable<any>>;
100
+ /**
101
+ * Asserts that `value` is a {@link FetchOneResult}.
102
+ */
103
+ export declare function assertIsFetchOneResult(value: unknown): asserts value is FetchOneResult<any, string, Selectable<any>>;
104
+ /**
105
+ * Asserts that `value` is a {@link FetchOneXResult}.
106
+ */
107
+ export declare function assertIsFetchOneXResult(value: unknown): asserts value is FetchOneXResult<any, string, Selectable<any>>;
108
+ /**
109
+ * Asserts that `value` is a {@link FetchSomeResult}.
110
+ */
111
+ export declare function assertIsFetchSomeResult(value: unknown): asserts value is FetchSomeResult<any, string, Selectable<any>>;
@@ -0,0 +1,10 @@
1
+ export { KyselyRizzolver } from './kysely-rizzolver.js';
2
+ export type { AllTableFields, AnyTableField, KyselyRizzolverBuilderForSchema, KyselyRizzolverBuilderNoSchema, KyDB as RizzolverDB, TableName } from './kysely-rizzolver.js';
3
+ export { assertIsFetchOneResult, assertIsFetchOneXResult, assertIsFetchSomeResult, isFetchOneResult, isFetchOneXResult, isFetchSomeResult, newFetchOneResult, newFetchOneXResult, newFetchSomeResult } from './fetch-result.js';
4
+ export type { AsFetchOneResult, AsFetchOneXResult, AsFetchSomeResult, FetchOneResult, FetchOneXResult, FetchResult, FetchSomeResult } from './fetch-result.js';
5
+ export { newSelector } from './selector.js';
6
+ export type { Selector } from './selector.js';
7
+ export { newQueryBuilder } from './query-builder.js';
8
+ export type { QueryBuilder } from './query-builder.js';
9
+ export { newModelCollection } from './model-collection.js';
10
+ export type { ModelCollection } from './model-collection.js';
@@ -0,0 +1,138 @@
1
+ import { type FetchResultFactory } from './fetch-result-factory.js';
2
+ import { type QueryBuilder } from './query-builder.js';
3
+ import { type Selector } from './selector.js';
4
+ import { type ModelCollection } from './model-collection.js';
5
+ export interface KyselyRizzolverBase<DB, T extends Record<keyof DB & string, readonly string[]>> {
6
+ readonly fields: T;
7
+ readonly fetches: FetchResultFactory<DB>;
8
+ }
9
+ /**
10
+ * A {@link KyselyRizzolver} is a class that is used to define the structure of
11
+ * a database schema.
12
+ *
13
+ * It streamlines instatiating type-safe {@link QueryBuilder}s,
14
+ * {@link Selector}s, {@link ModelCollection}s and {@link FetchResult}s.
15
+ *
16
+ * Define a new {@link KyselyRizzolver} using the
17
+ * {@link KyselyRizzolver.builder|.builderForSchema()} or
18
+ * {@link KyselyRizzolver.builderNoSchema|.builderNoSchema()}.
19
+ */
20
+ export declare class KyselyRizzolver<DB, T extends Record<keyof DB & string, readonly string[]>> implements KyselyRizzolverBase<DB, T> {
21
+ readonly fields: T;
22
+ readonly fetches: FetchResultFactory<DB>;
23
+ constructor(fields: T);
24
+ /**
25
+ * Intantiates a new {@link Selector} for the given table.
26
+ */
27
+ newSelector<Table extends keyof DB & string, Alias extends string>(table: Table, alias: Alias): Selector<this, Table, Alias, AllTableFields<this, Table>>;
28
+ /**
29
+ * Instantiates a new {@link QueryBuilder}.
30
+ */
31
+ newQueryBuilder(): QueryBuilder<this, {}>;
32
+ /**
33
+ * Instantiates a new {@link ModelCollection}.
34
+ */
35
+ newModelCollection(): ModelCollection<DB>;
36
+ /**
37
+ * Starts building a new {@link KyselyRizzolver} using a builder pattern for
38
+ * a schema.
39
+ *
40
+ * Call {@link KyselyRizzolverBuilderForSchema.table|.table()} for each
41
+ * table that exists on the `DB` type parameter with all of their column
42
+ * names as a const array. After all tables have been added, call
43
+ * {@link KyselyRizzolverBuilderForSchema.build|.build()} to get a new
44
+ * {@link KyselyRizzolver} instance.
45
+ *
46
+ * Example:
47
+ * ```
48
+ * const rizzolver = KyselyRizzolver.builderForSchema<DB>()
49
+ * .table('user', ['id', 'name', 'email'] as const)
50
+ * .table('post', ['id', 'title', 'content', 'authorId'] as const)
51
+ * .build();
52
+ * ```
53
+ *
54
+ * Note: The `as const` assertion is necessary for correct type inference.
55
+ */
56
+ static builderForSchema<DB>(): KyselyRizzolverBuilderForSchema<DB, {}>;
57
+ /**
58
+ * Starts building a new {@link KyselyRizzolver} using a builder pattern
59
+ * without a schema.
60
+ *
61
+ * Call {@link KyselyRizzolverBuilderNoSchema.table|.table()} for each
62
+ * table with all of their column names as a const array.
63
+ *
64
+ * Example:
65
+ * ```
66
+ * const rizzolver = KyselyRizzolver.builder()
67
+ * .table('user', ['id', 'name', 'email'] as const) // note `as const` is necessary
68
+ * .table('post', ['id', 'title', 'content', 'authorId'] as const)
69
+ * .build();
70
+ * ```
71
+ *
72
+ * Since this version of builder is schemaless, it cannot infer the value
73
+ * types for the columns. The `user` type will be `{ id: unknown, name:
74
+ * unknown, email: unknown }`.
75
+ *
76
+ * You may call
77
+ * {@link KyselyRizzolverBuilderNoSchema.asModel|.asModel\<M\>()}
78
+ * immediately after the .table() call to provide the types, where `M` is an
79
+ * type like `{ column1: type1, column2: type2, ... }`.
80
+ *
81
+ * Example:
82
+ * ```
83
+ * const rizzolver = KyselyRizzolver.builder()
84
+ * .table('user', ['id', 'name', 'email'] as const)
85
+ * .asModel<{id: number, name: string, email: string}>()
86
+ * .table('post', ['id', 'title', 'content', 'authorId'] as const)
87
+ * .asModel<{id: number, title: string, content: string, authorId: number}>()
88
+ * .build();
89
+ * ```
90
+ *
91
+ * p.s. if your .table() and .asModel() columns differ, it will let you know
92
+ * at compile time ;)
93
+ *
94
+ * Once all tables have been added, call
95
+ * {@link KyselyRizzolverBuilderNoSchema.build|.build()} to get a new
96
+ * {@link KyselyRizzolver} instance.
97
+ */
98
+ static builderNoSchema(): KyselyRizzolverBuilderNoSchema<{}, null>;
99
+ }
100
+ export type KyselyRizzolverBuilderForSchema<DB, T extends Partial<Record<keyof DB & string, readonly string[]>>> = {
101
+ table<K extends Exclude<keyof DB & string, keyof T>, U extends readonly (keyof DB[K])[]>(name: K, fields: U & ([keyof DB[K]] extends [U[number]] ? unknown : `Missing key: ${Exclude<keyof DB[K] & string, U[number]>}`)): KyselyRizzolverBuilderForSchema<DB, T & {
102
+ [key in K]: U;
103
+ }>;
104
+ build(): T extends Record<keyof DB & string, readonly string[]> ? KyselyRizzolver<DB, T> : never;
105
+ };
106
+ export type KyselyRizzolverBuilderNoSchema<T extends Record<string, {
107
+ model: any;
108
+ columns: readonly string[];
109
+ }>, Last extends keyof T | null> = {
110
+ table<K extends string, U extends readonly string[]>(name: K, fields: U): KyselyRizzolverBuilderNoSchema<T & {
111
+ [k in K]: {
112
+ model: Record<U[number], unknown>;
113
+ columns: U;
114
+ };
115
+ }, K>;
116
+ asModel<M>(): Last extends keyof T ? keyof M extends T[Last]['columns'][number] ? T[Last]['columns'][number] extends keyof M ? KyselyRizzolverBuilderNoSchema<T & {
117
+ [k in Last]: {
118
+ model: M;
119
+ columns: T[k]['columns'];
120
+ };
121
+ }, never> : `column '${Exclude<T[Last]['columns'][number], keyof M & string>}' defined in table() but missing from asModel()` : `column '${Exclude<keyof M & string, T[Last]['columns'][number]>}' defined in asModel() but missing from table()` : `asModel() must be called after table()`;
122
+ build(): KyselyRizzolver<{
123
+ [k in keyof T]: T[k]['model'];
124
+ }, {
125
+ [k in keyof T]: T[k]['columns'];
126
+ }>;
127
+ };
128
+ export type KyDB<KY extends KyselyRizzolverBase<any, any>> = KY extends KyselyRizzolverBase<infer DB, any> ? DB : never;
129
+ export type TableName<T extends KyselyRizzolverBase<any, any>> = keyof KyDB<T> & string;
130
+ /**
131
+ * A union of all the known fields of a table.
132
+ */
133
+ export type AnyTableField<KY extends KyselyRizzolverBase<any, any>, Table extends TableName<KY>> = keyof KyDB<KY>[Table] & string;
134
+ /**
135
+ * An array of all the known fields of a table, in a type that is compatible
136
+ * with that table's ${@link Selectable} type.
137
+ */
138
+ export type AllTableFields<KY extends KyselyRizzolverBase<any, any>, Table extends TableName<KY>> = KY extends KyselyRizzolverBase<any, infer T> ? T[Table] extends infer U ? U extends readonly AnyTableField<KY, Table>[] ? U : never : never : never;
@@ -0,0 +1,26 @@
1
+ import type { Selectable } from 'kysely';
2
+ /**
3
+ * A {@link ModelCollection} is a collection of {@link Selectable} instances,
4
+ * grouped by their table name and keyed by their id.
5
+ *
6
+ * This type purposefully discards specific type information to make it simpler
7
+ * to create and pass it around without worrying about type compatibility and
8
+ * casting.
9
+ *
10
+ * It does not preserve information on which tables are present in the
11
+ * collection, or which fields on their {@link Selectable}s have been gathered.
12
+ *
13
+ * If you need to preserve this information, you should just pass the result of
14
+ * the {@link QueryBuilder.run} method where needed. However, doing this is not
15
+ * recommended, as your types can easily become too nested to the point where
16
+ * TypeScript can't infer them correctly.
17
+ */
18
+ export type ModelCollection<DB> = {
19
+ add<Table extends keyof DB & string>(table: Table, selectable?: Selectable<DB[Table]>): ModelCollection<DB>;
20
+ get collection(): Data<DB>;
21
+ };
22
+ export declare function newModelCollection<DB>(init?: Data<DB>): ModelCollection<DB>;
23
+ type Data<DB> = {
24
+ [M in keyof DB & string]?: Partial<Record<number, Selectable<DB[M]>>>;
25
+ };
26
+ export {};
@@ -0,0 +1,156 @@
1
+ import { type FetchOneResult, type FetchOneXResult, type FetchSomeResult } from './fetch-result.js';
2
+ import type { AllTableFields, AnyTableField, KyDB, KyselyRizzolverBase, TableName } from './kysely-rizzolver.js';
3
+ import { type Selector, type SelectorResult } from './selector.js';
4
+ import { type ModelCollection } from './model-collection.js';
5
+ import type { UnionToTuple } from './type-helpers.js';
6
+ /**
7
+ * {@link QueryBuilder} makes it easier to work with multiple tables in a query
8
+ * and parse the results into their respective Kysely {@link Selectable}
9
+ * instances in a type-safe way.
10
+ *
11
+ * It works by adding {@link Selector}s using the {@link QueryBuilder.add}
12
+ * method, followed by a call to {@link QueryBuilder.run} to parse the results
13
+ * of the query using a simple builder pattern.
14
+ */
15
+ export interface QueryBuilder<KY extends KyselyRizzolverBase<any, any>, T extends Data<KY>> {
16
+ /**
17
+ * The record of {@link Selector} this query builder has, keyed by their alias.
18
+ */
19
+ selectors: T;
20
+ /**
21
+ * Given a {@link Selector} alias `k`, Gets a `"table as alias"` expression
22
+ * for that selector.
23
+ *
24
+ * This is a shorthand for `this.selectors[k].selectTable`
25
+ *
26
+ * Example:
27
+ * ```
28
+ * const qb = rizzolver
29
+ * .newQueryBuilder()
30
+ * .add('user', 'u');
31
+ *
32
+ * const userTable = qb.table('u'); // => 'user as u'
33
+ * ```
34
+ */
35
+ table<K extends keyof T>(tableAlias: K): T[K]['selectTable'];
36
+ /**
37
+ * Gets a const array of all the fields of all the {@link Selector}s this
38
+ * query builder has. The order is arbitrary and should not be relied upon.
39
+ */
40
+ allFields: FieldsOf<T, keyof T>;
41
+ /**
42
+ * Given one of more {@link Selector} aliases, gets an array of
43
+ * `"table.field as alias"` expressions for all of their fields.
44
+ *
45
+ * Example:
46
+ * ```
47
+ * const qb = rizzolver
48
+ * .newQueryBuilder()
49
+ * .add('user', 'u')
50
+ * .add('address', 'a');
51
+ *
52
+ * const userFields = qb.fieldsOf('u');
53
+ * // => ['u.id as _u_id', 'u.first_name as _u_first_name', 'u.last_name as _u_last_name']
54
+ * const addressFields = qb.fieldsOf('a');
55
+ * // => ['a.id as _a_id', 'a.street as _a_street', 'a.city as _a_city']
56
+ * const allFields = qb.fieldsOf('u', 'a');
57
+ * // equivalent to [...qb.fieldsOf('u'), ...qb.fieldsOf('a')]
58
+ * // => ['u.id as _u_id', 'u.first_name as _u_first_name', 'u.last_name as _u_last_name', 'a.id as _a_id', 'a.street as _a_street', 'a.city as _a_city']
59
+ * ```
60
+ */
61
+ fieldsOf<K extends keyof T>(...tableAliases: K[]): FieldsOf<T, K>;
62
+ field<K extends keyof T & string, F extends T[K]['input']['tableFields']>(field: `${K}.${F}`): {
63
+ value: `_${K}_${F}`;
64
+ from<A extends string>(alias: A): `${A}._${K}_${F}`;
65
+ };
66
+ /**
67
+ * Adds a new {@link Selector} to the query builder.
68
+ */
69
+ add<Table extends TableName<KY>, Alias extends string, Keys extends readonly AnyTableField<KY, Table>[] = AllTableFields<KY, Table>>(selector: Selector<KY, Table, Alias, Keys>): MoreData<KY, T, Table, Alias, Keys>;
70
+ /**
71
+ * Adds a new {@link Selector} to the query builder.
72
+ */
73
+ add<Table extends TableName<KY>, Alias extends string, Keys extends readonly AnyTableField<KY, Table>[] = AllTableFields<KY, Table>>(table: Table, alias: Alias, keys?: Keys): MoreData<KY, T, Table, Alias, Keys>;
74
+ /**
75
+ * Parses the results of a query into the {@link Selectable} instances
76
+ * defined by the {@link Selector}s this query builder has.
77
+ *
78
+ * Example:
79
+ * ```
80
+ * const result = await rizzolver
81
+ * .newQueryBuilder()
82
+ * .add('user', 'u')
83
+ * .add('address', 'a')
84
+ * .run((qb) =>
85
+ * db
86
+ * .selectFrom(qb.table('u'))
87
+ * .leftJoin(qb.table('a'), (join) => join.onRef('u.address_id', '=', 'a.id'))
88
+ * .select(qb.allFields)
89
+ * .execute()
90
+ * );
91
+ *
92
+ * const parsedRows = result.rows;
93
+ * // => [
94
+ * // {
95
+ * // row: { id: 1, first_name: 'John', last_name: 'Doe' },
96
+ * // selectors: {
97
+ * // u: { id: 1, first_name: 'John', last_name: 'Doe' },
98
+ * // a: { id: 10, street: '123 Main St', city: 'Springfield' }
99
+ * // }
100
+ * // },
101
+ * // {
102
+ * // row: { id: 2, first_name: 'Jane', last_name: 'Smith' },
103
+ * // selectors: {
104
+ * // u: { id: 2, first_name: 'Jane', last_name: 'Smith' },
105
+ * // a: undefined, // Jane has no address
106
+ * // }
107
+ * // },
108
+ * // ]
109
+ * ```
110
+ *
111
+ * To make it easier to consume the results, the query builder also provides
112
+ * methods to create {@link FetchOneResult}, {@link FetchOneXResult}, and
113
+ * {@link FetchSomeResult} instances for each selector by its alias:
114
+ * ```
115
+ * const result = await rizzolver
116
+ * .newQueryBuilder()
117
+ * .add('user', 'u')
118
+ * .run(...)
119
+ * .newFetchOneResult('u'); // => FetchOneResult<DB, 'user', Selectable<User>>
120
+ * ```
121
+ */
122
+ run<Row extends Record<string, unknown>>(rows: Row[]): DeferredResult<KY, T, Row>;
123
+ run<Row extends Record<string, unknown>>(callback: (qb: this) => Promise<Row[]>): DeferredResult<KY, T, Row>;
124
+ }
125
+ export declare function newQueryBuilder<KY extends KyselyRizzolverBase<any, any>>(rizzolver: KY): QueryBuilder<KY, {}>;
126
+ type Data<KY extends KyselyRizzolverBase<any, any>> = {
127
+ [alias: string]: Selector<KY, any, string, any>;
128
+ };
129
+ type MoreData<KY extends KyselyRizzolverBase<any, any>, T extends Data<KY>, Table extends TableName<KY>, A extends string, K extends readonly AnyTableField<KY, Table>[] = AllTableFields<KY, Table>> = QueryBuilder<KY, T & {
130
+ [k in A]: Selector<KY, Table, A, K>;
131
+ }>;
132
+ interface DeferredResult<KY extends KyselyRizzolverBase<any, any>, T extends Data<KY>, Row extends Record<string, unknown>> extends Promise<Result<KY, T, Row>> {
133
+ newFetchOneResult<K extends keyof T>(selectorAlias: K): Promise<FetchOneResult<KyDB<KY>, T[K]['input']['table'], SelectorResult<KY, any, T[K]['input']['table'], T[K]['input']['tableFields']>[number]['model'] & {}>>;
134
+ newFetchOneXResult<K extends keyof T>(selectorAlias: K): Promise<FetchOneXResult<KyDB<KY>, T[K]['input']['table'], SelectorResult<KY, any, T[K]['input']['table'], T[K]['input']['tableFields']>[number]['model'] & {}>>;
135
+ newFetchSomeResult<K extends keyof T>(selectorAlias: K): Promise<FetchSomeResult<KyDB<KY>, T[K]['input']['table'], SelectorResult<KY, any, T[K]['input']['table'], T[K]['input']['tableFields']>[number]['model'] & {}>>;
136
+ }
137
+ type Result<KY extends KyselyRizzolverBase<any, any>, T extends Data<KY>, Row extends Record<string, unknown>> = {
138
+ first: {
139
+ row: Row;
140
+ selectors: {
141
+ [k in keyof T]: ReturnType<T[k]['select']>[number]['model'];
142
+ };
143
+ } | undefined;
144
+ rows: {
145
+ row: Row;
146
+ selectors: {
147
+ [k in keyof T]: ReturnType<T[k]['select']>[number]['model'];
148
+ };
149
+ }[];
150
+ models: ModelCollection<KyDB<KY>>;
151
+ newFetchOneResult<K extends keyof T>(selectorAlias: K): FetchOneResult<KyDB<KY>, T[K]['input']['table'], SelectorResult<KY, any, T[K]['input']['table'], T[K]['input']['tableFields']>[number]['model'] & {}>;
152
+ newFetchOneXResult<K extends keyof T>(selectorAlias: K): FetchOneXResult<KyDB<KY>, T[K]['input']['table'], SelectorResult<KY, any, T[K]['input']['table'], T[K]['input']['tableFields']>[number]['model'] & {}>;
153
+ newFetchSomeResult<K extends keyof T>(selectorAlias: K): FetchSomeResult<KyDB<KY>, T[K]['input']['table'], SelectorResult<KY, any, T[K]['input']['table'], T[K]['input']['tableFields']>[number]['model'] & {}>;
154
+ };
155
+ type FieldsOf<T, K extends keyof T> = UnionToTuple<Pick<T, K> extends infer U ? U[keyof U] extends infer S ? S extends Selector<infer KY, infer Table, infer Alias, infer Keys> ? Selector<KY, Table, Alias, Keys>['selectFields'][number] : never : never : never>;
156
+ export {};
@@ -0,0 +1,72 @@
1
+ import type { Selectable } from 'kysely';
2
+ import type { AllTableFields, AnyTableField, KyDB, KyselyRizzolverBase, TableName } from './kysely-rizzolver.js';
3
+ /**
4
+ * A {@link Selector} makes it easier to build select expressions for a
5
+ * table in a type-safe way. It can process the results of queries into
6
+ * Kysely's {@link Selectable} instances.
7
+ *
8
+ * {@link Selector} is a low level utility that is used by
9
+ * {@link QueryBuilder} to work with multiple selectors.
10
+ */
11
+ export type Selector<KY extends KyselyRizzolverBase<any, any>, Table extends TableName<KY>, Alias extends string, TableFields extends readonly AnyTableField<KY, Table>[] = AllTableFields<KY, Table>> = {
12
+ input: {
13
+ /**
14
+ * The table name that's being selected from.
15
+ */
16
+ table: Table;
17
+ /**
18
+ * The alias for the table.
19
+ */
20
+ alias: Alias;
21
+ /**
22
+ * An array of the fields to be selected from the table.
23
+ *
24
+ * This can be omitted, in which case it will default to all the fields of
25
+ * the table.
26
+ */
27
+ tableFields: TableFields;
28
+ };
29
+ /**
30
+ * A `"table as alias"` expression for using in select expressions.
31
+ */
32
+ selectTable: TableAlias<KY, Table, Alias>;
33
+ /**
34
+ * An array of `"table.field as alias"` expressions for using in select expressions.
35
+ */
36
+ selectFields: FieldsAsAliases<Alias, TableFields>;
37
+ /**
38
+ * A utility method that allows you to reference a specific field.
39
+ */
40
+ field<Field extends TableFields[number]>(field: Field): {
41
+ str: `_${Alias}_${Field}`;
42
+ /**
43
+ * A utility method that allows you to reference the field from a different table alias.
44
+ *
45
+ * This is useful if you need to reference the field from a subquery for example.
46
+ */
47
+ from<A extends string>(alias: A): `${A}.${FieldAlias<Alias, Field>}`;
48
+ };
49
+ /**
50
+ * Parses the results of a query into the model defined by this selector for each row.
51
+ *
52
+ * Example:
53
+ * ```
54
+ * const selector = newSelector('user', 'u');
55
+ * const data = await db.selectFrom(selector.tableAlias).selectAll().execute();
56
+ * const parsed = selector.select(data);
57
+ * // => type would be { row: Row, model: Selectable<User> | undefined }[]
58
+ * ```
59
+ */
60
+ select<Row extends Record<string, unknown>>(rows: Row[]): SelectorResult<KY, Row, Table, TableFields>;
61
+ };
62
+ export declare function newSelector<KY extends KyselyRizzolverBase<any, any>, Table extends TableName<KY>, Alias extends string>(rizzolver: KY, tableName: Table, alias: Alias): Selector<KY, Table, Alias, AllTableFields<KY, Table>>;
63
+ export declare function newSelector<KY extends KyselyRizzolverBase<any, any>, Table extends TableName<KY>, Alias extends string, Keys extends readonly AnyTableField<KY, Table>[]>(rizzolver: KY, tableName: Table, alias: Alias, keys?: Keys): Selector<KY, Table, Alias, Keys>;
64
+ export type SelectorResult<KY extends KyselyRizzolverBase<any, any>, Row extends Record<string, unknown>, Table extends TableName<KY>, TableFields extends readonly AnyTableField<KY, Table>[] = AllTableFields<KY, Table>> = {
65
+ row: Row;
66
+ model: Selectable<Pick<KyDB<KY>[Table], TableFields[number]>> | undefined;
67
+ }[];
68
+ type TableAlias<KY extends KyselyRizzolverBase<any, any>, Table extends TableName<KY>, Alias extends string> = `${Table} as ${Alias}`;
69
+ type FieldAlias<TableAlias extends string, TableField extends string> = `_${TableAlias}_${TableField}`;
70
+ type FieldAsAlias<TableAlias extends string, TableField extends string> = `${TableAlias}.${TableField} as ${FieldAlias<TableAlias, TableField>}`;
71
+ type FieldsAsAliases<TableAlias extends string, TableFields extends readonly string[]> = TableFields extends readonly [infer TableField extends string, ...infer Tail extends string[]] ? [FieldAsAlias<TableAlias, TableField>, ...FieldsAsAliases<TableAlias, Tail>] : [];
72
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * I can't explain this.
3
+ *
4
+ * I badgered ChatGPT until it worked. it probably just referenced type-fest.
5
+ */
6
+ type LastInUnion<U> = (U extends any ? () => U : never) extends infer F ? (F extends any ? (k: F) => void : never) extends (k: infer I) => void ? I extends () => infer R ? R : never : never : never;
7
+ /**
8
+ * Recursively converts a union type to a tuple by splitting it into its head and tail.
9
+ */
10
+ export type UnionToTuple<U, Last = LastInUnion<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];
11
+ export {};
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "kysely-rizzolver",
3
+ "version": "0.0.2",
4
+ "description": "Complex Kysely queries, maximum rizz, type-safe every time.",
5
+ "author": "YLivay",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git://github.com/YLivay/kysely-rizzolver.git"
9
+ },
10
+ "license": "MIT",
11
+ "main": "dist/cjs/index.js",
12
+ "module": "dist/esm/index.js",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/esm/index.js",
16
+ "require": "./dist/cjs/index.js",
17
+ "default": "./dist/cjs/index.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "clean": "rm -rf dist",
22
+ "build": "npm run clean && (npm run build:esm & npm run build:cjs)",
23
+ "build:esm": "tsc -p tsconfig.json && echo '{\"type\": \"module\"}' > dist/esm/package.json",
24
+ "build:cjs": "tsc -p tsconfig-cjs.json"
25
+ },
26
+ "peerDependencies": {
27
+ "kysely": "^0.27.5"
28
+ },
29
+ "devDependencies": {
30
+ "typescript": "^5.7.3"
31
+ }
32
+ }