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.
- package/build/{BaseEntity.d.ts → src/BaseEntity.d.ts} +2 -1
- package/build/{BaseEntity.js → src/BaseEntity.js} +13 -9
- package/build/src/BaseEntity.js.map +1 -0
- package/build/{EntityManager.d.ts → src/EntityManager.d.ts} +139 -110
- package/build/{EntityManager.js → src/EntityManager.js} +281 -262
- package/build/src/EntityManager.js.map +1 -0
- package/build/{QueryBuilder.d.ts → src/QueryBuilder.d.ts} +53 -3
- package/build/src/QueryBuilder.js +341 -0
- package/build/src/QueryBuilder.js.map +1 -0
- package/build/src/Todo.d.ts +25 -0
- package/build/src/Todo.js +52 -0
- package/build/src/Todo.js.map +1 -0
- package/build/src/changes.d.ts +34 -0
- package/build/src/changes.js +37 -0
- package/build/src/changes.js.map +1 -0
- package/build/src/config.d.ts +43 -0
- package/build/src/config.js +114 -0
- package/build/src/config.js.map +1 -0
- package/build/{createOrUpdatePartial.d.ts → src/createOrUpdatePartial.d.ts} +2 -1
- package/build/{createOrUpdatePartial.js → src/createOrUpdatePartial.js} +42 -10
- package/build/src/createOrUpdatePartial.js.map +1 -0
- package/build/src/dataloaders/findDataLoader.d.ts +5 -0
- package/build/src/dataloaders/findDataLoader.js +28 -0
- package/build/src/dataloaders/findDataLoader.js.map +1 -0
- package/build/src/dataloaders/loadDataLoader.d.ts +3 -0
- package/build/src/dataloaders/loadDataLoader.js +37 -0
- package/build/src/dataloaders/loadDataLoader.js.map +1 -0
- package/build/src/dataloaders/manyToManyDataLoader.d.ts +5 -0
- package/build/src/dataloaders/manyToManyDataLoader.js +78 -0
- package/build/src/dataloaders/manyToManyDataLoader.js.map +1 -0
- package/build/src/dataloaders/manyToManyFindDataLoader.d.ts +5 -0
- package/build/src/dataloaders/manyToManyFindDataLoader.js +33 -0
- package/build/src/dataloaders/manyToManyFindDataLoader.js.map +1 -0
- package/build/src/dataloaders/oneToManyDataLoader.d.ts +4 -0
- package/build/src/dataloaders/oneToManyDataLoader.js +40 -0
- package/build/src/dataloaders/oneToManyDataLoader.js.map +1 -0
- package/build/src/dataloaders/oneToManyFindDataLoader.d.ts +5 -0
- package/build/src/dataloaders/oneToManyFindDataLoader.js +32 -0
- package/build/src/dataloaders/oneToManyFindDataLoader.js.map +1 -0
- package/build/src/dataloaders/oneToOneDataLoader.d.ts +4 -0
- package/build/src/dataloaders/oneToOneDataLoader.js +40 -0
- package/build/src/dataloaders/oneToOneDataLoader.js.map +1 -0
- package/build/src/drivers/IdAssigner.d.ts +33 -0
- package/build/src/drivers/IdAssigner.js +106 -0
- package/build/src/drivers/IdAssigner.js.map +1 -0
- package/build/src/drivers/InMemoryDriver.d.ts +29 -0
- package/build/src/drivers/InMemoryDriver.js +306 -0
- package/build/src/drivers/InMemoryDriver.js.map +1 -0
- package/build/src/drivers/PostgresDriver.d.ts +40 -0
- package/build/src/drivers/PostgresDriver.js +376 -0
- package/build/src/drivers/PostgresDriver.js.map +1 -0
- package/build/src/drivers/driver.d.ts +23 -0
- package/build/src/drivers/driver.js +3 -0
- package/build/src/drivers/driver.js.map +1 -0
- package/build/src/drivers/index.d.ts +4 -0
- package/build/src/drivers/index.js +17 -0
- package/build/src/drivers/index.js.map +1 -0
- package/build/{getProperties.d.ts → src/getProperties.d.ts} +0 -0
- package/build/{getProperties.js → src/getProperties.js} +1 -1
- package/build/src/getProperties.js.map +1 -0
- package/build/src/index.d.ts +62 -0
- package/build/src/index.js +263 -0
- package/build/src/index.js.map +1 -0
- package/build/src/keys.d.ts +30 -0
- package/build/{keys.js → src/keys.js} +48 -16
- package/build/src/keys.js.map +1 -0
- package/build/{loadLens.d.ts → src/loadLens.d.ts} +2 -2
- package/build/{loadLens.js → src/loadLens.js} +1 -1
- package/build/src/loadLens.js.map +1 -0
- package/build/src/loaded.d.ts +49 -0
- package/build/src/loaded.js +9 -0
- package/build/src/loaded.js.map +1 -0
- package/build/{newTestInstance.d.ts → src/newTestInstance.d.ts} +37 -3
- package/build/src/newTestInstance.js +342 -0
- package/build/src/newTestInstance.js.map +1 -0
- package/build/{collections → src/relations}/AbstractRelationImpl.d.ts +6 -5
- package/build/{collections → src/relations}/AbstractRelationImpl.js +0 -0
- package/build/src/relations/AbstractRelationImpl.js.map +1 -0
- package/build/src/relations/Collection.d.ts +26 -0
- package/build/src/relations/Collection.js +19 -0
- package/build/src/relations/Collection.js.map +1 -0
- package/build/{collections → src/relations}/CustomCollection.d.ts +6 -2
- package/build/{collections → src/relations}/CustomCollection.js +17 -9
- package/build/src/relations/CustomCollection.js.map +1 -0
- package/build/{collections → src/relations}/CustomReference.d.ts +7 -2
- package/build/{collections → src/relations}/CustomReference.js +16 -9
- package/build/src/relations/CustomReference.js.map +1 -0
- package/build/src/relations/LargeCollection.d.ts +17 -0
- package/build/src/relations/LargeCollection.js +3 -0
- package/build/src/relations/LargeCollection.js.map +1 -0
- package/build/{collections → src/relations}/ManyToManyCollection.d.ts +9 -2
- package/build/src/relations/ManyToManyCollection.js +249 -0
- package/build/src/relations/ManyToManyCollection.js.map +1 -0
- package/build/src/relations/ManyToManyLargeCollection.d.ts +25 -0
- package/build/src/relations/ManyToManyLargeCollection.js +97 -0
- package/build/src/relations/ManyToManyLargeCollection.js.map +1 -0
- package/build/src/relations/ManyToOneReference.d.ts +77 -0
- package/build/{collections → src/relations}/ManyToOneReference.js +101 -48
- package/build/src/relations/ManyToOneReference.js.map +1 -0
- package/build/{collections → src/relations}/OneToManyCollection.d.ts +10 -2
- package/build/{collections → src/relations}/OneToManyCollection.js +54 -59
- package/build/src/relations/OneToManyCollection.js.map +1 -0
- package/build/src/relations/OneToManyLargeCollection.d.ts +25 -0
- package/build/src/relations/OneToManyLargeCollection.js +83 -0
- package/build/src/relations/OneToManyLargeCollection.js.map +1 -0
- package/build/src/relations/OneToOneReference.d.ts +82 -0
- package/build/src/relations/OneToOneReference.js +168 -0
- package/build/src/relations/OneToOneReference.js.map +1 -0
- package/build/src/relations/PolymorphicReference.d.ts +69 -0
- package/build/src/relations/PolymorphicReference.js +210 -0
- package/build/src/relations/PolymorphicReference.js.map +1 -0
- package/build/src/relations/Reference.d.ts +29 -0
- package/build/src/relations/Reference.js +23 -0
- package/build/src/relations/Reference.js.map +1 -0
- package/build/src/relations/Relation.d.ts +10 -0
- package/build/src/relations/Relation.js +13 -0
- package/build/src/relations/Relation.js.map +1 -0
- package/build/src/relations/hasAsyncProperty.d.ts +36 -0
- package/build/src/relations/hasAsyncProperty.js +55 -0
- package/build/src/relations/hasAsyncProperty.js.map +1 -0
- package/build/{collections → src/relations}/hasManyDerived.d.ts +2 -1
- package/build/{collections → src/relations}/hasManyDerived.js +1 -1
- package/build/src/relations/hasManyDerived.js.map +1 -0
- package/build/{collections → src/relations}/hasManyThrough.d.ts +0 -0
- package/build/{collections → src/relations}/hasManyThrough.js +2 -2
- package/build/src/relations/hasManyThrough.js.map +1 -0
- package/build/{collections → src/relations}/hasOneDerived.d.ts +3 -2
- package/build/{collections → src/relations}/hasOneDerived.js +1 -1
- package/build/src/relations/hasOneDerived.js.map +1 -0
- package/build/{collections → src/relations}/hasOneThrough.d.ts +0 -0
- package/build/{collections → src/relations}/hasOneThrough.js +2 -2
- package/build/src/relations/hasOneThrough.js.map +1 -0
- package/build/src/relations/index.d.ts +18 -0
- package/build/src/relations/index.js +53 -0
- package/build/src/relations/index.js.map +1 -0
- package/build/{reverseHint.d.ts → src/reverseHint.d.ts} +2 -1
- package/build/{reverseHint.js → src/reverseHint.js} +13 -9
- package/build/src/reverseHint.js.map +1 -0
- package/build/src/rules.d.ts +23 -0
- package/build/src/rules.js +23 -0
- package/build/src/rules.js.map +1 -0
- package/build/src/serde.d.ts +121 -0
- package/build/src/serde.js +190 -0
- package/build/src/serde.js.map +1 -0
- package/build/{utils.d.ts → src/utils.d.ts} +2 -0
- package/build/{utils.js → src/utils.js} +10 -1
- package/build/src/utils.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +30 -15
- package/build/BaseEntity.js.map +0 -1
- package/build/EntityManager.js.map +0 -1
- package/build/EntityPersister.d.ts +0 -30
- package/build/EntityPersister.js +0 -197
- package/build/EntityPersister.js.map +0 -1
- package/build/QueryBuilder.js +0 -195
- package/build/QueryBuilder.js.map +0 -1
- package/build/changes.d.ts +0 -23
- package/build/changes.js +0 -14
- package/build/changes.js.map +0 -1
- package/build/collections/AbstractRelationImpl.js.map +0 -1
- package/build/collections/CustomCollection.js.map +0 -1
- package/build/collections/CustomReference.js.map +0 -1
- package/build/collections/ManyToManyCollection.js +0 -288
- package/build/collections/ManyToManyCollection.js.map +0 -1
- package/build/collections/ManyToOneReference.d.ts +0 -50
- package/build/collections/ManyToOneReference.js.map +0 -1
- package/build/collections/OneToManyCollection.js.map +0 -1
- package/build/collections/OneToOneReference.d.ts +0 -51
- package/build/collections/OneToOneReference.js +0 -132
- package/build/collections/OneToOneReference.js.map +0 -1
- package/build/collections/hasManyDerived.js.map +0 -1
- package/build/collections/hasManyThrough.js.map +0 -1
- package/build/collections/hasOneDerived.js.map +0 -1
- package/build/collections/hasOneThrough.js.map +0 -1
- package/build/collections/index.d.ts +0 -19
- package/build/collections/index.js +0 -49
- package/build/collections/index.js.map +0 -1
- package/build/createOrUpdatePartial.js.map +0 -1
- package/build/getProperties.js.map +0 -1
- package/build/index.d.ts +0 -140
- package/build/index.js +0 -278
- package/build/index.js.map +0 -1
- package/build/keys.d.ts +0 -21
- package/build/keys.js.map +0 -1
- package/build/loadLens.js.map +0 -1
- package/build/newTestInstance.js +0 -153
- package/build/newTestInstance.js.map +0 -1
- package/build/reverseHint.js.map +0 -1
- package/build/serde.d.ts +0 -47
- package/build/serde.js +0 -93
- package/build/serde.js.map +0 -1
- package/build/utils.js.map +0 -1
- package/package.json.bak +0 -27
- package/src/BaseEntity.ts +0 -104
- package/src/EntityManager.ts +0 -1263
- package/src/EntityPersister.ts +0 -240
- package/src/QueryBuilder.ts +0 -289
- package/src/changes.ts +0 -40
- package/src/collections/AbstractRelationImpl.ts +0 -28
- package/src/collections/CustomCollection.ts +0 -152
- package/src/collections/CustomReference.ts +0 -138
- package/src/collections/ManyToManyCollection.ts +0 -346
- package/src/collections/ManyToOneReference.ts +0 -215
- package/src/collections/OneToManyCollection.ts +0 -254
- package/src/collections/OneToOneReference.ts +0 -153
- package/src/collections/hasManyDerived.ts +0 -29
- package/src/collections/hasManyThrough.ts +0 -20
- package/src/collections/hasOneDerived.ts +0 -26
- package/src/collections/hasOneThrough.ts +0 -20
- package/src/collections/index.ts +0 -74
- package/src/createOrUpdatePartial.ts +0 -144
- package/src/getProperties.ts +0 -27
- package/src/index.ts +0 -400
- package/src/keys.ts +0 -75
- package/src/loadLens.ts +0 -126
- package/src/newTestInstance.ts +0 -205
- package/src/reverseHint.ts +0 -43
- package/src/serde.ts +0 -97
- package/src/utils.ts +0 -63
- package/tsconfig.json +0 -21
- 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
|
-
}
|
package/src/collections/index.ts
DELETED
|
@@ -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
|
-
}
|
package/src/getProperties.ts
DELETED
|
@@ -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
|
-
}
|