From 1497e2950c6b515af12f888609fa9725715367a4 Mon Sep 17 00:00:00 2001 From: Stephen Hanson Date: Wed, 29 Sep 2021 21:28:17 -0500 Subject: [PATCH] Allow specifying different return type for 'create' (#74) * Allow specifying different return type for 'create' * Update readme * Fix OnCreate type --- README.md | 199 ++++++++++++++----------- lib/__tests__/factory-builders.test.ts | 172 ++++++++++++++------- lib/__tests__/factory.test.ts | 52 ++++--- lib/__tests__/helpers/sleep.ts | 2 + lib/builder.ts | 49 ++++-- lib/factory.ts | 58 ++++--- lib/types.ts | 10 +- 7 files changed, 342 insertions(+), 200 deletions(-) create mode 100644 lib/__tests__/helpers/sleep.ts diff --git a/README.md b/README.md index d735d87..1d1946d 100644 --- a/README.md +++ b/README.md @@ -58,14 +58,6 @@ Pass parameters as the first argument to `build` to override your factory defaults. These parameters are deep-merged into the default object returned by your factory. -`build` also supports a seconds argument with the following keys: - -- `transient`: data for use in your factory that doesn't get overlaid onto your - result object. More on this in the [Transient - Params](#params-that-dont-map-to-the-result-object-transient-params) section -- `associations`: often not required but can be useful in order to short-circuit creating associations. More on this in the [Associations](#Associations) - section - ```typescript // my-test.test.ts import { factories } from './factories'; @@ -89,7 +81,13 @@ const user = await userFactory.create({ name: 'Maria' }); user.name; // Maria ``` -`create` returns a promise instead of the object itself but otherwise has the same API as `build`. The action that occurs when calling `create` can be specified in your factory's `onCreate` method as [described below](#on-create-hook). +`create` returns a promise instead of the object itself but otherwise has the same API as `build`. The action that occurs when calling `create` is specified by defining an `onCreate` method on your factory as [described below](#on-create-hook). + +`create` can also return a different type from `build`. This type can be specified when defining your factory: + +``` +Factory.define +``` ## Documentation @@ -135,47 +133,15 @@ export default Factory.define( ); ``` -### Associations - -Factories can import and reference other factories for associations: - -```typescript -import userFactory from './user'; - -const postFactory = Factory.define(() => ({ - title: 'My Blog Post', - author: userFactory.build(), -})); -``` - -If you'd like to be able to pass in an association when building your object and -short-circuit the call to `yourFactory.build()`, use the `associations` -variable provided to your factory: - -```typescript -const postFactory = Factory.define(({ associations }) => ({ - title: 'My Blog Post', - author: associations.author || userFactory.build(), -})); -``` - -Then build your object like this: +### `build` API -```typescript -const jordan = userFactory.build({ name: 'Jordan' }); -factories.post.build({}, { associations: { author: jordan } }); -``` - -If two factories reference each other, they can usually import each other -without issues, but TypeScript might require you to explicitly type your -factory before exporting so it can determine the type before the circular -references resolve: +`build` supports a second argument with the following keys: -```typescript -// the extra Factory typing can be necessary with circular imports -const postFactory: Factory = Factory.define(() => ({ ...})); -export default postFactory; -``` +- `transient`: data for use in your factory that doesn't get overlaid onto your + result object. More on this in the [Transient + Params](#params-that-dont-map-to-the-result-object-transient-params) section +- `associations`: often not required but can be useful in order to short-circuit creating associations. More on this in the [Associations](#Associations) + section ### Use `params` to access passed in properties @@ -284,55 +250,90 @@ export default Factory.define(({ sequence, afterBuild }) => { ### On-create hook -You can instruct factories to chain promises together when creating an object. -This allows you to perform asynchronous actions when building models such as -creating the model on a server. +Before using `create` to asynchronously create objects, an `onCreate` must be defined. ```typescript -export default Factory.define(({ sequence, onCreate }) => { +const userFactory = Factory.define(({ sequence, onCreate }) => { onCreate(user => { return apiService.create(user); }); return { - id: sequence, - name: 'Bob', - posts: [], + name: 'Maria', }; }); + +const user = await userFactory.create(); +``` + +### After-create hook + +Similar to `onCreate`, `afterCreate`s can also be defined. These are executed after the `onCreate`, and multiple can be defined for a given factory. + +```typescript +const userFactory Factory.define( + ({ sequence, onCreate, afterCreate }) => { + onCreate(user => { + return apiService.create(user); + }); + + afterCreate(async savedUser => savedUser); + + return { + id: sequence, + name: 'Bob', + posts: [], + }; + }, +); + +// can define additional afterCreates +const savedUser = userFactory.afterCreate(async savedUser => savedUser).create() ``` ### Extending factories -Factories can easily be extended using the extension methods: `params`, -`transient`, `associations`, `afterBuild`, and `onCreate`. These set default attributes that get passed to the factory on `build`: +Factories can be extended using the extension methods: `params`, `transient`, +`associations`, `afterBuild`, `afterCreate` and `onCreate`. These set default +attributes that get passed to the factory on `build`. They return a new factory +and do not modify the factory they are called on : ```typescript const userFactory = Factory.define(() => ({ - name: 'Kassandra', admin: false, })); const adminFactory = userFactory.params({ admin: true }); -const admin = adminFactory.build(); -admin.admin; // true +adminFactory.build().admin; // true +userFactory.build().admin; // false +``` + +`params`, `associations`, and `transient` behave in the same way as the arguments to `build`. The following are equivalent: + +```typescript +const user = userFactory + .params({ admin: true }) + .associations({ post: postFactory.build() }) + .transient({ name: 'Jared' }) + .build(); + +const user2 = userFactory.build( + { admin: true }, + { + associations: { post: postFactory.build() }, + transient: { name: 'Jared' }, + }, +); ``` -The extension methods return a new factory with the specified `params`, -`transientParams`, `associations`, or `afterBuild` added to it and do not -modify the factory they are called on. When `build` is called on the factory, -the `params`, `transientParams`, and `associations` are passed in along with -the values supplied to `build`. Values supplied to `build` override these -defaults. +Additionally, the following extension methods are available: -`afterBuild` just adds a function that is called when the object is built. -The `afterBuild` defined in `Factory.define` is always called first if -specified, and then any `afterBuild` functions defined with the extension -method are called sequentially in the order they were added. The `onCreate` -methods use the same order precedence. +- `afterBuild` - executed after an object is built. Multiple can be defined +- `onCreate` - defines or replaces the behavior of `create()`. Must be defined prior to calling `create()`. Only one can be defined. +- `afterCreate` - called after `onCreate()` before the object is returned from `create()`. Multiple can be defined These extension methods can be called multiple times to continue extending -factories, and they do not modify the original factory: +factories: ```typescript const eliFactory = userFactory @@ -358,8 +359,7 @@ If you find yourself frequently building objects with a certain set of properties, it might be time to either extend the factory or create a reusable builder method. -Factories are just classes, so adding reusable builder methods is as simple -as subclassing `Factory` and defining any desired methods: +Factories are just classes, so adding reusable builder methods can be achieved by subclassing `Factory` and defining any desired methods: ```typescript class UserFactory extends Factory { @@ -386,34 +386,57 @@ const user = userFactory.admin().registered().build() ``` To learn more about the factory builder methods `params`, `transient`, -`associations`, and `afterBuild`, see [Extending factories](#extending-factories), above. +`associations`, `afterBuild`, `onCreate`, and `afterCreate`, see [Extending factories](#extending-factories), above. -### Rewind Sequence +## Advanced -A factory's sequence can be rewound with `rewindSequence()`. -This sets the sequence back to its original starting value. +### Associations -Given the following factory +Factories can import and reference other factories for associations: ```typescript -export default Factory.define(({ sequence }) => ({ - email: `person${sequence}@example.com`, +import userFactory from './user'; + +const postFactory = Factory.define(() => ({ + title: 'My Blog Post', + author: userFactory.build(), })); ``` -You can rewind a factory's sequence at your discretion +If you'd like to be able to pass in an association when building your object and +short-circuit the call to `yourFactory.build()`, use the `associations` +variable provided to your factory: ```typescript -import { factories } from './factories'; +const postFactory = Factory.define(({ associations }) => ({ + title: 'My Blog Post', + author: associations.author || userFactory.build(), +})); +``` -factories.user.build(); // { email: 'person1@example.com' } -factories.user.build(); // { email: 'person2@example.com' } +Then build your object like this: -factories.user.rewindSequence(); +```typescript +const jordan = userFactory.build({ name: 'Jordan' }); +factories.post.build({}, { associations: { author: jordan } }); +``` -factories.user.build(); // { email: 'person1@example.com' } +If two factories reference each other, they can usually import each other +without issues, but TypeScript might require you to explicitly type your +factory before exporting so it can determine the type before the circular +references resolve: + +```typescript +// the extra Factory typing can be necessary with circular imports +const postFactory: Factory = Factory.define(() => ({ ...})); +export default postFactory; ``` +### Rewind Sequence + +A factory's sequence can be rewound with `rewindSequence()`. +This sets the sequence back to its original starting value. + ## Contributing See the [CONTRIBUTING] document. @@ -430,7 +453,7 @@ short. The Fishery is where things are built. ## License -Fishery is Copyright © 2020 Stephen Hanson and thoughtbot. It is free +Fishery is Copyright © 2021 Stephen Hanson and thoughtbot. It is free software, and may be redistributed under the terms specified in the [LICENSE](/LICENSE) file. diff --git a/lib/__tests__/factory-builders.test.ts b/lib/__tests__/factory-builders.test.ts index 5127fad..dcfe1d8 100644 --- a/lib/__tests__/factory-builders.test.ts +++ b/lib/__tests__/factory-builders.test.ts @@ -1,4 +1,5 @@ import { Factory } from 'fishery'; +import { sleep } from './helpers/sleep'; type Post = { id: string }; type User = { @@ -109,92 +110,153 @@ describe('afterBuild', () => { }); describe('onCreate', () => { - it('defines a function that is called to create', async () => { - const onCreate = jest.fn(user => { - user.id = '123'; - return Promise.resolve(user); + it('defines a function that is called on create', async () => { + type User = { id: string }; + const factory = Factory.define(() => ({ id: '1' })); + + const user = await factory + .onCreate(user => { + user.id = 'bla'; + return user; + }) + .create(); + expect(user.id).toEqual('bla'); + }); + + it('overrides onCreate from the generator', async () => { + type User = { id: string }; + const factory = Factory.define(({ onCreate }) => { + onCreate(user => { + user.id = 'generator'; + return user; + }); + + return { id: '1' }; }); - const user = await userFactory.onCreate(onCreate).create(); - expect(user.id).toEqual('123'); - expect(onCreate).toHaveBeenCalledWith(user); + + const user = await factory + .onCreate(user => { + user.id = 'builder'; + return user; + }) + .create(); + expect(user.id).toEqual('builder'); }); - it('calls chained or inherited onCreates sequentially', async () => { - const onCreate1 = jest.fn(user => { - user.id = 'a'; + it('raises an error if onCreate was not defined', async () => { + type User = { id: string }; + const factory = Factory.define(() => ({ id: '1' })); + + await expect(() => factory.create()).rejects.toHaveProperty( + 'message', + 'Attempted to call `create`, but no onCreate defined', + ); + }); +}); + +describe('afterCreate', () => { + it('defines a function that is called after the onCreate', async () => { + type User = { id: string }; + const factory = Factory.define(() => ({ id: '1' })); + const afterCreate = jest.fn(user => { + user.id = 'afterCreate'; return Promise.resolve(user); }); - const onCreate2 = jest.fn(user => { - user.id = 'b'; + + const user = await factory + .onCreate(user => { + user.id = 'onCreate'; + return user; + }) + .afterCreate(afterCreate) + .create(); + + expect(user.id).toEqual('afterCreate'); + }); + + it('calls chained or inherited afterCreates sequentially', async () => { + type User = { id: string }; + const factory = Factory.define(() => ({ id: '1' })); + + const afterCreate1 = jest.fn(async user => { + user.id = 'afterCreate1'; + sleep(10); + return user; + }); + + const afterCreate2 = jest.fn(user => { + user.id = 'afterCreate2'; return Promise.resolve(user); }); - const user = await userFactory - .onCreate(onCreate1) - .onCreate(onCreate2) + const user = await factory + .onCreate(u => u) + .afterCreate(afterCreate1) + .afterCreate(afterCreate2) .create(); - expect(user.id).toEqual('b'); - expect(onCreate1).toHaveBeenCalledTimes(1); - expect(onCreate2).toHaveBeenCalledTimes(1); + expect(user.id).toEqual('afterCreate2'); + expect(afterCreate1).toHaveBeenCalledTimes(1); + expect(afterCreate2).toHaveBeenCalledTimes(1); }); - it('calls onCreate from the generator function before those later defined by builder', async () => { - const onCreateGenerator = jest.fn(user => { + it('calls afterCreate from the generator function before those later defined by builder', async () => { + const afterCreateGenerator = jest.fn(user => { user.id = 'generator'; return Promise.resolve(user); }); - const onCreateBuilder = jest.fn(user => { + const afterCreateBuilder = jest.fn(user => { user.id = 'builder'; return Promise.resolve(user); }); type User = { id: string }; - const userFactory = Factory.define(({ onCreate }) => { - onCreate(onCreateGenerator); - return { id: '1' }; + const userFactory = Factory.define(({ afterCreate, onCreate }) => { + onCreate(user => user); + afterCreate(afterCreateGenerator); + return { id: '1', name: '1' }; }); - const user = await userFactory.onCreate(onCreateBuilder).create(); + const user = await userFactory.afterCreate(afterCreateBuilder).create(); expect(user.id).toEqual('builder'); - expect(onCreateGenerator).toHaveBeenCalledTimes(1); - expect(onCreateBuilder).toHaveBeenCalledTimes(1); + expect(afterCreateGenerator).toHaveBeenCalledTimes(1); + expect(afterCreateBuilder).toHaveBeenCalledTimes(1); }); - it('does not modify the original factory', async () => { - const onCreate = (user: User) => { - user.id = 'onCreate'; - return Promise.resolve(user); - }; - - userFactory.onCreate(onCreate); - const user = await userFactory.create(); - expect(user.id).toEqual('1'); - }); + it('chains return values from afterCreate hooks', async () => { + type User = { id: string }; + const factory = Factory.define(() => ({ id: '1' })); - it('chains return values from onCreate hooks', async () => { - const onCreate1 = jest.fn(async user => { - return Promise.resolve(userFactory.build({ id: 'onCreate1' })); + const afterCreate1 = jest.fn(async (user: User) => { + user.id = 'afterCreate1'; + return user; }); - const onCreate2 = jest.fn(async user => { - user.firstName = 'onCreate2'; - return Promise.resolve(user); + + const afterCreate2 = jest.fn(async user => { + user.id = 'afterCreate2'; + return user; }); - const user = await userFactory - .onCreate(onCreate1) - .onCreate(onCreate2) + + const user = await factory + .onCreate(user => { + user.id = 'onCreate'; + return user; + }) + .afterCreate(afterCreate1) + .afterCreate(afterCreate2) .create(); - expect(user.id).toEqual('onCreate1'); - expect(user.firstName).toEqual('onCreate2'); + expect(user.id).toEqual('afterCreate2'); }); - it('rejections are handled', async () => { - const onCreate = jest.fn(async user => { - user.id = 'rejection'; - return Promise.reject(user); + it('rejects if afterCreate fails', async () => { + const afterCreate = jest.fn(async user => { + return Promise.reject('failed'); }); - await expect(userFactory.onCreate(onCreate).create()).rejects.toMatchObject( - { id: 'rejection' }, - ); + await expect( + userFactory + .onCreate(user => user) + .afterCreate(afterCreate) + .create(), + ).rejects.toEqual('failed'); }); }); diff --git a/lib/__tests__/factory.test.ts b/lib/__tests__/factory.test.ts index c5d4df4..dd8f5f7 100644 --- a/lib/__tests__/factory.test.ts +++ b/lib/__tests__/factory.test.ts @@ -1,4 +1,4 @@ -import { CreateFn, Factory, HookFn } from 'fishery'; +import { OnCreateFn, Factory, HookFn } from 'fishery'; type User = { id: string; @@ -6,7 +6,8 @@ type User = { address?: { city: string; state: string }; }; -const userFactory = Factory.define(({ sequence }) => { +const userFactory = Factory.define(({ onCreate, sequence }) => { + onCreate(user => user); const name = 'Bob'; return { id: `user-${sequence}`, @@ -65,6 +66,35 @@ describe('factory.create', () => { expect(user.name).toEqual('susan'); expect(user.address?.state).toEqual('MI'); }); + + it('returns the type specified by the third type parameter', async () => { + type UserBeforeSave = { + name: string; + }; + + type User = { + name: string; + id: number; + }; + + const factory = Factory.define( + ({ onCreate }) => { + onCreate(async user => { + return { ...user, id: 2 }; + }); + + return { name: 'Ralph' }; + }, + ); + + const user = factory.build(); + const user2 = await factory.create(); + + // @ts-expect-error + user.id; + + expect(user2.id).toEqual(2); + }); }); describe('factory.createList', () => { @@ -126,30 +156,18 @@ describe('afterBuild', () => { }); describe('onCreate', () => { - it('passes the object for manipulation', async () => { + it('defines a function that is called on create', async () => { + type User = { id: string }; const factory = Factory.define(({ onCreate }) => { onCreate(user => { user.id = 'bla'; return Promise.resolve(user); }); - return { id: '1', name: 'Ralph' }; + return { id: '1' }; }); const user = await factory.create(); expect(user.id).toEqual('bla'); }); - - describe('when not a function', () => { - it('raises an error', () => { - const factory = Factory.define(({ onCreate }) => { - onCreate('5' as unknown as CreateFn); - return { id: '1', name: 'Ralph' }; - }); - - return expect(factory.create()).rejects.toThrowError( - /must be a function/, - ); - }); - }); }); diff --git a/lib/__tests__/helpers/sleep.ts b/lib/__tests__/helpers/sleep.ts new file mode 100644 index 0000000..28ac5fe --- /dev/null +++ b/lib/__tests__/helpers/sleep.ts @@ -0,0 +1,2 @@ +export const sleep = (ms: number) => + new Promise(resolve => setTimeout(resolve, ms)); diff --git a/lib/builder.ts b/lib/builder.ts index 0089773..83e5003 100644 --- a/lib/builder.ts +++ b/lib/builder.ts @@ -3,26 +3,28 @@ import { HookFn, GeneratorFnOptions, DeepPartial, - CreateFn, + OnCreateFn, + AfterCreateFn, } from './types'; -import mergeWith from 'lodash.mergewith'; import { merge, mergeCustomizer } from './merge'; -export class FactoryBuilder { +export class FactoryBuilder { constructor( - private generator: GeneratorFn, + private generator: GeneratorFn, private sequence: number, private params: DeepPartial, private transientParams: Partial, private associations: Partial, private afterBuilds: HookFn[], - private onCreates: CreateFn[], + private afterCreates: AfterCreateFn[], + private onCreate?: OnCreateFn, ) {} build() { - const generatorOptions: GeneratorFnOptions = { + const generatorOptions: GeneratorFnOptions = { sequence: this.sequence, afterBuild: this.setAfterBuild, + afterCreate: this.setAfterCreate, onCreate: this.setOnCreate, params: this.params, associations: this.associations, @@ -37,15 +39,22 @@ export class FactoryBuilder { async create() { const object = this.build(); - return this._callOnCreates(object); + const created = await this._callOnCreate(object); + return this._callAfterCreates(created); } setAfterBuild = (hook: HookFn) => { this.afterBuilds = [hook, ...this.afterBuilds]; }; - setOnCreate = (hook: CreateFn) => { - this.onCreates = [hook, ...this.onCreates]; + setAfterCreate = (hook: AfterCreateFn) => { + this.afterCreates = [hook, ...this.afterCreates]; + }; + + setOnCreate = (hook: OnCreateFn) => { + if (!this.onCreate) { + this.onCreate = hook; + } }; // merge params and associations into object. The only reason 'associations' @@ -66,16 +75,24 @@ export class FactoryBuilder { }); } - _callOnCreates(object: T): Promise { - let created = Promise.resolve(object); + async _callOnCreate(object: T): Promise { + if (!this.onCreate) { + throw new Error('Attempted to call `create`, but no onCreate defined'); + } + + return this.onCreate(object); + } + + async _callAfterCreates(object: C): Promise { + let created = object; - this.onCreates.forEach(onCreate => { - if (typeof onCreate === 'function') { - created = created.then(onCreate); + for (const afterCreate of this.afterCreates) { + if (typeof afterCreate === 'function') { + created = await afterCreate(created); } else { - throw new Error('"onCreate" must be a function'); + throw new Error('"afterCreate" must be a function'); } - }); + } return created; } diff --git a/lib/factory.ts b/lib/factory.ts index a7070da..a0d36a3 100644 --- a/lib/factory.ts +++ b/lib/factory.ts @@ -4,25 +4,27 @@ import { BuildOptions, GeneratorFnOptions, HookFn, - CreateFn, + OnCreateFn, + AfterCreateFn, } from './types'; import { FactoryBuilder } from './builder'; import { merge, mergeCustomizer } from './merge'; const SEQUENCE_START_VALUE = 1; -export class Factory { +export class Factory { // id is an object so it is shared between extended factories private id: { value: number } = { value: SEQUENCE_START_VALUE }; private _afterBuilds: HookFn[] = []; - private _onCreates: CreateFn[] = []; + private _afterCreates: AfterCreateFn[] = []; + private _onCreate?: OnCreateFn; private _associations: Partial = {}; private _params: DeepPartial = {}; private _transient: Partial = {}; constructor( - private readonly generator: (opts: GeneratorFnOptions) => T, + private readonly generator: (opts: GeneratorFnOptions) => T, ) {} /** @@ -32,10 +34,10 @@ export class Factory { * @template C The class of the factory object being created. * @param generator - your factory function */ - static define>( - this: new (generator: GeneratorFn) => C, - generator: GeneratorFn, - ): C { + static define>( + this: new (generator: GeneratorFn) => F, + generator: GeneratorFn, + ): F { return new this(generator); } @@ -69,7 +71,7 @@ export class Factory { async create( params: DeepPartial = {}, options: BuildOptions = {}, - ): Promise { + ): Promise { return this.builder(params, options).create(); } @@ -77,8 +79,8 @@ export class Factory { number: number, params: DeepPartial = {}, options: BuildOptions = {}, - ): Promise { - let list: Promise[] = []; + ): Promise { + let list: Promise[] = []; for (let i = 0; i < number; i++) { list.push(this.create(params, options)); } @@ -98,13 +100,28 @@ export class Factory { } /** - * Extend the factory by adding a function to be called on creation. - * @param onCreateFn - The function to call. IT accepts your object of type T. The value this function returns gets returned from "create" + * Define a transform that occurs when `create` is called on the factory. Specifying an `onCreate` overrides any previous `onCreate`s. + * To return a different type from `build`, specify a third type argument when defining the factory. + * @param onCreateFn - The function to call. IT accepts your object of type T. + * The value this function returns gets returned from "create" after any + * `afterCreate`s are run * @return a new factory */ - onCreate(onCreateFn: CreateFn): this { + onCreate(onCreateFn: OnCreateFn): this { const factory = this.clone(); - factory._onCreates.push(onCreateFn); + factory._onCreate = onCreateFn; + return factory; + } + + /** + * Extend the factory by adding a function to be called after creation. This is called after `onCreate` but before the object is returned from `create`. + * If multiple are defined, they are chained. + * @param afterCreateFn + * @return a new factory + */ + afterCreate(afterCreateFn: AfterCreateFn): this { + const factory = this.clone(); + factory._afterCreates.push(afterCreateFn); return factory; } @@ -148,12 +165,12 @@ export class Factory { this.id.value = SEQUENCE_START_VALUE; } - protected clone>(this: C): C { + protected clone>(this: F): F { const copy = new (this.constructor as { - new (generator: GeneratorFn): C; + new (generator: GeneratorFn): F; })(this.generator); Object.assign(copy, this); - copy._onCreates = [...this._onCreates]; + copy._afterCreates = [...this._afterCreates]; copy._afterBuilds = [...this._afterBuilds]; return copy; } @@ -166,14 +183,15 @@ export class Factory { params: DeepPartial = {}, options: BuildOptions = {}, ) { - return new FactoryBuilder( + return new FactoryBuilder( this.generator, this.sequence(), merge({}, this._params, params, mergeCustomizer), { ...this._transient, ...options.transient }, { ...this._associations, ...options.associations }, this._afterBuilds, - this._onCreates, + this._afterCreates, + this._onCreate, ); } } diff --git a/lib/types.ts b/lib/types.ts index 616cc0f..b17b46c 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -6,17 +6,19 @@ export type DeepPartial = { : DeepPartial; }; -export type GeneratorFnOptions = { +export type GeneratorFnOptions = { sequence: number; afterBuild: (fn: HookFn) => any; - onCreate: (fn: CreateFn) => any; + afterCreate: (fn: AfterCreateFn) => any; + onCreate: (fn: OnCreateFn) => any; params: DeepPartial; associations: Partial; transientParams: Partial; }; -export type GeneratorFn = (opts: GeneratorFnOptions) => T; +export type GeneratorFn = (opts: GeneratorFnOptions) => T; export type HookFn = (object: T) => any; -export type CreateFn = (object: T) => Promise; +export type OnCreateFn = (object: T) => C | Promise; +export type AfterCreateFn = (object: C) => C | Promise; export type BuildOptions = { associations?: Partial; transient?: Partial;