tinybase
Version:
A reactive data store and sync engine.
1,576 lines (1,537 loc) • 51.2 kB
text/typescript
/**
* The mergeable-store module contains the types, interfaces, and functions to
* work with MergeableStore objects, which provide merge and synchronization
* functionality.
*
* The main entry point to this module is the createMergeableStore function,
* which returns a new MergeableStore, a subtype of Store that can be merged
* with another with deterministic results.
*
* Please be aware that a lot of the types and methods exposed by this module
* are used internally within TinyBase itself (in particular the Synchronizer
* framework). They're documented here, but mostly for interest, and it is
* generally assumed that they won't be called directly by applications.
*
* As an application developer, it's more likely that you will continue to use
* the main Store methods for reading, writing, and listening to data, and rely
* on Synchronizer instances to keep the data in step with other places.
* @packageDocumentation
* @module mergeable-store
* @since v5.0.0
*/
import type {
CellIdFromSchema,
TableIdFromSchema,
ValueIdFromSchema,
} from '../../_internal/store/with-schemas/index.d.cts';
import type {Id} from '../../common/with-schemas/index.d.cts';
import type {
CellOrUndefined,
Content,
NoSchemas,
NoTablesSchema,
NoValuesSchema,
OptionalSchemas,
OptionalTablesSchema,
OptionalValuesSchema,
Store,
TablesSchema,
ValueOrUndefined,
ValuesSchema,
} from '../../store/with-schemas/index.d.cts';
/**
* The Hash type is used within the mergeable-store module to quickly compare
* the content of two objects.
*
* This is simply an alias for a JavaScript `number`.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type Hash = number;
/**
* The Time type is used within the mergeable-store module to store the value of
* a hybrid logical clock (HLC).
*
* It is simply an alias for a JavaScript `string`, but it comprises three HLC
* parts: a logical timestamp, a sequence counter, and a client Id. It is
* designed to be string-sortable and unique across all of the systems involved
* in synchronizing a MergeableStore.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type Time = string;
/**
* The Stamp type is used as metadata to decide how to merge two different
* MergeableStore objects together.
*
* It describes a combination of a value (or object), a Time, and optionally a
* Hash, all in an array.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type Stamp<Thing, Hashed extends boolean = false> = Hashed extends true
? [thing: Thing, time: Time, hash: Hash]
: [thing: Thing, time?: Time];
/**
* The ContentHashes type is used to quickly compare the content of two
* MergeableStore objects.
*
* It is simply an array of two Hash types, one for the MergeableStore's Tables
* and one for its Values.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Syncing
* @since v5.0.0
*/
export type ContentHashes = [tablesHash: Hash, valuesHash: Hash];
/**
* The TablesStamp type is used as metadata to decide how to merge two different
* sets of Tables together.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type TablesStamp<Hashed extends boolean = false> = Stamp<
* {[tableId: Id]: TableStamp<Hashed>},
* Hashed
* >;
* ```
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type TablesStamp<
Schema extends OptionalTablesSchema,
Hashed extends boolean = false,
> = Stamp<
{
[TableId in TableIdFromSchema<Schema>]?: TableStamp<
Schema,
TableId,
Hashed
>;
},
Hashed
>;
/**
* The TableHashes type is used to quickly compare the content of two sets of
* Table objects.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* {[tableId: Id]: Hash};
* ```
*
* It is simply an object of Hash types, one for each Table Id in the
* MergeableStore.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Syncing
* @since v5.0.0
*/
export type TableHashes<Schema extends OptionalTablesSchema> = {
[TableId in TableIdFromSchema<Schema>]?: Hash;
};
/**
* The TableStamp type is used as metadata to decide how to merge two different
* Table objects together.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type TableStamp<Hashed extends boolean = false> = Stamp<
* {[rowId: Id]: RowStamp<Hashed>},
* Hashed
* >;
* ```
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type TableStamp<
Schema extends OptionalTablesSchema,
TableId extends TableIdFromSchema<Schema>,
Hashed extends boolean = false,
> = Stamp<{[rowId: Id]: RowStamp<Schema, TableId, Hashed>}, Hashed>;
/**
* The RowHashes type is used to quickly compare the content of two sets of Row
* objects.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* {[tableId: Id]: {[rowId: Id]: Hash}};
* ```
*
* It is simply a nested object of Hash types, one for each Row Id, for each
* TableId, in the MergeableStore.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Syncing
* @since v5.0.0
*/
export type RowHashes<Schema extends OptionalTablesSchema> = {
[TableId in TableIdFromSchema<Schema>]?: {[rowId: Id]: Hash};
};
/**
* The RowStamp type is used as metadata to decide how to merge two different
* Row objects together.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type RowStamp<Hashed extends boolean = false> = Stamp<
* {[cellId: Id]: CellStamp<Hashed>},
* Hashed
* >;
* ```
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type RowStamp<
Schema extends OptionalTablesSchema,
TableId extends TableIdFromSchema<Schema>,
Hashed extends boolean = false,
> = Stamp<
{
[CellId in CellIdFromSchema<Schema, TableId>]?: CellStamp<
Schema,
TableId,
CellId,
Hashed
>;
},
Hashed
>;
/**
* The CellHashes type is used to quickly compare the content of two sets of
* Cell objects.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* {[tableId: Id]: {[rowId: Id]: {[cellId: Id]: Hash}}};
* ```
*
* It is simply a nested object of Hash types, one for each Cell Id, for each
* Row Id, for each TableId, in the MergeableStore.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Syncing
* @since v5.0.0
*/
export type CellHashes<Schema extends OptionalTablesSchema> = {
[TableId in TableIdFromSchema<Schema>]?: {
[rowId: Id]: {[CellId in CellIdFromSchema<Schema, TableId>]?: Hash};
};
};
/**
* The CellStamp type is used as metadata to decide how to merge two different
* Cell objects together.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type CellStamp<Hashed extends boolean = false> = Stamp<
* CellOrUndefined,
* Hashed
* >;
* ```
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type CellStamp<
Schema extends OptionalTablesSchema,
TableId extends TableIdFromSchema<Schema>,
CellId extends CellIdFromSchema<Schema, TableId>,
Hashed extends boolean = false,
> = Stamp<CellOrUndefined<Schema, TableId, CellId>, Hashed>;
/**
* The ValuesStamp type is used as metadata to decide how to merge two different
* sets of Values together.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type ValuesStamp<Hashed extends boolean = false> = Stamp<
* {[valueId: Id]: ValueStamp<Hashed>},
* Hashed
* >;
* ```
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type ValuesStamp<
Schema extends OptionalValuesSchema,
Hashed extends boolean = false,
> = Stamp<
{
[ValueId in ValueIdFromSchema<Schema>]?: ValueStamp<
Schema,
ValueId,
Hashed
>;
},
Hashed
>;
/**
* The ValueHashes type is used to quickly compare the content of two sets of
* Value objects.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* {[valueId: Id]: Hash};
* ```
*
* It is simply an object of Hash types, one for each Value Id in the
* MergeableStore.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Syncing
* @since v5.0.0
*/
export type ValueHashes<Schema extends OptionalValuesSchema> = {
[ValueId in ValueIdFromSchema<Schema>]?: Hash;
};
/**
* The ValueStamp type is used as metadata to decide how to merge two different
* Value objects together.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type ValueStamp<Hashed extends boolean = false> = Stamp<
* ValueOrUndefined,
* Hashed
* >;
* ```
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Stamps
* @since v5.0.0
*/
export type ValueStamp<
Schema extends OptionalValuesSchema,
ValueId extends ValueIdFromSchema<Schema>,
Hashed extends boolean = false,
> = Stamp<ValueOrUndefined<Schema, ValueId>, Hashed>;
/**
* The MergeableContent type represents the content of a MergeableStore and the
* metadata about that content) required to merge it with another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* [
* mergeableTables: TablesStamp<true>,
* mergeableValues: ValuesStamp<true>,
* ];
* ```
*
* It is simply an array of two Stamp types, one for the MergeableStore's Tables
* and one for its Values.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Mergeable
* @since v5.0.0
*/
export type MergeableContent<Schemas extends OptionalSchemas> = [
mergeableTables: TablesStamp<Schemas[0], true>,
mergeableValues: ValuesStamp<Schemas[1], true>,
];
/**
* The MergeableChanges type represents changes to the content of a
* MergeableStore and the metadata about that content) required to merge it with
* another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* export type MergeableChanges<Hashed extends boolean = false> = [
* mergeableTables: TablesStamp<Hashed>,
* mergeableValues: ValuesStamp<Hashed>,
* isChanges: 1,
* ];
* ```
*
* It is simply an array of two Stamp types, one for changes to the
* MergeableStore's Tables and one for changes to its Values. A final `1` is
* used to distinguish it from a full MergeableContent object.
*
* This type is mostly utilized internally within TinyBase itself and is
* generally assumed to be opaque to applications that use it.
* @category Mergeable
* @since v5.0.0
*/
export type MergeableChanges<
Schemas extends OptionalSchemas,
Hashed extends boolean = false,
> = [
mergeableTables: TablesStamp<Schemas[0], Hashed>,
mergeableValues: ValuesStamp<Schemas[1], Hashed>,
isChanges: 1,
];
/**
* The MergeableStore type represents a Store that carries with it sufficient
* metadata to be able to be merged with another MergeableStore with
* deterministic results.
*
* This is the key data type used when you need TinyBase data to be cleanly
* synchronized or merged with data elsewhere on the system, or on another
* system. It acts as a Conflict-Free Replicated Data Type (CRDT) which allows
* deterministic disambiguation of how changes to different instances should be
* merged.
*
* Please be aware that a lot of the methods exposed by this interface are used
* internally within TinyBase itself (in particular the Synchronizer framework).
* They're documented here, but mostly for interest, and it is generally assumed
* that they won't be called directly by applications.
*
* As an application developer, it's more likely that you will continue to use
* the main Store methods for reading, writing, and listening to data, and rely
* on Synchronizer instances to keep the data in step with other places.
*
* One possible exceptions is the merge method, which can be used to simply
* merge two co-located MergeableStore instances together.
* @example
* This example shows very simple usage of the MergeableStore: whereby two are
* created, updated with different data, and then merged with one another.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const localStore1 = createMergeableStore();
* const localStore2 = createMergeableStore();
*
* localStore1.setCell('pets', 'fido', 'color', 'brown');
* localStore2.setCell('pets', 'felix', 'color', 'black');
*
* localStore1.merge(localStore2);
*
* console.log(localStore1.getContent());
* // -> [{pets: {felix: {color: 'black'}, fido: {color: 'brown'}}}, {}]
*
* console.log(localStore2.getContent());
* // -> [{pets: {felix: {color: 'black'}, fido: {color: 'brown'}}}, {}]
*```
* @category Mergeable
* @since v5.0.0
*/
export interface MergeableStore<Schemas extends OptionalSchemas>
extends Store<Schemas> {
//
/**
* The getMergeableContent method returns the full content of a
* MergeableStore, together with the metadata required to make it mergeable
* with another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableContent(): MergeableContent;
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @returns A MergeableContent object for the full content of the
* MergeableStore.
* @example
* This example creates a MergeableStore, sets some data, and then accesses
* the content and metadata required to make it mergeable.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1'); // !resetHlc
*
* store.setCell('pets', 'fido', 'color', 'brown');
*
* console.log(store.getMergeableContent());
* // ->
* [
* [
* {
* pets: [
* {
* fido: [
* {color: ['brown', 'Nn1JUF-----FnHIC', 923684530]},
* '',
* 851131566,
* ],
* },
* '',
* 518810247,
* ],
* },
* '',
* 784336119,
* ],
* [{}, '', 0],
* ];
* ```
* @category Getter
* @since v5.0.0
*/
getMergeableContent(): MergeableContent<Schemas>;
/**
* The getMergeableContentHashes method returns hashes for the full content of
* a MergeableStore.
*
* If two MergeableStore instances have different hashes, that indicates that
* the mergeable Tables or Values within them are different and should be
* synchronized.
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @returns A ContentHashes array for the hashes of the full content of the
* MergeableStore.
* @example
* This example creates a MergeableStore, sets some data, and then accesses
* the content hashes.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1'); // !resetHlc
*
* store.setCell('pets', 'fido', 'color', 'brown');
* console.log(store.getMergeableContentHashes());
* // -> [784336119, 0]
*
* store.setValue('open', true);
* console.log(store.getMergeableContentHashes());
* // -> [784336119, 2829789038]
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableContentHashes(): ContentHashes;
/**
* The getMergeableTableHashes method returns hashes for the Table objects in
* a MergeableStore.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableTableHashes(): TableHashes;
* ```
*
* If two Table Ids have different hashes, that indicates that the content
* within them is different and should be synchronized.
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @returns A TableHashes object with the hashes of each Table in the
* MergeableStore.
* @example
* This example creates a MergeableStore, sets some data, and then accesses
* the Table hashes.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1'); // !resetHlc
*
* store.setCell('pets', 'fido', 'color', 'brown');
* console.log(store.getMergeableTableHashes());
* // -> {pets: 518810247}
*
* store.setCell('species', 'dog', 'price', 5);
* console.log(store.getMergeableTableHashes());
* // -> {pets: 518810247, species: 2324343240}
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableTableHashes(): TableHashes<Schemas[0]>;
/**
* The getMergeableTableDiff method returns information about new and
* differing Table objects of a MergeableStore relative to another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableTableDiff(
* otherTableHashes: TableHashes,
* ): [newTables: TablesStamp, differingTableHashes: TableHashes];
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param otherTableHashes The TableHashes of another MergeableStore.
* @returns A pair of objects describing the new and differing Table objects
* of this MergeableStore relative to the other.
* @example
* This example creates two MergeableStores, sets some differing data, and
* then identifies the differences in the Table objects of one versus the
* other. Once they have been merged, the differences are empty.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setTables({pets: {fido: {color: 'brown'}}});
*
* const store2 = createMergeableStore('store2');
* store2.setTables({
* pets: {fido: {species: 'dog'}},
* species: {dog: {price: 5}},
* });
*
* console.log(
* store2.getMergeableTableDiff(store1.getMergeableTableHashes()),
* );
* // ->
* [
* [{species: [{dog: [{price: [5, 'Nn1JUF----0CnH-J']}]}]}],
* {pets: 1212600658},
* ];
*
* store1.merge(store2);
*
* console.log(
* store2.getMergeableTableDiff(store1.getMergeableTableHashes()),
* );
* // -> [[{}], {}]
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableTableDiff(
otherTableHashes: TableHashes<Schemas[0]>,
): [
newTables: TablesStamp<Schemas[0]>,
differingTableHashes: TableHashes<Schemas[0]>,
];
/**
* The getMergeableRowHashes method returns hashes for Row objects in a
* MergeableStore.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableRowHashes(otherTableHashes: TableHashes): RowHashes;
* ```
*
* If two Row Ids have different hashes, that indicates that the content
* within them is different and should be synchronized.
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param otherTableHashes The TableHashes from the other MergeableStore so
* that the differences can be efficiently identified.
* @returns A RowHashes object with the hashes of each Row in the relevant
* Table objects of the MergeableStore.
* @example
* This example creates a MergeableStore, sets some data, and then accesses
* the Row hashes for the differing Table Ids.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setTables({pets: {fido: {color: 'brown'}, felix: {color: 'tan'}}});
*
* const store2 = createMergeableStore('store2');
* store2.setTables({pets: {fido: {color: 'black'}, felix: {color: 'tan'}}});
*
* console.log(
* store1.getMergeableRowHashes(
* store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
* ),
* );
* // -> {pets: {felix: 1683761402, fido: 851131566}}
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableRowHashes(
otherTableHashes: TableHashes<Schemas[0]>,
): RowHashes<Schemas[0]>;
/**
* The getMergeableRowDiff method returns information about new and differing
* Row objects of a MergeableStore relative to another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableRowDiff(
* otherTableRowHashes: RowHashes,
* ): [newRows: TablesStamp, differingRowHashes: RowHashes];
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param otherTableRowHashes The RowHashes of another MergeableStore.
* @returns A pair of objects describing the new and differing Row objects of
* this MergeableStore relative to the other.
* @example
* This example creates two MergeableStores, sets some differing data, and
* then identifies the differences in the Row objects of one versus the other.
* Once they have been merged, the differences are empty.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setTables({pets: {fido: {color: 'brown'}}});
*
* const store2 = createMergeableStore('store2');
* store2.setTables({pets: {fido: {color: 'black'}, felix: {color: 'tan'}}});
*
* console.log(
* store2.getMergeableRowDiff(
* store1.getMergeableRowHashes(
* store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
* ),
* ),
* );
* // ->
* [
* [{pets: [{felix: [{color: ['tan', 'Nn1JUF----0CnH-J']}]}]}],
* {pets: {fido: 1038491054}},
* ];
*
* store1.merge(store2);
*
* console.log(
* store2.getMergeableRowDiff(
* store1.getMergeableRowHashes(
* store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
* ),
* ),
* );
* // -> [[{}], {}]
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableRowDiff(
otherTableRowHashes: RowHashes<Schemas[0]>,
): [
newRows: TablesStamp<Schemas[0]>,
differingRowHashes: RowHashes<Schemas[0]>,
];
/**
* The getMergeableCellHashes method returns hashes for Cell objects in a
* MergeableStore.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableCellHashes(otherTableRowHashes: RowHashes): CellHashes;
* ```
*
* If two Cell Ids have different hashes, that indicates that the content
* within them is different and should be synchronized.
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param otherTableRowHashes The RowHashes from the other MergeableStore so
* that the differences can be efficiently identified.
* @returns A CellHashes object with the hashes of each Cell in the relevant
* Row objects of the MergeableStore.
* @example
* This example creates a MergeableStore, sets some data, and then accesses
* the Cell hashes for the differing Table Ids.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setTables({pets: {fido: {color: 'brown', species: 'dog'}}});
*
* const store2 = createMergeableStore('store2');
* store2.setTables({pets: {fido: {color: 'black', species: 'dog'}}});
*
* console.log(
* store1.getMergeableCellHashes(
* store2.getMergeableRowDiff(
* store1.getMergeableRowHashes(
* store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
* ),
* )[1],
* ),
* );
* // -> {pets: {fido: {color: 923684530, species: 227729753}}}
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableCellHashes(
otherTableRowHashes: RowHashes<Schemas[0]>,
): CellHashes<Schemas[0]>;
/**
* The getMergeableCellDiff method returns information about new and differing
* Cell objects of a MergeableStore relative to another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableCellDiff(otherTableRowCellHashes: CellHashes): TablesStamp;
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param otherTableRowCellHashes The CellHashes of another MergeableStore.
* @returns The new and differing Cell objects of this MergeableStore relative
* to the other.
* @example
* This example creates two MergeableStores, sets some differing data, and
* then identifies the differences in the Cell objects of one versus the
* other. Once they have been merged, the differences are empty.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setTables({pets: {fido: {color: 'brown'}}});
*
* const store2 = createMergeableStore('store2');
* store2.setTables({pets: {fido: {color: 'black', species: 'dog'}}});
*
* console.log(
* store2.getMergeableCellDiff(
* store1.getMergeableCellHashes(
* store2.getMergeableRowDiff(
* store1.getMergeableRowHashes(
* store2.getMergeableTableDiff(
* store1.getMergeableTableHashes(),
* )[1],
* ),
* )[1],
* ),
* ),
* );
* // ->
* [
* {
* pets: [
* {
* fido: [
* {
* color: ['black', 'Nn1JUF-----CnH-J'],
* species: ['dog', 'Nn1JUF----0CnH-J'],
* },
* ],
* },
* ],
* },
* ];
*
* store1.merge(store2);
*
* console.log(
* store2.getMergeableCellDiff(
* store1.getMergeableCellHashes(
* store2.getMergeableRowDiff(
* store1.getMergeableRowHashes(
* store2.getMergeableTableDiff(
* store1.getMergeableTableHashes(),
* )[1],
* ),
* )[1],
* ),
* ),
* );
* // -> [{}]
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableCellDiff(
otherTableRowCellHashes: CellHashes<Schemas[0]>,
): TablesStamp<Schemas[0]>;
/**
* The getMergeableValueHashes method returns hashes for the Value objects in
* a MergeableStore.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableValueHashes(): ValueHashes;
* ```
*
* If two Value Ids have different hashes, that indicates that the content
* within them is different and should be synchronized.
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @returns A ValueHashes object with the hashes of each Value in the
* MergeableStore.
* @example
* This example creates a MergeableStore, sets some data, and then accesses
* the Value hashes.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1'); // !resetHlc
*
* store.setValue('employees', 3);
* console.log(store.getMergeableValueHashes());
* // -> {employees: 1940815977}
*
* store.setValue('open', true);
* console.log(store.getMergeableValueHashes());
* // -> {employees: 1940815977, open: 3860530645}
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableValueHashes(): ValueHashes<Schemas[1]>;
/**
* The getMergeableValueDiff method returns information about new and
* differing Value objects of a MergeableStore relative to another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getMergeableValueDiff(otherValueHashes: ValueHashes): ValuesStamp;
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param otherValueHashes The ValueHashes of another MergeableStore.
* @returns The new and differing Value objects of this MergeableStore
* relative to the other.
* @example
* This example creates two MergeableStores, sets some differing data, and
* then identifies the differences in the Value objects of one versus the
* other. Once they have been merged, the differences are empty.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setValues({employees: 3});
*
* const store2 = createMergeableStore('store2');
* store2.setValues({employees: 4, open: true});
*
* console.log(
* store2.getMergeableValueDiff(store1.getMergeableValueHashes()),
* );
* // ->
* [
* {
* employees: [4, 'Nn1JUF-----CnH-J'],
* open: [true, 'Nn1JUF----0CnH-J'],
* },
* ];
*
* store1.merge(store2);
*
* console.log(
* store2.getMergeableValueDiff(store1.getMergeableValueHashes()),
* );
* // -> [{}]
* ```
* @category Syncing
* @since v5.0.0
*/
getMergeableValueDiff(
otherValueHashes: ValueHashes<Schemas[1]>,
): ValuesStamp<Schemas[1]>;
/**
* The setMergeableContent method sets the full content of a MergeableStore,
* together with the metadata required to make it mergeable with another.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* setMergeableContent(mergeableContent: MergeableContent): MergeableStore;
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param mergeableContent The full content and metadata of a MergeableStore.
* @returns A reference to the MergeableStore.
* @example
* This example creates a new MergeableStore and initializes it with
* the content and metadata from another.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setValues({employees: 3});
* console.log(store1.getMergeableContent());
* // ->
* [
* [{}, '', 0],
* [{employees: [3, 'Nn1JUF-----FnHIC', 1940815977]}, '', 1260895905],
* ];
*
* const store2 = createMergeableStore('store2');
* store2.setMergeableContent(store1.getMergeableContent());
* console.log(store2.getMergeableContent());
* // ->
* [
* [{}, '', 0],
* [{employees: [3, 'Nn1JUF-----FnHIC', 1940815977]}, '', 1260895905],
* ];
* ```
* @category Setter
* @since v5.0.0
*/
setMergeableContent(
mergeableContent: MergeableContent<Schemas>,
): MergeableStore<Schemas>;
/**
* The setDefaultContent method sets initial content of a MergeableStore.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* setDefaultContent(content: Content | (() => Content)): MergeableStore;
* ```
*
* This differs from the setMergeableContent method in that all of the
* metadata is initialized with a empty HLC timestamp - meaning that any
* changes applied to it will 'win', yet ensuring that at least default,
* initial data exists.
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
*
* Since v5.4.2, this method can also take a function that returns the
* content.
* @param content An array containing the tabular and keyed-value data to be
* set, or a function that returns the array.
* @returns A reference to the MergeableStore.
* @example
* This example creates a new MergeableStore with default data, and
* demonstrates that it is overwritten with another MergeableStore's data on
* merge, even if the other MergeableStore was provisioned earlier.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1'); // !resetHlc
* store1.setValues({employees: 3});
*
* const store2 = createMergeableStore('store2');
* store2.setDefaultContent([{}, {employees: 4}]);
* console.log(store2.getMergeableContent());
* // -> [[{}, "", 0], [{"employees": [4, "", 2414055963]}, "", 3035768673]]
*
* store2.merge(store1);
* console.log(store2.getContent());
* // -> [{}, {employees: 3}]
* ```
* @category Setter
* @since v5.0.0
*/
setDefaultContent(
content: Content<Schemas> | (() => Content<Schemas>),
): MergeableStore<Schemas>;
/**
* The getTransactionMergeableChanges method returns the net meaningful
* changes that have been made to a MergeableStore during a transaction.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getTransactionMergeableChanges(withHashes?: boolean): MergeableChanges<true>;
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param withHashes Whether to include hashes in the output, defaulting to
* false.
* @returns A MergeableChanges object representing the changes, with hashes.
* @example
* This example makes changes to the MergeableStore. At the end of the
* transaction, detail about what changed is enumerated.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1'); // !resetHlc
* store.setTables({pets: {fido: {species: 'dog', color: 'brown'}}});
* store.setValues({open: true});
*
* store
* .startTransaction()
* .setCell('pets', 'fido', 'color', 'black')
* .setValue('open', false)
* .finishTransaction(() => {
* console.log(store.getTransactionMergeableChanges());
* });
* // ->
* [
* [{pets: [{fido: [{color: ['black', 'Nn1JUF----2FnHIC']}]}]}],
* [{open: [false, 'Nn1JUF----3FnHIC']}],
* 1,
* ];
* ```
* @category Transaction
* @since v5.0.0
*/
getTransactionMergeableChanges(
withHashes?: boolean,
): MergeableChanges<Schemas, true>;
/**
* The applyMergeableChanges method applies a set of mergeable changes or
* content to the MergeableStore.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* applyMergeableChanges(
* mergeableChanges: MergeableChanges | MergeableContent,
* ): MergeableStore;
* ```
*
* The method is generally intended to be used internally within TinyBase
* itself and the return type is assumed to be opaque to applications that use
* it.
* @param mergeableChanges The MergeableChanges or MergeableContent to apply
* to the MergeableStore.
* @returns A reference to the MergeableStore.
* @example
* This example applies a MergeableChanges object that sets a Cell and removes
* a Value.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1')
* .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
* .setValues({open: true});
*
* store.applyMergeableChanges([
* [{pets: [{fido: [{color: ['black', 'Nn1JUF----2FnHIC']}]}]}],
* [{open: [null, 'Nn1JUF----3FnHIC']}],
* 1,
* ]);
* console.log(store.getTables());
* // -> {pets: {fido: {species: 'dog', color: 'black'}}}
* console.log(store.getValues());
* // -> {}
* ```
* @category Setter
* @since v5.0.0
*/
applyMergeableChanges(
mergeableChanges: MergeableChanges<Schemas> | MergeableContent<Schemas>,
): MergeableStore<Schemas>;
/**
* The merge method is a convenience method that applies the mergeable content
* from two MergeableStores to each other in order to bring them to the same
* state.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* merge(mergeableStore: MergeableStore): MergeableStore;
* ```
*
* This method is symmetrical: applying `store1` to `store2` will have exactly
* the same effect as applying `store2` to `store1`.
* @param mergeableStore A reference to the other MergeableStore to merge with
* this one.
* @returns A reference to this MergeableStore.
* @example
* This example merges two MergeableStore objects together. Note how the final
* part of the timestamps on each Cell give you a clue that the data comes
* from changes made to different MergeableStore objects.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store1 = createMergeableStore('store1');
* store1.setTables({pets: {fido: {species: 'dog', color: 'brown'}}});
*
* const store2 = createMergeableStore('store2');
* store2.setTables({pets: {felix: {species: 'cat', color: 'tan'}}});
*
* store1.merge(store2);
*
* console.log(store1.getContent());
* // ->
* [
* {
* pets: {
* felix: {color: 'tan', species: 'cat'},
* fido: {color: 'brown', species: 'dog'},
* },
* },
* {},
* ];
*
* console.log(store2.getContent());
* // ->
* [
* {
* pets: {
* felix: {color: 'tan', species: 'cat'},
* fido: {color: 'brown', species: 'dog'},
* },
* },
* {},
* ];
* console.log(store2.getMergeableContent());
* // ->
* [
* [
* {
* pets: [
* {
* felix: [
* {
* color: ['tan', 'Nn1JUF----0CnH-J', 2576658292],
* species: ['cat', 'Nn1JUF-----CnH-J', 3409607562],
* },
* '',
* 4146239216,
* ],
* fido: [
* {
* color: ['brown', 'Nn1JUF----0FnHIC', 1240535355],
* species: ['dog', 'Nn1JUF-----FnHIC', 290599168],
* },
* '',
* 3989065420,
* ],
* },
* '',
* 4155188296,
* ],
* },
* '',
* 972931118,
* ],
* [{}, '', 0],
* ];
* ```
* @category Setter
* @since v5.0.0
*/
merge(mergeableStore: MergeableStore<Schemas>): MergeableStore<Schemas>;
/**
* The isMergeable method lets you know if the Store is mergeable.
*
* This will always return false for a Store, and true for a MergeableStore.
* @returns Whether the Store is mergeable.
* @category Getter
* @since v5.0.0
*/
isMergeable(): boolean;
/**
* The setTablesSchema method lets you specify the TablesSchema of the tabular
* part of the Store.
*
* Note that this may result in a change to data in the Store, as defaults are
* applied or as invalid Table, Row, or Cell objects are removed. These
* changes will fire any listeners to that data, as expected.
*
* When no longer needed, you can also completely remove an existing
* TablesSchema with the delTablesSchema method.
* @param tablesSchema The TablesSchema to be set for the Store.
* @returns A reference to the Store.
* @example
* This example sets the TablesSchema of a Store after it has been created.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore().setTablesSchema({
* pets: {
* species: {type: 'string'},
* sold: {type: 'boolean', default: false},
* },
* });
* store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});
*
* console.log(store.getTables());
* // -> {pets: {0: {species: 'dog', sold: false}}}
* ```
* @category Setter
* @since v3.0.0
*/
setTablesSchema<TS extends TablesSchema>(
tablesSchema: TS,
): MergeableStore<[typeof tablesSchema, Schemas[1]]>;
/**
* The setValuesSchema method lets you specify the ValuesSchema of the keyed
* Values part of the Store.
*
* Note that this may result in a change to data in the Store, as defaults are
* applied or as invalid Values are removed. These changes will fire any
* listeners to that data, as expected.
*
* When no longer needed, you can also completely remove an existing
* ValuesSchema with the delValuesSchema method.
* @param valuesSchema The ValuesSchema to be set for the Store.
* @returns A reference to the Store.
* @example
* This example sets the ValuesSchema of a Store after it has been created.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore().setValuesSchema({
* open: {type: 'boolean', default: false},
* });
* store.setValue('open', 'maybe');
*
* console.log(store.getValues());
* // -> {open: false}
* ```
* @category Setter
* @since v3.0.0
*/
setValuesSchema<VS extends ValuesSchema>(
valuesSchema: VS,
): MergeableStore<[Schemas[0], typeof valuesSchema]>;
/**
* The setSchema method lets you specify the TablesSchema and ValuesSchema of
* the Store.
*
* Note that this may result in a change to data in the Store, as defaults are
* applied or as invalid Table, Row, Cell, or Value objects are removed. These
* changes will fire any listeners to that data, as expected.
*
* From v3.0 onwards, this method takes two arguments. The first is the
* TablesSchema object, the second the ValuesSchema. In previous versions
* (before the existence of the ValuesSchema data structure), only the first
* was present. For backwards compatibility the new second parameter is
* optional.
* @param tablesSchema The TablesSchema to be set for the Store.
* @param valuesSchema The ValuesSchema to be set for the Store.
* @returns A reference to the Store.
* @example
* This example sets the TablesSchema and ValuesSchema of a Store after it has
* been created.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore().setSchema(
* {
* pets: {
* species: {type: 'string'},
* sold: {type: 'boolean', default: false},
* },
* },
* {open: {type: 'boolean', default: false}},
* );
* store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});
* store.setValue('open', 'maybe');
*
* console.log(store.getTables());
* // -> {pets: {0: {species: 'dog', sold: false}}}
* console.log(store.getValues());
* // -> {open: false}
* ```
* @example
* This example sets just the TablesSchema of a Store after it has been
* created.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore().setSchema({
* pets: {
* species: {type: 'string'},
* sold: {type: 'boolean', default: false},
* },
* });
* store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});
*
* console.log(store.getTables());
* // -> {pets: {0: {species: 'dog', sold: false}}}
* ```
* @category Setter
* @since v1.0.0
*/
setSchema<TS extends TablesSchema, VS extends ValuesSchema>(
tablesSchema: TS,
valuesSchema?: VS,
): MergeableStore<
[
typeof tablesSchema,
Exclude<ValuesSchema, typeof valuesSchema> extends never
? NoValuesSchema
: NonNullable<typeof valuesSchema>,
]
>;
/**
* The delTablesSchema method lets you remove the TablesSchema of the Store.
* @returns A reference to the Store.
* @example
* This example removes the TablesSchema of a Store.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore().setTablesSchema({
* pets: {species: {type: 'string'}},
* });
* store.delTablesSchema();
* console.log(store.getTablesSchemaJson());
* // -> '{}'
* ```
* @category Deleter
* @since v1.0.0
*/
delTablesSchema<
ValuesSchema extends OptionalValuesSchema = Schemas[1],
>(): MergeableStore<[NoTablesSchema, ValuesSchema]>;
/**
* The delValuesSchema method lets you remove the ValuesSchema of the Store.
* @returns A reference to the Store.
* @example
* This example removes the ValuesSchema of a Store.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore().setValuesSchema({
* sold: {type: 'boolean', default: false},
* });
* store.delValuesSchema();
* console.log(store.getValuesSchemaJson());
* // -> '{}'
* ```
* @category Deleter
* @since v3.0.0
*/
delValuesSchema<
TablesSchema extends OptionalTablesSchema = Schemas[0],
>(): MergeableStore<[TablesSchema, NoValuesSchema]>;
/**
* The delSchema method lets you remove both the TablesSchema and ValuesSchema
* of the Store.
*
* Prior to v3.0, this method removed the TablesSchema only.
* @returns A reference to the Store.
* @example
* This example removes the TablesSchema and ValuesSchema of a Store.
*
* ```js
* import {createStore} from 'tinybase';
*
* const store = createStore()
* .setTablesSchema({
* pets: {species: {type: 'string'}},
* })
* .setValuesSchema({
* sold: {type: 'boolean', default: false},
* });
* store.delSchema();
* console.log(store.getSchemaJson());
* // -> '[{},{}]'
* ```
* @category Deleter
* @since v3.0.0
*/
delSchema(): MergeableStore<NoSchemas>;
}
/**
* The createMergeableStore function creates a MergeableStore, and is the main
* entry point into the mergeable-store module.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* createMergeableStore(uniqueId?: Id): MergeableStore;
* ```
*
* There is one optional parameter which is a uniqueId for the MergeableStore.
* This is used to distinguish conflicting changes made in the same millisecond
* by two different MergeableStore objects as its hash is added to the end of
* the HLC timestamps. Generally this can be omitted unless you have a need for
* deterministic HLCs, such as in a testing scenario. Otherwise, TinyBase will
* assign a unique Id to the Store at the time of creation.
* @returns A reference to the new MergeableStore.
* @example
* This example creates a MergeableStore.
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1');
*
* console.log(store.getContent());
* // -> [{}, {}]
* console.log(store.getMergeableContent());
* // -> [[{}, '', 0], [{}, '', 0]]
* ```
* @example
* This example creates a MergeableStore with some initial data:
*
* ```js
* import {createMergeableStore} from 'tinybase';
*
* const store = createMergeableStore('store1').setTables({
* pets: {fido: {species: 'dog'}},
* });
*
* console.log(store.getContent());
* // -> [{pets: {fido: {species: 'dog'}}}, {}]
* console.log(store.getMergeableContent());
* // ->
* [
* [
* {
* pets: [
* {
* fido: [
* {species: ['dog', 'Nn1JUF-----FnHIC', 290599168]},
* '',
* 2682656941,
* ],
* },
* '',
* 2102515304,
* ],
* },
* '',
* 35062