{"_id":"@amalgamaco/entity-store","_rev":"1-c396491579357c3c262f260f72874530","name":"@amalgamaco/entity-store","dist-tags":{"latest":"1.1.2"},"versions":{"1.1.1":{"name":"@amalgamaco/entity-store","version":"1.1.1","description":"A set of base classes for defining entities, stores for each entity, and relationships between them, facilitating the tasks of creating, fetching, updating and deleting them.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","lint":"eslint src/ tests/","test":"NODE_ENV=test JEST_JUNIT_OUTPUT_DIR=reports TZ=UTC jest","release":"release-it"},"repository":{"type":"git","url":"git+ssh://git@github.com/amalgamaco/entity-store.git"},"keywords":[],"author":"","license":"ISC","publishConfig":{"registry":"https://registry.npmjs.org","access":"public"},"devDependencies":{"@types/jest":"^28.1.6","@typescript-eslint/eslint-plugin":"^5.30.7","@typescript-eslint/parser":"^5.30.7","eslint":"^8.20.0","eslint-config-airbnb-base":"^15.0.0","eslint-import-resolver-typescript":"^3.3.0","eslint-plugin-import":"^2.26.0","fishery":"^2.2.2","jest":"^28.1.3","jest-junit":"^14.0.0","mobx":"^6.6.1","release-it":"^15.1.3","ts-jest":"^28.0.7","typescript":"^4.7.4"},"jest":{"collectCoverage":true,"coverageDirectory":"<rootDir>/reports/coverage","collectCoverageFrom":["src/**/*.ts"],"coverageReporters":["json","lcov","text","html","text-summary","cobertura"],"reporters":["default","jest-junit"],"testMatch":["<rootDir>/tests/**/?(*.)(spec|test).ts"],"transform":{"^.+\\.jsx?$":["ts-jest",{"experimentalDecorators":true}]},"preset":"ts-jest"},"release-it":{"git":{"tagName":"v${version}","requireCleanWorkingDir":false,"requireUpstream":true,"commitMessage":"Release v${version}","changelog":"npx auto-changelog --stdout --commit-limit false --unreleased --issue-url https://github.com/amalgamaco/entity-store/issues/{id} --merge-url https://github.com/amalgamaco/entity-store/pull/{id} --commit-url https://github.com/amalgamaco/entity-store/commit/{id}"},"hooks":{"after:bump":"npx auto-changelog -p"},"github":{"release":true,"releaseName":"Release v${version}"},"npm":{"skipChecks":true}},"peerDependencies":{"mobx":"^6.6.1"},"gitHead":"d095f6a8c38f1b3fde115f27eee922f1341cd4ee","bugs":{"url":"https://github.com/amalgamaco/entity-store/issues"},"homepage":"https://github.com/amalgamaco/entity-store#readme","_id":"@amalgamaco/entity-store@1.1.1","_nodeVersion":"18.14.2","_npmVersion":"9.6.1","dist":{"integrity":"sha512-RkgMOW7pe5Mp7xbOqSeQYdrP4RzwuDRpyx9cc8DfdwYKFTp0GwxFaofVB9FM7/hzcu7qIYEXkXmxzAeKqnc8Mg==","shasum":"5510a85cf599764f8ae4655493a1130da63650d3","tarball":"https://registry.npmjs.org/@amalgamaco/entity-store/-/entity-store-1.1.1.tgz","fileCount":2,"unpackedSize":16294,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBbmsryXpYaxYGES+U2RK1Ee3bc8IzuYkmmAuRNC8DOwIgZ06blozz5wsqoebozhAc6DrtIvMXPVWLLTIyKRBCovQ="}]},"_npmUser":{"name":"sebakz","email":"sebastian.kissling@gmail.com"},"directories":{},"maintainers":[{"name":"sebakz","email":"sebastian.kissling@gmail.com"},{"name":"ezeaguerre","email":"ezeaguerre@gmail.com"},{"name":"damianh97","email":"damianhuaier@gmail.com"},{"name":"maurobender","email":"maurobender@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/entity-store_1.1.1_1685992808056_0.10456954688335696"},"_hasShrinkwrap":false},"1.1.2":{"name":"@amalgamaco/entity-store","version":"1.1.2","description":"A set of base classes for defining entities, stores for each entity, and relationships between them, facilitating the tasks of creating, fetching, updating and deleting them.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","lint":"eslint src/ tests/","test":"NODE_ENV=test JEST_JUNIT_OUTPUT_DIR=reports TZ=UTC jest","release":"release-it"},"repository":{"type":"git","url":"git+ssh://git@github.com/amalgamaco/entity-store.git"},"keywords":[],"author":"","license":"ISC","publishConfig":{"registry":"https://registry.npmjs.org","access":"public"},"devDependencies":{"@types/jest":"^28.1.6","@typescript-eslint/eslint-plugin":"^5.30.7","@typescript-eslint/parser":"^5.30.7","eslint":"^8.20.0","eslint-config-airbnb-base":"^15.0.0","eslint-import-resolver-typescript":"^3.3.0","eslint-plugin-import":"^2.26.0","fishery":"^2.2.2","jest":"^28.1.3","jest-junit":"^14.0.0","mobx":"^6.6.1","release-it":"^15.1.3","ts-jest":"^28.0.7","typescript":"^4.7.4"},"jest":{"collectCoverage":true,"coverageDirectory":"<rootDir>/reports/coverage","collectCoverageFrom":["src/**/*.ts"],"coverageReporters":["json","lcov","text","html","text-summary","cobertura"],"reporters":["default","jest-junit"],"testMatch":["<rootDir>/tests/**/?(*.)(spec|test).ts"],"transform":{"^.+\\.jsx?$":["ts-jest",{"experimentalDecorators":true}]},"preset":"ts-jest"},"release-it":{"git":{"tagName":"v${version}","requireCleanWorkingDir":false,"requireUpstream":true,"commitMessage":"Release v${version}","changelog":"npx auto-changelog --stdout --commit-limit false --unreleased --issue-url https://github.com/amalgamaco/entity-store/issues/{id} --merge-url https://github.com/amalgamaco/entity-store/pull/{id} --commit-url https://github.com/amalgamaco/entity-store/commit/{id}"},"hooks":{"after:bump":"npx auto-changelog -p"},"github":{"release":true,"releaseName":"Release v${version}"},"npm":{"skipChecks":true}},"peerDependencies":{"mobx":"^6.6.1"},"gitHead":"7a18f4624cb35d5ef657b60bd6c9107410f0a78a","bugs":{"url":"https://github.com/amalgamaco/entity-store/issues"},"homepage":"https://github.com/amalgamaco/entity-store#readme","_id":"@amalgamaco/entity-store@1.1.2","_nodeVersion":"18.14.2","_npmVersion":"9.6.1","dist":{"integrity":"sha512-pkQso4cA+1pcFAB46WjSasLfZyED9a/jlVnUwfgNhM4VpYS/11MD3E6Uq5ubr3HWnLD+r8QYKbYMmqg+6uGHcw==","shasum":"4eaafd714470709d5f658b1ee5a6df9ccb6290d4","tarball":"https://registry.npmjs.org/@amalgamaco/entity-store/-/entity-store-1.1.2.tgz","fileCount":22,"unpackedSize":33153,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/785nBxgUsNZybwJXTjsW9hn27HULJMqRY150mT7a3QIhAPH68wOrxKGCRI4ZJ7b+tc8Z0iLqhRpW7ZrRGtGiABz7"}]},"_npmUser":{"name":"sebakz","email":"sebastian.kissling@gmail.com"},"directories":{},"maintainers":[{"name":"sebakz","email":"sebastian.kissling@gmail.com"},{"name":"ezeaguerre","email":"ezeaguerre@gmail.com"},{"name":"damianh97","email":"damianhuaier@gmail.com"},{"name":"maurobender","email":"maurobender@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/entity-store_1.1.2_1686069517838_0.03050432161395822"},"_hasShrinkwrap":false}},"time":{"created":"2023-06-05T19:20:07.994Z","1.1.1":"2023-06-05T19:20:08.194Z","modified":"2023-06-06T16:38:38.403Z","1.1.2":"2023-06-06T16:38:38.133Z"},"maintainers":[{"name":"sebakz","email":"sebastian.kissling@gmail.com"},{"name":"ezeaguerre","email":"ezeaguerre@gmail.com"},{"name":"damianh97","email":"damianhuaier@gmail.com"},{"name":"maurobender","email":"maurobender@gmail.com"}],"description":"A set of base classes for defining entities, stores for each entity, and relationships between them, facilitating the tasks of creating, fetching, updating and deleting them.","homepage":"https://github.com/amalgamaco/entity-store#readme","keywords":[],"repository":{"type":"git","url":"git+ssh://git@github.com/amalgamaco/entity-store.git"},"bugs":{"url":"https://github.com/amalgamaco/entity-store/issues"},"license":"ISC","readme":"# Entity Store Package\n\nA set of base classes for defining entities, stores for each entity, and relationships between them, facilitating the tasks of creating, fetching, updating and deleting them.\n\n[[_TOC_]]\n\n## Complete example\n\n```ts\n// entities/User.types.ts\nexport interface UserAttributes{\n\tid: number\n\temail: string\n\tfullName: string,\n\tavatarUrl: string,\n}\n\nexport interface UserSerialization {\n\tid: number\n\temail: string\n\tfull_name: string,\n\tavatar_url: string,\n}\n\n// entities/User.ts\nimport { StoreEntity, IRootStore } from '@amalgamaco/entity-store';\nimport { makeObservable, observable } from 'mobx';\nimport { UserAttributes, UserSerialization } from './User.types';\n\nexport default class User extends StoreEntity {\n\tid: number;\n\temail: string;\n\tfullName: string;\n\tavatarUrl: string;\n\n\tconstructor( attributes: UserAttributes, rootStore?: IRootStore ) {\n\t\tsuper( rootStore );\n\n\t\tthis.id = attributes.id;\n\t\tthis.email = attributes.email;\n\t\tthis.fullName = attributes.fullName;\n\t\tthis.avatarUrl = attributes.avatarUrl;\n\n\t\tmakeObservable( this, {\n\t\t\tid: observable,\n\t\t\temail: observable,\n\t\t\tfullName: observable,\n\t\t\tavatarUrl: observable\n\t\t} );\n\t}\n\n\tupdateWith( other: User ): User {\n\t\tthis.fullName = other.fullName;\n\t\tthis.email = other.email;\n\t\tthis.avatarUrl = other.avatarUrl;\n\n\t\treturn this;\n\t}\n\n\ttoJSON() {\n\t\treturn {\n\t\t\tid: this.id,\n\t\t\temail: this.email,\n\t\t\tfull_name: this.fullName,\n\t\t\tavatar_url: this.avatarUrl\n\t\t};\n\t}\n\n\tstatic fromJSON( attributes: UserSerialization, rootStore?: IRootStore ) {\n\t\treturn new User( {\n\t\t\tid: attributes.id,\n\t\t\temail: attributes.email,\n\t\t\tfullName: attributes.full_name,\n\t\t\tavatarUrl: attributes.avatar_url\n\t\t}, rootStore );\n\t}\n}\n\n// stores/RootStore.types.ts\nimport type { AttrsType, EntityStore } from '@amalgamaco/entity-store';\nimport User, { UserSerialization } from '../entities/User';\n\nexport type UserStore = EntityStore<User, AttrsType<typeof User>>;\n\nexport type UsersStoreSerialization = UserSerialization[];\n\nexport interface RootStoreSerialization {\n\tusersStore?: UsersStoreSerialization\n}\n\n// stores/RootStore.ts\nimport { makeAutoObservable } from 'mobx';\nimport { PersistableRootStore } from '@amalgama/mobx-store-persistor'; // Private repository\nimport { EntityStore, AttrsType } from '@amalgamaco/entity-store';\nimport User from '../entities/User';\nimport {\n\tRootStoreSerialization, UsersStoreSerialization, UserStore\n} from './RooStore.types';\n\nexport class RootStore implements PersistableRootStore {\n\tuserStore: UserStore;\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t[ key: string ]: any;\n\n\tconstructor() {\n\t\tthis.userStore = new EntityStore<User, AttrsType<typeof User>>( User, this );\n\n\t\tmakeAutoObservable( this );\n\t}\n\n\tserializationToPersist(): RootStoreSerialization {\n\t\treturn {\n\t\t\tusersStore: this.userStore.serialize() as UsersStoreSerialization\n\t\t};\n\t}\n\n\trehydrateWithSerialization( serialization: RootStoreSerialization ) {\n\t\tthis.userStore.hydrate( serialization.usersStore || [] );\n\t}\n\n\tgetStore( storeName: string ) {\n\t\treturn this[ `${storeName}Store` ] || null;\n\t}\n\n\tclearStore() {\n\t\tthis.userStore.clear();\n\t}\n}\n```\n\n## EntityStore\n\nA store for entities of a given type.\n\n```ts\nimport { EntityStore, AttrsType } from '@amalgamaco/entity-store';\nimport Item from '../entities/Item';\nimport { rootStore } from './shared';\n\nconst itemsStore = new EntityStore<Item, AttrsType<typeof Item>>( Item, rootStore );\nrootStore.itemsStore = itemsStore;\n\nconst item = itemsStore.create( { id: 1, ... } );\n```\n\n### Methods\n\n#### constructor( EntityClass, rootStore ): EntityStore<Entity, EntityAttrs>\nCreates a new store for the given `EntityClass` and `rootStore`.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __EntityClass__ | [`IEntityClass<Entity, EntityAttrs>`](#ientityclass) | The class of the entities that will be stored in this store. |\n| __rootStore__   | [`IRootStore`](#irootstore) | The root store that saves a reference to the stores that contain the relations for the entities stored in this store. When creating a new entity using the method `create`, this root store will be automatically set to the created store entity. |\n\n#### create( attributes: EntityAttrs )\nCreates a new entity with the given attributes using the entity's `constructor` method. Sets the store's root store as the entity root store.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __attributes__| `EntityAttrs` which should extend [`IEntityRequiredAttributes`](#ientityrequiredattributes) | The attributes to create the entity with. |\n\n#### add( entity: Entity )\nAdds a new entity to the store. If an entity with the same `id` already exists, it's updated with the passed entity by calling `updateWith` on the existing entity.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __entity__ | `Entity` which should extend [`IEntity`](#ientity) | The entity to add to the store. |\n\n#### has( id: ID ): boolean\nReturns `true` if there is an entity with the passed `id` stored in the store. Returns `false` otherwise.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __id__ | [`ID`](#id) | The `id` to check. |\n\n#### get( id: ID ): Entity | null\nReturns the entity with the passed `id` or `null` if there is no entity for that `id`.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __id__ | [`ID`](#id) | The `id` of the entity to retrieve from the store. |\n\n#### all(): Entity[]\nReturns a list with all the entities stored in the store.\n\n#### where( condition: ( entity: Entity ) => boolean ): Entity[]\nReturns a list of all the entities stored in the store that meet the passed condition.\n\n__Parameters__\n|  Name       | Type | Description   |\n| ---         | --- |  ---          |\n| __condition__ | `( entity: Entity ) => boolean` | The condition to check against the entities in the store. This callback receives an entity and must return a boolean indicating if the given entity meets the condition or not. |\n\n#### delete( id: ID )\nDeletes the entity with the passed `id` from the store.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __id__ | [`ID`](#id) | The `id` of the entity to delete. |\n\n#### deleteAll( ids: ID[] )\nDeletes all the entities identified by the passed `ids` list.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __ids__ | [`ID[]`](#id) | A list of `id`s of the entities to delete. |\n\n#### replace( entities: Entity[] )\nReplaces the stored entities with the ones passed.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __entities__ | `Entity[]` which should extend [`IEntity`](#ientity) | The entities to replace the store content with. |\n\n#### clear()\nEmpties the store.\n\n#### serialize(): IStoreSerialization\nSerializes the store. It rerurns a list of the serializations for all the entities in the store.\n\n#### hydrate( serialization : IStoreSerialization )\nFills the store with the entities created from the serialization passed.\n\n__Parameters__\n|  Name       |  Type  | Description   |\n| ---         |  ---   |  ---          |\n| __serialization__ | [`IStoreSerialization`](#istoreserialization) | The serialization that will be used to fill the store. |\n\n### Types\n\n#### ID\nThe type of the `id` attribute of an entity.\n\n```ts\ntype ID = number | string;\n```\n\n#### IRootStore\nRepresents the type of a `EntityStore`'s root store.\n\n```ts\ninterface IRootStore {\n\t[ key: string ]: IStore\n}\n```\n\n#### IEntity\nRepresents the type of a entity that will be stored in a `EntityStore`.\n\n```ts\ninterface IEntity {\n\tid: ID,\n\trootStore?: IRootStore\n\n\tupdateWith( anotherEntity: IEntity ): IEntity,\n\ttoJSON(): IEntitySerialization,\n}\n```\n\n#### IEntityClass\nRepresents the type of the class that contructs the entities that will be stored in a `EntityStore`.\n\n```ts\ninterface IEntityClass<T extends IEntity, Attrs extends IEntityRequiredAttributes> {\n\tnew( attributes: Attrs, rootStore?: IRootStore ): T\n\tfromJSON( attributes: IEntitySerialization, rootStore?: IRootStore ): T\n}\n```\n\n#### IEntityRequiredAttributes\nRepresents the required atttributes for all entities.\n\n```ts\ninterface IEntityRequiredAttributes {\n\tid: ID\n}\n```\n\n#### AttrsType\nRepresents the attributes of an Entity. This attributes are calculated as the first parameter of the entity's constructor.\n\n```ts\ntype AttrsType<T extends new ( ...args: any ) => any> = First<ConstructorParameters<T>>;\n```\n\n#### IEntitySerialization\nRepresents the serialization of an entity.\n\n```ts\ninterface IEntitySerialization {\n\tid: ID\n}\n```\n\n#### IStoreSerialization\nRepresents the serialization of a store.\n\n```ts\ntype IStoreSerialization = IEntitySerialization[];\n```\n\n#### JSONValue\nRepresents any possible JSON value.\n\n```ts\ntype JSONValue =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| { [x: string]: JSONValue }\n\t| Array<JSONValue>;\n```\n\n## StoreEntity\n\nA base class for entities that will be stored using an `EntityStore`.\n\n### Relations\nThe are two ways to define relations between entities, one using Typescript property decorators and one using a static function defined in the Entity class. You can use the one you find more convinient for you.\n\n#### decorators\nThis package provides two decorators to define properties that come from a related store: `@hasMany` and `@belongsTo`.\n\n##### @hasMany\nSpecifies that the decorated property values will be retrieved from a related store.\n\n__parameters__\n|  Name       | Description   |\n| ---         |  ---          |\n| __storeName__ | The name of the store in the root store to retrieve the related entities from. |\n| __lkName__ | The name of the property in this entity that returns the `ids` of the related entities. |\n\n__usage__\n```ts\nimport { StoreEntity, hasMany } from '@amalgamaco/entity-store';\nimport Comment from './Comment';\n\nclass Post extends StoreEntiy {\n\t// This property holds the ids of the related comments and\n\t// will be used to retrive the related comments from their store.\n\tcommentIDs: number[];\n\n\t// Here we decorate the comments property with the @hasMany decorator\n\t// passing the name of the related entities store and the name of the\n\t// property that holds the releated entities ids.\n\t@hasMany( 'commentsStore', 'commentIDs' )\n\tcomments!: Comment[];\n\n\t...\n}\n```\n\n__IMPORTANT__: Don't forget to add the `!` at the end of the decorated property definition telling\nTypescript that property will have a value (calculated by the decorator) even if we don't set a default\none.\n\n##### @belongsTo\nSpecifies that the decorated property value will be retrieved from a related store.\n\n__parameters__\n|  Name       | Description   |\n| ---         |  ---          |\n| __storeName__ | The name of the store in the root store to retrieve the related entity from. |\n| __lkName__ | The name of the property in this entity that returns the `id` of the related entity. |\n\n__usage__\n```ts\nimport { StoreEntity, belongsTo } from '@amalgamaco/entity-store';\nimport User from './User';\n\nclass Post extends StoreEntiy {\n\t// This property holds the id of the related author and\n\t// will be used to retrive the related author from its store.\n\tauthorID: number[];\n\n\t// Here we decorate the author property with the @belongsTo decorator\n\t// passing the name of the related entity store and the name of the\n\t// property that holds the releated entity id.\n\t@belongsTo( 'usersStore', 'authorID' )\n\tauthor?: User;\n\n\t...\n}\n```\n\n__IMPORTANT__: Don't forget to add the `?` at the end of the decorated property definition telling\nTypescript that property may be `undefined` and that we don't need to set a default value for it.\n\n#### relationships static method\nYou can also specify the entity relations using the `relationships` static method. This method must return a list of `IRelationshipConfig` items.\n\n__IRelationshipConfig__\nEach item on the `relationships` static method must meet the next interface:\n\n```ts\nexport interface IRelationshipConfig {\n\tname: string,\n\ttype: 'BELONGS_TO' | 'HAS_MANY',\n\tstore: string,\n\tlookupKey: string,\n}\n```\n\n- __name__: The name of the property that will hold the relationship.\n- __type__: The type of relationship to define:\n  - __BELONGS_TO__: This property will only return an `entity` whose `id` is indicated by the value of the property `lookupKey `.\n  - __HAS_MANY__: This property willl return a list of `entities` whose `ids` are indicated by the value of the property `lookupKey`.\n- __store__: The name of the store in the `root store` where the related entities are stored.\n- __lookupKey__: The name of the property in the `entity` that holds the `id` or `ids` of the related entities.\n\n__usage__\n```ts\nimport { StoreEntity } from '@amalgamaco/entity-store';\nimport User from './User';\nimport Comment from './Comment';\n\nclass Post extends StoreEntiy {\n\t// This property holds the id of the related author and\n\t// will be used to retrive the related author from its store.\n\tauthorID: number[];\n\n\t// This property holds the ids of the related comments and\n\t// will be used to retrive the related comments from their store.\n\tcommentIDs: number[];\n\n\tauthor?: User;\n\tcomments!: Comment[];\n\n\n\tstatic relationships(): IRelationshipConfig[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: 'author',\n\t\t\t\tlookupKey: 'authorID',\n\t\t\t\tstore: 'usersStore',\n\t\t\t\ttype: 'BELONGS_TO'\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'comments',\n\t\t\t\tlookupKey: 'commentIDs',\n\t\t\t\tstore: 'commentsStore',\n\t\t\t\ttype: 'HAS_MANY'\n\t\t\t}\n\t\t];\n\t}\n\n\t...\n}\n```\n\n__IMPORTANT__: When declaring the properties that will return the related entities don't forget to add a `?` at the end of the property name for `BELONGS_TO` relations and a `!` at the end of the property name for `HAS_MANY` relations to prevent Typescript from asking to initialize the properties with default values.\n","readmeFilename":"README.md"}