sparrow-controllers
Version:
Collection of platform-agnostic modules for creating secure data models for cryptocurrency wallets
116 lines • 4.75 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPersistentState = exports.getAnonymizedState = exports.BaseController = void 0;
const immer_1 = require("immer");
(0, immer_1.enablePatches)();
/**
* Controller class that provides state management, subscriptions, and state metadata
*/
class BaseController {
/**
* Creates a BaseController instance.
*
* @param options - Controller options.
* @param options.messenger - Controller messaging system.
* @param options.metadata - State metadata, describing how to "anonymize" the state, and which
* parts should be persisted.
* @param options.name - The name of the controller, used as a namespace for events and actions.
* @param options.state - Initial controller state.
*/
constructor({ messenger, metadata, name, state, }) {
this.messagingSystem = messenger;
this.name = name;
this.internalState = state;
this.metadata = metadata;
this.messagingSystem.registerActionHandler(`${name}:getState`, () => this.state);
}
/**
* Retrieves current controller state.
*
* @returns The current state.
*/
get state() {
return this.internalState;
}
set state(_) {
throw new Error(`Controller state cannot be directly mutated; use 'update' method instead.`);
}
/**
* Updates controller state. Accepts a callback that is passed a draft copy
* of the controller state. If a value is returned, it is set as the new
* state. Otherwise, any changes made within that callback to the draft are
* applied to the controller state.
*
* @param callback - Callback for updating state, passed a draft state
* object. Return a new state object or mutate the draft to update state.
*/
update(callback) {
// We run into ts2589, "infinite type depth", if we don't cast
// produceWithPatches here.
// The final, omitted member of the returned tuple are the inverse patches.
const [nextState, patches] = immer_1.produceWithPatches(this.internalState, callback);
this.internalState = nextState;
this.messagingSystem.publish(`${this.name}:stateChange`, nextState, patches);
}
/**
* Prepares the controller for garbage collection. This should be extended
* by any subclasses to clean up any additional connections or events.
*
* The only cleanup performed here is to remove listeners. While technically
* this is not required to ensure this instance is garbage collected, it at
* least ensures this instance won't be responsible for preventing the
* listeners from being garbage collected.
*/
destroy() {
this.messagingSystem.clearEventSubscriptions(`${this.name}:stateChange`);
}
}
exports.BaseController = BaseController;
/**
* Returns an anonymized representation of the controller state.
*
* By "anonymized" we mean that it should not contain any information that could be personally
* identifiable.
*
* @param state - The controller state.
* @param metadata - The controller state metadata, which describes how to derive the
* anonymized state.
* @returns The anonymized controller state.
*/
function getAnonymizedState(state, metadata) {
return deriveStateFromMetadata(state, metadata, 'anonymous');
}
exports.getAnonymizedState = getAnonymizedState;
/**
* Returns the subset of state that should be persisted.
*
* @param state - The controller state.
* @param metadata - The controller state metadata, which describes which pieces of state should be persisted.
* @returns The subset of controller state that should be persisted.
*/
function getPersistentState(state, metadata) {
return deriveStateFromMetadata(state, metadata, 'persist');
}
exports.getPersistentState = getPersistentState;
/**
* Use the metadata to derive state according to the given metadata property.
*
* @param state - The full controller state.
* @param metadata - The controller metadata.
* @param metadataProperty - The metadata property to use to derive state.
* @returns The metadata-derived controller state.
*/
function deriveStateFromMetadata(state, metadata, metadataProperty) {
return Object.keys(state).reduce((persistedState, key) => {
const propertyMetadata = metadata[key][metadataProperty];
const stateProperty = state[key];
if (typeof propertyMetadata === 'function') {
persistedState[key] = propertyMetadata(stateProperty);
}
else if (propertyMetadata) {
persistedState[key] = stateProperty;
}
return persistedState;
}, {});
}
//# sourceMappingURL=BaseControllerV2.js.map