joist-orm 0.1.536 → 1.0.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.
Files changed (221) hide show
  1. package/build/{BaseEntity.d.ts → src/BaseEntity.d.ts} +2 -1
  2. package/build/{BaseEntity.js → src/BaseEntity.js} +13 -9
  3. package/build/src/BaseEntity.js.map +1 -0
  4. package/build/{EntityManager.d.ts → src/EntityManager.d.ts} +139 -110
  5. package/build/{EntityManager.js → src/EntityManager.js} +281 -262
  6. package/build/src/EntityManager.js.map +1 -0
  7. package/build/{QueryBuilder.d.ts → src/QueryBuilder.d.ts} +53 -3
  8. package/build/src/QueryBuilder.js +341 -0
  9. package/build/src/QueryBuilder.js.map +1 -0
  10. package/build/src/Todo.d.ts +25 -0
  11. package/build/src/Todo.js +52 -0
  12. package/build/src/Todo.js.map +1 -0
  13. package/build/src/changes.d.ts +34 -0
  14. package/build/src/changes.js +37 -0
  15. package/build/src/changes.js.map +1 -0
  16. package/build/src/config.d.ts +43 -0
  17. package/build/src/config.js +114 -0
  18. package/build/src/config.js.map +1 -0
  19. package/build/{createOrUpdatePartial.d.ts → src/createOrUpdatePartial.d.ts} +2 -1
  20. package/build/{createOrUpdatePartial.js → src/createOrUpdatePartial.js} +42 -10
  21. package/build/src/createOrUpdatePartial.js.map +1 -0
  22. package/build/src/dataloaders/findDataLoader.d.ts +5 -0
  23. package/build/src/dataloaders/findDataLoader.js +28 -0
  24. package/build/src/dataloaders/findDataLoader.js.map +1 -0
  25. package/build/src/dataloaders/loadDataLoader.d.ts +3 -0
  26. package/build/src/dataloaders/loadDataLoader.js +37 -0
  27. package/build/src/dataloaders/loadDataLoader.js.map +1 -0
  28. package/build/src/dataloaders/manyToManyDataLoader.d.ts +5 -0
  29. package/build/src/dataloaders/manyToManyDataLoader.js +78 -0
  30. package/build/src/dataloaders/manyToManyDataLoader.js.map +1 -0
  31. package/build/src/dataloaders/manyToManyFindDataLoader.d.ts +5 -0
  32. package/build/src/dataloaders/manyToManyFindDataLoader.js +33 -0
  33. package/build/src/dataloaders/manyToManyFindDataLoader.js.map +1 -0
  34. package/build/src/dataloaders/oneToManyDataLoader.d.ts +4 -0
  35. package/build/src/dataloaders/oneToManyDataLoader.js +40 -0
  36. package/build/src/dataloaders/oneToManyDataLoader.js.map +1 -0
  37. package/build/src/dataloaders/oneToManyFindDataLoader.d.ts +5 -0
  38. package/build/src/dataloaders/oneToManyFindDataLoader.js +32 -0
  39. package/build/src/dataloaders/oneToManyFindDataLoader.js.map +1 -0
  40. package/build/src/dataloaders/oneToOneDataLoader.d.ts +4 -0
  41. package/build/src/dataloaders/oneToOneDataLoader.js +40 -0
  42. package/build/src/dataloaders/oneToOneDataLoader.js.map +1 -0
  43. package/build/src/drivers/IdAssigner.d.ts +33 -0
  44. package/build/src/drivers/IdAssigner.js +106 -0
  45. package/build/src/drivers/IdAssigner.js.map +1 -0
  46. package/build/src/drivers/InMemoryDriver.d.ts +29 -0
  47. package/build/src/drivers/InMemoryDriver.js +306 -0
  48. package/build/src/drivers/InMemoryDriver.js.map +1 -0
  49. package/build/src/drivers/PostgresDriver.d.ts +40 -0
  50. package/build/src/drivers/PostgresDriver.js +376 -0
  51. package/build/src/drivers/PostgresDriver.js.map +1 -0
  52. package/build/src/drivers/driver.d.ts +23 -0
  53. package/build/src/drivers/driver.js +3 -0
  54. package/build/src/drivers/driver.js.map +1 -0
  55. package/build/src/drivers/index.d.ts +4 -0
  56. package/build/src/drivers/index.js +17 -0
  57. package/build/src/drivers/index.js.map +1 -0
  58. package/build/{getProperties.d.ts → src/getProperties.d.ts} +0 -0
  59. package/build/{getProperties.js → src/getProperties.js} +1 -1
  60. package/build/src/getProperties.js.map +1 -0
  61. package/build/src/index.d.ts +62 -0
  62. package/build/src/index.js +263 -0
  63. package/build/src/index.js.map +1 -0
  64. package/build/src/keys.d.ts +30 -0
  65. package/build/{keys.js → src/keys.js} +48 -16
  66. package/build/src/keys.js.map +1 -0
  67. package/build/{loadLens.d.ts → src/loadLens.d.ts} +2 -2
  68. package/build/{loadLens.js → src/loadLens.js} +1 -1
  69. package/build/src/loadLens.js.map +1 -0
  70. package/build/src/loaded.d.ts +49 -0
  71. package/build/src/loaded.js +9 -0
  72. package/build/src/loaded.js.map +1 -0
  73. package/build/{newTestInstance.d.ts → src/newTestInstance.d.ts} +37 -3
  74. package/build/src/newTestInstance.js +342 -0
  75. package/build/src/newTestInstance.js.map +1 -0
  76. package/build/{collections → src/relations}/AbstractRelationImpl.d.ts +6 -5
  77. package/build/{collections → src/relations}/AbstractRelationImpl.js +0 -0
  78. package/build/src/relations/AbstractRelationImpl.js.map +1 -0
  79. package/build/src/relations/Collection.d.ts +26 -0
  80. package/build/src/relations/Collection.js +19 -0
  81. package/build/src/relations/Collection.js.map +1 -0
  82. package/build/{collections → src/relations}/CustomCollection.d.ts +6 -2
  83. package/build/{collections → src/relations}/CustomCollection.js +17 -9
  84. package/build/src/relations/CustomCollection.js.map +1 -0
  85. package/build/{collections → src/relations}/CustomReference.d.ts +7 -2
  86. package/build/{collections → src/relations}/CustomReference.js +16 -9
  87. package/build/src/relations/CustomReference.js.map +1 -0
  88. package/build/src/relations/LargeCollection.d.ts +17 -0
  89. package/build/src/relations/LargeCollection.js +3 -0
  90. package/build/src/relations/LargeCollection.js.map +1 -0
  91. package/build/{collections → src/relations}/ManyToManyCollection.d.ts +9 -2
  92. package/build/src/relations/ManyToManyCollection.js +249 -0
  93. package/build/src/relations/ManyToManyCollection.js.map +1 -0
  94. package/build/src/relations/ManyToManyLargeCollection.d.ts +25 -0
  95. package/build/src/relations/ManyToManyLargeCollection.js +97 -0
  96. package/build/src/relations/ManyToManyLargeCollection.js.map +1 -0
  97. package/build/src/relations/ManyToOneReference.d.ts +77 -0
  98. package/build/{collections → src/relations}/ManyToOneReference.js +101 -48
  99. package/build/src/relations/ManyToOneReference.js.map +1 -0
  100. package/build/{collections → src/relations}/OneToManyCollection.d.ts +10 -2
  101. package/build/{collections → src/relations}/OneToManyCollection.js +54 -59
  102. package/build/src/relations/OneToManyCollection.js.map +1 -0
  103. package/build/src/relations/OneToManyLargeCollection.d.ts +25 -0
  104. package/build/src/relations/OneToManyLargeCollection.js +83 -0
  105. package/build/src/relations/OneToManyLargeCollection.js.map +1 -0
  106. package/build/src/relations/OneToOneReference.d.ts +82 -0
  107. package/build/src/relations/OneToOneReference.js +168 -0
  108. package/build/src/relations/OneToOneReference.js.map +1 -0
  109. package/build/src/relations/PolymorphicReference.d.ts +69 -0
  110. package/build/src/relations/PolymorphicReference.js +210 -0
  111. package/build/src/relations/PolymorphicReference.js.map +1 -0
  112. package/build/src/relations/Reference.d.ts +29 -0
  113. package/build/src/relations/Reference.js +23 -0
  114. package/build/src/relations/Reference.js.map +1 -0
  115. package/build/src/relations/Relation.d.ts +10 -0
  116. package/build/src/relations/Relation.js +13 -0
  117. package/build/src/relations/Relation.js.map +1 -0
  118. package/build/src/relations/hasAsyncProperty.d.ts +36 -0
  119. package/build/src/relations/hasAsyncProperty.js +55 -0
  120. package/build/src/relations/hasAsyncProperty.js.map +1 -0
  121. package/build/{collections → src/relations}/hasManyDerived.d.ts +2 -1
  122. package/build/{collections → src/relations}/hasManyDerived.js +1 -1
  123. package/build/src/relations/hasManyDerived.js.map +1 -0
  124. package/build/{collections → src/relations}/hasManyThrough.d.ts +0 -0
  125. package/build/{collections → src/relations}/hasManyThrough.js +2 -2
  126. package/build/src/relations/hasManyThrough.js.map +1 -0
  127. package/build/{collections → src/relations}/hasOneDerived.d.ts +3 -2
  128. package/build/{collections → src/relations}/hasOneDerived.js +1 -1
  129. package/build/src/relations/hasOneDerived.js.map +1 -0
  130. package/build/{collections → src/relations}/hasOneThrough.d.ts +0 -0
  131. package/build/{collections → src/relations}/hasOneThrough.js +2 -2
  132. package/build/src/relations/hasOneThrough.js.map +1 -0
  133. package/build/src/relations/index.d.ts +18 -0
  134. package/build/src/relations/index.js +53 -0
  135. package/build/src/relations/index.js.map +1 -0
  136. package/build/{reverseHint.d.ts → src/reverseHint.d.ts} +2 -1
  137. package/build/{reverseHint.js → src/reverseHint.js} +13 -9
  138. package/build/src/reverseHint.js.map +1 -0
  139. package/build/src/rules.d.ts +23 -0
  140. package/build/src/rules.js +23 -0
  141. package/build/src/rules.js.map +1 -0
  142. package/build/src/serde.d.ts +121 -0
  143. package/build/src/serde.js +190 -0
  144. package/build/src/serde.js.map +1 -0
  145. package/build/{utils.d.ts → src/utils.d.ts} +2 -0
  146. package/build/{utils.js → src/utils.js} +10 -1
  147. package/build/src/utils.js.map +1 -0
  148. package/build/tsconfig.tsbuildinfo +1 -0
  149. package/package.json +30 -15
  150. package/build/BaseEntity.js.map +0 -1
  151. package/build/EntityManager.js.map +0 -1
  152. package/build/EntityPersister.d.ts +0 -30
  153. package/build/EntityPersister.js +0 -197
  154. package/build/EntityPersister.js.map +0 -1
  155. package/build/QueryBuilder.js +0 -195
  156. package/build/QueryBuilder.js.map +0 -1
  157. package/build/changes.d.ts +0 -23
  158. package/build/changes.js +0 -14
  159. package/build/changes.js.map +0 -1
  160. package/build/collections/AbstractRelationImpl.js.map +0 -1
  161. package/build/collections/CustomCollection.js.map +0 -1
  162. package/build/collections/CustomReference.js.map +0 -1
  163. package/build/collections/ManyToManyCollection.js +0 -288
  164. package/build/collections/ManyToManyCollection.js.map +0 -1
  165. package/build/collections/ManyToOneReference.d.ts +0 -50
  166. package/build/collections/ManyToOneReference.js.map +0 -1
  167. package/build/collections/OneToManyCollection.js.map +0 -1
  168. package/build/collections/OneToOneReference.d.ts +0 -51
  169. package/build/collections/OneToOneReference.js +0 -132
  170. package/build/collections/OneToOneReference.js.map +0 -1
  171. package/build/collections/hasManyDerived.js.map +0 -1
  172. package/build/collections/hasManyThrough.js.map +0 -1
  173. package/build/collections/hasOneDerived.js.map +0 -1
  174. package/build/collections/hasOneThrough.js.map +0 -1
  175. package/build/collections/index.d.ts +0 -19
  176. package/build/collections/index.js +0 -49
  177. package/build/collections/index.js.map +0 -1
  178. package/build/createOrUpdatePartial.js.map +0 -1
  179. package/build/getProperties.js.map +0 -1
  180. package/build/index.d.ts +0 -140
  181. package/build/index.js +0 -278
  182. package/build/index.js.map +0 -1
  183. package/build/keys.d.ts +0 -21
  184. package/build/keys.js.map +0 -1
  185. package/build/loadLens.js.map +0 -1
  186. package/build/newTestInstance.js +0 -153
  187. package/build/newTestInstance.js.map +0 -1
  188. package/build/reverseHint.js.map +0 -1
  189. package/build/serde.d.ts +0 -47
  190. package/build/serde.js +0 -93
  191. package/build/serde.js.map +0 -1
  192. package/build/utils.js.map +0 -1
  193. package/package.json.bak +0 -27
  194. package/src/BaseEntity.ts +0 -104
  195. package/src/EntityManager.ts +0 -1263
  196. package/src/EntityPersister.ts +0 -240
  197. package/src/QueryBuilder.ts +0 -289
  198. package/src/changes.ts +0 -40
  199. package/src/collections/AbstractRelationImpl.ts +0 -28
  200. package/src/collections/CustomCollection.ts +0 -152
  201. package/src/collections/CustomReference.ts +0 -138
  202. package/src/collections/ManyToManyCollection.ts +0 -346
  203. package/src/collections/ManyToOneReference.ts +0 -215
  204. package/src/collections/OneToManyCollection.ts +0 -254
  205. package/src/collections/OneToOneReference.ts +0 -153
  206. package/src/collections/hasManyDerived.ts +0 -29
  207. package/src/collections/hasManyThrough.ts +0 -20
  208. package/src/collections/hasOneDerived.ts +0 -26
  209. package/src/collections/hasOneThrough.ts +0 -20
  210. package/src/collections/index.ts +0 -74
  211. package/src/createOrUpdatePartial.ts +0 -144
  212. package/src/getProperties.ts +0 -27
  213. package/src/index.ts +0 -400
  214. package/src/keys.ts +0 -75
  215. package/src/loadLens.ts +0 -126
  216. package/src/newTestInstance.ts +0 -205
  217. package/src/reverseHint.ts +0 -43
  218. package/src/serde.ts +0 -97
  219. package/src/utils.ts +0 -63
  220. package/tsconfig.json +0 -21
  221. package/tsconfig.tsbuildinfo +0 -2646
package/src/keys.ts DELETED
@@ -1,75 +0,0 @@
1
- import { getMetadata, Entity } from "./EntityManager";
2
- import { BaseEntity } from "./BaseEntity";
3
-
4
- const tagDelimiter = ":";
5
-
6
- // I'm not entirely sure this is still necessary, but use a small subset of EntityMetadata so
7
- // that this file doesn't have to import the type and potentially create import cycles.
8
- type HasTagName = { tagName: string };
9
-
10
- // Before a referred-to object is saved, we keep its instance in our data
11
- // map, and then assume it will be persisted before we're asked to persist
12
- export function maybeResolveReferenceToId(value: any): string | undefined {
13
- return typeof value === "number" || typeof value === "string" ? value : value?.id;
14
- }
15
-
16
- /** Converts `value` to a number, i.e. for string ids, unless its undefined. */
17
- export function keyToNumber(meta: HasTagName, value: string): number;
18
- export function keyToNumber(meta: HasTagName, value: any): number | undefined;
19
- export function keyToNumber(meta: HasTagName, value: any): number | undefined {
20
- if (value === undefined || value === null) {
21
- return undefined;
22
- } else if (typeof value === "number") {
23
- return value;
24
- } else if (typeof value === "string") {
25
- const [tag, id] = value.split(tagDelimiter);
26
- if (id === undefined) {
27
- return Number(value);
28
- }
29
- if (tag !== meta.tagName) {
30
- throw new Error(`Invalid tagged id, expected tag ${meta.tagName}, got ${value}`);
31
- }
32
- return Number(id);
33
- } else {
34
- throw new Error(`Invalid key ${value}`);
35
- }
36
- }
37
-
38
- /** Converts `value` to a number, i.e. for string ids, unles its undefined. */
39
- export function keyToString(meta: HasTagName, value: any): string | undefined {
40
- return value === undefined || value === null ? undefined : `${meta.tagName}:${value}`;
41
- }
42
-
43
- /** Fails if any keys are tagged; used by internal functions b/c we still allow most direct API input to be untagged. */
44
- export function assertIdsAreTagged(keys: readonly string[]): void {
45
- const invalidKeys = keys.filter((k) => k.indexOf(tagDelimiter) === -1);
46
- if (invalidKeys.length > 0) {
47
- throw new Error(`Some keys are missing tags ${invalidKeys}`);
48
- }
49
- }
50
-
51
- /** Tags a potentially untagged id, while our API inputs still accept either tagged or untagged ids. */
52
- export function tagIfNeeded(meta: HasTagName, id: string): string {
53
- if (id.includes(tagDelimiter)) {
54
- return id;
55
- }
56
- return `${meta.tagName}${tagDelimiter}${id}`;
57
- }
58
-
59
- /** Removes the tag prefixes so we can use the keys for SQL operations. */
60
- export function deTagIds(meta: HasTagName, keys: readonly string[]): readonly string[] {
61
- return keys.map((k) => deTagId(meta, k));
62
- }
63
-
64
- export function deTagId(meta: HasTagName, id: string): string;
65
- export function deTagId(entity: Entity): string;
66
- export function deTagId(entityOrMeta: Entity | HasTagName, id?: string): string {
67
- const meta = entityOrMeta instanceof BaseEntity ? getMetadata(entityOrMeta) : (entityOrMeta as HasTagName);
68
- id = id ?? (entityOrMeta as Entity).id;
69
- return keyToNumber(meta, id!).toString();
70
- }
71
-
72
- /** Removes the tag prefixes so we can use the keys for SQL operations. */
73
- export function unsafeDeTagIds(keys: readonly string[]): readonly string[] {
74
- return keys.map((k) => k.split(tagDelimiter)).map((t) => (t.length === 0 ? t[0] : t[1]));
75
- }
package/src/loadLens.ts DELETED
@@ -1,126 +0,0 @@
1
- /** Generically matches on a Reference/Collection's load method. */
2
- type LoadLike<U> = { load(): Promise<U> };
3
-
4
- /** Generically matches a property or zero-arg method that returns a promise. */
5
- type PromiseFnLike<U> = () => PromiseLike<U>;
6
-
7
- /** Given a parent type U, and a new type V, returns V[] if U is already an array, i.e. we're in flatMap mode. */
8
- type MaybeArray<U, V> = U extends ReadonlyArray<any> ? (V extends ReadonlyArray<any> ? V : V[]) : V;
9
-
10
- /** Given a type T that we come across in the path, de-array it to continue our flatMap-ish semantics. */
11
- type MaybeDropArray<T> = T extends ReadonlyArray<infer U> ? U : T;
12
-
13
- type DropUndefined<T> = Exclude<T, undefined>;
14
-
15
- /**
16
- * A type for declaratively walking the object graph.
17
- *
18
- * This is not technically a Lens that can `get/set`, but the idea is generally the same.
19
- *
20
- * `T` is the type we're on, i.e. when `T = Author` we can do `.firstName` or `.lastName.
21
- * `R` is either a `T` or `T[]` or `T | undefined` and just captures whether we've hit a
22
- * list at any point in the lens navigation path.
23
- */
24
- export type Lens<T, R = T> = {
25
- [P in keyof T]: T[P] extends LoadLike<infer U>
26
- ? Lens<MaybeDropArray<DropUndefined<U>>, MaybeArray<R, U>>
27
- : T[P] extends PromiseFnLike<infer U>
28
- ? Lens<MaybeDropArray<DropUndefined<U>>, MaybeArray<R, U>>
29
- : Lens<MaybeDropArray<DropUndefined<T[P]>>, MaybeArray<R, T[P]>>;
30
- };
31
-
32
- /**
33
- * Allows declaratively loading/traversing several layers of references at once.
34
- *
35
- * I.e.:
36
- *
37
- * ```typescript
38
- * const publisher = await book.load(b => b.author.publisher);
39
- * ```
40
- */
41
- // For some reason accepting Lens<this, this> does not work when called from within the entity
42
- // subclass itself, so we use the codegen hammer in our subclass to force the right Lens type
43
- // in a .load stub that just calls us for the implementation.
44
- export async function loadLens<T, U, V>(start: T, fn: (lens: Lens<T>) => Lens<U, V>): Promise<V> {
45
- const paths = collectPaths(fn);
46
- let current: any = start;
47
- // Now evaluate each step of the path
48
- for await (const path of paths) {
49
- if (Array.isArray(current)) {
50
- current = (await Promise.all(current.map((c) => maybeLoad(c, path)))).flat();
51
- current = [...new Set(current)];
52
- } else {
53
- current = await maybeLoad(current, path);
54
- }
55
- }
56
- return current!;
57
- }
58
-
59
- function maybeLoad(object: any, path: string): unknown {
60
- if (object === undefined || object === null) {
61
- return undefined;
62
- }
63
- const value = object[path];
64
- if (value && typeof value === "object" && "load" in value) {
65
- return value.load();
66
- } else if (value && typeof value === "function") {
67
- return value.apply(object);
68
- } else {
69
- return value;
70
- }
71
- }
72
-
73
- /**
74
- * The synchronous version of `loadLens`.
75
- *
76
- * This assumes you've first evaluated the lens with `loadLens` and now can access it synchronously.
77
- */
78
- export function getLens<T, U, V>(start: T, fn: (lens: Lens<T>) => Lens<U, V>): V {
79
- const paths = collectPaths(fn);
80
- let current: any = start;
81
- // Now evaluate each step of the path
82
- for (const path of paths) {
83
- if (Array.isArray(current)) {
84
- current = current.map((c) => maybeGet(c, path)).flat();
85
- current = [...new Set(current)];
86
- } else {
87
- current = maybeGet(current, path);
88
- }
89
- }
90
- return current!;
91
- }
92
-
93
- function maybeGet(object: any, path: string): unknown {
94
- if (object === undefined || object === null) {
95
- return undefined;
96
- }
97
- const value = object[path];
98
- if (value && typeof value === "object" && "get" in value) {
99
- if (typeof value.get === "function") {
100
- return value.get();
101
- } else {
102
- return value.get;
103
- }
104
- } else if (value && typeof value === "function") {
105
- return value.apply(object);
106
- } else {
107
- return value;
108
- }
109
- }
110
-
111
- function collectPaths(fn: Function): string[] {
112
- const paths: string[] = [];
113
- // The proxy collects the path navigations that the user's `fn` lambda invokes.
114
- const proxy = new Proxy(
115
- {},
116
- {
117
- get(object, property, receiver) {
118
- paths.push(String(property));
119
- return receiver;
120
- },
121
- },
122
- );
123
- // Invoke the lens function to record the navigation path on our proxy
124
- fn(proxy as any);
125
- return paths;
126
- }
@@ -1,205 +0,0 @@
1
- import isPlainObject from "is-plain-object";
2
- import {
3
- ActualFactoryOpts,
4
- Entity,
5
- EntityConstructor,
6
- EntityManager,
7
- getMetadata,
8
- IdOf,
9
- isEntity,
10
- New,
11
- OptsOf,
12
- PrimitiveField,
13
- } from "./EntityManager";
14
- import { tagIfNeeded } from "./keys";
15
- import { fail } from "./utils";
16
-
17
- /**
18
- * DeepPartial-esque type specific to our `newTestInstance` factory.
19
- *
20
- * Specifically (vs using DeepPartial directly) this:
21
- *
22
- * 1. Adds a `use` tag of `Entity | Entity[]` that will be checked for existing
23
- * entities before creating new ones (i.e. "if `newTestInstance` needs a `Book`,
24
- * use the one in `use` instead of making a new one).
25
- *
26
- * 2. Works specifically against the constructor/entity opts fields.
27
- */
28
- export type FactoryOpts<T extends Entity> = DeepPartialOpts<T> & { use?: Entity | Entity[] };
29
-
30
- // Chosen b/c it's a monday https://www.timeanddate.com/calendar/monthly.html?year=2018&month=1&country=1
31
- export const jan1 = new Date(2018, 0, 1);
32
- export let testDate = jan1;
33
-
34
- /** Creates a test instance of `T`. */
35
- export function newTestInstance<T extends Entity>(
36
- em: EntityManager,
37
- cstr: EntityConstructor<T>,
38
- opts: FactoryOpts<T> = {},
39
- ): New<T> {
40
- const meta = getMetadata(cstr);
41
-
42
- // fullOpts will end up being a full/type-safe opts with every required field
43
- // filled in, either driven by the passed-in opts or by making new entities as-needed
44
- const fullOpts = Object.fromEntries(
45
- meta.fields
46
- .map((field) => {
47
- const { fieldName } = field;
48
-
49
- // Use the opts value if they passed one in
50
- if (fieldName in opts) {
51
- const optValue = (opts as any)[fieldName];
52
-
53
- // Watch for our "the parent is not yet created" null marker
54
- if (optValue === null) {
55
- return [];
56
- }
57
-
58
- // If this is a partial with defaults for the entity, call newTestInstance to get it created
59
- if (field.kind === "m2o") {
60
- if (isEntity(optValue)) {
61
- return [fieldName, optValue];
62
- } else if (optValue && typeof optValue === "string") {
63
- return [
64
- fieldName,
65
- em.entities.find((e) => e.id === optValue || getTestId(em, e) === optValue) ||
66
- fail(`Did not find tagged id ${optValue}`),
67
- ];
68
- } else if (optValue && !isPlainObject(optValue)) {
69
- return field.otherMetadata().factory(em, opts);
70
- }
71
- return [fieldName, field.otherMetadata().factory(em, { ...optValue, use: opts.use })];
72
- }
73
-
74
- // If this is a list of children, watch for partials that should be newTestInstance'd
75
- if (field.kind === "o2m" || field.kind == "m2m") {
76
- const values = (optValue as Array<any>).map((optValue) => {
77
- if (isEntity(optValue)) {
78
- return optValue;
79
- } else if (optValue && typeof optValue === "string") {
80
- return (
81
- em.entities.find((e) => e.id === optValue || getTestId(em, e) === optValue) ||
82
- fail(`Did not find tagged id ${optValue}`)
83
- );
84
- } else if (optValue && !isPlainObject(optValue)) {
85
- return field.otherMetadata().factory(em, optValue);
86
- }
87
- return field.otherMetadata().factory(em, {
88
- ...optValue,
89
- // We include null as a marker for "don't create the parent"; even if it's required,
90
- // once the child has been created, the act of adding it to our collection will get the
91
- // parent set. It might be better to do o2ms as a 2nd-pass, after we've done the em.create
92
- // call and could directly pass this entity instead of null.
93
- [field.otherFieldName]: null,
94
- use: opts.use,
95
- });
96
- });
97
- return [fieldName, values];
98
- }
99
-
100
- // Look for strings that want to use the test index
101
- if (typeof optValue === "string" && optValue.includes(testIndex)) {
102
- const actualIndex = getTestIndex(em, meta.cstr);
103
- return [fieldName, optValue.replace(testIndex, String(actualIndex))];
104
- }
105
-
106
- // Otherwise just use the user's opt value as-is
107
- return [fieldName, optValue];
108
- }
109
-
110
- if (field.kind === "primitive" && field.required && !field.derived && !field.protected) {
111
- return [fieldName, defaultValue(field)];
112
- } else if (field.kind === "m2o") {
113
- const otherMeta = field.otherMetadata();
114
-
115
- // If there is a single existing instance of this type, assume the caller is fine with that,
116
- // even if the field is not required.
117
- const existing = em.entities.filter((e) => e instanceof otherMeta.cstr);
118
- if (existing.length === 1) {
119
- return [fieldName, existing[0]];
120
- }
121
-
122
- // If there is a use type, assume the caller is fine with that, even if the field is not required
123
- const useEntity = maybeArray(opts.use)?.find((e) => e instanceof otherMeta.cstr);
124
- if (useEntity) {
125
- return [fieldName, useEntity];
126
- }
127
-
128
- // Otherwise only make a new entity only if the field is required
129
- if (field.required) {
130
- return [fieldName, otherMeta.factory(em, { use: opts.use })];
131
- }
132
- } else if (field.kind === "enum" && field.required) {
133
- return [fieldName, field.enumDetailType.getValues()[0]];
134
- }
135
- return [];
136
- })
137
- .filter((t) => t.length > 0),
138
- );
139
-
140
- return em.create(meta.cstr, fullOpts as any) as New<T>;
141
- }
142
-
143
- /**
144
- * A marker value for later replacement with the test instance's "unique-ish" index.
145
- *
146
- * This is meant to just be a helpful identifier in fields like entity names/descriptions for
147
- * debugging purposes.
148
- */
149
- export const testIndex = "TEST_INDEX";
150
-
151
- /**
152
- * Returns a unique-ish test index for putting in `name` fields.
153
- *
154
- * Note that `testIndex` is easier to just include in a string, because it doesn't require passing
155
- * the `EntityManger` and `type`. But if a factory really wants the test index as a number, they can
156
- * call this method.
157
- *
158
- * Despite the name, these are 1-based, i.e. the first `Author` is `a1`.
159
- */
160
- export function getTestIndex<T extends Entity>(em: EntityManager, type: EntityConstructor<T>): number {
161
- const existing = em.entities.filter((e) => e instanceof type);
162
- return existing.length + 1;
163
- }
164
-
165
- /** Fakes a probably-right id for un-persisted entities. Solely used for quick lookups in tests/factories. */
166
- function getTestId<T extends Entity>(em: EntityManager, entity: T): string {
167
- const meta = getMetadata(entity);
168
- const sameType = em.entities.filter((e) => e instanceof meta.cstr);
169
- return tagIfNeeded(meta, String(sameType.indexOf(entity) + 1));
170
- }
171
-
172
- function defaultValue(field: PrimitiveField): unknown {
173
- switch (field.type) {
174
- case "string":
175
- return field.fieldName;
176
- case "number":
177
- return 0;
178
- case "Date":
179
- return testDate;
180
- case "boolean":
181
- return false;
182
- }
183
- return null;
184
- }
185
-
186
- // Utility type to destructure T out of T | undefined
187
- type DefinedOr<T> = T | undefined;
188
-
189
- type DeepPartialOpts<T extends Entity> = AllowRelationsOrPartials<OptsOf<T>>;
190
-
191
- type AllowRelationsOrPartials<T> = {
192
- [P in keyof T]?: T[P] extends DefinedOr<infer U>
193
- ? U extends Array<infer V>
194
- ? V extends Entity
195
- ? Array<V | IdOf<V> | ActualFactoryOpts<V>>
196
- : T[P]
197
- : U extends Entity
198
- ? U | IdOf<U> | ActualFactoryOpts<U>
199
- : T[P]
200
- : T[P];
201
- };
202
-
203
- function maybeArray<T>(array: T | T[] | undefined): T[] | undefined {
204
- return array ? (Array.isArray(array) ? array : [array]) : undefined;
205
- }
@@ -1,43 +0,0 @@
1
- import { Entity, EntityConstructor, getMetadata, LoadHint } from "./EntityManager";
2
- import { fail } from "./utils";
3
-
4
- /**
5
- * Given a load hint of "given an entity, load these N things", return an array
6
- * of what those N things are, and reversed load hints to "come back" to the
7
- * original entity.
8
- */
9
- export function reverseHint<T extends Entity>(
10
- entityType: EntityConstructor<T>,
11
- hint: LoadHint<T>,
12
- ): [EntityConstructor<any>, string[]][] {
13
- if (typeof hint === "string") {
14
- // For a simple string hint, i.e. Book "author", find the Book.author field,
15
- // and use the metdata to find the otherFieldName, i.e. Author "books"
16
- const meta = getMetadata(entityType);
17
- const field = meta.fields.find((f) => f.fieldName === hint) || fail(`Invalid hint ${JSON.stringify(hint)}`);
18
- if (field.kind !== "m2m" && field.kind !== "m2o" && field.kind !== "o2m") {
19
- throw new Error("Invalid hint");
20
- }
21
- const otherMeta = field.otherMetadata();
22
- return [[otherMeta.cstr, [field.otherFieldName]]];
23
- } else if (Array.isArray(hint)) {
24
- // For an array of string hints, i.e. Author "authors" "books", recurse
25
- // into each individual hint (i.e. the logic right above this) and then
26
- // combine/flatten them.
27
- return (hint as string[]).map((hint) => reverseHint(entityType, hint as any)).flat();
28
- } else {
29
- // For a hash of hints, i.e. Author { books: reviews }, recurse for each
30
- // key in the hash, and then combine
31
- return Object.entries(hint).flatMap(([key, hint]) => {
32
- const meta = getMetadata(entityType);
33
- const field = meta.fields.find((f) => f.fieldName === key) || fail(`Invalid hint ${JSON.stringify(hint)}`);
34
- if (field.kind !== "m2m" && field.kind !== "m2o" && field.kind !== "o2m") {
35
- throw new Error("Invalid hint");
36
- }
37
- const otherMeta = field.otherMetadata();
38
- return reverseHint(otherMeta.cstr, hint).map(([e, hint]) => {
39
- return [e, [...hint, field.otherFieldName]] as [EntityConstructor<any>, string[]];
40
- });
41
- });
42
- }
43
- }
package/src/serde.ts DELETED
@@ -1,97 +0,0 @@
1
- import { EntityMetadata, keyToNumber, keyToString, maybeResolveReferenceToId } from "./index";
2
-
3
- export interface ColumnSerde {
4
- setOnEntity(data: any, row: any): void;
5
-
6
- setOnRow(data: any, row: any): void;
7
-
8
- getFromEntity(data: any): any;
9
-
10
- mapToDb(value: any): any;
11
- }
12
-
13
- export class SimpleSerde implements ColumnSerde {
14
- constructor(private fieldName: string, private columnName: string) {}
15
-
16
- setOnEntity(data: any, row: any): void {
17
- data[this.fieldName] = maybeNullToUndefined(row[this.columnName]);
18
- }
19
-
20
- setOnRow(data: any, row: any): void {
21
- row[this.columnName] = data[this.fieldName];
22
- }
23
-
24
- getFromEntity(data: any) {
25
- return data[this.fieldName];
26
- }
27
-
28
- mapToDb(value: any) {
29
- return value;
30
- }
31
- }
32
-
33
- /** Maps integer primary keys ot strings "because GraphQL". */
34
- export class PrimaryKeySerde implements ColumnSerde {
35
- constructor(private meta: () => EntityMetadata<any>, private fieldName: string, private columnName: string) {}
36
-
37
- setOnEntity(data: any, row: any): void {
38
- data[this.fieldName] = keyToString(this.meta(), row[this.columnName]);
39
- }
40
-
41
- setOnRow(data: any, row: any): void {
42
- row[this.columnName] = keyToNumber(this.meta(), data[this.fieldName]);
43
- }
44
-
45
- getFromEntity(data: any) {
46
- return keyToNumber(this.meta(), data[this.fieldName]);
47
- }
48
-
49
- mapToDb(value: any) {
50
- return keyToNumber(this.meta(), value);
51
- }
52
- }
53
-
54
- export class ForeignKeySerde implements ColumnSerde {
55
- // TODO EntityMetadata being in here is weird.
56
- constructor(private fieldName: string, private columnName: string, public otherMeta: () => EntityMetadata<any>) {}
57
-
58
- setOnEntity(data: any, row: any): void {
59
- data[this.fieldName] = keyToString(this.otherMeta(), row[this.columnName]);
60
- }
61
-
62
- setOnRow(data: any, row: any): void {
63
- row[this.columnName] = keyToNumber(this.otherMeta(), maybeResolveReferenceToId(data[this.fieldName]));
64
- }
65
-
66
- getFromEntity(data: any) {
67
- return keyToNumber(this.otherMeta(), maybeResolveReferenceToId(data[this.fieldName]));
68
- }
69
-
70
- mapToDb(value: any): any {
71
- return keyToNumber(this.otherMeta(), maybeResolveReferenceToId(value));
72
- }
73
- }
74
-
75
- export class EnumFieldSerde implements ColumnSerde {
76
- constructor(private fieldName: string, private columnName: string, private enumObject: any) {}
77
-
78
- setOnEntity(data: any, row: any): void {
79
- data[this.fieldName] = this.enumObject.findById(row[this.columnName])?.code;
80
- }
81
-
82
- setOnRow(data: any, row: any): void {
83
- row[this.columnName] = this.enumObject.findByCode(data[this.fieldName])?.id;
84
- }
85
-
86
- getFromEntity(data: any) {
87
- return this.enumObject.findByCode(data[this.fieldName])?.id;
88
- }
89
-
90
- mapToDb(value: any) {
91
- return this.enumObject.findByCode(value)?.id;
92
- }
93
- }
94
-
95
- function maybeNullToUndefined(value: any): any {
96
- return value === null ? undefined : value;
97
- }
package/src/utils.ts DELETED
@@ -1,63 +0,0 @@
1
- export function getOrSet<T extends Record<keyof unknown, unknown>>(
2
- record: T,
3
- key: keyof T,
4
- defaultValue: T[keyof T] | (() => T[keyof T]),
5
- ): T[keyof T] {
6
- if (record[key] === undefined) {
7
- record[key] = defaultValue instanceof Function ? defaultValue() : defaultValue;
8
- }
9
- return record[key];
10
- }
11
-
12
- export function fail(message?: string): never {
13
- throw new Error(message || "Failed");
14
- }
15
-
16
- export function remove<T>(array: T[], t: T): void {
17
- const index = array.indexOf(t);
18
- if (index > -1) {
19
- array.splice(index, 1);
20
- }
21
- }
22
-
23
- /** Returns 0 inclusive to n exclusive. */
24
- export function zeroTo(n: number): number[] {
25
- return [...Array(n).keys()];
26
- }
27
-
28
- export function groupBy<T, Y = T>(list: T[], fn: (x: T) => string, valueFn?: (x: T) => Y): Map<string, Y[]> {
29
- const result = new Map<string, Y[]>();
30
- list.forEach((o) => {
31
- const group = fn(o);
32
- if (!result.has(group)) {
33
- result.set(group, []);
34
- }
35
- result.get(group)!.push(valueFn === undefined ? ((o as any) as Y) : valueFn(o));
36
- });
37
- return result;
38
- }
39
-
40
- export function indexBy<T, Y = T>(list: T[], fn: (x: T) => string, valueFn?: (x: T) => Y): Map<string, Y> {
41
- const result = new Map<string, Y>();
42
- list.forEach((o) => {
43
- const group = fn(o);
44
- result.set(group, valueFn === undefined ? ((o as any) as Y) : valueFn(o));
45
- });
46
- return result;
47
- }
48
-
49
- export function partition<T>(array: ReadonlyArray<T>, f: (el: T) => boolean): [T[], T[]] {
50
- const trueElements: T[] = [];
51
- const falseElements: T[] = [];
52
- array.forEach((el) => {
53
- if (f(el)) {
54
- trueElements.push(el);
55
- } else {
56
- falseElements.push(el);
57
- }
58
- });
59
- return [trueElements, falseElements];
60
- }
61
-
62
- // Utility type to strip off null and defined and infer only T.
63
- export type NullOrDefinedOr<T> = T | null | undefined;
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2019",
4
- "module": "commonjs",
5
- "lib": ["es2019"],
6
- "strict": true,
7
- "noImplicitReturns": true,
8
- "types": ["jest", "node"],
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "composite": true,
12
- "sourceMap": true,
13
- "outDir": "./build",
14
- "rootDir": "./src",
15
- "baseUrl": "./src",
16
- "paths": {
17
- "joist-utils": ["../../utils/src"]
18
- }
19
- },
20
- "references": [{ "path": "../utils" }]
21
- }