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,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,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KyselyRizzolver = void 0;
4
+ const fetch_result_factory_js_1 = require("./fetch-result-factory.js");
5
+ const query_builder_js_1 = require("./query-builder.js");
6
+ const selector_js_1 = require("./selector.js");
7
+ const model_collection_js_1 = require("./model-collection.js");
8
+ /**
9
+ * A {@link KyselyRizzolver} is a class that is used to define the structure of
10
+ * a database schema.
11
+ *
12
+ * It streamlines instatiating type-safe {@link QueryBuilder}s,
13
+ * {@link Selector}s, {@link ModelCollection}s and {@link FetchResult}s.
14
+ *
15
+ * Define a new {@link KyselyRizzolver} using the
16
+ * {@link KyselyRizzolver.builder|.builderForSchema()} or
17
+ * {@link KyselyRizzolver.builderNoSchema|.builderNoSchema()}.
18
+ */
19
+ class KyselyRizzolver {
20
+ fields;
21
+ fetches;
22
+ constructor(fields) {
23
+ this.fields = fields;
24
+ this.fetches = (0, fetch_result_factory_js_1.newFetchResultFactory)();
25
+ }
26
+ /**
27
+ * Intantiates a new {@link Selector} for the given table.
28
+ */
29
+ newSelector(table, alias) {
30
+ return (0, selector_js_1.newSelector)(this, table, alias);
31
+ }
32
+ /**
33
+ * Instantiates a new {@link QueryBuilder}.
34
+ */
35
+ newQueryBuilder() {
36
+ return (0, query_builder_js_1.newQueryBuilder)(this);
37
+ }
38
+ /**
39
+ * Instantiates a new {@link ModelCollection}.
40
+ */
41
+ newModelCollection() {
42
+ return (0, model_collection_js_1.newModelCollection)();
43
+ }
44
+ /**
45
+ * Starts building a new {@link KyselyRizzolver} using a builder pattern for
46
+ * a schema.
47
+ *
48
+ * Call {@link KyselyRizzolverBuilderForSchema.table|.table()} for each
49
+ * table that exists on the `DB` type parameter with all of their column
50
+ * names as a const array. After all tables have been added, call
51
+ * {@link KyselyRizzolverBuilderForSchema.build|.build()} to get a new
52
+ * {@link KyselyRizzolver} instance.
53
+ *
54
+ * Example:
55
+ * ```
56
+ * const rizzolver = KyselyRizzolver.builderForSchema<DB>()
57
+ * .table('user', ['id', 'name', 'email'] as const)
58
+ * .table('post', ['id', 'title', 'content', 'authorId'] as const)
59
+ * .build();
60
+ * ```
61
+ *
62
+ * Note: The `as const` assertion is necessary for correct type inference.
63
+ */
64
+ static builderForSchema() {
65
+ return _newKyselyRizzolverBuilderForSchema({});
66
+ }
67
+ /**
68
+ * Starts building a new {@link KyselyRizzolver} using a builder pattern
69
+ * without a schema.
70
+ *
71
+ * Call {@link KyselyRizzolverBuilderNoSchema.table|.table()} for each
72
+ * table with all of their column names as a const array.
73
+ *
74
+ * Example:
75
+ * ```
76
+ * const rizzolver = KyselyRizzolver.builder()
77
+ * .table('user', ['id', 'name', 'email'] as const) // note `as const` is necessary
78
+ * .table('post', ['id', 'title', 'content', 'authorId'] as const)
79
+ * .build();
80
+ * ```
81
+ *
82
+ * Since this version of builder is schemaless, it cannot infer the value
83
+ * types for the columns. The `user` type will be `{ id: unknown, name:
84
+ * unknown, email: unknown }`.
85
+ *
86
+ * You may call
87
+ * {@link KyselyRizzolverBuilderNoSchema.asModel|.asModel\<M\>()}
88
+ * immediately after the .table() call to provide the types, where `M` is an
89
+ * type like `{ column1: type1, column2: type2, ... }`.
90
+ *
91
+ * Example:
92
+ * ```
93
+ * const rizzolver = KyselyRizzolver.builder()
94
+ * .table('user', ['id', 'name', 'email'] as const)
95
+ * .asModel<{id: number, name: string, email: string}>()
96
+ * .table('post', ['id', 'title', 'content', 'authorId'] as const)
97
+ * .asModel<{id: number, title: string, content: string, authorId: number}>()
98
+ * .build();
99
+ * ```
100
+ *
101
+ * p.s. if your .table() and .asModel() columns differ, it will let you know
102
+ * at compile time ;)
103
+ *
104
+ * Once all tables have been added, call
105
+ * {@link KyselyRizzolverBuilderNoSchema.build|.build()} to get a new
106
+ * {@link KyselyRizzolver} instance.
107
+ */
108
+ static builderNoSchema() {
109
+ return _newKyselyRizzolverBuilderNoSchema({}, null);
110
+ }
111
+ }
112
+ exports.KyselyRizzolver = KyselyRizzolver;
113
+ function _newKyselyRizzolverBuilderForSchema(fields) {
114
+ return {
115
+ table(tableName, tableFields) {
116
+ return _newKyselyRizzolverBuilderForSchema({
117
+ ...fields,
118
+ [tableName]: tableFields
119
+ });
120
+ },
121
+ build() {
122
+ return new KyselyRizzolver(fields);
123
+ }
124
+ };
125
+ }
126
+ function _newKyselyRizzolverBuilderNoSchema(fields, last) {
127
+ return {
128
+ table(tableName, tableFields) {
129
+ return _newKyselyRizzolverBuilderNoSchema({
130
+ ...fields,
131
+ [tableName]: {
132
+ model: null,
133
+ columns: tableFields
134
+ }
135
+ }, tableName);
136
+ },
137
+ asModel() {
138
+ if (!last) {
139
+ throw new Error('asModel() must be called after table()');
140
+ }
141
+ return _newKyselyRizzolverBuilderNoSchema(fields, null);
142
+ },
143
+ build() {
144
+ return new KyselyRizzolver(Object.fromEntries(Object.entries(fields).map((entry) => [entry[0], entry[1].columns])));
145
+ }
146
+ };
147
+ }
@@ -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,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.newModelCollection = newModelCollection;
4
+ function newModelCollection(init = {}) {
5
+ const collection = { ...init };
6
+ return {
7
+ add(table, selectable) {
8
+ if (!selectable ||
9
+ !('id' in selectable) ||
10
+ !selectable.id ||
11
+ typeof selectable.id !== 'number') {
12
+ return this;
13
+ }
14
+ if (!(table in collection)) {
15
+ collection[table] = {};
16
+ }
17
+ collection[table][selectable.id] = selectable;
18
+ return this;
19
+ },
20
+ get collection() {
21
+ return collection;
22
+ }
23
+ };
24
+ }
@@ -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,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.newQueryBuilder = newQueryBuilder;
4
+ const fetch_result_js_1 = require("./fetch-result.js");
5
+ const selector_js_1 = require("./selector.js");
6
+ const model_collection_js_1 = require("./model-collection.js");
7
+ function newQueryBuilder(rizzolver) {
8
+ const selectors = {};
9
+ return {
10
+ selectors,
11
+ table(tableAlias) {
12
+ return selectors[tableAlias].selectTable;
13
+ },
14
+ allFields: Object.values(selectors)
15
+ .map((selector) => selector.selectFields)
16
+ .flat(),
17
+ fieldsOf(...tableAliases) {
18
+ return tableAliases.map((alias) => selectors[alias].selectFields).flat();
19
+ },
20
+ field(field) {
21
+ const [t, f] = field.split('.');
22
+ const alias = `_${t}_${f}`;
23
+ return {
24
+ value: alias,
25
+ from(a) {
26
+ return `${a}.${alias}`;
27
+ }
28
+ };
29
+ },
30
+ add(selectorOrTable, alias, keys) {
31
+ let selector;
32
+ if (typeof selectorOrTable === 'string') {
33
+ if (!alias) {
34
+ throw new Error('Must provide an alias when calling QueryBuilder.add with a table name');
35
+ }
36
+ selector = (0, selector_js_1.newSelector)(rizzolver, selectorOrTable, alias, (keys ?? rizzolver.fields[selectorOrTable]));
37
+ }
38
+ else {
39
+ if (alias || keys) {
40
+ throw new Error('Must not provide an alias or keys when calling QueryBuilder.add with a selector');
41
+ }
42
+ selector = selectorOrTable;
43
+ }
44
+ selectors[selector.input.alias] = selector;
45
+ return this;
46
+ },
47
+ run(rowsOrCallback) {
48
+ const promise = (async () => {
49
+ const rows = Array.isArray(rowsOrCallback) ? rowsOrCallback : await rowsOrCallback(this);
50
+ const modelCollection = (0, model_collection_js_1.newModelCollection)();
51
+ const selectorResults = {};
52
+ for (const [alias, selector] of Object.entries(selectors)) {
53
+ const selectorResult = selector.select(rows);
54
+ for (const { model } of selectorResult) {
55
+ if (!model) {
56
+ continue;
57
+ }
58
+ modelCollection.add(selector.input.table, model);
59
+ }
60
+ selectorResults[alias] = selectorResult;
61
+ }
62
+ const result = [];
63
+ for (let i = 0; i < rows.length; i++) {
64
+ const row = rows[i];
65
+ const selectedModels = {};
66
+ for (const [alias, selectorResult] of Object.entries(selectorResults)) {
67
+ selectedModels[alias] = selectorResult[i].model;
68
+ }
69
+ result.push({ row, selectors: selectedModels });
70
+ }
71
+ return {
72
+ first: result.length ? result[0] : undefined,
73
+ rows: result,
74
+ models: modelCollection,
75
+ newFetchOneResult(selectorAlias) {
76
+ return (0, fetch_result_js_1.newFetchOneResult)(selectors[selectorAlias].input.table, (result.length ? result[0] : undefined)?.selectors[selectorAlias], modelCollection);
77
+ },
78
+ newFetchOneXResult(selectorAlias) {
79
+ return (0, fetch_result_js_1.newFetchOneXResult)(selectors[selectorAlias].input.table, (result.length ? result[0] : undefined)?.selectors[selectorAlias], modelCollection);
80
+ },
81
+ newFetchSomeResult(selectorAlias) {
82
+ return (0, fetch_result_js_1.newFetchSomeResult)(selectors[selectorAlias].input.table, result.map((r) => r.selectors[selectorAlias]).filter((r) => !!r), modelCollection);
83
+ }
84
+ };
85
+ })();
86
+ promise.newFetchOneResult = (selectorAlias) => promise.then((result) => result.newFetchOneResult(selectorAlias));
87
+ promise.newFetchOneXResult = (selectorAlias) => promise.then((result) => result.newFetchOneXResult(selectorAlias));
88
+ promise.newFetchSomeResult = (selectorAlias) => promise.then((result) => result.newFetchSomeResult(selectorAlias));
89
+ return promise;
90
+ }
91
+ };
92
+ }
@@ -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 {};