mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[PM-7419] add buffered state (#8706)
Introduces a state manager that buffers data until an observed dependency signals it should overwrite another state manager with the buffered data. It can be used to implement migrations of encrypted data, edit-apply loops (such as used for save operations), and to map between encryption keys/formats.
This commit is contained in:
100
libs/common/src/tools/generator/state/buffered-key-definition.ts
Normal file
100
libs/common/src/tools/generator/state/buffered-key-definition.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { UserKeyDefinition, UserKeyDefinitionOptions } from "../../../platform/state";
|
||||
// eslint-disable-next-line -- `StateDefinition` used as an argument
|
||||
import { StateDefinition } from "../../../platform/state/state-definition";
|
||||
|
||||
/** A set of options for customizing the behavior of a {@link BufferedKeyDefinition}
|
||||
*/
|
||||
export type BufferedKeyDefinitionOptions<Input, Output, Dependency> =
|
||||
UserKeyDefinitionOptions<Input> & {
|
||||
/** Checks whether the input type can be converted to the output type.
|
||||
* @param input the data that is rolling over.
|
||||
* @returns `true` if the definition is valid, otherwise `false`. If this
|
||||
* function is not specified, any truthy input is valid.
|
||||
*
|
||||
* @remarks this is intended for cases where you're working with validated or
|
||||
* signed data. It should be used to prevent data from being "laundered" through
|
||||
* synchronized state.
|
||||
*/
|
||||
isValid?: (input: Input, dependency: Dependency) => Promise<boolean>;
|
||||
|
||||
/** Transforms the input data format to its output format.
|
||||
* @param input the data that is rolling over.
|
||||
* @returns the converted value. If this function is not specified, the value
|
||||
* is asserted as the output type.
|
||||
*
|
||||
* @remarks This is intended for converting between, say, a replication format
|
||||
* and a disk format or rotating encryption keys.
|
||||
*/
|
||||
map?: (input: Input, dependency: Dependency) => Promise<Output>;
|
||||
|
||||
/** Checks whether an overwrite should occur
|
||||
* @param dependency the latest value from the dependency observable provided
|
||||
* to the buffered state.
|
||||
* @returns `true` if a overwrite should occur, otherwise `false`. If this
|
||||
* function is not specified, overwrites occur when the dependency is truthy.
|
||||
*
|
||||
* @remarks This is intended for waiting to overwrite until a dependency becomes
|
||||
* available (e.g. an encryption key or a user confirmation).
|
||||
*/
|
||||
shouldOverwrite?: (dependency: Dependency) => boolean;
|
||||
};
|
||||
|
||||
/** Storage and mapping settings for data stored by a `BufferedState`.
|
||||
*/
|
||||
export class BufferedKeyDefinition<Input, Output = Input, Dependency = true> {
|
||||
/**
|
||||
* Defines a buffered state
|
||||
* @param stateDefinition The domain of the buffer
|
||||
* @param key Domain key that identifies the buffered value. This key must
|
||||
* not be reused in any capacity.
|
||||
* @param options Configures the operation of the buffered state.
|
||||
*/
|
||||
constructor(
|
||||
readonly stateDefinition: StateDefinition,
|
||||
readonly key: string,
|
||||
readonly options: BufferedKeyDefinitionOptions<Input, Output, Dependency>,
|
||||
) {}
|
||||
|
||||
/** Converts the buffered key definition to a state provider
|
||||
* key definition
|
||||
*/
|
||||
toKeyDefinition() {
|
||||
const bufferedKey = new UserKeyDefinition<Input>(this.stateDefinition, this.key, this.options);
|
||||
|
||||
return bufferedKey;
|
||||
}
|
||||
|
||||
/** Checks whether the dependency triggers an overwrite. */
|
||||
shouldOverwrite(dependency: Dependency) {
|
||||
const shouldOverwrite = this.options?.shouldOverwrite;
|
||||
if (shouldOverwrite) {
|
||||
return shouldOverwrite(dependency);
|
||||
}
|
||||
|
||||
return dependency ? true : false;
|
||||
}
|
||||
|
||||
/** Converts the input data format to its output format.
|
||||
* @returns the converted value.
|
||||
*/
|
||||
map(input: Input, dependency: Dependency) {
|
||||
const map = this.options?.map;
|
||||
if (map) {
|
||||
return map(input, dependency);
|
||||
}
|
||||
|
||||
return Promise.resolve(input as unknown as Output);
|
||||
}
|
||||
|
||||
/** Checks whether the input type can be converted to the output type.
|
||||
* @returns `true` if the definition is valid, otherwise `false`.
|
||||
*/
|
||||
isValid(input: Input, dependency: Dependency) {
|
||||
const isValid = this.options?.isValid;
|
||||
if (isValid) {
|
||||
return isValid(input, dependency);
|
||||
}
|
||||
|
||||
return Promise.resolve(input ? true : false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user