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.
- 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
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
|
-
}
|
package/src/newTestInstance.ts
DELETED
|
@@ -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
|
-
}
|
package/src/reverseHint.ts
DELETED
|
@@ -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
|
-
}
|