tinybase
Version:
A reactive data store and sync engine.
578 lines (551 loc) • 18.8 kB
TypeScript
/**
* The common module of the TinyBase project provides a small collection of
* common types used across other modules.
* @packageDocumentation
* @module common
* @since v1.0.0
*/
import type {CellOrUndefined, ValueOrUndefined} from '../store/index.d.ts';
/**
* The Json type is a simple alias for a string, but is used to indicate that
* the string should be considered to be a JSON serialization of an object.
* @category General
* @since v1.0.0
*/
export type Json = string;
/**
* The Ids type is a simple alias for an array of strings, but is used to
* indicate that the strings should be considered to be the keys of objects
* (such as the Row Id strings used in a Table).
* @category Identity
* @since v1.0.0
*/
export type Ids = Id[];
/**
* The Id type is a simple alias for a string, but is used to indicate that the
* string should be considered to be the key of an object (such as a Row Id
* string used in a Table).
* @category Identity
* @since v1.0.0
*/
export type Id = string;
/**
* The IdOrNull type is a simple alias for the union of a string or `null`
* value, where the string should be considered to be the key of an objects
* (such as a Row Id string used in a Table), and typically `null` indicates a
* wildcard - such as when used in the Store addRowListener method.
* @category Identity
* @since v1.0.0
*/
export type IdOrNull = Id | null;
/**
* The ParameterizedCallback type represents a generic function that will take
* an optional parameter - such as the handler of a DOM event.
* @category Callback
* @since v1.0.0
*/
export type ParameterizedCallback<Parameter> = (parameter?: Parameter) => void;
/**
* The Callback type represents a function that is used as a callback and which
* does not take a parameter.
* @category Callback
* @since v1.0.0
*/
export type Callback = () => void;
/**
* The SortKey type represents a value that can be used by a sort function.
* @category Parameter
* @since v1.0.0
*/
export type SortKey = string | number | boolean;
/**
* The GetNow type is used to represent a function that returns the current time
* in milliseconds.
*
* This is used internally within the mergeable-store module, but is used for
* the createMergeableStore function's second optional argument to allow
* applications to override the clock used to generate timestamps.
* @category Stamps
* @since v6.2.0
*/
export type GetNow = () => number;
/**
* The Hlc type is a string that represents a Hybrid Logical Clock (HLC) value.
*
* HLCs are used to provide a globally unique timestamp that can be used to
* order events across distributed systems. The Hlc type in TinyBase is a
* sortable 16 character string that encodes a timestamp, a counter, and the
* hash of a unique client identifier.
*
* - 42 bits (7 chars) for the time in milliseconds (~139 years).
* - 24 bits (4 chars) for the counter (~16 million).
* - 30 bits (5 chars) for the hash of unique client id (~1 billion).
* @category Stamps
* @since v6.2.0
*/
export type Hlc = string;
/**
* The Hash type is used within TinyBase (for example in 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 v6.2.0
*/
export type Hash = number;
/**
* The defaultSorter function is provided as a convenience to sort keys
* alphanumerically, and can be provided to the `sliceIdSorter` and
* `rowIdSorter` parameters of the setIndexDefinition method in the indexes
* module, for example.
* @param sortKey1 The first item of the pair to compare.
* @param sortKey2 The second item of the pair to compare.
* @returns A number indicating how to sort the pair.
* @example
* This example creates an Indexes object.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore();
* const indexes = createIndexes(store);
* console.log(indexes.getIndexIds());
* // -> []
* ```
* @example
* This example creates a Store, creates an Indexes object, and defines an
* Index based on the first letter of the pets' names. The Slice Ids (and Row
* Ids within them) are alphabetically sorted using the defaultSorter function.
*
* ```js
* import {createIndexes, createStore, defaultSorter} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition(
* 'byFirst', // indexId
* 'pets', // tableId
* (_, rowId) => rowId[0], // each Row's Slice Id
* (_, rowId) => rowId, // each Row's sort key
* defaultSorter, // sort Slice Ids
* defaultSorter, // sort Row Ids by sort key
* );
*
* console.log(indexes.getSliceIds('byFirst'));
* // -> ['c', 'f']
* console.log(indexes.getSliceRowIds('byFirst', 'f'));
* // -> ['felix', 'fido']
* ```
* @category Convenience
* @since v1.0.0
*/
export function defaultSorter(sortKey1: SortKey, sortKey2: SortKey): number;
/**
* The getUniqueId function returns a unique string of a given length.
*
* This is used internally by TinyBase for the synchronizer protocol and for
* unique MergeableStore identifiers. But it is useful enough for it to be
* publicly exposed for purposes such as identifying shared collaboration rooms,
* or creating other Ids that need to be unique.
*
* The string may contain numbers, lower or upper case letters, or the '-' or
* '_' characters. This makes them URL-safe, and means they can be identified
* with a regex like `/[-_0-9A-Za-z]+/`.
*
* This function prefers to use the `crypto` module to generate random numbers,
* but where that is not available (such as in React Native), a `Math.random`
* implementation is used. Whilst that may not be cryptographically sound, it
* should suffice for most TinyBase-related purposes.
* @param length The desired length of the unique Id, defaulting to 16.
* @returns A unique Id of the required length.
* @example
* This example creates two 8 character long Ids and compares them.
*
* ```js
* import {getUniqueId} from 'tinybase';
*
* const id1 = getUniqueId(8);
* const id2 = getUniqueId(8);
*
* console.log(id1.length);
* // -> 8
* console.log(id2.length);
* // -> 8
* console.log(id1 == id2);
* // -> false
* ```
* @category Convenience
* @since v5.0.0
*/
export function getUniqueId(length?: number): Id;
/**
* The getHlcFunctions function returns a set of utility functions for working
* with the TinyBase Hybrid Logical Clock (HLC).
*
* An HLC is a sortable 16 character string that encodes a timestamp, a counter,
* and the hash of a unique client identifier. You should provide that unique
* client identifier as the `uniqueId` parameter to the function. Otherwise it
* will be defaulted and the client suffix of the HLCs the `getNextHlc` function
* generates will be non-deterministic.
*
* You can also provide a `getNow` function that returns the current time in
* milliseconds, which is useful for testing purposes where fully deterministic
* HLCs are required, rather than the current time.
*
* The stateful functions returned by this function are as follows.
*
* - `getNextHlc`: a function that returns the next HLC for this client based on
* the time and any other HLC values from other clients that have been seen.
* - `seenHlc`: a function that takes an HLC and updates the internal state of
* the functions to ensure that the next HLC returned by `getNextHlc` is
* greater than the given seen HLC.
* - `encodeHlc`: a function that takes a timestamp, a counter (and optionally a
* different `clientId`) and encodes the them into an HLC string.
* - `decodeHlc`: a function that takes an HLC and returns an array containing
* the logical time, counter, and client Id parts.
* - `getLastLogicalTime`: a function that returns the last logical time either
* generated or seen by this client.
* - `getLastCounter`: a function that returns the last counter either generated
* or seen by this client.
* - `getClientId`: a function that returns the client Id for this client;
* either uniquely generated or derived from the `uniqueId` parameter.
* @param uniqueId An optional unique Id for the client.
* @param getNow An optional function that generates millisecond timestamps,
* defaulting to `Date.now`.
* @returns An array of seven stateful functions as described above.
* @category Stamps
* @since v6.2.0
* @example
* This example gets the HLC functions (for a given client Id and a fixed time;
* both for illustrative purposes), and then uses them:
*
* ```js
* import {getHlcFunctions} from 'tinybase';
*
* const [
* getNextHlc,
* seenHlc,
* encodeHlc,
* decodeHlc,
* getLastLogicalTime,
* getLastCounter,
* getClientId,
* ] = getHlcFunctions('client1', () => 73267200000); // This client is in 1972.
*
* // Generate an HLC based on the fixed time and the client Id.
* console.log(getNextHlc());
* // -> '03E3B------mmxrx'
*
* // Generate the next HLC. The time has not changed, so the counter does.
* console.log(getNextHlc());
* // -> '03E3B-----0mmxrx'
*
* // Another client thinks it is 1973.
* seenHlc('0WakTk-----jmx_3');
* // Generate the next HLC.
*
* // What is the state for the current client?
* console.log(getLastLogicalTime());
* // -> 104803200000
* console.log(getLastCounter());
* // -> 0
* console.log(getClientId());
* // -> 'mmxrx'
*
* // Encode an arbitrary HLC.
* console.log(encodeHlc(73267203600, 7, 'client3'));
* // -> '03E3BsF---6kmxfM'
*
* // Decode it again.
* console.log(decodeHlc('03E3BsF---6kmxfM'));
* // -> [73267203600, 7, 'kmxfM']
* ```
*/
export function getHlcFunctions(
uniqueId?: Id,
getNow?: GetNow,
): [
getNextHlc: () => Hlc,
seenHlc: (remoteHlc: Hlc) => void,
encodeHlc: (logicalTime: number, counter: number, clientId?: Id) => Hlc,
decodeHlc: (hlc: Hlc) => [logicalTime: number, counter: number, clientId: Id],
getLastLogicalTime: () => number,
getLastCounter: () => number,
getClientId: () => Id,
];
/**
* The getHash function returns a deterministic hash of a string, which can be
* used to quickly compare the content of two entities.
*
* This is used internally within TinyBase (for example in the mergeable-store
* module) to quickly compare the content of two objects, but is also useful for
* applications that need to represent strings in a deterministic (though not
* cryptographically safe) way.
*
* The hash uses the Fowler–Noll–Vo hash approach, producing a JavaScript
* number. It is not guaranteed to be unique by any means, but small changes in
* input generally produce large changes in output, so it should be sufficient
* for most purposes.
* @param string The string to hash.
* @returns A hash of the string.
* @example
* This example gets the hash of two similar strings.
* ```js
* import {getHash} from 'tinybase';
* console.log(getHash('Hello, world!'));
* // -> 3985698964
* console.log(getHash('Hello, world?'));
* // -> 3549480870
* ```
* @category Hash
* @since v6.2.0
*/
export function getHash(string: string): Hash;
/**
* The addOrRemoveHash function combines two hashes together, which, because it
* is a simple alias for bitwise XOR, serves both as addition and removal of one
* hash from the other.
*
* This is used internally within TinyBase to collate hashes of objects, such as
* producing a hash for a Table which is composed of the hashes of its Rows.
* @param hash1 A first hash.
* @param hash2 A second hash to add or remove from the first.
* @returns The resulting hash of the two hashes added to or removed from each
* other.
* @example
* This example adds two hashes together.
* ```js
* import {addOrRemoveHash} from 'tinybase';
*
* const hash1 = 123456789;
* const hash2 = 987654321;
* console.log(addOrRemoveHash(hash1, hash2));
* // -> 1032168868
*
* console.log(addOrRemoveHash(1032168868, hash1));
* // -> 987654321
* console.log(addOrRemoveHash(1032168868, hash2));
* // -> 123456789
* ```
* @category Hash
* @since v6.2.0
*/
export function addOrRemoveHash(hash1: Hash, hash2: Hash): Hash;
/**
* The getTablesHash function returns a hash for the tabular part of a Store,
* based on each Table Id and hash.
* @param tableHashes An object containing each Table Id and its hash.
* @returns A hash of the Tables.
* @example
* This example gets the hash of the tabular part of a Store.
* ```js
* import {getTablesHash} from 'tinybase';
*
* const tableHashes = {
* pets: 4262151841, // hash of its contents
* };
*
* console.log(getTablesHash(tableHashes));
* // -> 278115833
* ```
* @category Hash
* @since v6.2.0
*/
export function getTablesHash(tableHashes: {[tableId: Id]: Hash}): Hash;
/**
* The getTableInTablesHash function returns a hash for a single Table in a
* Store, based on its Id and hash (in turn produced from its Rows).
* @param tableId The Id of the Table.
* @param tableHash The hash of the Table.
* @returns A hash of the Table based on its content and Id.
* @example
* This example gets the hash of a Table and its Id.
* ```js
* import {getTableInTablesHash} from 'tinybase';
*
* const tableId = 'pets';
* const tableHash = 4262151841; // hash of its contents
*
* console.log(getTableInTablesHash(tableId, tableHash));
* // -> 278115833
* ```
* @category Hash
* @since v6.2.0
*/
export function getTableInTablesHash(tableId: Id, tableHash: Hash): Hash;
/**
* The getTableHash function returns a hash for a single Table in a Store, based
* on each Row Id and hash.
* @param rowHashes An object containing each Row Id and its hash.
* @returns A hash of the Table.
* @example
* This example gets the hash of a Table.
* ```js
* import {getTableHash} from 'tinybase';
*
* const rowHashes = {
* fido: 1810444343, // hash of its contents
* };
*
* console.log(getTableHash(rowHashes));
* // -> 4262151841
* ```
* @category Hash
* @since v6.2.0
*/
export function getTableHash(rowHashes: {[rowId: Id]: Hash}): Hash;
/**
* The getRowInTableHash function returns a hash for a single Row in a Table,
* based on its Id and hash (in turn produced from its Cells).
* @param rowId The Id of the Row.
* @param rowHash The hash of the Row.
* @returns A hash of the Row based on its content and Id.
* @example
* This example gets the hash of a Row and its Id.
* ```js
* import {getRowInTableHash} from 'tinybase';
*
* const rowId = 'fido';
* const rowHash = 1810444343; // hash of its contents
*
* console.log(getRowInTableHash(rowId, rowHash));
* // -> 4262151841
* ```
* @category Hash
* @since v6.2.0
*/
export function getRowInTableHash(rowId: Id, rowHash: Hash): Hash;
/**
* The getRowHash function returns a hash for a single Row in a Table, based on
* each Cell Id and hash.
* @param cellHashes An object containing each Cell Id and its hash.
* @returns A hash of the Row.
* @example
* This example gets the hash of a Row.
* ```js
* import {getRowHash} from 'tinybase';
*
* const cellHashes = {
* species: 3002200796, // hash of 'dog' and '03E3B------mmxrx'
* };
*
* console.log(getRowHash(cellHashes));
* // -> 1810444343
* ```
* @category Hash
* @since v6.2.0
*/
export function getRowHash(cellHashes: {[cellId: Id]: Hash}): Hash;
/**
* The getCellInRowHash function returns a hash for a single Cell in a Row,
* based on its Id and hash (in turn produced from its value).
* @param cellId The Id of the Cell.
* @param cellHash The hash of the Cell.
* @returns A hash of the Cell based on its content and Id.
* @example
* This example gets the hash of a Cell and its Id.
* ```js
* import {getCellInRowHash} from 'tinybase';
*
* const cellId = 'species';
* const cellHash = '3002200796'; // hash of 'dog' and '03E3B------mmxrx'
*
* console.log(getCellInRowHash(cellId, cellHash));
* // -> 3777304796
* ```
* @category Hash
* @since v6.2.0
*/
export function getCellInRowHash(cellId: Id, cellHash: Hash): Hash;
/**
* The getCellHash function returns a hash for a single Cell, based on its value
* and the HLC of the Cell.
* @param cell The Cell's value (or `undefined`).
* @param cellHlc The HLC of the Cell.
* @returns A hash of the Cell.
* @example
* This example gets the hash of a Cell and its HLC.
* ```js
* import {getCellHash} from 'tinybase';
*
* const cell = 'dog';
* const cellHlc = '03E3B------mmxrx';
*
* console.log(getCellHash(cell, cellHlc));
* // -> 3002200796
* ```
* @category Hash
* @since v6.2.0
*/
export function getCellHash(cell: CellOrUndefined, cellHlc: Hlc): Hash;
/**
* The getValuesHash function returns a hash for a Values object, based on each
* Value Id and hash.
* @param valueHashes An object containing each Value Id and its hash.
* @returns A hash of the Values.
* @example
* This example gets the hash of a Values object.
* ```js
* import {getValuesHash} from 'tinybase';
*
* const valueHashes = {
* meaningOfLife: 312420374, // hash of 42 and '03E3B------mmxrx'
* };
*
* console.log(getValuesHash(valueHashes));
* // -> 4229195646
* ```
* @category Hash
* @since v6.2.0
*/
export function getValuesHash(valueHashes: {[valueId: Id]: Hash}): Hash;
/**
* The getValueInValuesHash function returns a hash for a single Value in a
* Values object, based on its Id and hash (in turn produced from its value).
* @param valueId The Id of the Value.
* @param valueHash The hash of the Value.
* @returns A hash of the Value based on its content and Id.
* @example
* This example gets the hash of a Value and its Id.
* ```js
* import {getValueInValuesHash} from 'tinybase';
*
* const valueId = 'meaningOfLife';
* const valueHash = 312420374; // hash of 42 and '03E3B------mmxrx'
*
* console.log(getValueInValuesHash(valueId, valueHash));
* // -> 330198963
* ```
* @category Hash
* @since v6.2.0
*/
export function getValueInValuesHash(valueId: Id, valueHash: Hash): Hash;
/**
* The getValueHash function returns a hash for a single Value, based on its
* value and the HLC of the Value.
* @param value The Value (or `undefined`).
* @param valueHlc The HLC of the Value.
* @returns A hash of the Value.
* @example
* This example gets the hash of a Value and its HLC.
* ```js
* import {getValueHash} from 'tinybase';
*
* const value = 42;
* const valueHlc = '03E3B------mmxrx';
*
* console.log(getValueHash(value, valueHlc));
* // -> 312420374
* ```
* @category Hash
* @since v6.2.0
*/
export function getValueHash(value: ValueOrUndefined, valueHlc: Hlc): Hash;