@apisr/drizzle-model 0.0.1

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 (47) hide show
  1. package/.turbo/turbo-check-types.log +2 -0
  2. package/ROADMAP.md +1 -0
  3. package/TODO.md +64 -0
  4. package/drizzle.config.ts +11 -0
  5. package/package.json +35 -0
  6. package/src/index.ts +1 -0
  7. package/src/model/builder.ts +46 -0
  8. package/src/model/config.ts +35 -0
  9. package/src/model/core/joins.ts +279 -0
  10. package/src/model/core/projection.ts +47 -0
  11. package/src/model/core/runtime.ts +249 -0
  12. package/src/model/core/thenable.ts +85 -0
  13. package/src/model/core/transform.ts +45 -0
  14. package/src/model/core/where.ts +183 -0
  15. package/src/model/core/with.ts +28 -0
  16. package/src/model/dialect.ts +11 -0
  17. package/src/model/foreigns.ts +31 -0
  18. package/src/model/format.ts +19 -0
  19. package/src/model/index.ts +1 -0
  20. package/src/model/methods/exclude.ts +32 -0
  21. package/src/model/methods/include.ts +3 -0
  22. package/src/model/methods/insert.ts +16 -0
  23. package/src/model/methods/levels.ts +2 -0
  24. package/src/model/methods/query/where.ts +48 -0
  25. package/src/model/methods/return.ts +39 -0
  26. package/src/model/methods/select.ts +38 -0
  27. package/src/model/methods/update.ts +4 -0
  28. package/src/model/methods/upsert.ts +54 -0
  29. package/src/model/methods/with.ts +40 -0
  30. package/src/model/model.ts +148 -0
  31. package/src/model/options.ts +64 -0
  32. package/src/model/query/operations.ts +170 -0
  33. package/src/model/relation.ts +121 -0
  34. package/src/model/result.ts +91 -0
  35. package/src/model/shape.ts +8 -0
  36. package/src/model/table.ts +127 -0
  37. package/src/types.ts +16 -0
  38. package/tests/builder-v2-mysql.type-test.ts +40 -0
  39. package/tests/builder-v2.type-test.ts +343 -0
  40. package/tests/builder.test.ts +63 -0
  41. package/tests/db.ts +25 -0
  42. package/tests/find.test.ts +155 -0
  43. package/tests/insert.test.ts +233 -0
  44. package/tests/relations.ts +38 -0
  45. package/tests/schema.ts +49 -0
  46. package/tsconfig.json +36 -0
  47. package/tsdown.config.ts +12 -0
@@ -0,0 +1,121 @@
1
+ import type {
2
+ TableRelationalConfig,
3
+ TablesRelationalConfig,
4
+ } from "drizzle-orm/relations";
5
+ import type { ModelIdentifier } from "./model.ts";
6
+ import type { IsTable, TableOutput } from "./table.ts";
7
+ import type { RecursiveBooleanRecord } from "../types.ts";
8
+
9
+ /**
10
+ * Defines the cardinality of a relation: "one" (hasOne) or "many" (hasMany).
11
+ */
12
+ export type RelationKind = "one" | "many";
13
+
14
+ /**
15
+ * Utility type to wrap a type T in an array if the relation kind is "many",
16
+ * otherwise returns T as is.
17
+ *
18
+ * @typeParam Kind - "one" or "many"
19
+ * @typeParam T - The type to potentially wrap
20
+ */
21
+ export type ApplyRelationCardinality<
22
+ Kind extends RelationKind,
23
+ T,
24
+ > = Kind extends "many" ? T[] : T;
25
+
26
+ /**
27
+ * Extracts the metadata for a specific relation from a table's configuration.
28
+ *
29
+ * @typeParam TTable - Relational configuration for the table
30
+ * @typeParam Key - Name of the relation field
31
+ */
32
+ export type RelationMeta<
33
+ TTable extends TableRelationalConfig,
34
+ Key extends string,
35
+ > = TTable["relations"][Key];
36
+
37
+ /**
38
+ * Resolves the configuration of the target table in a relation.
39
+ *
40
+ * @typeParam TSchema - Full relational schema
41
+ * @typeParam Meta - Metadata object containing the target table name
42
+ */
43
+ export type TargetTable<
44
+ TSchema extends TablesRelationalConfig,
45
+ Meta extends { targetTableName: string; },
46
+ > = TSchema[Meta["targetTableName"]];
47
+
48
+ /**
49
+ * Resolves the raw row type of a related table.
50
+ *
51
+ * @typeParam Key - Name of the relation
52
+ * @typeParam TSchema - Full relational schema
53
+ * @typeParam TTable - Relational configuration for the source table
54
+ */
55
+ export type RelationTargetRow<
56
+ Key extends string,
57
+ TSchema extends TablesRelationalConfig,
58
+ TTable extends TableRelationalConfig,
59
+ > = TableOutput<
60
+ IsTable<TargetTable<TSchema, RelationMeta<TTable, Key>>["table"]>
61
+ >;
62
+
63
+ /**
64
+ * Resolves the type of a single relation field based on the selection value.
65
+ *
66
+ * If the value is a nested selection object, it recursively resolves that selection
67
+ * and merges it with the target row type.
68
+ * Finally, it applies the correct cardinality (array or single object).
69
+ *
70
+ * @typeParam Key - Name of the relation
71
+ * @typeParam Value - The selection value (boolean or nested object)
72
+ * @typeParam TSchema - Full relational schema
73
+ * @typeParam TTable - Relational configuration for the source table
74
+ */
75
+ export type ResolveSingleRelation<
76
+ Key extends string,
77
+ Value,
78
+ TSchema extends TablesRelationalConfig,
79
+ TTable extends TableRelationalConfig,
80
+ > = Value extends Record<string, any>
81
+ ? ApplyRelationCardinality<
82
+ RelationMeta<TTable, Key>["relationType"],
83
+ ResolveRelationSelection<
84
+ Value,
85
+ TSchema,
86
+ TargetTable<TSchema, RelationMeta<TTable, Key>>
87
+ > &
88
+ RelationTargetRow<Key, TSchema, TTable>
89
+ >
90
+ : ApplyRelationCardinality<
91
+ RelationMeta<TTable, Key>["relationType"],
92
+ RelationTargetRow<Key, TSchema, TTable>
93
+ >;
94
+
95
+ /**
96
+ * Recursively resolves a selection object into a typed result object.
97
+ *
98
+ * Iterates over keys in the selection that are true or objects, resolving
99
+ * each relation and building the final output type.
100
+ *
101
+ * @typeParam TSelection - The selection object passed to .with()
102
+ * @typeParam TSchema - Full relational schema
103
+ * @typeParam TTable - Relational configuration for the current table
104
+ */
105
+ export type ResolveRelationSelection<
106
+ TSelection extends Record<string, any>,
107
+ TSchema extends TablesRelationalConfig,
108
+ TTable extends TableRelationalConfig,
109
+ > = {
110
+ [Key in keyof TSelection as TSelection[Key] extends
111
+ | true
112
+ | RecursiveBooleanRecord
113
+ | ModelIdentifier<any>
114
+ ? Key & string
115
+ : never]: ResolveSingleRelation<
116
+ Key & string,
117
+ TSelection[Key],
118
+ TSchema,
119
+ TTable
120
+ >;
121
+ };
@@ -0,0 +1,91 @@
1
+ import type {
2
+ TableRelationalConfig,
3
+ TablesRelationalConfig,
4
+ } from "drizzle-orm/relations";
5
+ import type { MethodWithResult, MethodWithValue } from "./methods/with.ts";
6
+ import type { MethodSelectResult, MethodSelectValue } from "./methods/select.ts";
7
+ import type {
8
+ MethodExcludeResult,
9
+ MethodExcludeValue,
10
+ } from "./methods/exclude.ts";
11
+ import type { MethodReturnResult } from "./methods/return.ts";
12
+ import type { ReturningIdDialects } from "./dialect.ts";
13
+ import type { TableOutput } from "./table.ts";
14
+ import type { ModelConfig } from "./config.ts";
15
+ import type { ResolveOptionsFormat } from "./options.ts";
16
+ import type { ModelFormatValue } from "./format.ts";
17
+
18
+ /**
19
+ * Represents the result of a model operation (like findMany or findFirst).
20
+ *
21
+ * It extends Promise to be awaitable, returning the result data.
22
+ * It also exposes a `.with()` method for chaining relation loading.
23
+ *
24
+ * @typeParam TResult - The type of the data returned (e.g. Row[])
25
+ * @typeParam TSchema - Full relational schema
26
+ * @typeParam TTable - Relational configuration for the current table
27
+ */
28
+ export interface ModelQueryResult<
29
+ TResult extends Record<string, any>,
30
+ TConfig extends ModelConfig,
31
+ TExcludedKeys extends string = string,
32
+ TSchema extends TablesRelationalConfig = TConfig["schema"],
33
+ TTable extends TableRelationalConfig = TConfig["table"],
34
+ TFormat extends Record<string, any> | undefined = ResolveOptionsFormat<TConfig["options"]["format"]>
35
+ > extends Promise<ModelFormatValue<TResult, TFormat>> {
36
+ with<TValue extends MethodWithValue<TSchema, TTable["relations"]>, TExcludeKeys extends string = TExcludedKeys | "with">(
37
+ value: TValue,
38
+ ): ModelQueryResult<
39
+ MethodWithResult<TValue, TResult, TSchema, TTable>,
40
+ TConfig,
41
+ TExcludeKeys
42
+ >,
43
+
44
+ select<TValue extends MethodSelectValue<TResult>, TExcludeKeys extends string = TExcludedKeys | "select">(
45
+ value: TValue,
46
+ ): ModelQueryResult<
47
+ MethodSelectResult<TValue, TResult>,
48
+ TConfig,
49
+ TExcludeKeys
50
+ >,
51
+
52
+ exclude<TValue extends MethodExcludeValue<TResult>, TExcludeKeys extends string = TExcludedKeys | "exclude">(
53
+ value: TValue,
54
+ ): ModelQueryResult<
55
+ MethodExcludeResult<TValue, TResult>,
56
+ TConfig,
57
+ TExcludeKeys
58
+ >,
59
+
60
+ raw<TExcludeKeys extends string = TExcludedKeys | "raw">(): ModelQueryResult<TResult, TConfig, TExcludeKeys, TSchema, TTable, undefined>;
61
+
62
+ debug(): any;
63
+ }
64
+
65
+ export interface ModelMutateResult<
66
+ TBaseResult extends Record<string, any> | void,
67
+ TConfig extends ModelConfig,
68
+ TResultType extends string = "one"
69
+ > extends Promise<TBaseResult> {
70
+ // TODO: Planned for future
71
+ // with<TValue extends MethodWithInsertValue<TSchema, TTable["relations"]>>(
72
+ // value: TValue,
73
+ // ): void;
74
+
75
+ return<
76
+ TValue extends MethodSelectValue<TableOutput<TConfig["table"]>> | undefined,
77
+ TReturnResult extends MethodReturnResult<TResultType, TConfig> = MethodReturnResult<TResultType, TConfig>,
78
+ TResult extends Record<string, any> = TValue extends undefined ? TReturnResult : MethodSelectResult<Exclude<TValue, undefined>, TReturnResult>,
79
+ >(
80
+ value?: TConfig["dialect"] extends ReturningIdDialects
81
+ ? never
82
+ : TValue,
83
+ ): Omit<
84
+ ModelMutateResult<
85
+ TResult,
86
+ TConfig,
87
+ TResultType
88
+ >,
89
+ "with"
90
+ >;
91
+ }
@@ -0,0 +1,8 @@
1
+ import type { ModelConfig } from "./config.ts";
2
+ import type { ModelBase, ModelIdentifier } from "./model.ts";
3
+ import type { ResolveOptionsMethods } from "./options.ts";
4
+
5
+ export type ModelShape<TConfig extends ModelConfig> =
6
+ ModelIdentifier<TConfig["table"]["name"]> &
7
+ ModelBase<TConfig> &
8
+ ResolveOptionsMethods<TConfig["options"]["methods"]>;
@@ -0,0 +1,127 @@
1
+ import type { InferInsertModel, InferSelectModel, Table } from "drizzle-orm";
2
+ import type { SchemaEntry, TableRelationalConfig } from "drizzle-orm/relations";
3
+
4
+ /**
5
+ * Extracts the column definitions from a Drizzle table configuration.
6
+ *
7
+ * This utility resolves the actual Drizzle table type and maps it
8
+ * to its available column metadata.
9
+ *
10
+ * @typeParam TTable - Relational configuration for the table
11
+ */
12
+ /**
13
+ * Returns the column map from the underlying Drizzle table definition.
14
+ *
15
+ * @typeParam TTable - Relational configuration for the table
16
+ */
17
+ export type TableColumns<TTable extends TableRelationalConfig> = IsTable<TTable["table"]>["_"]["columns"];
18
+
19
+ /**
20
+ * Resolves a single column definition from a table by column name.
21
+ *
22
+ * @typeParam TColumnName - Name of the column to extract
23
+ * @typeParam TTable - Relational configuration for the table
24
+ */
25
+ /**
26
+ * Selects a single column definition by name.
27
+ *
28
+ * @typeParam TColumnName - Name of the column to extract
29
+ * @typeParam TTable - Relational configuration for the table
30
+ */
31
+ export type TableColumn<
32
+ TColumnName extends string,
33
+ TTable extends TableRelationalConfig,
34
+ > = TableColumns<TTable>[TColumnName];
35
+
36
+ /**
37
+ * Resolves the inferred select model for a table-like input.
38
+ *
39
+ * @typeParam TTable - Relation config, schema entry, or table
40
+ */
41
+ export type TableOutput<TTable extends TableRelationalConfig | SchemaEntry | Table> =
42
+ NormalizeTable<TTable> extends Table ? InferSelectModel<NormalizeTable<TTable>> : never;
43
+ // Is relation?
44
+ // (TTable extends TableRelationalConfig
45
+ // ? InferSelectModel<IsTable<TTable["table"]>>
46
+ // : (TTable extends SchemaEntry
47
+ // // Is schema entry (view/table)?
48
+ // ? InferSelectModel<IsTable<TTable>>
49
+ // : (TTable extends Table
50
+ // // Is Table?
51
+ // ? InferSelectModel<TTable>
52
+ // : never)));
53
+
54
+ /**
55
+ * Normalizes relation config and schema entries into a Drizzle table type.
56
+ *
57
+ * @typeParam TTable - Relation config, schema entry, or table
58
+ */
59
+ export type NormalizeTable<TTable extends TableRelationalConfig | SchemaEntry | Table> =
60
+ // Is relation?
61
+ (TTable extends TableRelationalConfig
62
+ ? IsTable<TTable["table"]>
63
+ : (TTable extends SchemaEntry
64
+ // Is schema entry (view/table)?
65
+ ? IsTable<TTable>
66
+ : (TTable extends Table
67
+ // Is Table?
68
+ ? TTable
69
+ : never)));
70
+
71
+ /**
72
+ * Collects target table names from all relations.
73
+ *
74
+ * @typeParam TTable - Relational configuration for the table
75
+ */
76
+ export type TableRelationsTableName<
77
+ TTable extends TableRelationalConfig
78
+ > = {
79
+ [K in keyof TTable["relations"]]:
80
+ TTable["relations"][K]["targetTableName"]
81
+ }[keyof TTable["relations"]];
82
+
83
+ /**
84
+ * Maps non-"many" relations to their target table names.
85
+ *
86
+ * @typeParam TTable - Relational configuration for the table
87
+ */
88
+ type TableOneRelationsMap<TTable extends TableRelationalConfig> = {
89
+ [K in keyof TTable["relations"]as
90
+ TTable["relations"][K]["relationType"] extends "many"
91
+ ? never
92
+ : K
93
+ ]: TTable["relations"][K]["targetTableName"]
94
+ };
95
+
96
+ /**
97
+ * Collects target table names for non-"many" relations.
98
+ *
99
+ * @typeParam TTable - Relational configuration for the table
100
+ */
101
+ export type TableOneRelationsTableName<
102
+ TTable extends TableRelationalConfig
103
+ > = TableOneRelationsMap<TTable>[keyof TableOneRelationsMap<TTable>];
104
+
105
+ /**
106
+ * Narrows a type to a Drizzle table when possible.
107
+ *
108
+ * @typeParam T - Candidate table type
109
+ */
110
+ export type IsTable<T> = T extends Table ? T : never;
111
+
112
+ /**
113
+ * Infers insert model for a Drizzle table.
114
+ *
115
+ * @typeParam TTable - Drizzle table type
116
+ */
117
+ export type TableInsertModel<TTable extends Table> =
118
+ InferInsertModel<TTable>;
119
+
120
+ /**
121
+ * Accepts a single insert payload or a batch of insert payloads.
122
+ *
123
+ * @typeParam TTable - Drizzle table type
124
+ */
125
+ export type TableInsertValues<TTable extends Table> =
126
+ | InferInsertModel<TTable>
127
+ | InferInsertModel<TTable>[];
package/src/types.ts ADDED
@@ -0,0 +1,16 @@
1
+ export type Compose<T, U> = T & U;
2
+ export type Fallback<A, B> = A extends undefined ? B : A;
3
+ export type Replace<T, R> = Omit<T, keyof R> & R;
4
+ export type UnwrapArray<T> = T extends (infer R)[] ? R : T;
5
+
6
+ export type AddToValues<T extends Record<PropertyKey, any>, A> = {
7
+ [K in keyof T]: T[K] & A
8
+ };
9
+
10
+ export type AddUnionToValues<T extends Record<PropertyKey, any>, A> = {
11
+ [K in keyof T]: T[K] | A
12
+ };
13
+
14
+ export interface RecursiveBooleanRecord {
15
+ [key: string]: boolean | RecursiveBooleanRecord;
16
+ }
@@ -0,0 +1,40 @@
1
+ import { mysqlDb } from "./db";
2
+ import { modelBuilder } from "src/model";
3
+ import { boolean, int, text, mysqlTable, varchar } from 'drizzle-orm/mysql-core';
4
+ import { defineRelations } from "drizzle-orm";
5
+ import { esc } from "@/model/query/operations";
6
+
7
+ const usersTable = mysqlTable('users', {
8
+ id: int('id').primaryKey(),
9
+ name: text('name').notNull(),
10
+ verified: boolean('verified').notNull().default(false),
11
+ });
12
+
13
+ const usersTableDefFn = mysqlTable('users_default_fn', {
14
+ customId: varchar('id', { length: 256 }).primaryKey().$defaultFn(() => "123"),
15
+ name: text('name').notNull(),
16
+ });
17
+
18
+ const schema = {
19
+ usersTable,
20
+ usersTableDefFn
21
+ };
22
+
23
+ const relations = defineRelations(schema, (r) => ({
24
+ usersTable: {}
25
+ }));
26
+
27
+ const model = modelBuilder({
28
+ schema,
29
+ db: mysqlDb,
30
+ relations,
31
+ dialect: "MySQL"
32
+ });
33
+
34
+ const userModel = model("usersTableDefFn", {});
35
+
36
+ const testRaw1 = await userModel.where({
37
+ customId: esc("123")
38
+ }).delete().return();
39
+
40
+ // testRaw1[0].