joist-orm 0.1.538 → 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
@@ -1,20 +0,0 @@
1
- import { currentlyInstantiatingEntity, CustomReference, Entity, getLens, Lens, loadLens, Reference } from "../index";
2
-
3
- /**
4
- * Creates a CustomReference that will walk across references in the object graph.
5
- *
6
- * I.e. A BookReview "has one author" through the `review -> book -> author` relation.
7
- *
8
- * Because this is based on `CustomReference`, it will work in populates, i.e. `em.populate(review, "author")`.
9
- */
10
- export function hasOneThrough<T extends Entity, U extends Entity, N extends never | undefined, V extends U | N>(
11
- lens: (lens: Lens<T>) => Lens<V>,
12
- ): Reference<T, U, N> {
13
- const entity: T = currentlyInstantiatingEntity as T;
14
- return new CustomReference<T, U, N>(entity, {
15
- load: async (entity) => {
16
- await loadLens(entity, lens);
17
- },
18
- get: () => getLens(entity, lens),
19
- });
20
- }
@@ -1,74 +0,0 @@
1
- import {
2
- Collection,
3
- currentlyInstantiatingEntity,
4
- Entity,
5
- EntityMetadata,
6
- ManyToManyCollection,
7
- ManyToOneReference,
8
- OneToManyCollection,
9
- OneToOneReference,
10
- Reference,
11
- } from "../";
12
-
13
- export { OneToManyCollection } from "./OneToManyCollection";
14
- export { OneToOneReference } from "./OneToOneReference";
15
- export { ManyToOneReference } from "./ManyToOneReference";
16
- export { ManyToManyCollection } from "./ManyToManyCollection";
17
- export { CustomReference } from "./CustomReference";
18
- export { hasOneThrough } from "./hasOneThrough";
19
- export { hasOneDerived } from "./hasOneDerived";
20
- export { CustomCollection } from "./CustomCollection";
21
- export { hasManyThrough } from "./hasManyThrough";
22
- export { hasManyDerived } from "./hasManyDerived";
23
-
24
- /** An alias for creating `OneToManyCollection`s. */
25
- export function hasMany<T extends Entity, U extends Entity>(
26
- otherMeta: EntityMetadata<U>,
27
- fieldName: keyof T,
28
- otherFieldName: keyof U,
29
- otherColumnName: string,
30
- ): Collection<T, U> {
31
- const entity = currentlyInstantiatingEntity as T;
32
- return new OneToManyCollection(entity, otherMeta, fieldName, otherFieldName, otherColumnName);
33
- }
34
-
35
- /** An alias for creating `ManyToOneReference`s. */
36
- export function hasOne<T extends Entity, U extends Entity, N extends never | undefined>(
37
- otherMeta: EntityMetadata<U>,
38
- fieldName: keyof T,
39
- otherFieldName: keyof U,
40
- ): Reference<T, U, N> {
41
- const entity = currentlyInstantiatingEntity as T;
42
- return new ManyToOneReference<T, U, N>(entity, otherMeta, fieldName, otherFieldName);
43
- }
44
-
45
- /** An alias for creating `OneToOneReference`s. */
46
- export function hasOneToOne<T extends Entity, U extends Entity>(
47
- otherMeta: EntityMetadata<U>,
48
- fieldName: keyof T,
49
- otherFieldName: keyof U,
50
- ): Reference<T, U, undefined> {
51
- const entity = currentlyInstantiatingEntity as T;
52
- return new OneToOneReference<T, U>(entity, otherMeta, fieldName, otherFieldName);
53
- }
54
-
55
- /** An alias for creating `ManyToManyCollections`s. */
56
- export function hasManyToMany<T extends Entity, U extends Entity>(
57
- joinTableName: string,
58
- fieldName: keyof T,
59
- columnName: string,
60
- otherMeta: EntityMetadata<U>,
61
- otherFieldName: keyof U,
62
- otherColumnName: string,
63
- ): Collection<T, U> {
64
- const entity = currentlyInstantiatingEntity as T;
65
- return new ManyToManyCollection<T, U>(
66
- joinTableName,
67
- entity,
68
- fieldName,
69
- columnName,
70
- otherMeta,
71
- otherFieldName,
72
- otherColumnName,
73
- );
74
- }
@@ -1,144 +0,0 @@
1
- import { NullOrDefinedOr } from "./utils";
2
- import {
3
- Entity,
4
- EntityConstructor,
5
- EntityManager,
6
- getMetadata,
7
- IdOf,
8
- isEntity,
9
- isKey,
10
- OptIdsOf,
11
- OptsOf,
12
- } from "./EntityManager";
13
- import { PartialOrNull } from "./index";
14
-
15
- /**
16
- * The type for `EntityManager.createOrUpdateUnsafe` that allows "upsert"-ish behavior.
17
- *
18
- * I.e. `T` is an entity with an optional id (create if unset, update if set), and we recurse
19
- * into any relations (references or collections) to allow those relations themselves to be
20
- * any combination of 1) ids to existing entities, 2) entities directly, 3) null/undefined
21
- * with the appropriate partial-update behavior, or 4) partials.
22
- */
23
- export type DeepPartialOrNull<T extends Entity> = { id?: IdOf<T> | null } & AllowRelationsToBeIdsOrEntitiesOrPartials<
24
- PartialOrNull<OptsOf<T> & OptIdsOf<T>>
25
- > &
26
- OptIdsOf<T>;
27
-
28
- type AllowRelationsToBeIdsOrEntitiesOrPartials<T> = {
29
- [P in keyof T]: T[P] extends NullOrDefinedOr<infer U>
30
- ? U extends Array<infer V>
31
- ? V extends Entity
32
- ? Array<V | (DeepPartialOrNull<V> & { delete?: boolean | null; remove?: boolean | null }) | IdOf<V>> | null
33
- : T[P]
34
- : U extends Entity
35
- ? U | DeepPartialOrNull<U> | IdOf<U> | null
36
- : T[P]
37
- : T[P];
38
- };
39
-
40
- /**
41
- * A utility function to create-or-update entities coming from a partial-update style API.
42
- */
43
- export async function createOrUpdatePartial<T extends Entity>(
44
- em: EntityManager,
45
- constructor: EntityConstructor<T>,
46
- opts: DeepPartialOrNull<T>,
47
- ): Promise<T> {
48
- const { id, ...others } = opts as any;
49
- const meta = getMetadata(constructor);
50
- const isNew = id === null || id === undefined;
51
-
52
- // The values in others might be themselves partials, so walk through and resolve them to entities.
53
- const p = Object.entries(others).map(async ([key, value]) => {
54
- // Watch for the `bookId` / `bookIds` aliases
55
- const field = meta.fields.find((f) => f.fieldName === key) || meta.fields.find((f) => f.fieldIdName === key);
56
-
57
- if (!field) {
58
- // Allow delete/remove flags that we assume the API layer (i.e. GraphQL) will have specifically
59
- // allowed, i.e. this isn't the Rails form bug where users can POST in any random field they want.
60
- const flagField = key === "delete" || key === "remove";
61
- if (flagField) {
62
- // Pass these along for setOpts to look for
63
- return [key, value];
64
- }
65
- throw new Error(`Unknown field ${key}`);
66
- }
67
-
68
- // Don't use key b/c it might be the bookId alias
69
- const name = field.fieldName;
70
-
71
- if (field.kind === "m2o" && !isEntity(value)) {
72
- if (!value || isEntity(value)) {
73
- return [name, value];
74
- } else if (isKey(value)) {
75
- // This is a many-to-one reference
76
- const entity = await em.load(field.otherMetadata().cstr, value);
77
- return [name, entity];
78
- } else if (typeof value === "object" && value && !("id" in value)) {
79
- // This is a many-to-one partial into an existing reference that we need to resolve
80
- let currentValue: any;
81
- if (isNew) {
82
- // The parent is brand new so the child is defacto brand new as well
83
- currentValue = await createOrUpdatePartial(em, field.otherMetadata().cstr, value);
84
- } else {
85
- // The parent exists, see if it has an existing child we can update
86
- const parentEntity = await em.load(constructor, id, [name] as any);
87
- currentValue = (parentEntity as any)[name].get;
88
- if (currentValue) {
89
- await createOrUpdatePartial(em, field.otherMetadata().cstr, { id: currentValue.id, ...value });
90
- } else {
91
- // If it doesn't, go ahead and create a new one
92
- currentValue = await createOrUpdatePartial(em, field.otherMetadata().cstr, value);
93
- }
94
- }
95
- return [name, currentValue];
96
- } else {
97
- // This is a many-to-one partial into a new entity
98
- const entity = await createOrUpdatePartial(em, field.otherMetadata().cstr, value as any);
99
- return [name, entity];
100
- }
101
- } else if (field.kind === "o2m" || field.kind === "m2m") {
102
- // Look for one-to-many/many-to-many partials
103
-
104
- // We allow `delete` and `remove` commands but only if they don't collide with existing fields
105
- // Also we trust the API layer, i.e. GraphQL, to not let these fields leak unless explicitly allowed.
106
- const allowDelete = !field.otherMetadata().fields.some((f) => f.fieldName === "delete");
107
- const allowRemove = !field.otherMetadata().fields.some((f) => f.fieldName === "remove");
108
-
109
- const entities = !value
110
- ? []
111
- : (value as Array<any>).map(async (value) => {
112
- if (!value || isEntity(value)) {
113
- return value;
114
- } else if (isKey(value)) {
115
- return await em.load(field.otherMetadata().cstr, value);
116
- } else {
117
- // Look for `delete: true/false` and `remove: true/false` markers
118
- const deleteMarker = allowDelete && value["delete"];
119
- const removeMarker = allowRemove && value["remove"];
120
- // Remove the markers, regardless of true/false, before recursing into createOrUpdatePartial to avoid unknown fields
121
- if (deleteMarker !== undefined) delete value.delete;
122
- if (removeMarker !== undefined) delete value.remove;
123
- const entity = await createOrUpdatePartial(em, field.otherMetadata().cstr, value as any);
124
- // Put the markers back for setOpts to find
125
- if (deleteMarker === true) entity.delete = true;
126
- if (removeMarker === true) entity.remove = true;
127
- return entity;
128
- }
129
- });
130
- return [name, await Promise.all(entities)];
131
- } else {
132
- return [name, value];
133
- }
134
- });
135
- const _opts = Object.fromEntries(await Promise.all(p)) as OptsOf<T>;
136
-
137
- if (isNew) {
138
- return em.createPartial(constructor, _opts);
139
- } else {
140
- const entity = await em.load(constructor, id);
141
- entity.setPartial(_opts);
142
- return entity;
143
- }
144
- }
@@ -1,27 +0,0 @@
1
- import * as EM from "./EntityManager";
2
- import { Entity, EntityMetadata } from "./EntityManager";
3
-
4
- // Hack to make currentlyInstantiatingEntity assignable
5
- const em = EM;
6
-
7
- /**
8
- * Returns the relations in `meta`, both those defined in the codegen file + any user-defined `CustomReference`s.
9
- *
10
- * This is a little tricky because field assignments don't show up on the prototype, so we actually
11
- * instantiate a throw-away instance to observe the side-effect of what fields it has.
12
- */
13
- export function getProperties<T extends Entity>(meta: EntityMetadata<T>): string[] {
14
- return [
15
- ...Object.getOwnPropertyNames(meta.cstr.prototype),
16
- ...Object.keys(
17
- new meta.cstr(
18
- {
19
- register: (metadata: any, entity: any) => {
20
- em.currentlyInstantiatingEntity = entity;
21
- },
22
- } as any,
23
- {},
24
- ),
25
- ),
26
- ].filter((key) => key !== "constructor" && !key.startsWith("__"));
27
- }
package/src/index.ts DELETED
@@ -1,400 +0,0 @@
1
- import { AbstractRelationImpl } from "./collections/AbstractRelationImpl";
2
- import {
3
- currentFlushSecret,
4
- Entity,
5
- EntityConstructor,
6
- EntityManager,
7
- EntityMetadata,
8
- EntityOrmField,
9
- getMetadata,
10
- IdOf,
11
- Loaded,
12
- LoadHint,
13
- OptsOf,
14
- RelationsIn,
15
- } from "./EntityManager";
16
- import { reverseHint } from "./reverseHint";
17
-
18
- export * from "./keys";
19
- export * from "./serde";
20
- export * from "./EntityManager";
21
- export { newPgConnectionConfig } from "joist-utils";
22
- export * from "./reverseHint";
23
- export * from "./changes";
24
- export { DeepPartialOrNull } from "./createOrUpdatePartial";
25
- export { fail } from "./utils";
26
- export * from "./collections";
27
- export * from "./QueryBuilder";
28
- export { BaseEntity } from "./BaseEntity";
29
- export * from "./loadLens";
30
- export * from "./getProperties";
31
- export * from "./newTestInstance";
32
-
33
- const F = Symbol();
34
- const G = Symbol();
35
- const H = Symbol();
36
-
37
- /** A relationship from `T` to `U`, could be any of many-to-one, one-to-many, or many-to-many. */
38
- export interface Relation<T extends Entity, U extends Entity> {
39
- // Make our Relation somewhat non-structural, otherwise since it's a marker interface,
40
- // types like `number` or `string` will match it. This also seems to nudge the type
41
- // inference inside of `LoadHint` to go beyond "this generic T of Entity has id and __orm"
42
- // to "no really this generic T has fields firstName, title, etc.".
43
- // See https://stackoverflow.com/questions/53448100/generic-type-of-extended-interface-not-inferred
44
- [F]?: T;
45
- [G]?: U;
46
- }
47
-
48
- /**
49
- * A many-to-one / foreign key from `T` to `U`, i.e. book to author.
50
- *
51
- * The `N` generic is for whether the field is optional (i.e. the foreign key column is
52
- * nullable). If it is optional, `N` will be `undefined`, which makes the return types
53
- * `U | undefined`. If it is not optional, `N` will be `never`, making the return types
54
- * `U | never` which becomes just `U`.
55
- */
56
- export interface Reference<T extends Entity, U extends Entity, N extends never | undefined> extends Relation<T, U> {
57
- /** Returns the id of the current assigned entity, or `undefined` if the assigned entity has no id yet, or `undefined` if this column is nullable and currently unset. */
58
- id: IdOf<U> | undefined;
59
-
60
- /** Returns the id of the current assigned entity or a runtime error if it's either a) unset or b) set to a new entity that doesn't have an `id` yet. */
61
- idOrFail: IdOf<U>;
62
-
63
- idUntagged: string | undefined;
64
-
65
- idUntaggedOrFail: string;
66
-
67
- load(opts?: { withDeleted: boolean }): Promise<U | N>;
68
-
69
- set(other: U | N): void;
70
-
71
- /** Returns `true` if this relation is currently set (i.e. regardless of whether it's loaded, or if it is set but the assigned entity doesn't have an id saved. */
72
- isSet: boolean;
73
-
74
- [H]?: N;
75
- }
76
-
77
- /** Adds a known-safe `get` accessor. */
78
- export interface LoadedReference<T extends Entity, U extends Entity, N extends never | undefined>
79
- extends Omit<Reference<T, U, N>, "id"> {
80
- // Since we've fetched the entity from the db, we're going to omit out the "| undefined" from Reference.id
81
- // which handles "this reference is set to a new entity" and just assume the id is there (or else N which
82
- // is for nullable references, which will just always be potentially `undefined`).
83
- //
84
- // Note that, similar to `.get`, this is _usually_ right, but if the user mutates the object graph after the
85
- // populate, i.e. they change some fields to have actually-new / not-included-in-the-`populate` call entities,
86
- // then these might turn into runtime errors. But the ergonomics are sufficiently better that it is worth it.
87
- id: IdOf<T> | N;
88
-
89
- getWithDeleted: U | N;
90
- get: U | N;
91
- }
92
-
93
- /** A collection of `U` within `T`, either one-to-many or many-to-many. */
94
- export interface Collection<T extends Entity, U extends Entity> extends Relation<T, U> {
95
- load(opts?: { withDeleted: boolean }): Promise<ReadonlyArray<U>>;
96
-
97
- find(id: IdOf<U>): Promise<U | undefined>;
98
-
99
- add(other: U): void;
100
-
101
- remove(other: U): void;
102
- }
103
-
104
- /** Adds a known-safe `get` accessor. */
105
- export interface LoadedCollection<T extends Entity, U extends Entity> extends Collection<T, U> {
106
- getWithDeleted: ReadonlyArray<U>;
107
- get: ReadonlyArray<U>;
108
-
109
- set(values: U[]): void;
110
-
111
- removeAll(): void;
112
- }
113
-
114
- // https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/
115
- interface Flavoring<FlavorT> {
116
- _type?: FlavorT;
117
- }
118
- export type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;
119
-
120
- export function setField(entity: Entity, fieldName: string, newValue: any): boolean {
121
- ensureNotDeleted(entity, { ignore: "pending" });
122
- const em = getEm(entity);
123
-
124
- if (em.isFlushing) {
125
- const { flushSecret } = currentFlushSecret.getStore() || {};
126
-
127
- if (flushSecret === undefined) {
128
- throw new Error(`Cannot set '${fieldName}' on ${entity} during a flush outside of a entity hook`);
129
- }
130
-
131
- if (flushSecret !== em["flushSecret"]) {
132
- throw new Error(`Attempting to reuse a hook context outside its flush loop`);
133
- }
134
- }
135
-
136
- const { data, originalData } = entity.__orm;
137
-
138
- // "Un-dirty" our originalData if newValue is reverting to originalData
139
- if (fieldName in originalData) {
140
- if (originalData[fieldName] === newValue) {
141
- data[fieldName] = newValue;
142
- delete originalData[fieldName];
143
- return true;
144
- }
145
- }
146
-
147
- // Push this logic into a field serde type abstraction?
148
- const currentValue = data[fieldName];
149
- if (currentValue === newValue) {
150
- return false;
151
- }
152
-
153
- // Only save the currentValue on the 1st change of this field
154
- if (!(fieldName in originalData)) {
155
- originalData[fieldName] = currentValue;
156
- }
157
- data[fieldName] = newValue;
158
- return true;
159
- }
160
-
161
- /**
162
- * Sets each value in `values` on the current entity.
163
- *
164
- * The default behavior is that passing a value as either `null` or `undefined` will set
165
- * the field as `undefined`, i.e. automatic `null` to `undefined` conversion.
166
- *
167
- * However, if you pass `ignoreUndefined: true`, then any opt that is `undefined` will be treated
168
- * as "do not set", and `null` will still mean "set to `undefined`". This is useful for implementing
169
- * APIs were an input of `undefined` means "do not set / noop" and `null` means "unset".
170
- */
171
- export function setOpts<T extends Entity>(
172
- entity: T,
173
- values: Partial<OptsOf<T>> | string | undefined,
174
- opts?: { calledFromConstructor?: boolean; partial?: boolean },
175
- ): void {
176
- // If `values` is a string (i.e. the id), this instance is being hydrated from a database row, so skip all this.
177
- // If `values` is undefined, we're being called by `createPartial` that will do its own opt handling.
178
- if (values === undefined || typeof values === "string") {
179
- return;
180
- }
181
- const { calledFromConstructor, partial } = opts || {};
182
- const meta = getMetadata(entity);
183
-
184
- Object.entries(values as {}).forEach(([key, _value]) => {
185
- const field = meta.fields.find((f) => f.fieldName === key);
186
- if (!field) {
187
- throw new Error(`Unknown field ${key}`);
188
- }
189
-
190
- // If ignoreUndefined is set, we treat undefined as a noop
191
- if (partial && _value === undefined) {
192
- return;
193
- }
194
- // We let optional opts fields be `| null` for convenience, and convert to undefined.
195
- const value = _value === null || (typeof _value === "string" && _value.trim() === "") ? undefined : _value;
196
- const current = (entity as any)[key];
197
- if (current instanceof AbstractRelationImpl) {
198
- if (calledFromConstructor) {
199
- current.setFromOpts(value);
200
- } else if (partial && (field.kind === "o2m" || field.kind === "m2m")) {
201
- // For setPartial collections, we individually add/remove instead of set.
202
- const allowDelete = !field.otherMetadata().fields.some((f) => f.fieldName === "delete");
203
- const allowRemove = !field.otherMetadata().fields.some((f) => f.fieldName === "remove");
204
- (value as any[]).forEach((e) => {
205
- if (allowDelete && e.delete === true) {
206
- getEm(entity).delete(e);
207
- } else if (allowRemove && e.remove === true) {
208
- (current as any).remove(e);
209
- } else {
210
- (current as any).add(e);
211
- }
212
- });
213
- } else {
214
- current.set(value);
215
- }
216
- } else {
217
- (entity as any)[key] = value;
218
- }
219
- });
220
- if (calledFromConstructor) {
221
- Object.values(entity).forEach((v) => {
222
- if (v instanceof AbstractRelationImpl) {
223
- v.initializeForNewEntity();
224
- }
225
- });
226
- }
227
- }
228
-
229
- export function ensureNotDeleted(entity: Entity, opts: { ignore?: EntityOrmField["deleted"] } = {}): void {
230
- if (entity.isDeletedEntity && (opts.ignore === undefined || entity.__orm.deleted !== opts.ignore)) {
231
- throw new Error(`${entity} is marked as deleted`);
232
- }
233
- }
234
-
235
- /** Adds `null` to every key in `T` to accept partial-update-style input. */
236
- export type PartialOrNull<T> = {
237
- [P in keyof T]?: T[P] | null;
238
- };
239
-
240
- export function getRequiredKeys<T extends Entity>(entity: T): string[];
241
- export function getRequiredKeys<T extends Entity>(type: EntityConstructor<T>): string[];
242
- export function getRequiredKeys<T extends Entity>(entityOrType: T | EntityConstructor<T>): string[] {
243
- return getMetadata(entityOrType as any)
244
- .fields.filter((f) => f.required)
245
- .map((f) => f.fieldName);
246
- }
247
-
248
- /** Entity validation errors; if `entity` is invalid, throw a `ValidationError`. */
249
- export type ValidationRule<T extends Entity> = (
250
- entity: T,
251
- ) => MaybePromise<string | ValidationError | ValidationError[] | undefined>;
252
-
253
- type MaybePromise<T> = T | PromiseLike<T>;
254
-
255
- export type ValidationError = { entity: Entity; message: string };
256
-
257
- export class ValidationErrors extends Error {
258
- constructor(public errors: ValidationError[]) {
259
- super(errorMessage(errors));
260
- }
261
- }
262
-
263
- export function newRequiredRule<T extends Entity>(key: keyof T): ValidationRule<T> {
264
- return (entity) => (entity.__orm.data[key] === undefined ? `${key} is required` : undefined);
265
- }
266
-
267
- function errorMessage(errors: ValidationError[]): string {
268
- if (errors.length === 1) {
269
- return `Validation error: ${errors[0].message}`;
270
- } else {
271
- return `Validation errors (${errors.length}): ${errors.map((e) => e.message).join(", ")}`;
272
- }
273
- }
274
-
275
- export type EntityHook = "beforeFlush" | "beforeDelete" | "afterValidation" | "afterCommit";
276
- type HookFn<T extends Entity, C> = (entity: T, ctx: C) => MaybePromise<void>;
277
-
278
- export class ConfigData<T extends Entity, C> {
279
- /** The validation rules for this entity type. */
280
- rules: ValidationRule<T>[] = [];
281
- /** The async derived fields for this entity type. */
282
- asyncDerivedFields: Partial<Record<keyof T, [LoadHint<T>, (entity: T) => any]>> = {};
283
- /** The hooks for this instance. */
284
- hooks: Record<EntityHook, HookFn<T, C>[]> = {
285
- beforeDelete: [],
286
- beforeFlush: [],
287
- afterCommit: [],
288
- afterValidation: [],
289
- };
290
- // Load-hint-ish structures that point back to instances that depend on us for validation rules.
291
- reactiveRules: string[][] = [];
292
- // Load-hint-ish structures that point back to instances that depend on us for derived values.
293
- reactiveDerivedValues: string[][] = [];
294
- cascadeDeleteFields: Array<keyof RelationsIn<T>> = [];
295
- }
296
-
297
- export class ConfigApi<T extends Entity, C> {
298
- __data = new ConfigData<T, C>();
299
-
300
- addRule<H extends LoadHint<T>>(populate: H, rule: ValidationRule<Loaded<T, H>>): void;
301
- addRule(rule: ValidationRule<T>): void;
302
- addRule(ruleOrHint: ValidationRule<T> | any, maybeRule?: ValidationRule<any>): void {
303
- if (typeof ruleOrHint === "function") {
304
- this.__data.rules.push(ruleOrHint);
305
- } else {
306
- const fn = async (entity: T) => {
307
- const em = getEm(entity);
308
- const loaded = await em.populate(entity, ruleOrHint);
309
- return maybeRule!(loaded);
310
- };
311
- // Squirrel our hint away where configureMetadata can find it
312
- (fn as any).hint = ruleOrHint;
313
- this.__data.rules.push(fn);
314
- }
315
- }
316
-
317
- cascadeDelete(relationship: keyof RelationsIn<T>): void {
318
- this.__data.cascadeDeleteFields.push(relationship);
319
- this.beforeDelete(relationship, (entity) => {
320
- const relation = (entity[relationship] as any) as AbstractRelationImpl<T>;
321
- relation.onEntityDelete();
322
- });
323
- }
324
-
325
- /** Registers `fn` as the lambda to provide the async value for `key`. */
326
- setAsyncDerivedField<P extends keyof T, H extends LoadHint<T>>(
327
- key: P,
328
- populate: H,
329
- fn: (entity: Loaded<T, H>) => T[P],
330
- ): void {
331
- this.__data.asyncDerivedFields[key] = [populate, fn as any];
332
- }
333
-
334
- private addHook(hook: EntityHook, ruleOrHint: HookFn<T, C> | any, maybeFn?: HookFn<Loaded<T, any>, C>) {
335
- if (typeof ruleOrHint === "function") {
336
- this.__data.hooks[hook].push(ruleOrHint);
337
- } else {
338
- const fn = async (entity: T, ctx: C) => {
339
- // TODO Use this for reactive beforeFlush
340
- const em = getEm(entity);
341
- const loaded = await em.populate(entity, ruleOrHint);
342
- return maybeFn!(loaded, ctx);
343
- };
344
- // Squirrel our hint away where configureMetadata can find it
345
- (fn as any).hint = ruleOrHint;
346
- this.__data.hooks[hook].push(fn);
347
- }
348
- }
349
-
350
- beforeDelete<H extends LoadHint<T>>(populate: H, fn: HookFn<Loaded<T, H>, C>): void;
351
- beforeDelete(fn: HookFn<T, C>): void;
352
- beforeDelete(ruleOrHint: HookFn<T, C> | any, maybeFn?: HookFn<Loaded<T, any>, C>): void {
353
- this.addHook("beforeDelete", ruleOrHint, maybeFn);
354
- }
355
-
356
- beforeFlush<H extends LoadHint<T>>(populate: H, fn: HookFn<Loaded<T, H>, C>): void;
357
- beforeFlush(fn: HookFn<T, C>): void;
358
- beforeFlush(ruleOrHint: HookFn<T, C> | any, maybeFn?: HookFn<Loaded<T, any>, C>): void {
359
- this.addHook("beforeFlush", ruleOrHint, maybeFn);
360
- }
361
-
362
- afterValidation<H extends LoadHint<T>>(populate: H, fn: HookFn<Loaded<T, H>, C>): void;
363
- afterValidation(fn: HookFn<T, C>): void;
364
- afterValidation(ruleOrHint: HookFn<T, C> | any, maybeFn?: HookFn<Loaded<T, any>, C>): void {
365
- this.addHook("afterValidation", ruleOrHint, maybeFn);
366
- }
367
-
368
- afterCommit(fn: HookFn<T, C>): void {
369
- this.addHook("afterCommit", fn);
370
- }
371
- }
372
-
373
- /** Processes the metas based on any custom calls to the `configApi` hooks. */
374
- export function configureMetadata(metas: EntityMetadata<any>[]): void {
375
- metas.forEach((meta) => {
376
- // Look for reactive validation rules to reverse
377
- meta.config.__data.rules.forEach((rule) => {
378
- if ((rule as any).hint) {
379
- const reversals = reverseHint(meta.cstr, (rule as any).hint);
380
- // For each reversal, tell its config about the reverse hint to force-re-validate
381
- // the original rule's instance any time it changes.
382
- reversals.forEach(([otherEntity, reverseHint]) => {
383
- getMetadata(otherEntity).config.__data.reactiveRules.push(reverseHint);
384
- });
385
- }
386
- });
387
- // Look for reactive async derived values rules to reverse
388
- Object.entries(meta.config.__data.asyncDerivedFields).forEach(([, entry]) => {
389
- const hint = entry![0];
390
- const reversals = reverseHint(meta.cstr, hint);
391
- reversals.forEach(([otherEntity, reverseHint]) => {
392
- getMetadata(otherEntity).config.__data.reactiveDerivedValues.push(reverseHint);
393
- });
394
- });
395
- });
396
- }
397
-
398
- export function getEm(entity: Entity): EntityManager {
399
- return entity.__orm.em;
400
- }