Fix StartOS 0.4 TypeScript packaging to match SDK API
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 adamhamlin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
# Deep Equality Javascript Data Structures
|
||||
|
||||
[](https://badge.fury.io/js/deep-equality-data-structures)
|
||||
[](https://github.com/adamhamlin/deep-equality-data-structures/actions/workflows/ci.yaml)
|
||||
|
||||
A drop-in replacement for ES native `Map` and `Set` with deep equality support for objects.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install deep-equality-data-structures
|
||||
```
|
||||
|
||||
## Why?
|
||||
|
||||
ES `Map` and `Set` only support referential equality:
|
||||
|
||||
```typescript
|
||||
interface MyType {
|
||||
a: number;
|
||||
}
|
||||
const set = new Set<MyType>();
|
||||
set.add({ a: 1 });
|
||||
set.add({ a: 1 });
|
||||
set.size; // 2
|
||||
```
|
||||
|
||||
Now, using deep equality:
|
||||
|
||||
```typescript
|
||||
import { DeepSet } from 'deep-equality-data-structures';
|
||||
|
||||
interface MyType {
|
||||
a: number;
|
||||
}
|
||||
const set = new DeepSet<MyType>();
|
||||
set.add({ a: 1 });
|
||||
set.add({ a: 1 });
|
||||
set.size; // 1
|
||||
```
|
||||
|
||||
## How?
|
||||
|
||||
This project relies on the [object-hash](https://github.com/puleos/object-hash) library to normalize object types to unique strings.
|
||||
|
||||
## Comparable Interface
|
||||
|
||||
The following supplemental comparisons/methods are included:
|
||||
|
||||
- `equals`
|
||||
- `contains`
|
||||
- `union`
|
||||
- `intersection`
|
||||
- `difference`
|
||||
|
||||
```typescript
|
||||
// COMPARISONS
|
||||
const set1 = new DeepSet([{ a: 1 }, { b: 2 }]);
|
||||
const set2 = new DeepSet([{ b: 2 }, { a: 1 }]);
|
||||
set1.equals(set2); // true
|
||||
|
||||
const set3 = new DeepSet([{ a: 1 }]);
|
||||
set1.equals(set3); // false
|
||||
set1.contains(set3); // true
|
||||
|
||||
// SET OPERATIONS (available for maps, too)
|
||||
const set3 = new DeepSet([{ a: 1 }, { b: 2 }]);
|
||||
const set4 = new DeepSet([{ b: 2 }, { c: 3 }]);
|
||||
set3.union(set4); // DeepSet([{ a: 1 }, { b: 2 }, { c: 3 }])
|
||||
set3.intersection(set4); // DeepSet([{ b: 2 }])
|
||||
set3.difference(set4); // DeepSet([{ a: 1 }])
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The default settings should be suitable for most use cases, but behavior can be configured.
|
||||
|
||||
```typescript
|
||||
new DeepSet<V>(values?, options?)
|
||||
new DeepMap<K,V>(entries?, options?)
|
||||
```
|
||||
|
||||
The `options` argument is a superset of the options defined for [object-hash](https://github.com/puleos/object-hash#hashvalue-options), with the same defaults (exception: the default algoritm is `md5`). There are also library-specific options.
|
||||
|
||||
### Library-specific options:
|
||||
|
||||
- `transformer` - a custom function that transforms Map keys/Set values prior to hashing. It does not affect the values that are stored.
|
||||
|
||||
```typescript
|
||||
type MyType = { val: number; other: number };
|
||||
const a: MyType = { val: 1, other: 1 };
|
||||
const b: MyType = { val: 1, other: 2 };
|
||||
const transformer = (obj: MyType) => ({ val: obj.val });
|
||||
|
||||
const set = new DeepSet([a, b]);
|
||||
set.size; // 2
|
||||
const set = new DeepSet([a, b], { transformer });
|
||||
set.size; // 1
|
||||
|
||||
[...set.values()]; // [{ val: 1, other: 2 }]
|
||||
```
|
||||
|
||||
- `mapValueTransformer` - a custom function that transforms Map values prior to hashing. This is only relevant to the `.equals`/`.contains` operations from the [Comparable interface](#comparable-interface), as well as the [Bi-Directional DeepMap](#bi-directional-deepmap). It does not affect the values that are stored.
|
||||
|
||||
```typescript
|
||||
type MyType = { val: number; other: number };
|
||||
const a: MyType = { val: 1, other: 1 };
|
||||
const b: MyType = { val: 1, other: 2 };
|
||||
const mapValueTransformer = (obj: MyType) => ({ val: obj.val });
|
||||
|
||||
const map1 = new DeepMap([[1, a]]);
|
||||
const map2 = new DeepMap([[1, b]]);
|
||||
map1.equals(map2); // false
|
||||
|
||||
const map1 = new DeepMap([[1, a]], { mapValueTransformer });
|
||||
const map2 = new DeepMap([[1, b]], { mapValueTransformer });
|
||||
map1.equals(map2); // true
|
||||
|
||||
[...map1.entries()]; // [[1, { val: 1, other: 1 }]]
|
||||
[...map2.entries()]; // [[1, { val: 1, other: 2 }]]
|
||||
```
|
||||
|
||||
- `useToJsonTransform` - if true, only use JSON-serializable properties when computing hashes, equality, etc. (default: false)
|
||||
|
||||
> _NOTE: This transform will always be applied BEFORE `transformer` and `mapValueTransformer`, if applicable._
|
||||
|
||||
```typescript
|
||||
class A {
|
||||
constructor(public x: number) {}
|
||||
}
|
||||
class B {
|
||||
constructor(public x: number) {}
|
||||
}
|
||||
const a = new A(45);
|
||||
const b = new B(45);
|
||||
|
||||
const set = new DeepSet([a, b]);
|
||||
set.size; // 2
|
||||
const set = new DeepSet([a, b], { useToJsonTransform: true });
|
||||
set.size; // 1
|
||||
```
|
||||
|
||||
- `caseInsensitive` - If true, all string values--including keys/values within objects and arrays--will be evaluated as case-insensitive. (default: false)
|
||||
|
||||
> _NOTE: This transform will always be applied AFTER `transformer` and `mapValueTransformer`, if applicable. For objects, it will be applied before `replacer` (from object-hash options)._
|
||||
|
||||
```typescript
|
||||
const a = { key: 'value' };
|
||||
const b = { key: 'VALUE' };
|
||||
|
||||
const set = new DeepSet([a, b]);
|
||||
set.size; // 2
|
||||
const set = new DeepSet([a, b], { caseInsensitive: true });
|
||||
set.size; // 1
|
||||
```
|
||||
|
||||
## Bi-Directional DeepMap
|
||||
|
||||
This library also exposes a `BiDirectionalDeepMap` class, which supports O(1) lookups by both keys and values. It provides the following extended API:
|
||||
|
||||
- _`hasValue(val: V): boolean`_: Returns true if `val` exists as a value in the map
|
||||
- _`getKeyByValue(val: V): K | undefined`_: Returns the key associated with `val` if it exists
|
||||
- _`deleteByValue(val: V): boolean`_: Removes the key-value pair whose value is `val` and returns true if found
|
||||
|
||||
### Caveats
|
||||
|
||||
Note that this "two-way" map has the traditional caveats:
|
||||
|
||||
- There is a ~2x memory footprint
|
||||
- Keys and values must be 1-to-1, meaning each key must have a distinct value and vice versa. This implementation will error if attempting to set a key-value pair whose _value_ is already present in the map with a different _key_.
|
||||
|
||||
## Static Utility Methods
|
||||
|
||||
- _`areEqual(values, options?)`_: Returns true if all elements in `values` are equal. This can be useful when you need to quickly
|
||||
test equality of more than 2 values, or when you want to specify an equality transform (via `options.transformer`).
|
||||
|
||||
## Notes/Caveats
|
||||
|
||||
- This still supports primitive keys/values like traditional `Map`/`Set`.
|
||||
- Don't mutate objects stored in the data structure. The internal representation is not affected by this mutation, so behavior may be unexpected.
|
||||
- Don't mutate objects in the user-supplied `transformer` or `mapValueTransformer` functions. It will affect the stored version.
|
||||
- This implementation does not explicitly "handle" key collisions. However, with the default algorithm (MD5), even if a map contained one TRILLION entries, the probability of a collision on the next insert is only 0.000000000000001. If you need better odds, use SHA1, SHA256, etc.
|
||||
|
||||
## CI/CD
|
||||
|
||||
Using Github Actions, the CI build will run on all pull requests and pushes/merges to main.
|
||||
|
||||
This project uses [Conventional Commits](https://www.conventionalcommits.org/) and [standard-version](https://github.com/conventional-changelog/standard-version) to facilitate versioning and changelogs.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export { DeepSet } from './src/set';
|
||||
export { DeepMap } from './src/map';
|
||||
export { BiDirectionalDeepMap } from './src/map.bi-directional';
|
||||
export { Options } from './src/options';
|
||||
export { areEqual } from './src/areEqual';
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.areEqual = exports.BiDirectionalDeepMap = exports.DeepMap = exports.DeepSet = void 0;
|
||||
var set_1 = require("./src/set");
|
||||
Object.defineProperty(exports, "DeepSet", { enumerable: true, get: function () { return set_1.DeepSet; } });
|
||||
var map_1 = require("./src/map");
|
||||
Object.defineProperty(exports, "DeepMap", { enumerable: true, get: function () { return map_1.DeepMap; } });
|
||||
var map_bi_directional_1 = require("./src/map.bi-directional");
|
||||
Object.defineProperty(exports, "BiDirectionalDeepMap", { enumerable: true, get: function () { return map_bi_directional_1.BiDirectionalDeepMap; } });
|
||||
var areEqual_1 = require("./src/areEqual");
|
||||
Object.defineProperty(exports, "areEqual", { enumerable: true, get: function () { return areEqual_1.areEqual; } });
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { Options } from './options';
|
||||
/**
|
||||
* Static utility for doing a one-time equality check across the provided values.
|
||||
* @param values list whose elements will be compared to each other
|
||||
* @param options configuration options
|
||||
* @returns true if every element in `values` is equal to every other element
|
||||
* @throws {Error} if `values` list is empty
|
||||
*/
|
||||
export declare function areEqual<V, TxV = V>(values: V[], options?: Options<V, null, TxV, null>): boolean;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.areEqual = areEqual;
|
||||
const errors_1 = require("./errors");
|
||||
const set_1 = require("./set");
|
||||
/**
|
||||
* Static utility for doing a one-time equality check across the provided values.
|
||||
* @param values list whose elements will be compared to each other
|
||||
* @param options configuration options
|
||||
* @returns true if every element in `values` is equal to every other element
|
||||
* @throws {Error} if `values` list is empty
|
||||
*/
|
||||
function areEqual(values, options) {
|
||||
if (values.length === 0) {
|
||||
throw new errors_1.DeepEqualityDataStructuresError('Empty values list passed to areEqual function');
|
||||
}
|
||||
const set = new set_1.DeepSet(values, options);
|
||||
return set.size === 1;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Interface for comparable maps/sets
|
||||
*/
|
||||
export interface Comparable<T> {
|
||||
equals(other: T): boolean;
|
||||
contains(other: T): boolean;
|
||||
union(other: T): T;
|
||||
intersection(other: T): T;
|
||||
difference(other: T): T;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export declare class DeepEqualityDataStructuresError extends Error {
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DeepEqualityDataStructuresError = void 0;
|
||||
class DeepEqualityDataStructuresError extends Error {
|
||||
}
|
||||
exports.DeepEqualityDataStructuresError = DeepEqualityDataStructuresError;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { DeepMap } from './map';
|
||||
import { Options } from './options';
|
||||
/**
|
||||
* A DeepMap implementation that supports O(1) lookups by both keys and values
|
||||
* NOTE: All key-value pairs must be 1-to-1
|
||||
*/
|
||||
export declare class BiDirectionalDeepMap<K, V, TxK = K, TxV = V> extends DeepMap<K, V, TxK, TxV> {
|
||||
private readonly valueMap;
|
||||
/**
|
||||
* @param entries optional list of key-value pairs to initialize the map
|
||||
* @param options configuration options
|
||||
*/
|
||||
constructor(entries?: readonly (readonly [K, V])[] | null, options?: Options<K, V, TxK, TxV>);
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
set(key: K, val: V): this;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
delete(key: K): boolean;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* @returns true if the given value is present in the key-value map.
|
||||
*/
|
||||
hasValue(val: V): boolean;
|
||||
/**
|
||||
* @returns the key associated with the specified value
|
||||
*/
|
||||
getKeyByValue(val: V): K | undefined;
|
||||
/**
|
||||
* @returns true if a value in the map existed and has been removed, else false
|
||||
*/
|
||||
deleteByValue(val: V): boolean;
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BiDirectionalDeepMap = void 0;
|
||||
const errors_1 = require("./errors");
|
||||
const map_1 = require("./map");
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* A DeepMap implementation that supports O(1) lookups by both keys and values
|
||||
* NOTE: All key-value pairs must be 1-to-1
|
||||
*/
|
||||
class BiDirectionalDeepMap extends map_1.DeepMap {
|
||||
/**
|
||||
* @param entries optional list of key-value pairs to initialize the map
|
||||
* @param options configuration options
|
||||
*/
|
||||
constructor(entries, options) {
|
||||
super(entries, options);
|
||||
const valueEntries = entries ? entries.map(([key, val]) => [this.normalizeValue(val), key]) : null;
|
||||
this.valueMap = new Map(valueEntries);
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
set(key, val) {
|
||||
// Enforce 1-to-1: Don't allow writing a value which is already present in the map for a different key
|
||||
const preexistingValueKey = this.getKeyByValue(val);
|
||||
if (preexistingValueKey !== undefined && this.normalizeKey(preexistingValueKey) !== this.normalizeKey(key)) {
|
||||
throw new errors_1.DeepEqualityDataStructuresError(`Could not set key='${(0, utils_1.stringify)(key)}': The value='${(0, utils_1.stringify)(val)}' is already associated with key='${(0, utils_1.stringify)(preexistingValueKey)}'`);
|
||||
}
|
||||
this.valueMap.set(this.normalizeValue(val), key);
|
||||
return super.set(key, val);
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
delete(key) {
|
||||
const val = this.get(key);
|
||||
if (val) {
|
||||
this.valueMap.delete(this.normalizeValue(val));
|
||||
}
|
||||
return super.delete(key);
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear() {
|
||||
this.valueMap.clear();
|
||||
super.clear();
|
||||
}
|
||||
// BI-DIRECTIONAL API
|
||||
/**
|
||||
* @returns true if the given value is present in the key-value map.
|
||||
*/
|
||||
hasValue(val) {
|
||||
return this.valueMap.has(this.normalizeValue(val));
|
||||
}
|
||||
/**
|
||||
* @returns the key associated with the specified value
|
||||
*/
|
||||
getKeyByValue(val) {
|
||||
return this.valueMap.get(this.normalizeValue(val));
|
||||
}
|
||||
/**
|
||||
* @returns true if a value in the map existed and has been removed, else false
|
||||
*/
|
||||
deleteByValue(val) {
|
||||
const key = this.getKeyByValue(val);
|
||||
return key ? this.delete(key) : false;
|
||||
}
|
||||
}
|
||||
exports.BiDirectionalDeepMap = BiDirectionalDeepMap;
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
import { Comparable } from './comparable';
|
||||
import { Normalized } from './normalizer';
|
||||
import { Options } from './options';
|
||||
/**
|
||||
* A Map implementation that supports deep equality for object keys.
|
||||
*/
|
||||
export declare class DeepMap<K, V, TxK = K, TxV = V> extends Map<K, V> implements Comparable<DeepMap<K, V, TxK, TxV>> {
|
||||
private options;
|
||||
private readonly normalizer;
|
||||
private readonly map;
|
||||
/**
|
||||
* @param entries optional list of key-value pairs to initialize the map
|
||||
* @param options configuration options
|
||||
*/
|
||||
constructor(entries?: readonly (readonly [K, V])[] | null, options?: Options<K, V, TxK, TxV>);
|
||||
/**
|
||||
* Getter for number of kev-value pairs in the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
get size(): number;
|
||||
/**
|
||||
* Returns true if the given key is present in the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
has(key: K): boolean;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
set(key: K, val: V): this;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
get(key: K): V | undefined;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
delete(key: K): boolean;
|
||||
/**
|
||||
* Clear all key-value pairs from the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
forEach(callbackfn: (val: V, key: K, map: Map<K, V>) => void): void;
|
||||
/**
|
||||
* @yields the next key-value pair in the map
|
||||
* @inheritdoc
|
||||
*/
|
||||
[Symbol.iterator](): IterableIterator<[K, V]>;
|
||||
/**
|
||||
* @yields the next key-value pair in the map
|
||||
* @inheritdoc
|
||||
*/
|
||||
entries(): IterableIterator<[K, V]>;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
keys(): IterableIterator<K>;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
values(): IterableIterator<V>;
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns true if the entries of `other` are the same as this map
|
||||
*/
|
||||
equals(other: this): boolean;
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns true if the entries of `other` are all contained in this map
|
||||
*/
|
||||
contains(other: this): boolean;
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns a new map whose keys are the union of keys between `this` and `other` maps.
|
||||
*
|
||||
* NOTE: If both maps prescribe the same key, the key-value pair from `this` will be retained.
|
||||
*/
|
||||
union(other: this): DeepMap<K, V, TxK, TxV>;
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns a new map containing all key-value pairs in `this` that are also present in `other`.
|
||||
*/
|
||||
intersection(other: this): DeepMap<K, V, TxK, TxV>;
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns a new map containing all key-value pairs in `this` that are not present in `other`.
|
||||
*/
|
||||
difference(other: this): DeepMap<K, V, TxK, TxV>;
|
||||
protected normalizeKey(input: K): Normalized<TxK>;
|
||||
protected normalizeValue(input: V): Normalized<TxV>;
|
||||
private validateUsingSameOptionsAs;
|
||||
/**
|
||||
* @returns true if the key is present in the provided map w/ the specified value
|
||||
*/
|
||||
private keyValuePairIsPresentIn;
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DeepMap = void 0;
|
||||
const errors_1 = require("./errors");
|
||||
const normalizer_1 = require("./normalizer");
|
||||
/**
|
||||
* A Map implementation that supports deep equality for object keys.
|
||||
*/
|
||||
class DeepMap extends Map {
|
||||
// NOTE: This is actually a thin wrapper. We're not using super other than to drive the (typed) API contract.
|
||||
/**
|
||||
* @param entries optional list of key-value pairs to initialize the map
|
||||
* @param options configuration options
|
||||
*/
|
||||
constructor(entries, options = {}) {
|
||||
super();
|
||||
this.options = options;
|
||||
this.normalizer = new normalizer_1.Normalizer(options);
|
||||
const transformedEntries = entries
|
||||
? entries.map(([key, val]) => [this.normalizeKey(key), { key, val }])
|
||||
: null;
|
||||
this.map = new Map(transformedEntries);
|
||||
}
|
||||
/**
|
||||
* Getter for number of kev-value pairs in the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
get size() {
|
||||
return this.map.size;
|
||||
}
|
||||
/**
|
||||
* Returns true if the given key is present in the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
has(key) {
|
||||
return this.map.has(this.normalizeKey(key));
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
set(key, val) {
|
||||
this.map.set(this.normalizeKey(key), { key, val });
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
get(key) {
|
||||
var _a;
|
||||
return (_a = this.map.get(this.normalizeKey(key))) === null || _a === void 0 ? void 0 : _a.val;
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
delete(key) {
|
||||
return this.map.delete(this.normalizeKey(key));
|
||||
}
|
||||
/**
|
||||
* Clear all key-value pairs from the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
forEach(callbackfn) {
|
||||
this.map.forEach((pair, _key, _internalMap) => {
|
||||
callbackfn(pair.val, pair.key, this);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @yields the next key-value pair in the map
|
||||
* @inheritdoc
|
||||
*/
|
||||
*[Symbol.iterator]() {
|
||||
for (const [_hashStr, pair] of this.map[Symbol.iterator]()) {
|
||||
yield [pair.key, pair.val];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @yields the next key-value pair in the map
|
||||
* @inheritdoc
|
||||
*/
|
||||
*entries() {
|
||||
for (const entry of this[Symbol.iterator]()) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
*keys() {
|
||||
for (const [key, _val] of this[Symbol.iterator]()) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
*values() {
|
||||
for (const [_key, val] of this[Symbol.iterator]()) {
|
||||
yield val;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns true if the entries of `other` are the same as this map
|
||||
*/
|
||||
equals(other) {
|
||||
this.validateUsingSameOptionsAs(other);
|
||||
return this.size === other.size && this.contains(other);
|
||||
}
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns true if the entries of `other` are all contained in this map
|
||||
*/
|
||||
contains(other) {
|
||||
this.validateUsingSameOptionsAs(other);
|
||||
return [...other.entries()].every(([key, val]) => this.keyValuePairIsPresentIn(key, val, this));
|
||||
}
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns a new map whose keys are the union of keys between `this` and `other` maps.
|
||||
*
|
||||
* NOTE: If both maps prescribe the same key, the key-value pair from `this` will be retained.
|
||||
*/
|
||||
union(other) {
|
||||
this.validateUsingSameOptionsAs(other);
|
||||
return new DeepMap([...other.entries(), ...this.entries()], this.options);
|
||||
}
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns a new map containing all key-value pairs in `this` that are also present in `other`.
|
||||
*/
|
||||
intersection(other) {
|
||||
this.validateUsingSameOptionsAs(other);
|
||||
const intersectingPairs = [...this.entries()].filter(([key, val]) => this.keyValuePairIsPresentIn(key, val, other));
|
||||
return new DeepMap(intersectingPairs, this.options);
|
||||
}
|
||||
/**
|
||||
* @param other the map to compare against
|
||||
* @returns a new map containing all key-value pairs in `this` that are not present in `other`.
|
||||
*/
|
||||
difference(other) {
|
||||
this.validateUsingSameOptionsAs(other);
|
||||
const differencePairs = [...this.entries()].filter(([key, val]) => !this.keyValuePairIsPresentIn(key, val, other));
|
||||
return new DeepMap(differencePairs, this.options);
|
||||
}
|
||||
// PRIVATE/PROTECTED METHODS FOLLOW...
|
||||
normalizeKey(input) {
|
||||
return this.normalizer.normalizeKey(input);
|
||||
}
|
||||
normalizeValue(input) {
|
||||
return this.normalizer.normalizeValue(input);
|
||||
}
|
||||
validateUsingSameOptionsAs(other) {
|
||||
if (this.normalizer.getOptionsChecksum() !== other['normalizer'].getOptionsChecksum()) {
|
||||
throw new errors_1.DeepEqualityDataStructuresError('Structures must use same options for Comparable interface operations');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns true if the key is present in the provided map w/ the specified value
|
||||
*/
|
||||
keyValuePairIsPresentIn(key, val, mapToCheck) {
|
||||
const checkVal = mapToCheck.get(key);
|
||||
return checkVal !== undefined && this.normalizeValue(checkVal) === this.normalizeValue(val);
|
||||
}
|
||||
}
|
||||
exports.DeepMap = DeepMap;
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { Options } from './options';
|
||||
/**
|
||||
* Result of object-hash hashing function
|
||||
*/
|
||||
type HashedObject = string;
|
||||
/**
|
||||
* Type for normalized input.
|
||||
*/
|
||||
export type Normalized<T> = HashedObject | T;
|
||||
/**
|
||||
* Class that normalizes object types to strings via hashing
|
||||
*/
|
||||
export declare class Normalizer<K, V, TxK, TxV> {
|
||||
private readonly objectHashOptions;
|
||||
private readonly caseInsensitive;
|
||||
private readonly keyTransformer;
|
||||
private readonly valueTransformer;
|
||||
private readonly optionsChecksum;
|
||||
constructor(options?: Options<K, V, TxK, TxV>);
|
||||
/**
|
||||
* @returns the checksum for the options passed to this Normalizer
|
||||
*/
|
||||
getOptionsChecksum(): string;
|
||||
/**
|
||||
* Normalize the input by transforming and then hashing the result (if an object)
|
||||
* @param input the input to normalize
|
||||
* @returns the normalized result
|
||||
*/
|
||||
normalizeKey(input: K): Normalized<TxK>;
|
||||
/**
|
||||
* Normalize the input by transforming and then hashing the result (if an object)
|
||||
* @param input the input to normalize
|
||||
* @returns the normalized result
|
||||
*/
|
||||
normalizeValue(input: V): Normalized<TxV>;
|
||||
private normalizeHelper;
|
||||
/**
|
||||
* Returns true if the input is a javascript object.
|
||||
*/
|
||||
private static isObject;
|
||||
}
|
||||
export {};
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Normalizer = void 0;
|
||||
const object_hash_1 = __importDefault(require("object-hash"));
|
||||
const options_1 = require("./options");
|
||||
const transformers_1 = require("./transformers");
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Class that normalizes object types to strings via hashing
|
||||
*/
|
||||
class Normalizer {
|
||||
constructor(options = {}) {
|
||||
this.optionsChecksum = (0, object_hash_1.default)(options);
|
||||
const _a = (0, options_1.getOptionsWithDefaults)(options), { transformer, mapValueTransformer, useToJsonTransform, caseInsensitive } = _a, objectHashOptions = __rest(_a, ["transformer", "mapValueTransformer", "useToJsonTransform", "caseInsensitive"]);
|
||||
this.objectHashOptions = objectHashOptions;
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
this.keyTransformer = useToJsonTransform
|
||||
? (0, utils_1.chain)([transformers_1.Transformers.jsonSerializeDeserialize, transformer])
|
||||
: transformer;
|
||||
this.valueTransformer = useToJsonTransform
|
||||
? (0, utils_1.chain)([transformers_1.Transformers.jsonSerializeDeserialize, mapValueTransformer])
|
||||
: mapValueTransformer;
|
||||
if (caseInsensitive) {
|
||||
// NOTE: This block ensures case-insensitivity inside objects only.
|
||||
// See normalizeHelper() for logic which handles primitive strings
|
||||
const caseInsensitiveReplacer = (val) => typeof val === 'string' ? val.toLowerCase() : val;
|
||||
const { replacer } = this.objectHashOptions;
|
||||
this.objectHashOptions.replacer = replacer
|
||||
? (0, utils_1.chain)([caseInsensitiveReplacer, replacer])
|
||||
: caseInsensitiveReplacer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @returns the checksum for the options passed to this Normalizer
|
||||
*/
|
||||
getOptionsChecksum() {
|
||||
return this.optionsChecksum;
|
||||
}
|
||||
/**
|
||||
* Normalize the input by transforming and then hashing the result (if an object)
|
||||
* @param input the input to normalize
|
||||
* @returns the normalized result
|
||||
*/
|
||||
normalizeKey(input) {
|
||||
return this.normalizeHelper(this.keyTransformer(input));
|
||||
}
|
||||
/**
|
||||
* Normalize the input by transforming and then hashing the result (if an object)
|
||||
* @param input the input to normalize
|
||||
* @returns the normalized result
|
||||
*/
|
||||
normalizeValue(input) {
|
||||
return this.normalizeHelper(this.valueTransformer(input));
|
||||
}
|
||||
normalizeHelper(input) {
|
||||
if (Normalizer.isObject(input)) {
|
||||
return (0, object_hash_1.default)(input, this.objectHashOptions);
|
||||
}
|
||||
else if (this.caseInsensitive && typeof input === 'string') {
|
||||
return input.toLowerCase();
|
||||
}
|
||||
else {
|
||||
// Primitive value, don't hash
|
||||
return input;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if the input is a javascript object.
|
||||
*/
|
||||
static isObject(input) {
|
||||
return typeof input === 'object' && input !== null;
|
||||
}
|
||||
}
|
||||
exports.Normalizer = Normalizer;
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { NormalOption as ObjectHashOptions } from 'object-hash';
|
||||
import { TransformFunction } from './transformers';
|
||||
import { Require } from './utils';
|
||||
/**
|
||||
* Library options
|
||||
*/
|
||||
interface DeepEqualityDataStructuresOptions<K, V, TxK, TxV> {
|
||||
/**
|
||||
* A function that transforms Map keys or Set values prior to normalization.
|
||||
*
|
||||
* NOTE: The caller is responsible for not mutating object inputs.
|
||||
*/
|
||||
transformer?: TransformFunction<K, TxK>;
|
||||
/**
|
||||
* A function that transforms Map values prior to normalization.
|
||||
*
|
||||
* NOTE: The caller is responsible for not mutating object inputs.
|
||||
*/
|
||||
mapValueTransformer?: TransformFunction<V, TxV>;
|
||||
/**
|
||||
* If true, objects will be JSON-serialized/deserialized into "plain" objects prior to hashing.
|
||||
*/
|
||||
useToJsonTransform?: boolean;
|
||||
/**
|
||||
* If true, all string values (including keys/values within objects and arrays) will use case-insensitive equality comparisons.
|
||||
*/
|
||||
caseInsensitive?: boolean;
|
||||
}
|
||||
export type Options<K, V, TxK, TxV> = ObjectHashOptions & DeepEqualityDataStructuresOptions<K, V, TxK, TxV>;
|
||||
/**
|
||||
* Given the specified options, resolve default values as appropriate.
|
||||
*/
|
||||
export declare function getOptionsWithDefaults<K, V, TxK, TxV>(options: Options<K, V, TxK, TxV>): Require<Options<K, V, TxK, TxV>, keyof DeepEqualityDataStructuresOptions<K, V, TxK, TxV>>;
|
||||
export {};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getOptionsWithDefaults = getOptionsWithDefaults;
|
||||
const transformers_1 = require("./transformers");
|
||||
/**
|
||||
* Given the specified options, resolve default values as appropriate.
|
||||
*/
|
||||
function getOptionsWithDefaults(options) {
|
||||
return Object.assign({
|
||||
// Default options
|
||||
algorithm: 'md5', transformer: transformers_1.Transformers.identity, mapValueTransformer: transformers_1.Transformers.identity, useToJsonTransform: false, caseInsensitive: false }, options);
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { Comparable } from './comparable';
|
||||
import { Options } from './options';
|
||||
/**
|
||||
* A Set implementation that supports deep equality for values.
|
||||
*/
|
||||
export declare class DeepSet<V, TxV = V> extends Set<V> implements Comparable<DeepSet<V, TxV>> {
|
||||
private options?;
|
||||
private readonly map;
|
||||
/**
|
||||
* @param values optional list of values to initialize the set
|
||||
* @param options configuration options
|
||||
*/
|
||||
constructor(values?: readonly V[] | null, options?: Options<V, null, TxV, null> | undefined);
|
||||
/**
|
||||
* Getter for number of elements in the set.
|
||||
* @inheritdoc
|
||||
*/
|
||||
get size(): number;
|
||||
/**
|
||||
* Returns true if the given value is present in the set.
|
||||
* @inheritdoc
|
||||
*/
|
||||
has(val: V): boolean;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
add(val: V): this;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
delete(val: V): boolean;
|
||||
/**
|
||||
* Clear all values from the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
forEach(callbackfn: (val: V, val2: V, set: Set<V>) => void): void;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
[Symbol.iterator](): IterableIterator<V>;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
entries(): IterableIterator<[V, V]>;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
keys(): IterableIterator<V>;
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
values(): IterableIterator<V>;
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns true if the values of `other` are the same as this set
|
||||
*/
|
||||
equals(other: this): boolean;
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns true if the values of `other` are all contained in this set
|
||||
*/
|
||||
contains(other: this): boolean;
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns a new set whose values are the union of `this` and `other`.
|
||||
*/
|
||||
union(other: this): DeepSet<V, TxV>;
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns a new set containing all values in `this` that are also in `other`.
|
||||
*/
|
||||
intersection(other: this): DeepSet<V, TxV>;
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns a new set containing all values in `this` that are not also in `other`.
|
||||
*/
|
||||
difference(other: this): DeepSet<V, TxV>;
|
||||
private getSetFromMapKeys;
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DeepSet = void 0;
|
||||
const map_1 = require("./map");
|
||||
/**
|
||||
* A Set implementation that supports deep equality for values.
|
||||
*/
|
||||
class DeepSet extends Set {
|
||||
// NOTE: This is actually a thin wrapper. We're not using super other than to drive the (typed) API contract.
|
||||
/**
|
||||
* @param values optional list of values to initialize the set
|
||||
* @param options configuration options
|
||||
*/
|
||||
constructor(values, options) {
|
||||
super();
|
||||
this.options = options;
|
||||
const transformedEntries = values ? values.map((el) => [el, null]) : null;
|
||||
this.map = new map_1.DeepMap(transformedEntries, options);
|
||||
}
|
||||
/**
|
||||
* Getter for number of elements in the set.
|
||||
* @inheritdoc
|
||||
*/
|
||||
get size() {
|
||||
return this.map.size;
|
||||
}
|
||||
/**
|
||||
* Returns true if the given value is present in the set.
|
||||
* @inheritdoc
|
||||
*/
|
||||
has(val) {
|
||||
return this.map.has(val);
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
add(val) {
|
||||
this.map.set(val, null);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
delete(val) {
|
||||
return this.map.delete(val);
|
||||
}
|
||||
/**
|
||||
* Clear all values from the map.
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
forEach(callbackfn) {
|
||||
this.map.forEach((_mapVal, mapKey, _map) => {
|
||||
callbackfn(mapKey, mapKey, this);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
*[Symbol.iterator]() {
|
||||
for (const [key, _val] of this.map[Symbol.iterator]()) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
*entries() {
|
||||
for (const val of this[Symbol.iterator]()) {
|
||||
yield [val, val];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
*keys() {
|
||||
for (const val of this[Symbol.iterator]()) {
|
||||
yield val;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
*values() {
|
||||
yield* this.keys();
|
||||
}
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns true if the values of `other` are the same as this set
|
||||
*/
|
||||
equals(other) {
|
||||
return this.map.equals(other['map']);
|
||||
}
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns true if the values of `other` are all contained in this set
|
||||
*/
|
||||
contains(other) {
|
||||
return this.map.contains(other['map']);
|
||||
}
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns a new set whose values are the union of `this` and `other`.
|
||||
*/
|
||||
union(other) {
|
||||
return this.getSetFromMapKeys(this.map.union(other['map']));
|
||||
}
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns a new set containing all values in `this` that are also in `other`.
|
||||
*/
|
||||
intersection(other) {
|
||||
return this.getSetFromMapKeys(this.map.intersection(other['map']));
|
||||
}
|
||||
/**
|
||||
* @param other the set to compare against
|
||||
* @returns a new set containing all values in `this` that are not also in `other`.
|
||||
*/
|
||||
difference(other) {
|
||||
return this.getSetFromMapKeys(this.map.difference(other['map']));
|
||||
}
|
||||
getSetFromMapKeys(map) {
|
||||
return new DeepSet([...map.keys()], this.options);
|
||||
}
|
||||
}
|
||||
exports.DeepSet = DeepSet;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export type TransformFunction<T, R> = (input: T) => R;
|
||||
export declare class Transformers {
|
||||
static identity<T, R = T>(input: T): R;
|
||||
static jsonSerializeDeserialize<T, R = T>(obj: T): R;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Transformers = void 0;
|
||||
class Transformers {
|
||||
static identity(input) {
|
||||
// Just make the types happy :)
|
||||
return input;
|
||||
}
|
||||
static jsonSerializeDeserialize(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
}
|
||||
exports.Transformers = Transformers;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Require the keys K from T if optional.
|
||||
*/
|
||||
export type Require<T, K extends keyof T> = T & {
|
||||
[P in K]-?: T[P];
|
||||
};
|
||||
/**
|
||||
* Format an unknown value to a string for display
|
||||
*/
|
||||
export declare function stringify(value: unknown): string;
|
||||
/**
|
||||
* Chain a list of functions
|
||||
* @returns a function that accepts the args of the first function in the functions list. Each subsequent function is invoked with
|
||||
* the return value of the previous function.
|
||||
*/
|
||||
export declare function chain<TArgs extends any[], T1>(functions: [(...args: TArgs) => T1]): (...args: TArgs) => T1;
|
||||
export declare function chain<TArgs extends any[], T1, T2>(functions: [(...args: TArgs) => T1, (arg: T1) => T2]): (...args: TArgs) => T2;
|
||||
export declare function chain<TArgs extends any[], T1, T2, T3>(functions: [(...args: TArgs) => T1, (arg: T1) => T2, (arg: T2) => T3]): (...args: TArgs) => T3;
|
||||
export declare function chain<TArgs extends any[], T1, T2, T3, T4>(functions: [(...args: TArgs) => T1, (arg: T1) => T2, (arg: T2) => T3, (arg: T3) => T4]): (...args: TArgs) => T4;
|
||||
export declare function chain<TArgs extends any[], T1, T2, T3, T4, T5>(functions: [(...args: TArgs) => T1, (arg: T1) => T2, (arg: T2) => T3, (arg: T3) => T4, (arg: T4) => T5]): (...args: TArgs) => T5;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.stringify = stringify;
|
||||
exports.chain = chain;
|
||||
/**
|
||||
* Format an unknown value to a string for display
|
||||
*/
|
||||
function stringify(value) {
|
||||
if (value !== null && typeof value === 'object') {
|
||||
// For objects, defer an overridden toString, otherwise use JSON.stringify
|
||||
return 'toString' in value && ![Object.prototype.toString, Array.prototype.toString].includes(value.toString)
|
||||
? value.toString()
|
||||
: JSON.stringify(value);
|
||||
}
|
||||
else {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
function chain(functions) {
|
||||
const [head, ...tail] = functions;
|
||||
return (...args) => {
|
||||
return tail.reduce((acc, fn) => fn(acc), head(...args));
|
||||
};
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "deep-equality-data-structures",
|
||||
"version": "2.0.0",
|
||||
"description": "Javascript data structures (e.g., Map, Set) that support deep object equality",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"/dist"
|
||||
],
|
||||
"scripts": {
|
||||
"prepare": "husky && npm run compile",
|
||||
"_lint": "eslint --fix",
|
||||
"_lint:check": "eslint",
|
||||
"_format": " prettier --write --ignore-unknown",
|
||||
"_format:check": "prettier --check --ignore-unknown",
|
||||
"fix": "npm run _lint . && npm run _format .",
|
||||
"check": "npm run _lint:check . && npm run _format:check .",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "npm run compile -- -watch",
|
||||
"test": "jest",
|
||||
"pretest:ci": "npm run check",
|
||||
"test:ci": "npm test"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.ts": "npm run _lint -- --cache",
|
||||
"*": "npm run _format"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/adamhamlin/deep-equality-data-structures.git"
|
||||
},
|
||||
"keywords": [
|
||||
"deep equality",
|
||||
"deep",
|
||||
"equality",
|
||||
"map",
|
||||
"set",
|
||||
"data structure"
|
||||
],
|
||||
"author": "Adam C Hamlin <achamlin@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/adamhamlin/deep-equality-data-structures/issues"
|
||||
},
|
||||
"homepage": "https://github.com/adamhamlin/deep-equality-data-structures#readme",
|
||||
"dependencies": {
|
||||
"object-hash": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@adamhamlin/eslint-config": "^1.4.1",
|
||||
"@tsconfig/recommended": "^1.0.7",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/object-hash": "^3.0.6",
|
||||
"husky": "^9.1.5",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user