Refactor Visitor and Transformer to include an instance graph

main
Jackson Morgan 9 months ago
parent 2a0c2b0629
commit 3316818633
  1. 10
      packages/type-traverser/README.md
  2. 126
      packages/type-traverser/src/Visitors.ts
  3. 4
      packages/type-traverser/src/index.ts
  4. 2
      packages/type-traverser/src/instanceGraph/InstanceGraph.ts
  5. 2
      packages/type-traverser/src/instanceGraph/ReverseRelationshipTypes.ts
  6. 2
      packages/type-traverser/src/instanceGraph/nodes/InstanceNode.ts
  7. 47
      packages/type-traverser/src/transformer/Transformer.ts
  8. 88
      packages/type-traverser/src/transformer/Transformers.ts
  9. 22
      packages/type-traverser/src/transformer/transformerSubTraversers/TransformerInterfaceSubTraverser.ts
  10. 6
      packages/type-traverser/src/transformer/transformerSubTraversers/TransformerParentSubTraverser.ts
  11. 24
      packages/type-traverser/src/transformer/transformerSubTraversers/TransformerPrimitiveSubTraverser.ts
  12. 14
      packages/type-traverser/src/transformer/transformerSubTraversers/TransformerUnionSubTraverser.ts
  13. 2
      packages/type-traverser/src/transformer/transformerSubTraversers/util/CircularDependencyAwaiter.ts
  14. 0
      packages/type-traverser/src/transformer/transformerSubTraversers/util/MultiMap.ts
  15. 0
      packages/type-traverser/src/transformer/transformerSubTraversers/util/MultiSet.ts
  16. 0
      packages/type-traverser/src/transformer/transformerSubTraversers/util/SuperPromise.ts
  17. 0
      packages/type-traverser/src/transformer/transformerSubTraversers/util/timeout.ts
  18. 4
      packages/type-traverser/src/transformer/transformerSubTraversers/util/transformerSubTraverserTypes.ts
  19. 48
      packages/type-traverser/src/visitor/Visitor.ts
  20. 157
      packages/type-traverser/src/visitor/Visitors.ts
  21. 24
      packages/type-traverser/src/visitor/visitorSubTraversers/VisitorInterfaceSubTraverser.ts
  22. 2
      packages/type-traverser/src/visitor/visitorSubTraversers/VisitorParentSubTraverser.ts
  23. 18
      packages/type-traverser/src/visitor/visitorSubTraversers/VisitorPrimitiveSubTraverser.ts
  24. 19
      packages/type-traverser/src/visitor/visitorSubTraversers/VisitorUnionSubTraverser.ts
  25. 6
      packages/type-traverser/src/visitor/visitorSubTraversers/util/visitorSubTraverserTypes.ts
  26. 156
      packages/type-traverser/test/integration/InstanceGraph.test.ts
  27. 0
      packages/type-traverser/test/integration/avatar/AvatarBrokenTransformer.ts
  28. 0
      packages/type-traverser/test/integration/avatar/AvatarErroringTransformer.ts
  29. 4
      packages/type-traverser/test/integration/avatar/AvatarTraverserDefinition.ts
  30. 0
      packages/type-traverser/test/integration/avatar/AvatarTraverserTypes.ts
  31. 170
      packages/type-traverser/test/integration/avatar/avatar.test.ts
  32. 0
      packages/type-traverser/test/integration/avatar/sampleData.ts
  33. 19
      packages/type-traverser/test/integration/avatar_old/avatar.old.ts

@ -159,14 +159,14 @@ There are two fields for the primitive sub-traverser:
- `type`: The typescript type corresponding to this primitive.
### Creating a traverser definition
Typescript typings aren't available at runtime, so the next step is to translate the `TraverserTypes` that we made into a standard JSON object called a "TraverserDefinition". But, don't worry! This will be easy. If you define a variable as a `TraverserDefinition<TraverserType>`, your IDE's IntelliSense will be able to direct you through exactly what to fill out, as seen below.
Typescript typings aren't available at runtime, so the next step is to translate the `TraverserTypes` that we made into a standard JSON object called a "TraverserDefinitions". But, don't worry! This will be easy. If you define a variable as a `TraverserDefinitions<TraverserType>`, your IDE's IntelliSense will be able to direct you through exactly what to fill out, as seen below.
![Traverse Definition IntelliSense](/tutorialImages/TraveserDefinitionIntellisense.png)
In our example, the TraverserDefinition looks like:
In our example, the TraverserDefinitions looks like:
```typescript
const avatarTraverserDefinition: TraverserDefinition<AvatarTraverserTypes> = {
const avatarTraverserDefinitions: TraverserDefinitions<AvatarTraverserTypes> = {
Element: {
kind: "primitive",
},
@ -193,7 +193,7 @@ const avatarTraverserDefinition: TraverserDefinition<AvatarTraverserTypes> = {
```
#### Defining a Union Selector
The only part of the TraverserDefinition that isn't just blindly following IntelliSense is the `selector` on a Union sub-traverser. A `selector` is given the item and should return the TypeName corresponding to the item.
The only part of the TraverserDefinitions that isn't just blindly following IntelliSense is the `selector` on a Union sub-traverser. A `selector` is given the item and should return the TypeName corresponding to the item.
In the above example, `"Bender"` is returned if the given item has a `"element"` property because a `"NonBender"` does not include an `"element"` property.
@ -204,7 +204,7 @@ In our example, this is how we instantiate the traverser
```typescript
const avatarTraverser = new Traverser<AvatarTraverserTypes>(
avatarTraverserDefinition
avatarTraverserDefinitions
);
```

@ -1,126 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {
InterfaceType,
PrimitiveType,
TraverserTypes,
UnionType,
} from ".";
export type InterfaceVisitorFunction<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
Context,
> = (originalData: Type["type"], context: Context) => Promise<void>;
export type InterfaceVisitorPropertyFunction<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
PropertyName extends keyof Type["properties"],
Context,
> = (
originalData: Types[Type["properties"][PropertyName]]["type"],
context: Context,
) => Promise<void>;
export type InterfaceVisitorDefinition<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
Context,
> = {
visitor: InterfaceVisitorFunction<Types, Type, Context>;
properties: {
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction<
Types,
Type,
PropertyName,
Context
>;
};
};
export type UnionVisitorFunction<
Types extends TraverserTypes<any>,
Type extends UnionType<keyof Types>,
Context,
> = (originalData: Type["type"], context: Context) => Promise<void>;
export type UnionVisitorDefinition<
Types extends TraverserTypes<any>,
Type extends UnionType<keyof Types>,
Context,
> = UnionVisitorFunction<Types, Type, Context>;
export type PrimitiveVisitorFunction<Type extends PrimitiveType, Context> = (
originalData: Type["type"],
context: Context,
) => Promise<void>;
export type PrimitiveVisitorDefinition<
Type extends PrimitiveType,
Context,
> = PrimitiveVisitorFunction<Type, Context>;
export type VisitorDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Context,
> = Types[TypeName] extends InterfaceType<keyof Types>
? InterfaceVisitorDefinition<Types, Types[TypeName], Context>
: Types[TypeName] extends UnionType<keyof Types>
? UnionVisitorDefinition<Types, Types[TypeName], Context>
: Types[TypeName] extends PrimitiveType
? PrimitiveVisitorDefinition<Types[TypeName], Context>
: never;
export type Visitors<Types extends TraverserTypes<any>, Context> = {
[TypeName in keyof Types]: VisitorDefinition<Types, TypeName, Context>;
};
/**
* Input
*/
export type InterfaceVisitorInputDefinition<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
Context,
> = {
visitor: InterfaceVisitorFunction<Types, Type, Context>;
properties?: Partial<{
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction<
Types,
Type,
PropertyName,
Context
>;
}>;
};
export type UnionVisitorInputDefinition<
Types extends TraverserTypes<any>,
Type extends UnionType<keyof Types>,
Context,
> = UnionVisitorFunction<Types, Type, Context>;
export type PrimitiveVisitorInputDefinition<
Type extends PrimitiveType,
Context,
> = PrimitiveVisitorFunction<Type, Context>;
export type VisitorInputDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Context,
> = Types[TypeName] extends InterfaceType<keyof Types>
? InterfaceVisitorInputDefinition<Types, Types[TypeName], Context>
: Types[TypeName] extends UnionType<keyof Types>
? UnionVisitorInputDefinition<Types, Types[TypeName], Context>
: Types[TypeName] extends PrimitiveType
? PrimitiveVisitorInputDefinition<Types[TypeName], Context>
: never;
export type VisitorsInput<
Types extends TraverserTypes<any>,
Context,
> = Partial<{
[TypeName in keyof Types]: VisitorInputDefinition<Types, TypeName, Context>;
}>;

@ -5,5 +5,5 @@ export * from "./transformer/TransformerReturnTypes";
export * from "./transformer/TransformerReturnTypesDefaults";
export * from "./traverser/Traverser";
export * from "./transformer/Transformer";
export * from "./Visitor";
export * from "./Visitors";
export * from "./visitor/Visitor";
export * from "./visitor/Visitors";

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { MultiMap } from "../transformerSubTraversers/util/MultiMap";
import { MultiMap } from "../transformer/transformerSubTraversers/util/MultiMap";
import type { TraverserTypes } from "../traverser/TraverserTypes";
import {
createInstanceNodeFor,

@ -4,7 +4,7 @@ import type {
PrimitiveType,
TraverserTypes,
UnionType,
} from "./traverser/TraverserTypes";
} from "../traverser/TraverserTypes";
export type InterfaceReverseRelationshipIndentifier<
Types extends TraverserTypes<any>,

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { TraverserDefinition } from "../..";
import type { ParentIdentifiers } from "../../reverseRelationshipTypes";
import type { ParentIdentifiers } from "../../instanceGraph/ReverseRelationshipTypes";
import type { TraverserTypes } from "../../traverser/TraverserTypes";
import type { InstanceGraph } from "../instanceGraph";
import type { InstanceNodeFor } from "./createInstanceNodeFor";

@ -13,10 +13,10 @@ import type {
UnionReturnType,
UnionType,
} from "..";
import { transformerParentSubTraverser } from "../transformerSubTraversers/TransformerParentSubTraverser";
import { CircularDepenedencyAwaiter } from "../transformerSubTraversers/util/CircularDependencyAwaiter";
import { MultiMap } from "../transformerSubTraversers/util/MultiMap";
import { SuperPromise } from "../transformerSubTraversers/util/SuperPromise";
import { transformerParentSubTraverser } from "./transformerSubTraversers/TransformerParentSubTraverser";
import { CircularDepenedencyAwaiter } from "./transformerSubTraversers/util/CircularDependencyAwaiter";
import { MultiMap } from "./transformerSubTraversers/util/MultiMap";
import { SuperPromise } from "./transformerSubTraversers/util/SuperPromise";
import type {
GetTransformedChildrenFunction,
InterfaceTransformerDefinition,
@ -28,6 +28,7 @@ import type {
UnionTransformerDefinition,
UnionTransformerInputDefinition,
} from "./Transformers";
import { InstanceGraph } from "../instanceGraph/instanceGraph";
// TODO: Lots of "any" in this file. I'm just done with fancy typescript,
// but if I ever feel so inclined, I should fix this in the future.
@ -54,12 +55,14 @@ export class Transformer<
}
private applyDefaultInterfaceTransformerProperties<
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnType extends InterfaceReturnType<Type>,
>(
typeName: keyof Types,
typePropertiesInput: InterfaceTransformerInputDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -67,6 +70,7 @@ export class Transformer<
>["properties"],
): InterfaceTransformerDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -89,6 +93,7 @@ export class Transformer<
return agg;
}, {}) as InterfaceTransformerDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -97,12 +102,14 @@ export class Transformer<
}
private applyDefaultInterfaceTransformer<
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnType extends InterfaceReturnType<Type>,
>(
typeName: keyof Types,
typeInput?: InterfaceTransformerInputDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -110,6 +117,7 @@ export class Transformer<
>,
): InterfaceTransformerDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -139,11 +147,13 @@ export class Transformer<
}
private applyDefaultUnionTransformer<
Type extends UnionType<keyof Types>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
ReturnType extends UnionReturnType,
>(
typeInput?: UnionTransformerInputDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -151,6 +161,7 @@ export class Transformer<
>,
): UnionTransformerDefinition<
Types,
TypeName,
Type,
ApplyTransformerReturnTypesDefaults<Types, InputReturnTypes>,
ReturnType,
@ -168,11 +179,24 @@ export class Transformer<
}
private applyDefaultPrimitiveTransformer<
Type extends PrimitiveType,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
ReturnType extends PrimitiveReturnType,
>(
typeInput?: PrimitiveTransformerInputDefinition<Type, ReturnType, Context>,
): PrimitiveTransformerDefinition<Type, ReturnType, Context> {
typeInput?: PrimitiveTransformerInputDefinition<
Types,
TypeName,
Type,
ReturnType,
Context
>,
): PrimitiveTransformerDefinition<
Types,
TypeName,
Type,
ReturnType,
Context
> {
if (!typeInput) {
return async (originalData) => {
return originalData;
@ -229,12 +253,15 @@ export class Transformer<
>[TypeName]["return"]
> {
const superPromise = new SuperPromise();
const instanceGraph = new InstanceGraph(this.traverserDefinition);
instanceGraph.getNodeFor(item, itemTypeName);
const toReturn = await transformerParentSubTraverser(item, itemTypeName, {
traverserDefinition: this.traverserDefinition,
transformers: this.transformers,
executingPromises: new MultiMap(),
circularDependencyAwaiter: new CircularDepenedencyAwaiter(),
superPromise,
instanceGraph,
context,
});
await superPromise.wait();

@ -12,6 +12,8 @@ import type {
UnionType,
} from "..";
import type { InterfaceInstanceNode } from "../instanceGraph/nodes/InterfaceInstanceNode";
import type { PrimitiveInstanceNode } from "../instanceGraph/nodes/PrimitiveInstanceNode";
import type { UnionInstanceNode } from "../instanceGraph/nodes/UnionInstanceNode";
export type GetTransformedChildrenFunction<TransformedChildrenType> =
() => Promise<TransformedChildrenType>;
@ -22,7 +24,8 @@ export type SetReturnPointerFunction<ReturnType> = (
export type InterfaceTransformerFunction<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnType extends InterfaceReturnType<Type>,
Context,
> = (
@ -31,13 +34,14 @@ export type InterfaceTransformerFunction<
[PropertyName in keyof ReturnType["properties"]]: ReturnType["properties"][PropertyName];
}>,
setReturnPointer: SetReturnPointerFunction<ReturnType["return"]>,
node: InterfaceInstanceNode<Types, Type>,
node: InterfaceInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<ReturnType["return"]>;
export type InterfaceTransformerPropertyFunction<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnTypes extends TransformerReturnTypes<Types>,
ReturnType extends InterfaceReturnType<Type>,
PropertyName extends keyof Type["properties"],
@ -47,20 +51,29 @@ export type InterfaceTransformerPropertyFunction<
getTransfromedChildren: GetTransformedChildrenFunction<
ReturnTypes[Type["properties"][PropertyName]]["return"]
>,
node: InterfaceInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<ReturnType["properties"][PropertyName]>;
export type InterfaceTransformerDefinition<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnTypes extends TransformerReturnTypes<Types>,
ReturnType extends InterfaceReturnType<Type>,
Context,
> = {
transformer: InterfaceTransformerFunction<Types, Type, ReturnType, Context>;
transformer: InterfaceTransformerFunction<
Types,
TypeName,
Type,
ReturnType,
Context
>;
properties: {
[PropertyName in keyof Type["properties"]]: InterfaceTransformerPropertyFunction<
Types,
TypeName,
Type,
ReturnTypes,
ReturnType,
@ -72,7 +85,8 @@ export type InterfaceTransformerDefinition<
export type UnionTransformerFunction<
Types extends TraverserTypes<any>,
Type extends UnionType<keyof Types>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
ReturnTypes extends TransformerReturnTypes<Types>,
ReturnType extends UnionReturnType,
Context,
@ -82,31 +96,45 @@ export type UnionTransformerFunction<
ReturnTypes[Type["typeNames"]]["return"]
>,
setReturnPointer: SetReturnPointerFunction<ReturnType["return"]>,
node: UnionInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<ReturnType["return"]>;
export type UnionTransformerDefinition<
Types extends TraverserTypes<any>,
Type extends UnionType<keyof Types>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
ReturnTypes extends TransformerReturnTypes<Types>,
ReturnType extends UnionReturnType,
Context,
> = UnionTransformerFunction<Types, Type, ReturnTypes, ReturnType, Context>;
> = UnionTransformerFunction<
Types,
TypeName,
Type,
ReturnTypes,
ReturnType,
Context
>;
export type PrimitiveTransformerFunction<
Type extends PrimitiveType,
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
ReturnType extends PrimitiveReturnType,
Context,
> = (
originalData: Type["type"],
node: PrimitiveInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<ReturnType["return"]>;
export type PrimitiveTransformerDefinition<
Type extends PrimitiveType,
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
ReturnType extends PrimitiveReturnType,
Context,
> = PrimitiveTransformerFunction<Type, ReturnType, Context>;
> = PrimitiveTransformerFunction<Types, TypeName, Type, ReturnType, Context>;
export type TransformerDefinition<
Types extends TraverserTypes<any>,
@ -117,6 +145,7 @@ export type TransformerDefinition<
? ReturnTypes[TypeName] extends InterfaceReturnType<Types[TypeName]>
? InterfaceTransformerDefinition<
Types,
TypeName,
Types[TypeName],
ReturnTypes,
ReturnTypes[TypeName],
@ -127,6 +156,7 @@ export type TransformerDefinition<
? ReturnTypes[TypeName] extends UnionReturnType
? UnionTransformerDefinition<
Types,
TypeName,
Types[TypeName],
ReturnTypes,
ReturnTypes[TypeName],
@ -136,6 +166,8 @@ export type TransformerDefinition<
: Types[TypeName] extends PrimitiveType
? ReturnTypes[TypeName] extends PrimitiveReturnType
? PrimitiveTransformerDefinition<
Types,
TypeName,
Types[TypeName],
ReturnTypes[TypeName],
Context
@ -161,15 +193,23 @@ export type Transformers<
*/
export type InterfaceTransformerInputDefinition<
Types extends TraverserTypes<any>,
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnTypes extends TransformerReturnTypes<Types>,
ReturnType extends InterfaceReturnType<Type>,
Context,
> = {
transformer: InterfaceTransformerFunction<Types, Type, ReturnType, Context>;
transformer: InterfaceTransformerFunction<
Types,
TypeName,
Type,
ReturnType,
Context
>;
properties?: Partial<{
[PropertyName in keyof Type["properties"]]: InterfaceTransformerPropertyFunction<
Types,
TypeName,
Type,
ReturnTypes,
ReturnType,
@ -181,17 +221,27 @@ export type InterfaceTransformerInputDefinition<
export type UnionTransformerInputDefinition<
Types extends TraverserTypes<any>,
Type extends UnionType<keyof Types>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
ReturnTypes extends TransformerReturnTypes<Types>,
ReturnType extends UnionReturnType,
Context,
> = UnionTransformerFunction<Types, Type, ReturnTypes, ReturnType, Context>;
> = UnionTransformerFunction<
Types,
TypeName,
Type,
ReturnTypes,
ReturnType,
Context
>;
export type PrimitiveTransformerInputDefinition<
Type extends PrimitiveType,
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
ReturnType extends PrimitiveReturnType,
Context,
> = PrimitiveTransformerFunction<Type, ReturnType, Context>;
> = PrimitiveTransformerFunction<Types, TypeName, Type, ReturnType, Context>;
export type TransformerInputDefinition<
Types extends TraverserTypes<any>,
@ -202,6 +252,7 @@ export type TransformerInputDefinition<
? ReturnTypes[TypeName] extends InterfaceReturnType<Types[TypeName]>
? InterfaceTransformerInputDefinition<
Types,
TypeName,
Types[TypeName],
ReturnTypes,
ReturnTypes[TypeName],
@ -212,6 +263,7 @@ export type TransformerInputDefinition<
? ReturnTypes[TypeName] extends UnionReturnType
? UnionTransformerInputDefinition<
Types,
TypeName,
Types[TypeName],
ReturnTypes,
ReturnTypes[TypeName],
@ -221,6 +273,8 @@ export type TransformerInputDefinition<
: Types[TypeName] extends PrimitiveType
? ReturnTypes[TypeName] extends PrimitiveReturnType
? PrimitiveTransformerInputDefinition<
Types,
TypeName,
Types[TypeName],
ReturnTypes[TypeName],
Context

@ -1,20 +1,21 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { TraverserTypes } from "..";
import type { TraverserTypes } from "../../";
import type {
InterfaceReturnType,
TransformerReturnTypes,
} from "../TransformerReturnTypes";
import type { InterfaceTransformerDefinition } from "../Transformers";
import type { InterfaceTraverserDefinition } from "../traverser/TraverserDefinition";
import type { InterfaceType } from "../traverser/TraverserTypes";
import type { InterfaceTraverserDefinition } from "../../traverser/TraverserDefinition";
import type { InterfaceType } from "../../traverser/TraverserTypes";
import { transformerParentSubTraverser } from "./TransformerParentSubTraverser";
import type { TransformerSubTraverserGlobals } from "./util/transformerSubTraverserTypes";
import type { InterfaceInstanceNode } from "../../instanceGraph/nodes/InterfaceInstanceNode";
export async function transformerInterfaceSubTraverser<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
ReturnTypes extends TransformerReturnTypes<Types>,
Type extends InterfaceType<keyof Types>,
Type extends InterfaceType<keyof Types> & Types[TypeName],
ReturnType extends InterfaceReturnType<Type>,
Context,
>(
@ -40,6 +41,7 @@ export async function transformerInterfaceSubTraverser<
itemTypeName
] as unknown as InterfaceTransformerDefinition<
Types,
TypeName,
Type,
ReturnTypes,
ReturnType,
@ -99,6 +101,14 @@ export async function transformerInterfaceSubTraverser<
return toReturn;
}
},
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as InterfaceInstanceNode<
Types,
TypeName,
Type
>,
globals.context,
);
return [propertyName, transformedProperty];
@ -111,6 +121,10 @@ export async function transformerInterfaceSubTraverser<
(input) => {
resolve(input);
},
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as InterfaceInstanceNode<Types, TypeName, Type>,
globals.context,
);
resolve(transformedObject);

@ -1,5 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { BaseReturnType, BaseTraverserTypes, TraverserTypes } from "..";
import type {
BaseReturnType,
BaseTraverserTypes,
TraverserTypes,
} from "../../";
import type { TransformerReturnTypes } from "../TransformerReturnTypes";
import { transformerInterfaceSubTraverser } from "./TransformerInterfaceSubTraverser";
import { transformerPrimitiveSubTraverser } from "./TransformerPrimitiveSubTraverser";

@ -1,18 +1,19 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { TraverserTypes } from "..";
import type { TraverserTypes } from "../../";
import type { PrimitiveInstanceNode } from "../../instanceGraph/nodes/PrimitiveInstanceNode";
import type {
PrimitiveReturnType,
TransformerReturnTypes,
} from "../TransformerReturnTypes";
import type { PrimitiveTransformerDefinition } from "../Transformers";
import type { PrimitiveType } from "../traverser/TraverserTypes";
import type { PrimitiveType } from "../../traverser/TraverserTypes";
import type { TransformerSubTraverserGlobals } from "./util/transformerSubTraverserTypes";
export async function transformerPrimitiveSubTraverser<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
ReturnTypes extends TransformerReturnTypes<Types>,
Type extends PrimitiveType,
Type extends PrimitiveType & Types[TypeName],
ReturnType extends PrimitiveReturnType,
Context,
>(
@ -23,6 +24,19 @@ export async function transformerPrimitiveSubTraverser<
const { transformers } = globals;
const transformer = transformers[
itemTypeName
] as unknown as PrimitiveTransformerDefinition<Type, ReturnType, Context>;
return transformer(item, globals.context);
] as unknown as PrimitiveTransformerDefinition<
Types,
TypeName,
Type,
ReturnType,
Context
>;
return transformer(
item,
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as PrimitiveInstanceNode<Types, TypeName, Type>,
globals.context,
);
}

@ -1,12 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { TraverserTypes } from "..";
import type { TraverserTypes } from "../../";
import type { UnionInstanceNode } from "../../instanceGraph/nodes/UnionInstanceNode";
import type {
TransformerReturnTypes,
UnionReturnType,
} from "../TransformerReturnTypes";
import type { UnionTransformerDefinition } from "../Transformers";
import type { UnionTraverserDefinition } from "../traverser/TraverserDefinition";
import type { UnionType } from "../traverser/TraverserTypes";
import type { UnionTraverserDefinition } from "../../traverser/TraverserDefinition";
import type { UnionType } from "../../traverser/TraverserTypes";
import { transformerParentSubTraverser } from "./TransformerParentSubTraverser";
import type { TransformerSubTraverserGlobals } from "./util/transformerSubTraverserTypes";
@ -14,7 +15,7 @@ export async function transformerUnionSubTraverser<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
ReturnTypes extends TransformerReturnTypes<Types>,
Type extends UnionType<keyof Types>,
Type extends UnionType<keyof Types> & Types[TypeName],
ReturnType extends UnionReturnType,
Context,
>(
@ -39,6 +40,7 @@ export async function transformerUnionSubTraverser<
itemTypeName
] as unknown as UnionTransformerDefinition<
Types,
TypeName,
Type,
ReturnTypes,
ReturnType,
@ -66,6 +68,10 @@ export async function transformerUnionSubTraverser<
(input) => {
resolve(input);
},
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as UnionInstanceNode<Types, TypeName, Type>,
globals.context,
);
resolve(transformedObject);

@ -1,4 +1,4 @@
import type { KeyTypes } from "../..";
import type { KeyTypes } from "../../../";
import { MultiMap } from "./MultiMap";
import { MultiSet } from "./MultiSet";
import type { TransformerSubTraverserExecutingPromises } from "./transformerSubTraverserTypes";

@ -6,7 +6,8 @@ import type {
TransformerReturnTypes,
TraverserDefinitions,
TraverserTypes,
} from "../..";
} from "../../../";
import type { InstanceGraph } from "../../../instanceGraph/instanceGraph";
import type { Transformers } from "../../Transformers";
import type { CircularDepenedencyAwaiter } from "./CircularDependencyAwaiter";
import type { MultiMap } from "./MultiMap";
@ -35,6 +36,7 @@ export interface TransformerSubTraverserGlobals<
executingPromises: TransformerSubTraverserExecutingPromises<keyof Types>;
circularDependencyAwaiter: CircularDepenedencyAwaiter;
superPromise: SuperPromise;
instanceGraph: InstanceGraph<Types>;
context: Context;
}

@ -15,8 +15,9 @@ import type {
UnionVisitorInputDefinition,
Visitors,
VisitorsInput,
} from ".";
import { MultiSet } from "./transformerSubTraversers/util/MultiSet";
} from "../";
import { InstanceGraph } from "../instanceGraph/instanceGraph";
import { MultiSet } from "../transformer/transformerSubTraversers/util/MultiSet";
import { visitorParentSubTraverser } from "./visitorSubTraversers/VisitorParentSubTraverser";
// TODO: Lots of "any" in this file. I'm just done with fancy typescript,
@ -39,15 +40,17 @@ export class Visitor<
}
private applyDefaultInterfaceVisitorProperties<
Type extends InterfaceType<keyof Types>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
>(
typeName: keyof Types,
typePropertiesInput: InterfaceVisitorInputDefinition<
Types,
TypeName,
Type,
Context
>["properties"],
): InterfaceVisitorDefinition<Types, Type, Context>["properties"] {
): InterfaceVisitorDefinition<Types, TypeName, Type, Context>["properties"] {
return Object.keys(
(this.traverserDefinition[typeName] as InterfaceTraverserDefinition<Type>)
.properties,
@ -60,13 +63,21 @@ export class Visitor<
};
}
return agg;
}, {}) as InterfaceVisitorDefinition<Types, Type, Context>["properties"];
}, {}) as InterfaceVisitorDefinition<
Types,
TypeName,
Type,
Context
>["properties"];
}
private applyDefaultInterfaceVisitor<Type extends InterfaceType<keyof Types>>(
private applyDefaultInterfaceVisitor<
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
>(
typeName: keyof Types,
typeInput?: InterfaceVisitorInputDefinition<Types, Type, Context>,
): InterfaceVisitorDefinition<Types, Type, Context> {
typeInput?: InterfaceVisitorInputDefinition<Types, TypeName, Type, Context>,
): InterfaceVisitorDefinition<Types, TypeName, Type, Context> {
if (!typeInput) {
return {
visitor: async () => {
@ -84,9 +95,12 @@ export class Visitor<
};
}
private applyDefaultUnionVisitor<Type extends UnionType<keyof Types>>(
typeInput?: UnionVisitorInputDefinition<Types, Type, Context>,
): UnionVisitorDefinition<Types, Type, Context> {
private applyDefaultUnionVisitor<
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
>(
typeInput?: UnionVisitorInputDefinition<Types, TypeName, Type, Context>,
): UnionVisitorDefinition<Types, TypeName, Type, Context> {
if (!typeInput) {
return async () => {
return;
@ -95,9 +109,12 @@ export class Visitor<
return typeInput;
}
private applyDefaultPrimitiveVisitor<Type extends PrimitiveType>(
typeInput?: PrimitiveVisitorInputDefinition<Type, Context>,
): PrimitiveVisitorDefinition<Type, Context> {
private applyDefaultPrimitiveVisitor<
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
>(
typeInput?: PrimitiveVisitorInputDefinition<Types, TypeName, Type, Context>,
): PrimitiveVisitorDefinition<Types, TypeName, Type, Context> {
if (!typeInput) {
return async () => {
return;
@ -134,10 +151,13 @@ export class Visitor<
itemTypeName: TypeName,
context: Context,
): Promise<void> {
const instanceGraph = new InstanceGraph(this.traverserDefinition);
instanceGraph.getNodeFor(item, itemTypeName);
const toReturn = await visitorParentSubTraverser(item, itemTypeName, {
traverserDefinition: this.traverserDefinition,
visitors: this.visitors,
visitedObjects: new MultiSet(),
instanceGraph,
context,
});
return toReturn;

@ -0,0 +1,157 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type {
InterfaceType,
PrimitiveType,
TraverserTypes,
UnionType,
} from "../";
import type { InterfaceInstanceNode } from "../instanceGraph/nodes/InterfaceInstanceNode";
import type { PrimitiveInstanceNode } from "../instanceGraph/nodes/PrimitiveInstanceNode";
import type { UnionInstanceNode } from "../instanceGraph/nodes/UnionInstanceNode";
export type InterfaceVisitorFunction<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
Context,
> = (
originalData: Type["type"],
node: InterfaceInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<void>;
export type InterfaceVisitorPropertyFunction<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
PropertyName extends keyof Type["properties"],
Context,
> = (
originalData: Types[Type["properties"][PropertyName]]["type"],
node: InterfaceInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<void>;
export type InterfaceVisitorDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
Context,
> = {
visitor: InterfaceVisitorFunction<Types, TypeName, Type, Context>;
properties: {
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction<
Types,
TypeName,
Type,
PropertyName,
Context
>;
};
};
export type UnionVisitorFunction<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
Context,
> = (
originalData: Type["type"],
node: UnionInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<void>;
export type UnionVisitorDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
Context,
> = UnionVisitorFunction<Types, TypeName, Type, Context>;
export type PrimitiveVisitorFunction<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
Context,
> = (
originalData: Type["type"],
node: PrimitiveInstanceNode<Types, TypeName, Type>,
context: Context,
) => Promise<void>;
export type PrimitiveVisitorDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
Context,
> = PrimitiveVisitorFunction<Types, TypeName, Type, Context>;
export type VisitorDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Context,
> = Types[TypeName] extends InterfaceType<keyof Types>
? InterfaceVisitorDefinition<Types, TypeName, Types[TypeName], Context>
: Types[TypeName] extends UnionType<keyof Types>
? UnionVisitorDefinition<Types, TypeName, Types[TypeName], Context>
: Types[TypeName] extends PrimitiveType
? PrimitiveVisitorDefinition<Types, TypeName, Types[TypeName], Context>
: never;
export type Visitors<Types extends TraverserTypes<any>, Context> = {
[TypeName in keyof Types]: VisitorDefinition<Types, TypeName, Context>;
};
/**
* Input
*/
export type InterfaceVisitorInputDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types> & Types[TypeName],
Context,
> = {
visitor: InterfaceVisitorFunction<Types, TypeName, Type, Context>;
properties?: Partial<{
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction<
Types,
TypeName,
Type,
PropertyName,
Context
>;
}>;
};
export type UnionVisitorInputDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types> & Types[TypeName],
Context,
> = UnionVisitorFunction<Types, TypeName, Type, Context>;
export type PrimitiveVisitorInputDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType & Types[TypeName],
Context,
> = PrimitiveVisitorFunction<Types, TypeName, Type, Context>;
export type VisitorInputDefinition<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Context,
> = Types[TypeName] extends InterfaceType<keyof Types>
? InterfaceVisitorInputDefinition<Types, TypeName, Types[TypeName], Context>
: Types[TypeName] extends UnionType<keyof Types>
? UnionVisitorInputDefinition<Types, TypeName, Types[TypeName], Context>
: Types[TypeName] extends PrimitiveType
? PrimitiveVisitorInputDefinition<Types, TypeName, Types[TypeName], Context>
: never;
export type VisitorsInput<
Types extends TraverserTypes<any>,
Context,
> = Partial<{
[TypeName in keyof Types]: VisitorInputDefinition<Types, TypeName, Context>;
}>;

@ -1,14 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { InterfaceVisitorDefinition, TraverserTypes } from "..";
import type { InterfaceTraverserDefinition } from "../traverser/TraverserDefinition";
import type { InterfaceType } from "../traverser/TraverserTypes";
import type { InterfaceVisitorDefinition, TraverserTypes } from "../../";
import type { InterfaceInstanceNode } from "../../instanceGraph/nodes/InterfaceInstanceNode";
import type { InterfaceTraverserDefinition } from "../../traverser/TraverserDefinition";
import type { InterfaceType } from "../../traverser/TraverserTypes";
import type { VisitorSubTraverserGlobals } from "./util/visitorSubTraverserTypes";
import { visitorParentSubTraverser } from "./VisitorParentSubTraverser";
export async function visitorInterfaceSubTraverser<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends InterfaceType<keyof Types>,
Type extends InterfaceType<keyof Types> & Types[TypeName],
Context,
>(
item: Type["type"],
@ -22,16 +23,27 @@ export async function visitorInterfaceSubTraverser<
] as InterfaceTraverserDefinition<Type>;
const visitor = visitors[
itemTypeName
] as unknown as InterfaceVisitorDefinition<Types, Type, Context>;
] as unknown as InterfaceVisitorDefinition<Types, TypeName, Type, Context>;
await Promise.all([
visitor.visitor(item, globals.context),
visitor.visitor(
item,
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as InterfaceInstanceNode<Types, TypeName, Type>,
globals.context,
),
Promise.all(
Object.entries(definition.properties).map(async ([propertyName]) => {
const originalObject = item[propertyName];
const originalPropertyDefinition = definition.properties[propertyName];
const propertyVisitorPromise = visitor.properties[propertyName](
originalObject,
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as InterfaceInstanceNode<Types, TypeName, Type>,
globals.context,
);
let propertyTraverserPromise: Promise<void | void[]>;

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { BaseTraverserTypes, TraverserTypes } from "..";
import type { BaseTraverserTypes, TraverserTypes } from "../../";
import type {
VisitorSubTraverser,
VisitorSubTraverserGlobals,

@ -1,12 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { PrimitiveVisitorDefinition, TraverserTypes } from "..";
import type { PrimitiveType } from "../traverser/TraverserTypes";
import type { PrimitiveVisitorDefinition, TraverserTypes } from "../../";
import type { PrimitiveInstanceNode } from "../../instanceGraph/nodes/PrimitiveInstanceNode";
import type { PrimitiveType } from "../../traverser/TraverserTypes";
import type { VisitorSubTraverserGlobals } from "./util/visitorSubTraverserTypes";
export async function visitorPrimitiveSubTraverser<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends PrimitiveType,
Type extends PrimitiveType & Types[TypeName],
Context,
>(
item: Type["type"],
@ -16,6 +17,13 @@ export async function visitorPrimitiveSubTraverser<
const { visitors } = globals;
const visitor = visitors[
itemTypeName
] as unknown as PrimitiveVisitorDefinition<Type, Context>;
return visitor(item, globals.context);
] as unknown as PrimitiveVisitorDefinition<Types, TypeName, Type, Context>;
return visitor(
item,
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as PrimitiveInstanceNode<Types, TypeName, Type>,
globals.context,
);
}

@ -1,14 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { TraverserTypes, UnionVisitorDefinition } from "..";
import type { UnionTraverserDefinition } from "../traverser/TraverserDefinition";
import type { UnionType } from "../traverser/TraverserTypes";
import type { TraverserTypes, UnionVisitorDefinition } from "../../";
import type { UnionInstanceNode } from "../../instanceGraph/nodes/UnionInstanceNode";
import type { UnionTraverserDefinition } from "../../traverser/TraverserDefinition";
import type { UnionType } from "../../traverser/TraverserTypes";
import type { VisitorSubTraverserGlobals } from "./util/visitorSubTraverserTypes";
import { visitorParentSubTraverser } from "./VisitorParentSubTraverser";
export async function visitorUnionSubTraverser<
Types extends TraverserTypes<any>,
TypeName extends keyof Types,
Type extends UnionType<keyof Types>,
Type extends UnionType<keyof Types> & Types[TypeName],
Context,
>(
item: Type["type"],
@ -21,12 +22,20 @@ export async function visitorUnionSubTraverser<
] as UnionTraverserDefinition<Type>;
const visitor = visitors[itemTypeName] as unknown as UnionVisitorDefinition<
Types,
TypeName,
Type,
Context
>;
const itemSpecificTypeName = definition.selector(item);
await Promise.all([
visitor(item, globals.context),
visitor(
item,
globals.instanceGraph.getNodeFor(
item,
itemTypeName,
) as unknown as UnionInstanceNode<Types, TypeName, Type>,
globals.context,
),
visitorParentSubTraverser(item, itemSpecificTypeName, globals),
]);
}

@ -4,8 +4,9 @@ import type {
TraverserDefinitions,
TraverserTypes,
Visitors,
} from "../..";
import type { MultiSet } from "../../transformerSubTraversers/util/MultiSet";
} from "../../../";
import type { InstanceGraph } from "../../../instanceGraph/instanceGraph";
import type { MultiSet } from "../../../transformer/transformerSubTraversers/util/MultiSet";
export type VisitorSubTraverser<
Types extends TraverserTypes<any>,
@ -25,5 +26,6 @@ export interface VisitorSubTraverserGlobals<
traverserDefinition: TraverserDefinitions<Types>;
visitors: Visitors<Types, Context>;
visitedObjects: MultiSet<object, keyof Types>;
instanceGraph: InstanceGraph<Types>;
context: Context;
}

@ -0,0 +1,156 @@
import type { TraverserDefinitions, ValidateTraverserTypes } from "../../src";
import { InstanceGraph } from "../../src/instanceGraph/instanceGraph";
describe("InstanceGraph", () => {
/**
* Types
*/
type Element = "Water" | "Earth" | "Fire" | "Air";
interface Bender {
name: string;
element: Element;
friends: Person[];
}
interface NonBender {
name: string;
friends: Person[];
}
type Person = Bender | NonBender;
/**
* Raw Data to Traverse
*/
const aang: Bender = {
name: "Aang",
element: "Air",
friends: [],
};
const sokka: NonBender = {
name: "Sokka",
friends: [],
};
const katara: Bender = {
name: "Katara",
element: "Water",
friends: [],
};
aang.friends.push(sokka, katara);
sokka.friends.push(aang, katara);
katara.friends.push(aang, sokka);
/**
* Traverser Types
*/
type AvatarTraverserTypes = ValidateTraverserTypes<{
Element: {
kind: "primitive";
type: Element;
};
Bender: {
kind: "interface";
type: Bender;
properties: {
element: "Element";
friends: "Person";
};
};
NonBender: {
kind: "interface";
type: NonBender;
properties: {
friends: "Person";
};
};
Person: {
kind: "union";
type: Person;
typeNames: "Bender" | "NonBender";
};
}>;
/**
* Create the traverser definition
*/
const avatarTraverserDefinition: TraverserDefinitions<AvatarTraverserTypes> =
{
Element: {
kind: "primitive",
},
Bender: {
kind: "interface",
properties: {
element: "Element",
friends: "Person",
},
},
NonBender: {
kind: "interface",
properties: {
friends: "Person",
},
},
Person: {
kind: "union",
selector: (item) => {
return (item as Bender).element ? "Bender" : "NonBender";
},
},
};
describe("Build Instance Graph", () => {
it("returns child nodes when child methods are called.", () => {
const graph = new InstanceGraph(avatarTraverserDefinition);
const aangBender = graph.getNodeFor(aang, "Bender");
expect(aangBender.typeName).toBe("Bender");
expect(aangBender.instance.name).toBe("Aang");
// child
const aangElement = aangBender.child("element");
expect(aangElement.instance).toBe("Air");
expect(aangElement.typeName).toBe("Element");
const aangFriends = aangBender.child("friends");
expect(aangFriends.length).toBe(2);
const sokkaPerson = aangFriends[0];
const kataraPerson = aangFriends[1];
expect(sokkaPerson.instance.name).toBe("Sokka");
expect(kataraPerson.instance.name).toBe("Katara");
expect(sokkaPerson.typeName).toBe("Person");
expect(kataraPerson.typeName).toBe("Person");
const sokkaNonBender = sokkaPerson.child();
expect(sokkaNonBender.instance.name).toBe("Sokka");
expect(sokkaNonBender.typeName).toBe("NonBender");
if (sokkaNonBender.typeName === "NonBender") {
const aangPerson = sokkaNonBender.child("friends")[0];
const aangBender2 = aangPerson.child();
expect(aangBender2).toBe(aangBender);
}
// allChildren
const [childElemement, childSokka, childKatara] =
aangBender.allChildren();
expect(childElemement.instance).toBe("Air");
expect((childSokka.instance as NonBender).name).toBe("Sokka");
expect((childKatara.instance as Bender).name).toBe("Katara");
const childOfSokkaPerson = sokkaPerson.allChildren();
expect(childOfSokkaPerson.length).toBe(1);
expect(childOfSokkaPerson[0].instance.name).toBe("Sokka");
});
it("returns parent nodes when parent methods are called.", () => {
const graph = new InstanceGraph(avatarTraverserDefinition);
const aangBender = graph.getNodeFor(aang, "Bender");
// parent
const [aangPerson] = aangBender.parent("Person");
expect(aangPerson.instance.name).toBe("Aang");
expect(aangPerson.typeName).toBe("Person");
const [sokkaNonBender] = aangPerson.parent("NonBender", "friends");
const [kataraBender] = aangPerson.parent("Bender", "friends");
expect(sokkaNonBender.typeName).toBe("NonBender");
expect(sokkaNonBender.instance.name).toBe("Sokka");
expect(kataraBender.typeName).toBe("Bender");
expect(kataraBender.instance.name).toBe("Katara");
});
});
describe("Transformer", () => {
it("transforms", () => {});
});
});

@ -1,7 +1,7 @@
import type { TraverserDefinition } from "../../../src";
import type { TraverserDefinitions } from "../../../src";
import type { AvatarTraverserTypes, Bender } from "./AvatarTraverserTypes";
export const avatarTraverserDefinition: TraverserDefinition<AvatarTraverserTypes> =
export const avatarTraverserDefinition: TraverserDefinitions<AvatarTraverserTypes> =
{
Element: {
kind: "primitive",

@ -1,161 +1,19 @@
import type {
TraverserDefinitions,
ValidateTraverserTypes,
} from "../../../src";
import { InstanceGraph } from "../../../src/instanceGraph/instanceGraph";
import { BrokenAvatarTransformer } from "./AvatarBrokenTransformer";
import { AvatarErroringTransformer } from "./AvatarErroringTransformer";
import { aang } from "./sampleData";
describe("AvatarExample", () => {
/**
* Types
*/
type Element = "Water" | "Earth" | "Fire" | "Air";
interface Bender {
name: string;
element: Element;
friends: Person[];
}
interface NonBender {
name: string;
friends: Person[];
}
type Person = Bender | NonBender;
/**
* Raw Data to Traverse
*/
const aang: Bender = {
name: "Aang",
element: "Air",
friends: [],
};
const sokka: NonBender = {
name: "Sokka",
friends: [],
};
const katara: Bender = {
name: "Katara",
element: "Water",
friends: [],
};
aang.friends.push(sokka, katara);
sokka.friends.push(aang, katara);
katara.friends.push(aang, sokka);
/**
* Traverser Types
*/
type AvatarTraverserTypes = ValidateTraverserTypes<{
Element: {
kind: "primitive";
type: Element;
};
Bender: {
kind: "interface";
type: Bender;
properties: {
element: "Element";
friends: "Person";
};
};
NonBender: {
kind: "interface";
type: NonBender;
properties: {
friends: "Person";
};
};
Person: {
kind: "union";
type: Person;
typeNames: "Bender" | "NonBender";
};
}>;
/**
* Create the traverser definition
*/
const avatarTraverserDefinition: TraverserDefinitions<AvatarTraverserTypes> =
{
Element: {
kind: "primitive",
},
Bender: {
kind: "interface",
properties: {
element: "Element",
friends: "Person",
},
},
NonBender: {
kind: "interface",
properties: {
friends: "Person",
},
},
Person: {
kind: "union",
selector: (item) => {
return (item as Bender).element ? "Bender" : "NonBender";
},
},
};
describe("Build Instance Graph", () => {
it("returns child nodes when child methods are called.", () => {
const graph = new InstanceGraph(avatarTraverserDefinition);
const aangBender = graph.getNodeFor(aang, "Bender");
expect(aangBender.typeName).toBe("Bender");
expect(aangBender.instance.name).toBe("Aang");
// child
const aangElement = aangBender.child("element");
expect(aangElement.instance).toBe("Air");
expect(aangElement.typeName).toBe("Element");
const aangFriends = aangBender.child("friends");
expect(aangFriends.length).toBe(2);
const sokkaPerson = aangFriends[0];
const kataraPerson = aangFriends[1];
expect(sokkaPerson.instance.name).toBe("Sokka");
expect(kataraPerson.instance.name).toBe("Katara");
expect(sokkaPerson.typeName).toBe("Person");
expect(kataraPerson.typeName).toBe("Person");
const sokkaNonBender = sokkaPerson.child();
expect(sokkaNonBender.instance.name).toBe("Sokka");
expect(sokkaNonBender.typeName).toBe("NonBender");
if (sokkaNonBender.typeName === "NonBender") {
const aangPerson = sokkaNonBender.child("friends")[0];
const aangBender2 = aangPerson.child();
expect(aangBender2).toBe(aangBender);
}
// allChildren
const [childElemement, childSokka, childKatara] =
aangBender.allChildren();
expect(childElemement.instance).toBe("Air");
expect((childSokka.instance as NonBender).name).toBe("Sokka");
expect((childKatara.instance as Bender).name).toBe("Katara");
const childOfSokkaPerson = sokkaPerson.allChildren();
expect(childOfSokkaPerson.length).toBe(1);
expect(childOfSokkaPerson[0].instance.name).toBe("Sokka");
});
it("returns parent nodes when parent methods are called.", () => {
const graph = new InstanceGraph(avatarTraverserDefinition);
const aangBender = graph.getNodeFor(aang, "Bender");
// parent
const [aangPerson] = aangBender.parent("Person");
expect(aangPerson.instance.name).toBe("Aang");
expect(aangPerson.typeName).toBe("Person");
const [sokkaNonBender] = aangPerson.parent("NonBender", "friends");
const [kataraBender] = aangPerson.parent("Bender", "friends");
expect(sokkaNonBender.typeName).toBe("NonBender");
expect(sokkaNonBender.instance.name).toBe("Sokka");
expect(kataraBender.typeName).toBe("Bender");
expect(kataraBender.instance.name).toBe("Katara");
});
describe("Avatar", () => {
it("Throws an error before entering an infinite loop", async () => {
await expect(
BrokenAvatarTransformer.transform(aang, "Bender", undefined),
).rejects.toThrow(
`Circular dependency found. Use the 'setReturnPointer' function. The loop includes the 'Bender' type`,
);
});
describe("Transformer", () => {
it("transforms", () => {
})
it("Bubbles errors", async () => {
await expect(
AvatarErroringTransformer.transform(aang, "Bender", undefined),
).rejects.toThrow("No Non Benders Allowed");
});
});

@ -1,19 +0,0 @@
import { BrokenAvatarTransformer } from "./AvatarBrokenTransformer";
import { AvatarErroringTransformer } from "./AvatarErroringTransformer";
import { aang } from "./sampleData";
describe("Avatar", () => {
it("Throws an error before entering an infinite loop", async () => {
await expect(
BrokenAvatarTransformer.transform(aang, "Bender", undefined),
).rejects.toThrow(
`Circular dependency found. Use the 'setReturnPointer' function. The loop includes the 'Bender' type`,
);
});
it("Bubbles errors", async () => {
await expect(
AvatarErroringTransformer.transform(aang, "Bender", undefined),
).rejects.toThrow("No Non Benders Allowed");
});
});
Loading…
Cancel
Save