vsrepo 2.4.0 → 2.5.0

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.
@@ -0,0 +1,108 @@
1
+ import { RelationKeys } from "../vsrepo/vsrepo-relations.type";
2
+ import { SeeMode } from "./see-mode.type";
3
+ /**
4
+ * Loose structural constraint for the `Options` accepted by {@link InferMethodReturn}.
5
+ * Mirrors the four keys of `MethodOptions` without depending on the entity type.
6
+ */
7
+ type InferMethodReturnOptions = {
8
+ select?: object | undefined;
9
+ relations?: object | undefined;
10
+ see?: SeeMode | undefined;
11
+ db?: unknown;
12
+ };
13
+ /** Flattens an intersection into a single object type (keeps modifiers). */
14
+ type Simplify<X> = {
15
+ [K in keyof X]: X[K];
16
+ } & {};
17
+ /** Safe `O[K]`: `undefined` when `K` is not a key of `O`. */
18
+ type Get<O, K extends string> = K extends keyof O ? O[K] : undefined;
19
+ /**
20
+ * How a given option (`select`/`relations`) was typed:
21
+ * - `"absent"` — not provided (or explicitly `undefined`);
22
+ * - `"unknown"` — declared but not narrowed (optional/wide, e.g. a variable annotated as
23
+ * plain `MethodOptions<T>`), so its contents can't be known at compile time;
24
+ * - `"known"` — a narrowed object type (e.g. `{ id: true }` obtained via `satisfies`).
25
+ */
26
+ type OptionState<O, K extends string> = [Get<O, K>] extends [undefined] ? "absent" : undefined extends Get<O, K> ? "unknown" : "known";
27
+ /** Keys present in a `select`/`relations` spec (`undefined` spec has none). */
28
+ type SpecKeys<S> = S extends object ? keyof S : never;
29
+ /**
30
+ * Nested spec of key `K` inside `S`: the object itself when it's a nested
31
+ * `select`/`relations`, or `undefined` when the value is just `true`.
32
+ */
33
+ type SubSpec<S, K> = S extends object ? K extends keyof S ? NonNullable<S[K]> extends object ? NonNullable<S[K]> : undefined : undefined : undefined;
34
+ /** Keys of `T` that are scalar fields (i.e. not relations). */
35
+ type ScalarKeys<T> = Exclude<keyof T, RelationKeys<T>>;
36
+ /** Which option is currently driving the shape. */
37
+ type SpecMode = "select" | "relations";
38
+ /** Scalar keys kept in the result: only the ones in a `select` spec, all of them otherwise. */
39
+ type ResultScalarKeys<T, Spec, Mode extends SpecMode> = Mode extends "select" ? Spec extends object ? Extract<SpecKeys<Spec>, ScalarKeys<T>> : ScalarKeys<T> : ScalarKeys<T>;
40
+ /** Relation keys kept in the result: the relation keys present in the spec. */
41
+ type ResultRelationKeys<T, Spec> = Extract<SpecKeys<Spec>, RelationKeys<T>>;
42
+ /** Applies the shape to a relation field, preserving its array-ness and nullability. */
43
+ type MapRelation<F, Spec, Mode extends SpecMode> = F extends readonly (infer U)[] ? Shape<U, Spec, Mode>[] : F extends null | undefined ? F : Shape<F, Spec, Mode>;
44
+ /**
45
+ * Shape of a single entity `T` given a `select` spec (`Mode = "select"`) or
46
+ * a `relations` spec (`Mode = "relations"`).
47
+ *
48
+ * - `select` mode: only the fields present in the spec; a relation set to `true` brings all of
49
+ * its scalar fields (no nested relations), a nested object restricts it further.
50
+ * - `relations` mode: every scalar field plus the relations present in the spec (recursively).
51
+ * - No spec (`undefined`): every scalar field, no relations.
52
+ */
53
+ type Shape<T, Spec, Mode extends SpecMode> = Simplify<{
54
+ [K in keyof T as K extends ResultScalarKeys<T, Spec, Mode> ? K : never]: T[K];
55
+ } & {
56
+ [K in keyof T as K extends ResultRelationKeys<T, Spec> ? K : never]: MapRelation<T[K], SubSpec<Spec, K>, Mode>;
57
+ }>;
58
+ /**
59
+ * Resolves the shape of a single entity `T` (not an array, not nullable) from the method options.
60
+ * `select` takes precedence: when present, `relations` is ignored. If the option that drives
61
+ * the shape is not narrowed, its contents are unknown and `T` is returned unchanged.
62
+ */
63
+ type InferEntity<T, O> = OptionState<O, "select"> extends "known" ? Shape<T, NonNullable<Get<O, "select">>, "select"> : OptionState<O, "select"> extends "unknown" ? T : OptionState<O, "relations"> extends "known" ? Shape<T, NonNullable<Get<O, "relations">>, "relations"> : OptionState<O, "relations"> extends "unknown" ? T : Shape<T, undefined, "relations">;
64
+ /**
65
+ * Opt-in **strict** return typing: narrows the type returned by a repository method
66
+ * according to the `select` / `relations` that were actually passed to it.
67
+ *
68
+ * By default, `VSRepository` methods type their return as the whole entity, ignoring
69
+ * `select` and `relations` (the same approach TypeORM takes). Use this utility when you
70
+ * prefer a tighter type:
71
+ *
72
+ * - **no `select`/`relations`** — only the scalar fields of the entity (no relations);
73
+ * - **`relations`** — the scalar fields plus the requested relations (nested ones included);
74
+ * - **`select`** — only the selected fields. A relation selected with `true` brings all of its
75
+ * scalar fields; a nested `select` restricts it further. Relations selected this way are
76
+ * loaded even without `relations`.
77
+ *
78
+ * `select` takes precedence over `relations`: when `select` is present, `relations` is
79
+ * ignored by the type. This mirrors the Prisma 7 adapter and never promises fields an
80
+ * adapter might not return.
81
+ *
82
+ * `see` and `db` don't affect the result type.
83
+ *
84
+ * The first type argument is what the method returns: `Entity`, `Entity | null` or
85
+ * `Entity[]` — `null`/array-ness are preserved (also on relation fields, e.g.
86
+ * `address: Address | null`).
87
+ *
88
+ * The options must keep their literal type, so declare them with `satisfies MethodOptions<T>`
89
+ * (or inline). If the options are typed as plain `MethodOptions<T>`, nothing is known
90
+ * at compile time and the whole entity `T` is returned unchanged (the default typing).
91
+ *
92
+ * @template T What the method returns: `Entity`, `Entity | null` or `Entity[]`.
93
+ * @template Options The options object passed to the method (`typeof options`).
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * const options = {
98
+ * select: { id: true, name: true, products: { id: true } },
99
+ * } satisfies MethodOptions<User>;
100
+ *
101
+ * const users: InferMethodReturn<User[], typeof options> = await userRepository.getAll(options);
102
+ * // { id: string; name: string; products: { id: string }[] }[]
103
+ * ```
104
+ *
105
+ * @publicApi
106
+ */
107
+ export type InferMethodReturn<T, Options extends InferMethodReturnOptions = {}> = T extends readonly (infer U)[] ? InferEntity<U, Options>[] : T extends null | undefined ? T : InferEntity<T, Options>;
108
+ export {};
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=infer-method-return.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-method-return.type.js","sourceRoot":"","sources":["../../../src/types/utils/infer-method-return.type.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import { VSRepoOrmTypes } from "../vsrepo/vsrepo-orm-types.type";
2
+ import { VSRepoRelations } from "../vsrepo/vsrepo-relations.type";
3
+ import { VSRepoSelect } from "../vsrepo/vsrepo-select.type";
4
+ import { InferMethodReturn } from "./infer-method-return.type";
5
+ import { MethodOptions } from "./methods-options.type";
6
+ /** Entity type behind a method's return: `E`, `E | null`, `E[]` or `E[] | null` all give `E`. */
7
+ type EntityOf<R> = NonNullable<R> extends readonly (infer U)[] ? U : NonNullable<R>;
8
+ /**
9
+ * Deeply checks that a `select`/`relations` object only has keys that exist in the reference
10
+ * spec (`VSRepoSelect<E>`/`VSRepoRelations<E>`), turning unknown keys into `never`.
11
+ *
12
+ * Needed because a generic parameter (`Options` in {@link InferMethodType}) loses TypeScript's
13
+ * excess property check, so a typo like `select: { id: true, idd: true }` would otherwise be
14
+ * silently accepted.
15
+ *
16
+ * Non-object values (e.g. `true`) resolve to `unknown` (the identity of `&`): they are already
17
+ * validated by the `Options` constraint, and contributing nothing here keeps stray members of
18
+ * `boolean` (like `valueOf`) out of the editor's suggestions.
19
+ */
20
+ type ExactSpec<S, Ref> = S extends object ? [Extract<Ref, object>] extends [never] ? never : {
21
+ [K in keyof S]: K extends keyof Extract<Ref, object> ? ExactSpec<S[K], NonNullable<Extract<Ref, object>[K]>> : never;
22
+ } : unknown;
23
+ /** Applies {@link ExactSpec} to `select` and `relations`; `see`/`db` pass through, unknown keys become `never`. */
24
+ type ExactOptions<O, E> = {
25
+ [K in keyof O]: K extends "select" ? ExactSpec<O[K], VSRepoSelect<E>> : K extends "relations" ? ExactSpec<O[K], VSRepoRelations<E>> : K extends "see" | "db" ? O[K] : never;
26
+ };
27
+ /**
28
+ * Type for declaring a dynamic method whose return type is **strictly inferred** from the
29
+ * `select` / `relations` passed on each call (see {@link InferMethodReturn} for the exact rules).
30
+ *
31
+ * It has two call signatures:
32
+ * - `(...args) => Promise<...>` — no `options`: only the scalar fields of the entity;
33
+ * - `(...args, options?: MethodOptions<Entity, OrmTypes>) => Promise<...>` — the return is
34
+ * inferred from the `options` passed.
35
+ *
36
+ * `Entity` is taken from the return type (`Entity`, `Entity | null`, `Entity[]`, ...).
37
+ *
38
+ * The `options` parameter is always the last one, after every argument in `Args` (as in a
39
+ * regular dynamic method signature). Unknown keys in `select`/`relations` are rejected, just like
40
+ * with a plain `MethodOptions<Entity>` parameter, and the editor autocompletes them.
41
+ *
42
+ * Meant for dynamic methods that return entities. Methods that don't (`existsBy...`,
43
+ * `countBy...`, etc.) can keep their regular signature.
44
+ *
45
+ * @template Args Positional arguments of the method, without `options` (e.g. `[name: string]`).
46
+ * @template R What the method resolves to: `Entity`, `Entity | null` or `Entity[]`.
47
+ * @template K ORM type map (`dbClient`/`dbTransaction`) used to type the `db` option. Optional.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * class UserRepository extends VSRepository<User, string, MyOrmTypes> {
52
+ * @DynamicMethod()
53
+ * declare findByName: InferMethodType<[name: string], User[], MyOrmTypes>;
54
+ * }
55
+ *
56
+ * const users = await userRepository.findByName("John", { select: { id: true, name: true } });
57
+ * // { id: string; name: string }[]
58
+ * ```
59
+ *
60
+ * @publicApi
61
+ */
62
+ export type InferMethodType<Args extends unknown[], R, K extends VSRepoOrmTypes = VSRepoOrmTypes> = {
63
+ (...args: Args): Promise<InferMethodReturn<R, {}>>;
64
+ <Options extends MethodOptions<EntityOf<R>, K>>(...args: [...Args, options?: Options & ExactOptions<Options, EntityOf<R>>]): Promise<InferMethodReturn<R, Options>>;
65
+ };
66
+ export {};
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=infer-method-type.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-method-type.type.js","sourceRoot":"","sources":["../../../src/types/utils/infer-method-type.type.ts"],"names":[],"mappings":""}
@@ -9,16 +9,12 @@ export type SortDirection = "asc" | "desc" | "ASC" | "DESC";
9
9
  /**
10
10
  * Ordering shape for a single level of an entity's fields.
11
11
  *
12
- * Scalar fields accept a `SortDirection` directly; nested object (to-one
13
- * relation) fields accept a nested `Ordering`. Array (to-many relation)
14
- * fields are not orderable and are excluded.
15
- *
16
12
  * @template T Entity type being ordered.
17
13
  *
18
14
  * @publicApi
19
15
  */
20
16
  export type OrderByField<T> = {
21
- [P in keyof T]?: NonNullable<T[P]> extends Primitive ? SortDirection : NonNullable<T[P]> extends Array<any> ? never : NonNullable<T[P]> extends object ? Ordering<NonNullable<T[P]>> : SortDirection;
17
+ [P in keyof T as NonNullable<T[P]> extends Primitive ? P : never]?: SortDirection;
22
18
  };
23
19
  /**
24
20
  * Ordering accepted by repository methods that support `order`, such as `getAll` or a dynamic method with `Ordered`.
@@ -14,7 +14,7 @@ export type VSRepoOptions<T, K> = {
14
14
  /** Adapter that translates the repository's operations into calls against the underlying ORM/database. */
15
15
  adapter: VSRepoAdapter<T>;
16
16
  /** Name of the field that represents the entity's primary key (PK). */
17
- pkName: KeysOfType<T, K>;
17
+ pkName?: KeysOfType<T, K>;
18
18
  /**
19
19
  * Name of the field used for soft-delete.
20
20
  *
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "vsrepo",
3
- "version": "2.4.0",
4
- "description": "ORM-agnostic repository pattern library with full TypeScript support and automatic type inference.",
3
+ "version": "2.5.0",
4
+ "description": "ORM-agnostic repository pattern library",
5
5
  "homepage": "https://github.com/jaobrabo123/VSRepository#readme",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/jaobrabo123/VSRepository.git"
9
9
  },
10
+ "bugs": {
11
+ "email": "joaodev.azevedo@outlook.com",
12
+ "url": "https://github.com/jaobrabo123/VSRepoDrizzleAdapter/issues"
13
+ },
10
14
  "main": "./dist/index.js",
11
15
  "types": "./dist/index.d.ts",
12
16
  "exports": {
@@ -20,7 +24,8 @@
20
24
  "files": [
21
25
  "dist",
22
26
  "README.md",
23
- "README.pt-BR.md"
27
+ "README.pt-BR.md",
28
+ "CHANGELOG.md"
24
29
  ],
25
30
  "engines": {
26
31
  "node": ">=18"
@@ -38,6 +43,7 @@
38
43
  "keywords": [
39
44
  "prisma",
40
45
  "typeorm",
46
+ "drizzle",
41
47
  "repository",
42
48
  "orm",
43
49
  "typescript",