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.
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/cjs/fetch-result-factory.d.ts +52 -0
- package/dist/cjs/fetch-result-factory.js +88 -0
- package/dist/cjs/fetch-result.d.ts +111 -0
- package/dist/cjs/fetch-result.js +117 -0
- package/dist/cjs/index.d.ts +10 -0
- package/dist/cjs/index.js +21 -0
- package/dist/cjs/kysely-rizzolver.d.ts +138 -0
- package/dist/cjs/kysely-rizzolver.js +147 -0
- package/dist/cjs/model-collection.d.ts +26 -0
- package/dist/cjs/model-collection.js +24 -0
- package/dist/cjs/query-builder.d.ts +156 -0
- package/dist/cjs/query-builder.js +92 -0
- package/dist/cjs/selector.d.ts +72 -0
- package/dist/cjs/selector.js +45 -0
- package/dist/cjs/type-helpers.d.ts +11 -0
- package/dist/cjs/type-helpers.js +2 -0
- package/dist/esm/fetch-result-factory.js +85 -0
- package/dist/esm/fetch-result.js +105 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/kysely-rizzolver.js +143 -0
- package/dist/esm/model-collection.js +21 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/query-builder.js +89 -0
- package/dist/esm/selector.js +42 -0
- package/dist/esm/type-helpers.js +1 -0
- package/dist/types/fetch-result-factory.d.ts +52 -0
- package/dist/types/fetch-result.d.ts +111 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/kysely-rizzolver.d.ts +138 -0
- package/dist/types/model-collection.d.ts +26 -0
- package/dist/types/query-builder.d.ts +156 -0
- package/dist/types/selector.d.ts +72 -0
- package/dist/types/type-helpers.d.ts +11 -0
- 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
|
+
}
|