New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

adaka

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

adaka - npm Package Compare versions

Comparing version 0.0.10 to 0.0.11

dist/cjs/_internal/selector.js

7

CHANGELOG.md
# Changelog
## 0.0.11 / 2024-03-22
**New**
- Add support for multiple update operations in single call.
**Fixes**
- Restore clone mode default to "copy".
## 0.0.10 / 2024-03-04

@@ -4,0 +11,0 @@ **New**

151

dist/cjs/_internal/store.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Selector = exports.Store = exports.createStore = void 0;
exports.Store = exports.createStore = void 0;
const mingo_1 = require("mingo");

@@ -10,4 +10,4 @@ const core_1 = require("mingo/core");

const util_1 = require("mingo/util");
const selector_1 = require("./selector");
const util_2 = require("./util");
const NONE = Symbol();
const EMPTY_QUERY = new mingo_1.Query({});

@@ -43,4 +43,2 @@ /** helper to create query object. */

this.selectors = new Map();
// signals for notifying selectors of changes.
this.signals = new Map();
// flag for checking modifications to the entire state.

@@ -52,6 +50,6 @@ this.modified = true;

useStrictMode: false }));
this.mutate = (0, updater_1.createUpdater)(Object.assign({ cloneMode: "none" }, options));
this.mutate = (0, updater_1.createUpdater)(Object.assign(Object.assign({ cloneMode: "copy" }, options), { queryOptions: this.queryOptions }));
}
/**
* Returns the current state as a frozen object subject to the given criteria.
* Returns the current state as a frozen object subject to the given criteria.s
* When no options are specified, returns the full state.

@@ -104,3 +102,3 @@ *

if (this.selectors.has(hash)) {
return this.selectors.get(hash);
return this.selectors.get(hash).selector;
}

@@ -116,3 +114,3 @@ // get expected paths to monitor for changes.

// create and add a new selector
const selector = new Selector(this, mkQuery(condition, this.queryOptions), projection);
const selector = new selector_1.Selector(this.getState.bind(this), mkQuery(condition, this.queryOptions), projection);
// if no field is specified, select everything.

@@ -132,5 +130,3 @@ const pred = !expected.size

};
// this.selectors.add(selector);
this.signals.set(selector, signal);
this.selectors.set(hash, selector);
this.selectors.set(hash, { selector, signal });
return selector;

@@ -141,3 +137,3 @@ }

*
* @param {UpdateExpression} expr Update expression as a MongoDB update query.
* @param {UpdateExpression | UpdateExpression[]} expr Update expression as a MongoDB update query.
* @param {Array<RawObject>} arrayFilters Array filter expressions to filter elements to update.

@@ -148,3 +144,6 @@ * @param {RawObject} condition Condition to check before applying update.

update(expr, arrayFilters = [], condition = {}) {
const fields = this.mutate(this.state, expr, arrayFilters, condition);
const query = mkQuery(condition, this.queryOptions);
const fields = Array.from(new Set(
// apply mutations
(0, util_1.ensureArray)(expr).flatMap(e => this.mutate(this.state, e, arrayFilters, query))));
// return if state is unchanged

@@ -154,2 +153,4 @@ if (!fields.length) {

}
// maintain stability.
fields.sort();
// set modified flag

@@ -159,4 +160,3 @@ this.modified = true;

let notifyCount = 0;
this.selectors.forEach(selector => {
const signal = this.signals.get(selector);
for (const { selector, signal } of this.selectors.values()) {
// record the number of listeners before notifying the selector.

@@ -167,3 +167,3 @@ // upon notification a listener will be removed from the selector if it throws or is configured to run once.

notifyCount += size;
});
}
return { modified: true, fields, notifyCount };

@@ -173,120 +173,1 @@ }

exports.Store = Store;
/**
* Provides an observable interface for selecting customized views of the state.
* Listeners can subscribe to be notified of changes in the view repeatedely or once.
*/
class Selector {
/**
* Construct a new selector
* @param store Reference to the store object.
* @param query Query object for checking conditions based on MongoDB filter query.
* @param projection View of the state to select expressed as MongoDB projection query.
*/
constructor(store, query, projection) {
this.store = store;
this.query = query;
this.projection = projection;
// iteration happens in insertion order.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
this.listeners = new Set();
// listeners to be run once only also included in the main listener set.
this.onceOnly = new Set();
// flag used to control when to use cached value.
this.cached = false;
}
/** Returns the number of subscribers to this selector. */
get size() {
return this.listeners.size;
}
/**
* Returns the current state view subject to the selector criteria.
* The value is only recomputed when the depedent fields in the criteria change.
*
* @returns {T | undefined}
*/
getState() {
// return cached if value has not changed since
if (this.cached)
return this.value;
// update cached status
this.cached = true;
// project fields and freeze final value if query passes
return (this.value = this.store.getState(this.projection, this.query));
}
/**
* Notify all listeners with the current value of the selector if different from the previous value.
* If a listener throws an exception when notified, it is removed and does not receive future notifications.
*/
notifyAll() {
// only recompute if there are active listeners.
if (!this.listeners.size)
return;
const prev = this.cached ? this.getState() : NONE;
// reset the cache when notifyAll() is called.
this.cached = false;
// compute new value.
const val = this.getState();
// No change so skip notifications. If a new subscriber was added after the last notification, it will be skipped here as well.
// This is becuase the state has still not changed after it was added. For new subscribers to receive current state on subcsription,
// they should be registered with {runImmediately: true}.
if ((0, util_1.isEqual)(prev, val))
return;
for (const f of this.listeners) {
/*eslint-disable*/
try {
f(val);
}
catch (_a) {
// on error unsubscribe listener
this.listeners.delete(f);
}
finally {
// if runOnce, cleanup afterwards
if (this.onceOnly.delete(f)) {
this.listeners.delete(f);
}
}
/*eslint-disable-enable*/
}
}
/**
* Subscribe a listener to be notified about state updates.
*
* @param listener The function to receive new data on update.
* @returns {Unsubscribe} Function to unsubscribe listener.
*/
subscribe(listener, options) {
// check if we are reregistering the same observer
if (this.listeners.has(listener)) {
throw new Error("Listener already subscribed.");
}
// setup to throw after first run.
if (options && options.runOnce) {
this.onceOnly.add(listener);
}
this.listeners.add(listener);
const unsub = () => {
this.onceOnly.delete(listener);
this.listeners.delete(listener);
};
if (options && options.runImmediately) {
// immediately invoke
const val = this.getState();
if (val !== undefined) {
try {
listener(val);
}
catch (e) {
unsub();
throw e;
}
finally {
if (this.onceOnly.has(listener))
unsub();
}
}
}
return unsub;
}
}
exports.Selector = Selector;

@@ -17,2 +17,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./_internal/selector"), exports);
__exportStar(require("./_internal/store"), exports);

@@ -6,5 +6,5 @@ import { Query } from "mingo";

import { createUpdater } from "mingo/updater";
import { assert, cloneDeep, isEqual, normalize, stringify } from "mingo/util";
import { assert, cloneDeep, ensureArray, isEqual, normalize, stringify } from "mingo/util";
import { Selector } from "./selector";
import { cloneFrozen, getDependentPaths, isProjectExpression, sameAncestor } from "./util";
const NONE = Symbol();
const EMPTY_QUERY = new Query({});

@@ -39,4 +39,2 @@ /** helper to create query object. */

this.selectors = new Map();
// signals for notifying selectors of changes.
this.signals = new Map();
// flag for checking modifications to the entire state.

@@ -48,6 +46,6 @@ this.modified = true;

useStrictMode: false }));
this.mutate = createUpdater(Object.assign({ cloneMode: "none" }, options));
this.mutate = createUpdater(Object.assign(Object.assign({ cloneMode: "copy" }, options), { queryOptions: this.queryOptions }));
}
/**
* Returns the current state as a frozen object subject to the given criteria.
* Returns the current state as a frozen object subject to the given criteria.s
* When no options are specified, returns the full state.

@@ -100,3 +98,3 @@ *

if (this.selectors.has(hash)) {
return this.selectors.get(hash);
return this.selectors.get(hash).selector;
}

@@ -112,3 +110,3 @@ // get expected paths to monitor for changes.

// create and add a new selector
const selector = new Selector(this, mkQuery(condition, this.queryOptions), projection);
const selector = new Selector(this.getState.bind(this), mkQuery(condition, this.queryOptions), projection);
// if no field is specified, select everything.

@@ -128,5 +126,3 @@ const pred = !expected.size

};
// this.selectors.add(selector);
this.signals.set(selector, signal);
this.selectors.set(hash, selector);
this.selectors.set(hash, { selector, signal });
return selector;

@@ -137,3 +133,3 @@ }

*
* @param {UpdateExpression} expr Update expression as a MongoDB update query.
* @param {UpdateExpression | UpdateExpression[]} expr Update expression as a MongoDB update query.
* @param {Array<RawObject>} arrayFilters Array filter expressions to filter elements to update.

@@ -144,3 +140,6 @@ * @param {RawObject} condition Condition to check before applying update.

update(expr, arrayFilters = [], condition = {}) {
const fields = this.mutate(this.state, expr, arrayFilters, condition);
const query = mkQuery(condition, this.queryOptions);
const fields = Array.from(new Set(
// apply mutations
ensureArray(expr).flatMap(e => this.mutate(this.state, e, arrayFilters, query))));
// return if state is unchanged

@@ -150,2 +149,4 @@ if (!fields.length) {

}
// maintain stability.
fields.sort();
// set modified flag

@@ -155,4 +156,3 @@ this.modified = true;

let notifyCount = 0;
this.selectors.forEach(selector => {
const signal = this.signals.get(selector);
for (const { selector, signal } of this.selectors.values()) {
// record the number of listeners before notifying the selector.

@@ -163,123 +163,5 @@ // upon notification a listener will be removed from the selector if it throws or is configured to run once.

notifyCount += size;
});
}
return { modified: true, fields, notifyCount };
}
}
/**
* Provides an observable interface for selecting customized views of the state.
* Listeners can subscribe to be notified of changes in the view repeatedely or once.
*/
export class Selector {
/**
* Construct a new selector
* @param store Reference to the store object.
* @param query Query object for checking conditions based on MongoDB filter query.
* @param projection View of the state to select expressed as MongoDB projection query.
*/
constructor(store, query, projection) {
this.store = store;
this.query = query;
this.projection = projection;
// iteration happens in insertion order.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
this.listeners = new Set();
// listeners to be run once only also included in the main listener set.
this.onceOnly = new Set();
// flag used to control when to use cached value.
this.cached = false;
}
/** Returns the number of subscribers to this selector. */
get size() {
return this.listeners.size;
}
/**
* Returns the current state view subject to the selector criteria.
* The value is only recomputed when the depedent fields in the criteria change.
*
* @returns {T | undefined}
*/
getState() {
// return cached if value has not changed since
if (this.cached)
return this.value;
// update cached status
this.cached = true;
// project fields and freeze final value if query passes
return (this.value = this.store.getState(this.projection, this.query));
}
/**
* Notify all listeners with the current value of the selector if different from the previous value.
* If a listener throws an exception when notified, it is removed and does not receive future notifications.
*/
notifyAll() {
// only recompute if there are active listeners.
if (!this.listeners.size)
return;
const prev = this.cached ? this.getState() : NONE;
// reset the cache when notifyAll() is called.
this.cached = false;
// compute new value.
const val = this.getState();
// No change so skip notifications. If a new subscriber was added after the last notification, it will be skipped here as well.
// This is becuase the state has still not changed after it was added. For new subscribers to receive current state on subcsription,
// they should be registered with {runImmediately: true}.
if (isEqual(prev, val))
return;
for (const f of this.listeners) {
/*eslint-disable*/
try {
f(val);
}
catch (_a) {
// on error unsubscribe listener
this.listeners.delete(f);
}
finally {
// if runOnce, cleanup afterwards
if (this.onceOnly.delete(f)) {
this.listeners.delete(f);
}
}
/*eslint-disable-enable*/
}
}
/**
* Subscribe a listener to be notified about state updates.
*
* @param listener The function to receive new data on update.
* @returns {Unsubscribe} Function to unsubscribe listener.
*/
subscribe(listener, options) {
// check if we are reregistering the same observer
if (this.listeners.has(listener)) {
throw new Error("Listener already subscribed.");
}
// setup to throw after first run.
if (options && options.runOnce) {
this.onceOnly.add(listener);
}
this.listeners.add(listener);
const unsub = () => {
this.onceOnly.delete(listener);
this.listeners.delete(listener);
};
if (options && options.runImmediately) {
// immediately invoke
const val = this.getState();
if (val !== undefined) {
try {
listener(val);
}
catch (e) {
unsub();
throw e;
}
finally {
if (this.onceOnly.has(listener))
unsub();
}
}
}
return unsub;
}
}

@@ -0,1 +1,2 @@

export * from "./_internal/selector";
export * from "./_internal/store";

@@ -5,13 +5,3 @@ import { Query } from "mingo";

import { UpdateExpression } from "mingo/updater";
/** Observes a selector for changes in store and optionally return updates to apply. */
export type Listener<T> = (data: T) => void;
/** Unsbuscribe from receiving further notifications */
export type Unsubscribe = () => void;
/** Options to pass on subscription. */
export interface SubscribeOptions {
/** Immediately run the listener when register. Any error will bubble up immediately. */
readonly runImmediately?: boolean;
/** Run only once. */
readonly runOnce?: boolean;
}
import { Selector } from "./selector";
/** Result from update operation which returns useful details. */

@@ -22,3 +12,3 @@ export interface UpdateResult {

/** The fields in the state object that were modified. */
readonly fields?: string[];
readonly fields?: Readonly<string[]>;
/** The number of listeners notified. */

@@ -41,6 +31,5 @@ readonly notifyCount?: number;

*/
export declare class Store<T extends RawObject = RawObject> {
export declare class Store<S extends RawObject = RawObject> {
private readonly state;
private readonly selectors;
private readonly signals;
private readonly queryOptions;

@@ -50,5 +39,5 @@ private readonly mutate;

private prevState;
constructor(initialState: T, options?: UpdateOptions);
constructor(initialState: S, options?: UpdateOptions);
/**
* Returns the current state as a frozen object subject to the given criteria.
* Returns the current state as a frozen object subject to the given criteria.s
* When no options are specified, returns the full state.

@@ -60,3 +49,3 @@ *

*/
getState<P extends T>(projection?: Record<keyof P, AnyVal> | RawObject, condition?: RawObject | Query): P | undefined;
getState<P extends RawObject & S>(projection?: Record<keyof P, AnyVal> | RawObject, condition?: RawObject | Query): P | undefined;
/**

@@ -69,7 +58,7 @@ * Creates a new observable for a view of the state.

*/
select<P extends RawObject>(projection: Record<keyof P, AnyVal> | RawObject, condition?: RawObject): Selector<P>;
select<P extends RawObject = S>(projection: Record<keyof P, AnyVal> | RawObject, condition?: RawObject): Selector<P>;
/**
* Dispatches an update expression to mutate the state. Triggers a notification to relevant selectors only.
*
* @param {UpdateExpression} expr Update expression as a MongoDB update query.
* @param {UpdateExpression | UpdateExpression[]} expr Update expression as a MongoDB update query.
* @param {Array<RawObject>} arrayFilters Array filter expressions to filter elements to update.

@@ -79,44 +68,3 @@ * @param {RawObject} condition Condition to check before applying update.

*/
update(expr: UpdateExpression, arrayFilters?: RawObject[], condition?: RawObject): UpdateResult;
update(expr: UpdateExpression | UpdateExpression[], arrayFilters?: RawObject[], condition?: RawObject): UpdateResult;
}
/**
* Provides an observable interface for selecting customized views of the state.
* Listeners can subscribe to be notified of changes in the view repeatedely or once.
*/
export declare class Selector<T extends RawObject = RawObject> {
private readonly store;
private readonly query;
private readonly projection;
private readonly listeners;
private readonly onceOnly;
private value;
private cached;
/**
* Construct a new selector
* @param store Reference to the store object.
* @param query Query object for checking conditions based on MongoDB filter query.
* @param projection View of the state to select expressed as MongoDB projection query.
*/
constructor(store: Store, query: Query, projection: Record<keyof T, AnyVal> | RawObject);
/** Returns the number of subscribers to this selector. */
get size(): number;
/**
* Returns the current state view subject to the selector criteria.
* The value is only recomputed when the depedent fields in the criteria change.
*
* @returns {T | undefined}
*/
getState(): T | undefined;
/**
* Notify all listeners with the current value of the selector if different from the previous value.
* If a listener throws an exception when notified, it is removed and does not receive future notifications.
*/
notifyAll(): void;
/**
* Subscribe a listener to be notified about state updates.
*
* @param listener The function to receive new data on update.
* @returns {Unsubscribe} Function to unsubscribe listener.
*/
subscribe(listener: Listener<T>, options?: SubscribeOptions): Unsubscribe;
}

@@ -0,1 +1,2 @@

export * from "./_internal/selector";
export * from "./_internal/store";
{
"name": "adaka",
"version": "0.0.10",
"version": "0.0.11",
"description": "High-precision state management using MongoDB query language.",

@@ -22,3 +22,3 @@ "main": "./dist/cjs/index.js",

"dependencies": {
"mingo": "^6.4.12"
"mingo": "^6.4.13"
},

@@ -25,0 +25,0 @@ "devDependencies": {},

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc