UNPKG

tinybase

Version:

A reactive data store and sync engine.

1,152 lines (1,127 loc) 39.7 kB
/** * The checkpoints module of the TinyBase project provides the ability to * create and track checkpoints made to the data in Store objects. * * The main entry point to this module is the createCheckpoints function, which * returns a new Checkpoints object. From there, you can create new checkpoints, * go forwards or backwards to others, and register listeners for when the list * of checkpoints change. * @packageDocumentation * @module checkpoints * @since v1.0.0 */ import type {Id, IdOrNull, Ids} from '../../common/with-schemas/index.d.ts'; import type {OptionalSchemas, Store} from '../../store/with-schemas/index.d.ts'; /** * The CheckpointIds type is a representation of the list of checkpoint Ids * stored in a Checkpoints object. * * There are three parts to a CheckpointsIds array: * * - The 'backward' checkpoint Ids that can be rolled backward to (in other * words, the checkpoints in the undo stack for this Store). They are in * chronological order with the oldest checkpoint at the start of the array. * - The current checkpoint Id of the Store's state, or `undefined` if the * current state has not been checkpointed. * - The 'forward' checkpoint Ids that can be rolled forward to (in other words, * the checkpoints in the redo stack for this Store). They are in * chronological order with the newest checkpoint at the end of the array. * @category Identity * @since v1.0.0 */ export type CheckpointIds = [Ids, Id | undefined, Ids]; /** * The CheckpointCallback type describes a function that takes a Checkpoint's * Id. * * A CheckpointCallback is provided when using the forEachCheckpoint method, * so that you can do something based on every Checkpoint in the Checkpoints * object. See that method for specific examples. * @param checkpointId The Id of the Checkpoint that the callback can operate * on. * @category Callback * @since v1.0.0 */ export type CheckpointCallback = (checkpointId: Id, label?: string) => void; /** * The CheckpointIdsListener type describes a function that is used to listen to * changes to the checkpoint Ids in a Checkpoints object. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * (checkpoints: Checkpoints) => void; * ``` * * A CheckpointIdsListener is provided when using the addCheckpointIdsListener * method. See that method for specific examples. * * When called, a CheckpointIdsListener is given a reference to the Checkpoints * object. * @param checkpoints A reference to the Checkpoints object that changed. * @category Listener * @since v1.0.0 */ export type CheckpointIdsListener<Schemas extends OptionalSchemas> = ( checkpoints: Checkpoints<Schemas>, ) => void; /** * The CheckpointListener type describes a function that is used to listen to * changes to a checkpoint's label in a Checkpoints object. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * ( * checkpoints: Checkpoints, * checkpointId: Id, * ) => void; * ``` * * A CheckpointListener is provided when using the addCheckpointListener method. * See that method for specific examples. * * When called, a CheckpointListener is given a reference to the Checkpoints * object, and the Id of the checkpoint whose label changed. * @param checkpoints A reference to the Checkpoints object that changed. * @param checkpointId The Id of the checkpoint that changed. * @category Listener * @since v1.0.0 */ export type CheckpointListener<Schemas extends OptionalSchemas> = ( checkpoints: Checkpoints<Schemas>, checkpointId: Id, ) => void; /** * The CheckpointsListenerStats type describes the number of listeners * registered with the Checkpoints object, and can be used for debugging * purposes. * * A CheckpointsListenerStats object is returned from the getListenerStats * method. * @category Development * @since v1.0.0 */ export type CheckpointsListenerStats = { /** * The number of CheckpointIdsListener functions registered with the * Checkpoints object. * @category Stat * @since v1.0.0 */ checkpointIds: number; /** * The number of CheckpointListener functions registered with the Checkpoints * object. * @category Stat * @since v1.0.0 */ checkpoint: number; }; /** * A Checkpoints object lets you set checkpoints on a Store, and move forward * and backward through them to create undo and redo functionality. * * Create a Checkpoints object easily with the createCheckpoints function. From * there, you can set checkpoints (with the addCheckpoint method), query the * checkpoints available (with the getCheckpointIds method), move forward and * backward through them (with the goBackward method, goForward method, and goTo * method), and add listeners for when the list checkpoints changes (with the * addCheckpointIdsListener method). * * Checkpoints work for both changes to tabular data and to keyed value data. * * Every checkpoint can be given a label which can be used to describe the * actions that changed the Store before this checkpoint. This can be useful for * interfaces that let users 'Undo [last action]'. * @example * This example shows a simple lifecycle of a Checkpoints object: from creation, * to adding a checkpoint, getting the list of available checkpoints, and then * registering and removing a listener for them. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore() * .setTables({pets: {fido: {sold: false}}}) * .setValue('open', true); * * const checkpoints = createCheckpoints(store); * checkpoints.setSize(200); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * checkpoints.goBackward(); * console.log(store.getCell('pets', 'fido', 'sold')); * // -> false * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['1']] * * store.setValue('open', false); * checkpoints.addCheckpoint('closed'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '2', []] * * checkpoints.goBackward(); * console.log(store.getValue('open')); * // -> true * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['2']] * * const listenerId = checkpoints.addCheckpointIdsListener(() => { * console.log(checkpoints.getCheckpointIds()); * }); * store.setCell('pets', 'fido', 'species', 'dog'); * // -> [['0'], undefined, []] * checkpoints.addCheckpoint(); * // -> [['0'], '3', []] * // Previous redo of checkpoints '1' and '2' are now not possible. * * checkpoints.delListener(listenerId); * ``` * @see Using Checkpoints guide * @see Todo App demos * @see Drawing demo * @category Checkpoints * @since v1.0.0 */ export interface Checkpoints<in out Schemas extends OptionalSchemas> { /** * The setSize method lets you specify how many checkpoints the Checkpoints * object will store. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * setSize(size: number): Checkpoints; * ``` * * If you set more checkpoints than this size, the oldest checkpoints will be * pruned to make room for more recent ones. * * The default size for a newly-created Checkpoints object is 100. * @param size The number of checkpoints that this Checkpoints object should * hold. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, adds a Checkpoints object, reduces the size * of the Checkpoints object dramatically and then creates more than that * number of checkpoints to demonstrate the oldest being pruned. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {views: 0}}}); * * const checkpoints = createCheckpoints(store); * checkpoints.setSize(2); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'views', 1); * checkpoints.addCheckpoint(); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * store.setCell('pets', 'fido', 'views', 2); * checkpoints.addCheckpoint(); * console.log(checkpoints.getCheckpointIds()); * // -> [['0', '1'], '2', []] * * store.setCell('pets', 'fido', 'views', 3); * checkpoints.addCheckpoint(); * console.log(checkpoints.getCheckpointIds()); * // -> [['1', '2'], '3', []] * ``` * @category Configuration * @since v1.0.0 */ setSize(size: number): Checkpoints<Schemas>; /** * The addCheckpoint method records a checkpoint of the Store into the * Checkpoints object that can be reverted to in the future. * * If no changes have been made to the Store since the last time a checkpoint * was made, this method will have no effect. * * The optional `label` parameter can be used to describe the actions that * changed the Store before this checkpoint. This can be useful for interfaces * that let users 'Undo [last action]'. * @param label An optional label to describe the actions leading up to this * checkpoint. * @returns The Id of the newly-created checkpoint. * @example * This example creates a Store, adds a Checkpoints object, and adds two * checkpoints, one with a label. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'species', 'dog'); * const checkpointId1 = checkpoints.addCheckpoint(); * console.log(checkpointId1); * // -> '1' * * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0', '1'], '2', []] * * console.log(checkpoints.getCheckpoint('2')); * // -> 'sale' * ``` * @category Setter * @since v1.0.0 */ addCheckpoint(label?: string): Id; /** * The setCheckpoint method updates the label for a checkpoint in the * Checkpoints object after it has been created. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * setCheckpoint(checkpointId: Id, label: string): Checkpoints; * ``` * * The `label` parameter can be used to describe the actions that changed the * Store before the given checkpoint. This can be useful for interfaces that * let users 'Undo [last action]'. * * Generally you will provide the `label` parameter when the addCheckpoint * method is called. Use this setCheckpoint method only when you need to * change the label at a later point. * * You cannot add a label to a checkpoint that does not yet exist. * @param checkpointId The Id of the checkpoint to set the label for. * @param label A label to describe the actions leading up to this checkpoint * or left undefined if you want to clear the current label. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, adds a Checkpoints object, and sets two * checkpoints, one with a label, which are both then re-labelled. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'species', 'dog'); * checkpoints.addCheckpoint(); * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * * console.log(checkpoints.getCheckpoint('1')); * // -> '' * console.log(checkpoints.getCheckpoint('2')); * // -> 'sale' * * checkpoints.setCheckpoint('1', 'identified'); * checkpoints.setCheckpoint('2', ''); * * console.log(checkpoints.getCheckpoint('1')); * // -> 'identified' * console.log(checkpoints.getCheckpoint('2')); * // -> '' * * checkpoints.setCheckpoint('3', 'unknown'); * console.log(checkpoints.getCheckpoint('3')); * // -> undefined * ``` * @category Setter * @since v1.0.0 */ setCheckpoint(checkpointId: Id, label: string): Checkpoints<Schemas>; /** * The getStore method returns a reference to the underlying Store that is * backing this Checkpoints object. * @returns A reference to the Store. * @example * This example creates a Checkpoints object against a newly-created Store * and then gets its reference in order to update its data and set a * checkpoint. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * getStore(): Store; * ``` * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const checkpoints = createCheckpoints(createStore()); * checkpoints.getStore().setCell('pets', 'fido', 'species', 'dog'); * checkpoints.addCheckpoint(); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * ``` * @category Getter * @since v1.0.0 */ getStore(): Store<Schemas>; /** * The getCheckpointIds method returns an array of the checkpoint Ids being * managed by this Checkpoints object. * * The returned CheckpointIds array contains 'backward' checkpoint Ids, the * current checkpoint Id (if present), and the 'forward' checkpointIds. * Together, these are sufficient to understand the state of the Checkpoints * object and what movement is possible backward or forward through the * checkpoint stack. * @returns A CheckpointIds array, containing the checkpoint Ids managed by * this Checkpoints object. * @example * This example creates a Store, adds a Checkpoints object, and then gets the * Ids of the checkpoints as it sets them and moves around the stack. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * checkpoints.goBackward(); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['1']] * * checkpoints.goForward(); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * ``` * @category Getter * @since v1.0.0 */ getCheckpointIds(): CheckpointIds; /** * The forEachCheckpoint method takes a function that it will then call for * each Checkpoint in a specified Checkpoints object. * * This method is useful for iterating over the structure of the Checkpoints * object in a functional style. The `checkpointCallback` parameter is a * CheckpointCallback function that will be called with the Id of each * Checkpoint. * @param checkpointCallback The function that should be called for every * Checkpoint. * @example * This example iterates over each Checkpoint in a Checkpoints object. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * const checkpoints = createCheckpoints(store); * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * * checkpoints.forEachCheckpoint((checkpointId, label) => { * console.log(`${checkpointId}:${label}`); * }); * // -> '0:' * // -> '1:sale' * ``` * @category Iterator * @since v1.0.0 */ forEachCheckpoint(checkpointCallback: CheckpointCallback): void; /** * The hasCheckpoint method returns a boolean indicating whether a given * Checkpoint exists in the Checkpoints object. * @param checkpointId The Id of a possible Checkpoint in the Checkpoints * object. * @returns Whether a Checkpoint with that Id exists. * @example * This example shows two simple Checkpoint existence checks. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * const checkpoints = createCheckpoints(store); * console.log(checkpoints.hasCheckpoint('0')); * // -> true * console.log(checkpoints.hasCheckpoint('1')); * // -> false * ``` * @category Getter * @since v1.0.0 */ hasCheckpoint(checkpointId: Id): boolean; /** * The getCheckpoint method fetches the label for a checkpoint, if it had been * provided at the time of the addCheckpoint method or set subsequently with * the setCheckpoint method. * * If the checkpoint has had no label provided, this method will return an * empty string. * @param checkpointId The Id of the checkpoint to get the label for. * @returns A string label for the requested checkpoint, an empty string if it * was never set, or `undefined` if the checkpoint does not exist. * @example * This example creates a Store, adds a Checkpoints object, and sets a * checkpoint with a label, before retrieving it again. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * store.setCell('pets', 'fido', 'sold', true); * console.log(checkpoints.addCheckpoint('sale')); * // -> '1' * * console.log(checkpoints.getCheckpoint('1')); * // -> 'sale' * ``` * @example * This example creates a Store, adds a Checkpoints object, and sets a * checkpoint without a label, setting it subsequently. A non-existent * checkpoint return an `undefined` label. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint(); * console.log(checkpoints.getCheckpoint('1')); * // -> '' * * checkpoints.setCheckpoint('1', 'sold'); * console.log(checkpoints.getCheckpoint('1')); * // -> 'sold' * * console.log(checkpoints.getCheckpoint('2')); * // -> undefined * ``` * @category Getter * @since v1.0.0 */ getCheckpoint(checkpointId: Id): string | undefined; /** * The addCheckpointIdsListener method registers a listener function with the * Checkpoints object that will be called whenever its set of checkpoints * changes. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * addCheckpointIdsListener(listener: CheckpointIdsListener): Id; * ``` * * The provided listener is a CheckpointIdsListener function, and will be * called with a reference to the Checkpoints object. * @param listener The function that will be called whenever the checkpoints * change. * @returns A unique Id for the listener that can later be used to remove it. * @example * This example creates a Store, a Checkpoints object, and then registers a * listener that responds to any changes to the checkpoints. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * const listenerId = checkpoints.addCheckpointIdsListener(() => { * console.log('Checkpoint Ids changed'); * console.log(checkpoints.getCheckpointIds()); * }); * * store.setCell('pets', 'fido', 'species', 'dog'); * // -> 'Checkpoint Ids changed' * // -> [['0'], undefined, []] * * checkpoints.addCheckpoint(); * // -> 'Checkpoint Ids changed' * // -> [['0'], '1', []] * * checkpoints.goBackward(); * // -> 'Checkpoint Ids changed' * // -> [[], '0', ['1']] * * checkpoints.goForward(); * // -> 'Checkpoint Ids changed' * // -> [['0'], '1', []] * * checkpoints.delListener(listenerId); * ``` * @category Listener * @since v1.0.0 */ addCheckpointIdsListener(listener: CheckpointIdsListener<Schemas>): Id; /** * The addCheckpointListener method registers a listener function with the * Checkpoints object that will be called whenever the label of a checkpoint * changes. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * addCheckpointListener( * checkpointId: IdOrNull, * listener: CheckpointListener, * ): Id; * ``` * * You can either listen to a single checkpoint label (by specifying the * checkpoint Id as the method's first parameter), or changes to any * checkpoint label (by providing a `null` wildcard). * * The provided listener is a CheckpointListener function, and will be called * with a reference to the Checkpoints object, and the Id of the checkpoint * whose label changed. * @param checkpointId The Id of the checkpoint to listen to, or `null` as a * wildcard. * @param listener The function that will be called whenever the checkpoint * label changes. * @returns A unique Id for the listener that can later be used to remove it. * @example * This example creates a Store, a Checkpoints object, and then registers a * listener that responds to any changes to a specific checkpoint label, * including when the checkpoint no longer exists. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {species: 'dog'}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * const listenerId = checkpoints.addCheckpointListener('1', () => { * console.log('Checkpoint 1 label changed'); * console.log(checkpoints.getCheckpoint('1')); * }); * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * // -> 'Checkpoint 1 label changed' * // -> 'sale' * * checkpoints.setCheckpoint('1', 'sold'); * // -> 'Checkpoint 1 label changed' * // -> 'sold' * * checkpoints.setCheckpoint('1', 'sold'); * // The listener is not called when the label does not change. * * checkpoints.goTo('0'); * store.setCell('pets', 'fido', 'sold', false); * // -> 'Checkpoint 1 label changed' * // -> undefined * // The checkpoint no longer exists. * * checkpoints.delListener(listenerId); * ``` * @category Listener * @since v1.0.0 */ addCheckpointListener( checkpointId: IdOrNull, listener: CheckpointListener<Schemas>, ): Id; /** * The delListener method removes a listener that was previously added to the * Checkpoints object. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * delListener(listenerId: Id): Checkpoints; * ``` * * Use the Id returned by the addCheckpointIdsListener method. Note that the * Checkpoints 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 Checkpoints object. * @example * This example creates a Store, a Checkpoints object, registers a listener, * and then removes it. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * const listenerId = checkpoints.addCheckpointIdsListener(() => { * console.log('checkpoints changed'); * }); * * store.setCell('pets', 'fido', 'species', 'dog'); * // -> 'checkpoints changed' * * checkpoints.addCheckpoint(); * // -> 'checkpoints changed' * * checkpoints.delListener(listenerId); * * store.setCell('pets', 'fido', 'sold', 'true'); * // -> undefined * // The listener is not called. * ``` * @category Listener * @since v1.0.0 */ delListener(listenerId: Id): Checkpoints<Schemas>; /** * The goBackward method moves the state of the underlying Store back to the * previous checkpoint, effectively performing an 'undo' on the Store data. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * goBackward(): Checkpoints; * ``` * * If there is no previous checkpoint to return to, this method has no effect. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, a Checkpoints object, makes a change and then * goes backward to the state of the Store before the change. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * checkpoints.goBackward(); * console.log(store.getCell('pets', 'fido', 'sold')); * // -> false * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['1']] * ``` * @category Movement * @since v1.0.0 */ goBackward(): Checkpoints<Schemas>; /** * The goForward method moves the state of the underlying Store forwards to a * future checkpoint, effectively performing an 'redo' on the Store data. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * goForward(): Checkpoints; * ``` * * If there is no future checkpoint to return to, this method has no effect. * * Note that if you have previously used the goBackward method to undo * changes, the forwards 'redo' stack will only exist while you do not make * changes to the Store. In general the goForward method is expected to be * used to redo changes that were just undone. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, a Checkpoints object, makes a change and then * goes backward to the state of the Store before the change. It then goes * forward again to restore the state with the changes. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * checkpoints.goBackward(); * console.log(store.getCell('pets', 'fido', 'sold')); * // -> false * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['1']] * * checkpoints.goForward(); * console.log(store.getCell('pets', 'fido', 'sold')); * // -> true * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * ``` * @example * This example creates a Store, a Checkpoints object, makes a change and then * goes backward to the state of the Store before the change. It makes a new * change, the redo stack disappears, and then the attempt to forward again * has no effect. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * checkpoints.goBackward(); * console.log(store.getCell('pets', 'fido', 'sold')); * // -> false * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['1']] * * store.setCell('pets', 'fido', 'color', 'brown'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], undefined, []] * * checkpoints.goForward(); * console.log(store.getCell('pets', 'fido', 'sold')); * // -> false * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], undefined, []] * // The original change cannot be redone. * ``` * @category Movement * @since v1.0.0 */ goForward(): Checkpoints<Schemas>; /** * The goTo method moves the state of the underlying Store backwards or * forwards to a specified checkpoint. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * goTo(checkpointId: Id): Checkpoints; * ``` * * If there is no checkpoint with the Id specified, this method has no effect. * @param checkpointId The Id of the checkpoint to move to. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, a Checkpoints object, makes two changes and * then goes directly to the state of the Store before the two changes. It * then goes forward again one change, also using the goTo method. Finally it * tries to go to a checkpoint that does not exist. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * store.setCell('pets', 'fido', 'color', 'brown'); * checkpoints.addCheckpoint('identification'); * store.setCell('pets', 'fido', 'sold', true); * checkpoints.addCheckpoint('sale'); * console.log(checkpoints.getCheckpointIds()); * // -> [['0', '1'], '2', []] * * checkpoints.goTo('0'); * console.log(store.getTables()); * // -> {pets: {fido: {sold: false}}} * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', ['1', '2']] * * checkpoints.goTo('1'); * console.log(store.getTables()); * // -> {pets: {fido: {sold: false, color: 'brown'}}} * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', ['2']] * * checkpoints.goTo('3'); * console.log(store.getTables()); * // -> {pets: {fido: {sold: false, color: 'brown'}}} * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', ['2']] * ``` * @category Movement * @since v1.0.0 */ goTo(checkpointId: Id): Checkpoints<Schemas>; /** * The clear method resets this Checkpoints object to its initial state, * removing all the checkpoints it has been managing. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * clear(): Checkpoints; * ``` * * Obviously this method should be used with caution as it destroys the * ability to undo or redo recent changes to the Store (though of course the * Store itself is not reset by this method). * * This method can be useful when a Store is being loaded via a Persister * asynchronously after the Checkpoints object has been attached, and you * don't want users to be able to undo the initial load of the data. In this * case you could call the clear method immediately after the initial load so * that that is the baseline from which all subsequent changes are tracked. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, a Checkpoints object, adds a listener, makes * a change and then clears the checkpoints. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * const listenerId = checkpoints.addCheckpointIdsListener(() => { * console.log('checkpoints changed'); * }); * * store.setCell('pets', 'fido', 'color', 'brown'); * // -> 'checkpoints changed' * checkpoints.addCheckpoint(); * // -> 'checkpoints changed' * store.setCell('pets', 'fido', 'sold', true); * // -> 'checkpoints changed' * checkpoints.addCheckpoint(); * // -> 'checkpoints changed' * * console.log(store.getTables()); * // -> {pets: {fido: {sold: true, color: 'brown'}}} * console.log(checkpoints.getCheckpointIds()); * // -> [['0', '1'], '2', []] * * checkpoints.clear(); * // -> 'checkpoints changed' * * console.log(store.getTables()); * // -> {pets: {fido: {sold: true, color: 'brown'}}} * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * checkpoints.delListener(listenerId); * ``` * @category Lifecycle * @since v1.0.0 */ clear(): Checkpoints<Schemas>; /** * The clearForward method resets just the 'redo' checkpoints it has been * managing. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * clearForward(): Checkpoints; * ``` * * Obviously this method should be used with caution as it destroys the * ability to redo recent changes to the Store (though of course the Store * itself is not reset by this method). * * This method can be useful when you want to prohibit a user from redoing * changes they have undone. The 'backward' redo stack, and current checkpoint * are not affected. * @returns A reference to the Checkpoints object. * @example * This example creates a Store, a Checkpoints object, adds a listener, makes * a change and then clears the forward checkpoints. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * * const listenerId = checkpoints.addCheckpointIdsListener(() => { * console.log('checkpoints changed'); * }); * * store.setCell('pets', 'fido', 'color', 'brown'); * // -> 'checkpoints changed' * checkpoints.addCheckpoint(); * // -> 'checkpoints changed' * store.setCell('pets', 'fido', 'sold', true); * // -> 'checkpoints changed' * checkpoints.addCheckpoint(); * // -> 'checkpoints changed' * checkpoints.goBackward(); * // -> 'checkpoints changed' * * console.log(store.getTables()); * // -> {pets: {fido: {color: 'brown', sold: false}}} * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', ['2']] * * checkpoints.clearForward(); * // -> 'checkpoints changed' * * console.log(checkpoints.getCheckpointIds()); * // -> [['0'], '1', []] * * checkpoints.delListener(listenerId); * ``` * @category Lifecycle * @since v4.5.3 */ clearForward(): Checkpoints<Schemas>; /** * The destroy method should be called when this Checkpoints 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 Checkpoints object (that registers a * CellListener with the underlying Store), and then destroys it again, * removing the listener. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore().setTables({pets: {fido: {sold: false}}}); * * const checkpoints = createCheckpoints(store); * console.log(store.getListenerStats().cell); * // -> 1 * * checkpoints.destroy(); * * console.log(store.getListenerStats().cell); * // -> 0 * ``` * @category Lifecycle * @since v1.0.0 */ destroy(): void; /** * The getListenerStats method provides a set of statistics about the * listeners registered with the Checkpoints object, and is used for debugging * purposes. * * The CheckpointsListenerStats 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 CheckpointsListenerStats object containing Checkpoints listener * statistics. * @example * This example gets the listener statistics of a Checkpoints object. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore(); * const checkpoints = createCheckpoints(store); * checkpoints.addCheckpointIdsListener(() => { * console.log('Checkpoint Ids changed'); * }); * checkpoints.addCheckpointListener(null, () => { * console.log('Checkpoint label changed'); * }); * * console.log(checkpoints.getListenerStats()); * // -> {checkpointIds: 1, checkpoint: 1} * ``` * @category Development * @since v1.0.0 */ getListenerStats(): CheckpointsListenerStats; } /** * The createCheckpoints function creates a Checkpoints object, and is the main * entry point into the checkpoints module. * * This has schema-based typing. The following is a simplified representation: * * ```ts override * createCheckpoints(store: Store): Checkpoints; * ``` * * A given Store can only have one Checkpoints object associated with it. If you * call this function twice on the same Store, your second call will return a * reference to the Checkpoints object created by the first. * @param store The Store for which to set Checkpoints. * @returns A reference to the new Checkpoints object. * @example * This example creates a Checkpoints object. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore(); * const checkpoints = createCheckpoints(store); * console.log(checkpoints.getCheckpointIds()); * // -> [[], '0', []] * ``` * @example * This example creates a Checkpoints object, and calls the method a second * time for the same Store to return the same object. * * ```js * import {createCheckpoints, createStore} from 'tinybase'; * * const store = createStore(); * const checkpoints1 = createCheckpoints(store); * const checkpoints2 = createCheckpoints(store); * console.log(checkpoints1 === checkpoints2); * // -> true * ``` * @category Creation * @since v1.0.0 */ export function createCheckpoints<Schemas extends OptionalSchemas>( store: Store<Schemas>, ): Checkpoints<Schemas>;