tinybase
Version:
A reactive data store and sync engine.
1,321 lines (1,294 loc) • 46.2 kB
text/typescript
/**
* The relationships module of the TinyBase project provides the ability to
* create and track relationships between the data in Store objects.
*
* The main entry point to this module is the createRelationships function,
* which returns a new Relationships object. From there, you can create new
* Relationship definitions, access the associations within those Relationships
* directly, and register listeners for when they change.
* @packageDocumentation
* @module relationships
* @since v1.0.0
*/
import type {Id, IdOrNull, Ids} from '../common/index.d.cts';
import type {GetCell, RowCallback, Store} from '../store/index.d.cts';
/**
* The Relationship type represents the concept of a map that connects one Row
* object to another, often in another Table.
*
* The Relationship has a one-to-many nature. One local Row Id is linked to one
* remote Row Id (in the remote Table), as described by the
* setRelationshipDefinition method - and one remote Row Id may map back to
* multiple local Row Ids (in the local Table).
*
* A Relationship where the local Table is the same as the remote Table can be
* used to model a 'linked list', where Row A references Row B, Row B references
* Row C, and so on.
*
* Note that the Relationship type is not actually used in the API, and you
* instead enumerate and access its structure with the getRemoteRowId method,
* the getLocalRowIds method, and the getLinkedRowIds method.
* @category Concept
* @since v1.0.0
*/
export type Relationship = {
remoteRowId: {[localRowId: Id]: Id};
localRowIds: {[remoteRowId: Id]: Ids};
linkedRowIds: {[firstRowId: Id]: Ids};
};
/**
* The RelationshipCallback type describes a function that takes a
* Relationship's Id and a callback to loop over each local Row within it.
*
* A RelationshipCallback is provided when using the forEachRelationship method,
* so that you can do something based on every Relationship in the Relationships
* object. See that method for specific examples.
* @param relationshipId The Id of the Relationship that the callback can
* operate on.
* @param forEachRow A function that will let you iterate over the local Row
* objects in this Relationship.
* @category Callback
* @since v1.0.0
*/
export type RelationshipCallback = (
relationshipId: Id,
forEachRow: (rowCallback: RowCallback) => void,
) => void;
/**
* The RelationshipIdsListener type describes a function that is used to listen
* to Relationship definitions being added or removed.
*
* A RelationshipIdsListener is provided when using the
* addRelationshipIdsListener method. See that method for specific examples.
*
* When called, a RelationshipIdsListener is given a reference to the
* Relationships object.
* @param relationships A reference to the Relationships object that changed.
* @category Listener
* @since v1.0.0
*/
export type RelationshipIdsListener = (relationships: Relationships) => void;
/**
* The RemoteRowIdListener type describes a function that is used to listen to
* changes to the remote Row Id end of a Relationship.
*
* A RemoteRowIdListener is provided when using the addRemoteRowIdListener
* method. See that method for specific examples.
*
* When called, a RemoteRowIdListener is given a reference to the Relationships
* object, the Id of the Relationship that changed, and the Id of the local Row
* whose remote Row Id changed.
* @param relationships A reference to the Relationships object that changed.
* @param relationshipId The Id of the Relationship that changed.
* @param localRowId The Id of the local Row whose remote Row Id changed.
* @category Listener
* @since v1.0.0
*/
export type RemoteRowIdListener = (
relationships: Relationships,
relationshipId: Id,
localRowId: Id,
) => void;
/**
* The LocalRowIdsListener type describes a function that is used to listen to
* changes to the local Row Id ends of a Relationship.
*
* A LocalRowIdsListener is provided when using the addLocalRowIdsListener
* method. See that method for specific examples.
*
* When called, a LocalRowIdsListener is given a reference to the Relationships
* object, the Id of the Relationship that changed, and the Id of the remote Row
* whose local Row Ids changed.
* @param relationships A reference to the Relationships object that changed.
* @param relationshipId The Id of the Relationship that changed.
* @param remoteRowId The Id of the remote Row whose local Row Ids changed.
* @category Listener
* @since v1.0.0
*/
export type LocalRowIdsListener = (
relationships: Relationships,
relationshipId: Id,
remoteRowId: Id,
) => void;
/**
* The LinkedRowIdsListener type describes a function that is used to listen to
* changes to the local Row Id ends of a Relationship.
*
* A LinkedRowIdsListener is provided when using the addLinkedRowIdsListener
* method. See that method for specific examples.
*
* When called, a LinkedRowIdsListener is given a reference to the Relationships
* object, the Id of the Relationship that changed, and the Id of the first Row
* of the the linked list whose members changed.
* @param relationships A reference to the Relationships object that changed.
* @param relationshipId The Id of the Relationship that changed.
* @param firstRowId The Id of the first Row of the the linked list whose
* members changed.
* @category Listener
* @since v1.0.0
*/
export type LinkedRowIdsListener = (
relationships: Relationships,
relationshipId: Id,
firstRowId: Id,
) => void;
/**
* The RelationshipsListenerStats type describes the number of listeners
* registered with the Relationships object, and can be used for debugging
* purposes.
*
* A RelationshipsListenerStats object is returned from the getListenerStats
* method.
* @category Development
* @since v1.0.0
*/
export type RelationshipsListenerStats = {
/**
* The number of RemoteRowIdListener functions registered with the
* Relationships object.
* @category Stat
* @since v1.0.0
*/
remoteRowId: number;
/**
* The number of LocalRowIdsListener functions registered with the
* Relationships object.
* @category Stat
* @since v1.0.0
*/
localRowIds: number;
/**
* The number of LinkedRowId functions registered with the Relationships
* object.
* @category Stat
* @since v1.0.0
*/
linkedRowIds: number;
};
/**
* A Relationships object lets you associate a Row in a one Table with the Id of
* a Row in another Table.
*
* This is useful for creating parent-child relationships between the data in
* different Table objects, but it can also be used to model a linked list of
* Row objects in the same Table.
*
* Create a Relationships object easily with the createRelationships function.
* From there, you can add new Relationship definitions (with the
* setRelationshipDefinition method), query their contents (with the
* getRemoteRowId method, the getLocalRowIds method, and the getLinkedRowIds
* method), and add listeners for when they change (with the
* addRemoteRowIdListener method, the addLocalRowIdsListener method, and the
* addLinkedRowIdsListener method).
*
* This module defaults to creating relationships between Row objects by using
* one of their Cell values. However, far more complex relationships can be
* configured with a custom function.
* @example
* This example shows a very simple lifecycle of a Relationships object: from
* creation, to adding definitions (both local/remote table and linked list),
* getting their contents, and then registering and removing listeners for them.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog', next: 'felix'},
* felix: {species: 'cat', next: 'cujo'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
*
* // A local/remote table relationship:
* relationships.setRelationshipDefinition(
* 'petSpecies', // relationshipId
* 'pets', // localTableId to link from
* 'species', // remoteTableId to link to
* 'species', // cellId containing remote key
* );
* console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
* // -> 'dog'
* console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
* // -> ['fido', 'cujo']
*
* // A linked list relationship:
* relationships.setRelationshipDefinition(
* 'petSequence', // relationshipId
* 'pets', // localTableId to link from
* 'pets', // the same remoteTableId to link within
* 'next', // cellId containing link key
* );
* console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
* // -> ['fido', 'felix', 'cujo']
*
* const listenerId1 = relationships.addLocalRowIdsListener(
* 'petSpecies',
* 'dog',
* () => {
* console.log('petSpecies relationship (to dog) changed');
* console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
* },
* );
* const listenerId2 = relationships.addLinkedRowIdsListener(
* 'petSequence',
* 'fido',
* () => {
* console.log('petSequence linked list (from fido) changed');
* console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
* },
* );
*
* store.setRow('pets', 'toto', {species: 'dog'});
* // -> 'petSpecies relationship (to dog) changed'
* // -> ['fido', 'cujo', 'toto']
*
* store.setCell('pets', 'cujo', 'next', 'toto');
* // -> 'petSequence linked list (from fido) changed'
* // -> ['fido', 'felix', 'cujo', 'toto']
*
* relationships.delListener(listenerId1);
* relationships.delListener(listenerId2);
* relationships.destroy();
* ```
* @see Using Relationships guide
* @see Drawing demo
* @category Relationships
* @since v1.0.0
*/
export interface Relationships {
//
/**
* The setRelationshipDefinition method lets you set the definition of a
* Relationship.
*
* Every Relationship definition is identified by a unique Id, and if you
* re-use an existing Id with this method, the previous definition is
* overwritten.
*
* An Relationship is based on connections between Row objects, often in two
* different Table objects. Therefore the definition requires the
* `localTableId` parameter to specify the 'local' Table to create the
* Relationship from, and the `remoteTableId` parameter to specify the
* 'remote' Table to create Relationship to.
*
* A linked list Relationship is one that has the same Table specified as both
* local Table Id and remote Table Id, allowing you to create a sequence of
* Row objects within that one Table.
*
* A local Row is related to a remote Row by specifying which of its (local)
* Cell values contains the (remote) Row Id, using the `getRemoteRowId`
* parameter. Alternatively, a custom function can be provided that produces
* your own remote Row Id from the local Row as a whole.
* @param relationshipId The Id of the Relationship to define.
* @param localTableId The Id of the local Table for the Relationship.
* @param remoteTableId The Id of the remote Table for the Relationship (or
* the same as the `localTableId` in the case of a linked list).
* @param getRemoteRowId Either the Id of the Cell containing, or a function
* that produces, the Id that is used to indicate which Row in the remote
* Table a local Row is related to.
* @returns A reference to the Relationships object.
* @example
* This example creates a Store, creates a Relationships object, and defines
* a simple Relationship based on the values in the `species` Cell of the
* `pets` Table that relates a Row to another in the `species` Table.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies', // relationshipId
* 'pets', // localTableId to link from
* 'species', // remoteTableId to link to
* 'species', // cellId containing remote key
* );
*
* console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
* // -> 'dog'
* console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
* // -> ['fido', 'cujo']
* ```
* @example
* This example creates a Store, creates a Relationships object, and defines
* a linked list Relationship based on the values in the `next` Cell of the
* `pets` Table that relates a Row to another in the same Table.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', next: 'felix'},
* felix: {species: 'cat', next: 'cujo'},
* cujo: {species: 'dog'},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSequence', // relationshipId
* 'pets', // localTableId to link from
* 'pets', // the same remoteTableId to link within
* 'next', // cellId containing link key
* );
*
* console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
* // -> ['fido', 'felix', 'cujo']
* ```
* @category Configuration
* @since v1.0.0
*/
setRelationshipDefinition(
relationshipId: Id,
localTableId: Id,
remoteTableId: Id,
getRemoteRowId: Id | ((getCell: GetCell, localRowId: Id) => Id),
): Relationships;
/**
* The delRelationshipDefinition method removes an existing Relationship
* definition.
* @param relationshipId The Id of the Relationship to remove.
* @returns A reference to the Relationships object.
* @example
* This example creates a Store, creates a Relationships object, defines a
* simple Relationship, and then removes it.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
* console.log(relationships.getRelationshipIds());
* // -> ['petSpecies']
*
* relationships.delRelationshipDefinition('petSpecies');
* console.log(relationships.getRelationshipIds());
* // -> []
* ```
* @category Configuration
* @since v1.0.0
*/
delRelationshipDefinition(relationshipId: Id): Relationships;
/**
* The getStore method returns a reference to the underlying Store that is
* backing this Relationships object.
* @returns A reference to the Store.
* @example
* This example creates a Relationships object against a newly-created Store
* and then gets its reference in order to update its data.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const relationships = createRelationships(createStore());
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
* relationships.getStore().setCell('pets', 'fido', 'species', 'dog');
* console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
* // -> 'dog'
* ```
* @category Getter
* @since v1.0.0
*/
getStore(): Store;
/**
* The getRelationshipIds method returns an array of the Relationship Ids
* registered with this Relationships object.
* @returns An array of Ids.
* @example
* This example creates a Relationships object with two definitions, and then
* gets the Ids of the definitions.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const relationships = createRelationships(createStore())
* .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
* .setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');
* console.log(relationships.getRelationshipIds());
* // -> ['petSpecies', 'petSequence']
* ```
* @category Getter
* @since v1.0.0
*/
getRelationshipIds(): Ids;
/**
* The forEachRelationship method takes a function that it will then call for
* each Relationship in a specified Relationships object.
*
* This method is useful for iterating over the structure of the Relationships
* object in a functional style. The `relationshipCallback` parameter is a
* RelationshipCallback function that will be called with the Id of each
* Relationship, and with a function that can then be used to iterate over
* each local Row involved in the Relationship.
* @param relationshipCallback The function that should be called for every
* Relationship.
* @example
* This example iterates over each Relationship in a Relationships object, and
* lists each Row Id within them.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', next: 'felix'},
* felix: {species: 'cat', next: 'cujo'},
* cujo: {species: 'dog'},
* });
* const relationships = createRelationships(store)
* .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
* .setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');
*
* relationships.forEachRelationship((relationshipId, forEachRow) => {
* console.log(relationshipId);
* forEachRow((rowId) => console.log(`- ${rowId}`));
* });
* // -> 'petSpecies'
* // -> '- fido'
* // -> '- felix'
* // -> '- cujo'
* // -> 'petSequence'
* // -> '- fido'
* // -> '- felix'
* // -> '- cujo'
* ```
* @category Iterator
* @since v1.0.0
*/
forEachRelationship(relationshipCallback: RelationshipCallback): void;
/**
* The hasRelationship method returns a boolean indicating whether a given
* Relationship exists in the Relationships object.
* @param relationshipId The Id of a possible Relationship in the
* Relationships object.
* @returns Whether a Relationship with that Id exists.
* @example
* This example shows two simple Relationship existence checks.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const relationships = createRelationships(
* createStore(),
* ).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');
* console.log(relationships.hasRelationship('petSpecies'));
* // -> true
* console.log(relationships.hasRelationship('petColor'));
* // -> false
* ```
* @category Getter
* @since v1.0.0
*/
hasRelationship(relationshipId: Id): boolean;
/**
* The getLocalTableId method returns the Id of the underlying local Table
* that is used in the Relationship.
*
* If the Relationship Id is invalid, the method returns `undefined`.
* @param relationshipId The Id of a Relationship.
* @returns The Id of the local Table backing the Relationship, or
* `undefined`.
* @example
* This example creates a Relationship object, a single Relationship
* definition, and then queries it (and a non-existent definition) to get the
* underlying local Table Id.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const relationships = createRelationships(createStore());
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* console.log(relationships.getLocalTableId('petSpecies'));
* // -> 'pets'
* console.log(relationships.getLocalTableId('petColor'));
* // -> undefined
* ```
* @category Getter
* @since v1.0.0
*/
getLocalTableId(relationshipId: Id): Id | undefined;
/**
* The getRemoteTableId method returns the Id of the underlying remote Table
* that is used in the Relationship.
*
* If the Relationship Id is invalid, the method returns `undefined`.
* @param relationshipId The Id of a Relationship.
* @returns The Id of the remote Table backing the Relationship, or
* `undefined`.
* @example
* This example creates a Relationship object, a single Relationship
* definition, and then queries it (and a non-existent definition) to get the
* underlying remote Table Id.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const relationships = createRelationships(createStore());
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* console.log(relationships.getRemoteTableId('petSpecies'));
* // -> 'species'
* console.log(relationships.getRemoteTableId('petColor'));
* // -> undefined
* ```
* @category Getter
* @since v1.0.0
*/
getRemoteTableId(relationshipId: Id): Id | undefined;
/**
* The getRemoteRowId method gets the remote Row Id for a given local Row in a
* Relationship.
*
* If the identified Relationship or Row does not exist (or if the definition
* references a Table that does not exist) then `undefined` is returned.
* @param relationshipId The Id of the Relationship.
* @param localRowId The Id of the local Row in the Relationship.
* @returns The remote Row Id in the Relationship, or `undefined`.
* @example
* This example creates a Store, creates a Relationships object, and defines
* a simple Relationship. It then uses getRemoteRowId to see the remote Row Id
* in the Relationship (and also the remote Row Ids for a local Row that does
* not exist, and for a Relationship that has not been defined).
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
* // -> 'dog'
* console.log(relationships.getRemoteRowId('petSpecies', 'toto'));
* // -> undefined
* console.log(relationships.getRemoteRowId('petColor', 'fido'));
* // -> undefined
* ```
* @category Getter
* @since v1.0.0
*/
getRemoteRowId(relationshipId: Id, localRowId: Id): Id | undefined;
/**
* The getLocalRowIds method gets the local Row Ids for a given remote Row in
* a Relationship.
*
* If the identified Relationship or Row does not exist (or if the definition
* references a Table that does not exist) then an empty array is returned.
* @param relationshipId The Id of the Relationship.
* @param remoteRowId The Id of the remote Row in the Relationship.
* @returns The local Row Ids in the Relationship, or an empty array.
* @example
* This example creates a Store, creates a Relationships object, and defines
* a simple Relationship. It then uses getLocalRowIds to see the local Row Ids
* in the Relationship (and also the local Row Ids for a remote Row that does
* not exist, and for a Relationship that has not been defined).
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
* // -> ['fido', 'cujo']
* console.log(relationships.getLocalRowIds('petSpecies', 'worm'));
* // -> []
* console.log(relationships.getLocalRowIds('petColor', 'brown'));
* // -> []
* ```
* @category Getter
* @since v1.0.0
*/
getLocalRowIds(relationshipId: Id, remoteRowId: Id): Ids;
/**
* The getLinkedRowIds method gets the linked Row Ids for a given Row in a
* linked list Relationship.
*
* A linked list Relationship is one that has the same Table specified as both
* local Table Id and remote Table Id, allowing you to create a sequence of
* Row objects within that one Table.
*
* If the identified Relationship or Row does not exist (or if the definition
* references a Table that does not exist) then an array containing just the
* first Row Id is returned.
* @param relationshipId The Id of the Relationship.
* @param firstRowId The Id of the first Row in the linked list Relationship.
* @returns The linked Row Ids in the Relationship.
* @example
* This example creates a Store, creates a Relationships object, and defines
* a simple linked list Relationship. It then uses getLinkedRowIds to see the
* linked Row Ids in the Relationship (and also the linked Row Ids for a Row
* that does not exist, and for a Relationship that has not been defined).
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', next: 'felix'},
* felix: {species: 'cat', next: 'cujo'},
* cujo: {species: 'dog'},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSequence',
* 'pets',
* 'pets',
* 'next',
* );
*
* console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
* // -> ['fido', 'felix', 'cujo']
* console.log(relationships.getLinkedRowIds('petSequence', 'felix'));
* // -> ['felix', 'cujo']
* console.log(relationships.getLinkedRowIds('petSequence', 'toto'));
* // -> ['toto']
* console.log(relationships.getLinkedRowIds('petFriendships', 'fido'));
* // -> ['fido']
* ```
* @category Getter
* @since v1.0.0
*/
getLinkedRowIds(relationshipId: Id, firstRowId: Id): Ids;
/**
* The addRelationshipIdsListener method registers a listener function with
* the Relationships object that will be called whenever a Relationship
* definition is added or removed.
*
* The provided listener is a RelationshipIdsListener function, and will be
* called with a reference to the Relationships object.
* @param listener The function that will be called whenever a Relationship
* definition is added or removed.
* @example
* This example creates a Store, a Relationships object, and then registers a
* listener that responds to the addition and the removal of a Relationship
* definition.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* const listenerId = relationships.addRelationshipIdsListener(
* (relationships) => {
* console.log(relationships.getRelationshipIds());
* },
* );
*
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
* // -> ['petSpecies']
* relationships.delRelationshipDefinition('petSpecies');
* // -> []
*
* relationships.delListener(listenerId);
* ```
* @category Listener
* @since v4.1.0
*/
addRelationshipIdsListener(listener: RelationshipIdsListener): Id;
/**
* The addRemoteRowIdListener method registers a listener function with the
* Relationships object that will be called whenever a remote Row Id in a
* Relationship changes.
*
* You can either listen to a single local Row (by specifying the Relationship
* Id and local Row Id as the method's first two parameters), or changes to
* any local Row (by providing a `null` wildcards).
*
* Both, either, or neither of the `relationshipId` and `localRowId`
* parameters can be wildcarded with `null`. You can listen to a specific
* local Row in a specific Relationship, any local Row in a specific
* Relationship, a specific local Row in any Relationship, or any local Row in
* any Relationship.
*
* The provided listener is a RemoteRowIdListener function, and will be called
* with a reference to the Relationships object, the Id of the Relationship,
* and the Id of the local Row that had its remote Row change.
* @param relationshipId The Id of the Relationship to listen to, or `null` as
* a wildcard.
* @param localRowId The Id of the local Row to listen to, or `null` as a
* wildcard.
* @param listener The function that will be called whenever the remote Row Id
* changes.
* @returns A unique Id for the listener that can later be used to remove it.
* @example
* This example creates a Store, a Relationships object, and then registers a
* listener that responds to any changes to a specific local Row's remote Row.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* const listenerId = relationships.addRemoteRowIdListener(
* 'petSpecies',
* 'cujo',
* (relationships) => {
* console.log('petSpecies relationship (from cujo) changed');
* console.log(relationships.getRemoteRowId('petSpecies', 'cujo'));
* },
* );
*
* store.setCell('pets', 'cujo', 'species', 'wolf');
* // -> 'petSpecies relationship (from cujo) changed'
* // -> 'wolf'
*
* relationships.delListener(listenerId);
* ```
* @example
* This example creates a Store, a Relationships object, and then registers a
* listener that responds to any changes to any local Row's remote Row. It
* also illustrates how you can use the getStore method and the getRemoteRowId
* method to resolve the remote Row as a whole.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* cujo: {species: 'dog', color: 'brown'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* })
* .setTable('color', {
* brown: {discount: 0.1},
* black: {discount: 0},
* grey: {discount: 0.2},
* });
*
* const relationships = createRelationships(store)
* .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
* .setRelationshipDefinition('petColor', 'pets', 'color', 'color');
*
* const listenerId = relationships.addRemoteRowIdListener(
* null,
* null,
* (relationships, relationshipId, localRowId) => {
* console.log(
* `${relationshipId} relationship (from ${localRowId}) changed`,
* );
* console.log(relationships.getRemoteRowId(relationshipId, localRowId));
* console.log(
* relationships
* .getStore()
* .getRow(
* relationships.getRemoteTableId(relationshipId),
* relationships.getRemoteRowId(relationshipId, localRowId),
* ),
* );
* },
* );
*
* store.setRow('pets', 'cujo', {species: 'wolf', color: 'grey'});
* // -> 'petSpecies relationship (from cujo) changed'
* // -> 'wolf'
* // -> {price: 10}
* // -> 'petColor relationship (from cujo) changed'
* // -> 'grey'
* // -> {discount: 0.2}
*
* relationships.delListener(listenerId);
* ```
* @category Listener
* @since v1.0.0
*/
addRemoteRowIdListener(
relationshipId: IdOrNull,
localRowId: IdOrNull,
listener: RemoteRowIdListener,
): Id;
/**
* The addLocalRowIdsListener method registers a listener function with the
* Relationships object that will be called whenever the local Row Ids in
* a Relationship change.
*
* You can either listen to a single local Row (by specifying the Relationship
* Id and local Row Id as the method's first two parameters), or changes to
* any local Row (by providing a `null` wildcards).
*
* Both, either, or neither of the `relationshipId` and `remoteRowId`
* parameters can be wildcarded with `null`. You can listen to a specific
* remote Row in a specific Relationship, any remote Row in a specific
* Relationship, a specific remote Row in any Relationship, or any remote Row
* in any Relationship.
*
* The provided listener is a LocalRowIdsListener function, and will be called
* with a reference to the Relationships object, the Id of the Relationship,
* and the Id of the remote Row that had its local Row objects change.
* @param relationshipId The Id of the Relationship to listen to, or `null` as
* a wildcard.
* @param remoteRowId The Id of the remote Row to listen to, or `null` as a
* wildcard.
* @param listener The function that will be called whenever the local Row Ids
* change.
* @returns A unique Id for the listener that can later be used to remove it.
* @example
* This example creates a Store, a Relationships object, and then registers a
* listener that responds to any changes to a specific remote Row's local Row
* objects.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* const listenerId = relationships.addLocalRowIdsListener(
* 'petSpecies',
* 'dog',
* (relationships) => {
* console.log('petSpecies relationship (to dog) changed');
* console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
* },
* );
*
* store.setRow('pets', 'toto', {species: 'dog'});
* // -> 'petSpecies relationship (to dog) changed'
* // -> ['fido', 'cujo', 'toto']
*
* relationships.delListener(listenerId);
* ```
* @example
* This example creates a Store, a Relationships object, and then registers a
* listener that responds to any changes to any remote Row's local Row
* objects.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* cujo: {species: 'dog', color: 'brown'},
* toto: {species: 'dog', color: 'grey'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* })
* .setTable('color', {
* brown: {discount: 0.1},
* black: {discount: 0},
* grey: {discount: 0.2},
* });
*
* const relationships = createRelationships(store)
* .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
* .setRelationshipDefinition('petColor', 'pets', 'color', 'color');
*
* const listenerId = relationships.addLocalRowIdsListener(
* null,
* null,
* (relationships, relationshipId, remoteRowId) => {
* console.log(
* `${relationshipId} relationship (to ${remoteRowId}) changed`,
* );
* console.log(relationships.getLocalRowIds(relationshipId, remoteRowId));
* },
* );
*
* store.setRow('pets', 'cujo', {species: 'wolf', color: 'grey'});
* // -> 'petSpecies relationship (to dog) changed'
* // -> ['fido', 'toto']
* // -> 'petSpecies relationship (to wolf) changed'
* // -> ['cujo']
* // -> 'petColor relationship (to brown) changed'
* // -> ['fido']
* // -> 'petColor relationship (to grey) changed'
* // -> ['toto', 'cujo']
*
* relationships.delListener(listenerId);
* ```
* @category Listener
* @since v1.0.0
*/
addLocalRowIdsListener(
relationshipId: IdOrNull,
remoteRowId: IdOrNull,
listener: LocalRowIdsListener,
): Id;
/**
* The addLinkedRowIdsListener method registers a listener function with the
* Relationships object that will be called whenever the linked Row Ids in a
* linked list Relationship change.
*
* A linked list Relationship is one that has the same Table specified as both
* local Table Id and remote Table Id, allowing you to create a sequence of
* Row objects within that one Table.
*
* You listen to changes to a linked list starting from a single first Row by
* specifying the Relationship Id and local Row Id as the method's first two
* parameters.
*
* Unlike other listener registration methods, you cannot provide `null`
* wildcards for the first two parameters of the addLinkedRowIdsListener
* method. This prevents the prohibitive expense of tracking all the possible
* linked lists (and partial linked lists within them) in a Store.
*
* The provided listener is a LinkedRowIdsListener function, and will be
* called with a reference to the Relationships object, the Id of the
* Relationship, and the Id of the first Row that had its linked list change.
* @param relationshipId The Id of the Relationship to listen to.
* @param firstRowId The Id of the first Row of the linked list to listen to.
* @param listener The function that will be called whenever the linked Row
* Ids change.
* @returns A unique Id for the listener that can later be used to remove it.
* @example
* This example creates a Store, a Relationships object, and then registers a
* listener that responds to any changes to a specific first Row's linked Row
* objects.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', next: 'felix'},
* felix: {species: 'cat', next: 'cujo'},
* cujo: {species: 'dog'},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSequence',
* 'pets',
* 'pets',
* 'next',
* );
*
* const listenerId = relationships.addLinkedRowIdsListener(
* 'petSequence',
* 'fido',
* (relationships) => {
* console.log('petSequence linked list (from fido) changed');
* console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
* },
* );
*
* store.setRow('pets', 'toto', {species: 'dog'});
* store.setCell('pets', 'cujo', 'next', 'toto');
* // -> 'petSequence linked list (from fido) changed'
* // -> ['fido', 'felix', 'cujo', 'toto']
*
* relationships.delListener(listenerId);
* ```
* @category Listener
* @since v1.0.0
*/
addLinkedRowIdsListener(
relationshipId: Id,
firstRowId: Id,
listener: LinkedRowIdsListener,
): Id;
/**
* The delListener method removes a listener that was previously added to the
* Relationships object.
*
* Use the Id returned by whichever method was used to add the listener. Note
* that the Relationships object may re-use this Id for future listeners added
* to it.
* @param listenerId The Id of the listener to remove.
* @returns A reference to the Relationships object.
* @example
* This example creates a Store, a Relationships object, registers a listener,
* and then removes it.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
*
* const listenerId = relationships.addLocalRowIdsListener(
* 'petSpecies',
* 'dog',
* () => {
* console.log('petSpecies relationship (to dog) changed');
* },
* );
*
* store.setRow('pets', 'toto', {species: 'dog'});
* // -> 'petSpecies relationship (to dog) changed'
*
* relationships.delListener(listenerId);
*
* store.setRow('pets', 'toto', {species: 'dog'});
* // -> undefined
* // The listener is not called.
* ```
* @category Listener
* @since v1.0.0
*/
delListener(listenerId: Id): Relationships;
/**
* The destroy method should be called when this Relationships object is no
* longer used.
*
* This guarantees that all of the listeners that the object registered with
* the underlying Store are removed and it can be correctly garbage collected.
* @example
* This example creates a Store, adds a Relationships object with a
* definition (that registers a RowListener with the underlying Store),
* and then destroys it again, removing the listener.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore()
* .setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* })
* .setTable('species', {
* wolf: {price: 10},
* dog: {price: 5},
* cat: {price: 4},
* });
*
* const relationships = createRelationships(store);
* relationships.setRelationshipDefinition(
* 'petSpecies',
* 'pets',
* 'species',
* 'species',
* );
* console.log(store.getListenerStats().row);
* // -> 1
*
* relationships.destroy();
*
* console.log(store.getListenerStats().row);
* // -> 0
* ```
* @category Lifecycle
* @since v1.0.0
*/
destroy(): void;
/**
* The getListenerStats method provides a set of statistics about the
* listeners registered with the Relationships object, and is used for
* debugging purposes.
*
* The RelationshipsListenerStats object contains a breakdown of the different
* types of listener.
*
* The method is intended to be used during development to ensure your
* application is not leaking listener registrations, for example.
* @returns A RelationshipsListenerStats object containing Relationships
* listener statistics.
* @example
* This example gets the listener statistics of a Relationships object.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore();
* const relationships = createRelationships(store);
* relationships.addRemoteRowIdListener(null, null, () => {
* console.log('Remote Row Id changed');
* });
* relationships.addLocalRowIdsListener(null, null, () => {
* console.log('Local Row Id changed');
* });
*
* const listenerStats = relationships.getListenerStats();
* console.log(listenerStats.remoteRowId);
* // -> 1
* console.log(listenerStats.localRowIds);
* // -> 1
* ```
* @category Development
* @since v1.0.0
*/
getListenerStats(): RelationshipsListenerStats;
//
}
/**
* The createRelationships function creates a Relationships object, and is the
* main entry point into the relationships module.
*
* A given Store can only have one Relationships object associated with it. If
* you call this function twice on the same Store, your second call will return
* a reference to the Relationships object created by the first.
* @param store The Store for which to register Relationships.
* @returns A reference to the new Relationships object.
* @example
* This example creates a Relationships object.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore();
* const relationships = createRelationships(store);
* console.log(relationships.getRelationshipIds());
* // -> []
* ```
* @example
* This example creates a Relationships object, and calls the method a second
* time for the same Store to return the same object.
*
* ```js
* import {createRelationships, createStore} from 'tinybase';
*
* const store = createStore();
* const relationships1 = createRelationships(store);
* const relationships2 = createRelationships(store);
* console.log(relationships1 === relationships2);
* // -> true
* ```
* @category Creation
* @since v1.0.0
*/
export function createRelationships(store: Store): Relationships;