kysely-rizzolver 0.0.2

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 (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 {};