Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@finnair/path

Package Overview
Dependencies
Maintainers
0
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@finnair/path - npm Package Compare versions

Comparing version 5.4.0 to 6.0.0

10

CHANGELOG.md

@@ -6,2 +6,12 @@ # Change Log

# [6.0.0](https://github.com/finnair/v-validation/compare/v5.4.0...v6.0.0) (2024-09-16)
### Features
- @finnair/diff package with Diff and VersionInfo ([#111](https://github.com/finnair/v-validation/issues/111)) ([3b26d49](https://github.com/finnair/v-validation/commit/3b26d49b63851fbcfce9b15efc53ad5418ae4de4))
### BREAKING CHANGES
- More general and efficient PathMatcher API.
# [5.4.0](https://github.com/finnair/v-validation/compare/v5.3.0...v5.4.0) (2024-05-08)

@@ -8,0 +18,0 @@

3

dist/cjs/matchers.d.ts

@@ -13,6 +13,5 @@ import { Path, PathComponent } from './Path.js';

export declare function isPathExpression(component: PathComponent | PathExpression): component is PathExpression;
export declare class Node {
export interface Node {
readonly path: Path;
readonly value: any;
constructor(path: Path, value: any);
}

@@ -19,0 +18,0 @@ export declare class IndexMatcher implements PathExpression {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnyProperty = exports.AnyIndex = exports.UnionMatcher = exports.PropertyMatcher = exports.IndexMatcher = exports.Node = exports.isPathExpression = void 0;
exports.AnyProperty = exports.AnyIndex = exports.UnionMatcher = exports.PropertyMatcher = exports.IndexMatcher = exports.isPathExpression = void 0;
const Path_js_1 = require("./Path.js");

@@ -9,11 +9,2 @@ function isPathExpression(component) {

exports.isPathExpression = isPathExpression;
class Node {
path;
value;
constructor(path, value) {
this.path = path;
this.value = value;
}
}
exports.Node = Node;
class IndexMatcher {

@@ -20,0 +11,0 @@ index;

import { Path, PathComponent } from './Path.js';
import { Node, PathExpression, PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher } from './matchers.js';
export interface ResultCollector {
/**
* Collect a matching path and value. Return true to continue matching or false to stop.
*/
(path: Path, value: any): boolean;
}
export declare class PathMatcher {

@@ -7,8 +13,27 @@ private readonly expressions;

private constructor();
find(root: any, first?: boolean): Node[];
findFirst(root: any): undefined | Node;
findValues(root: any, first?: boolean): any[];
findFirstValue(root: any): any;
find(root: any, collector: ResultCollector): void;
findAll(root: any, acceptUndefined?: boolean): Node[];
findFirst(root: any, acceptUndefined?: boolean): undefined | Node;
findValues(root: any, acceptUndefined?: boolean): any[];
findFirstValue(root: any, acceptUndefined?: boolean): any;
/**
* Exact match: path length must match the number of expressions and all expressions must match. Only sibling paths match.
*
* @param path
* @returns true if path is an exact match to expressions
*/
match(path: Path): boolean;
/**
* Prefix match: path length must be equal or longer than the number of expressions and all expressions must match. All sibling and child paths match.
*
* @param path
* @returns true the start the path matches
*/
prefixMatch(path: Path): boolean;
/**
* Partial match: path length can be less than or more than the number of expressions, but all corresponding expressions must match. All parent, sibling and child paths match.
*
* @param path
* @returns true if all path components match
*/
partialMatch(path: Path): boolean;

@@ -15,0 +40,0 @@ toJSON(): string;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnionMatcher = exports.Node = exports.IndexMatcher = exports.PropertyMatcher = exports.AnyProperty = exports.AnyIndex = exports.PathMatcher = void 0;
exports.UnionMatcher = exports.IndexMatcher = exports.PropertyMatcher = exports.AnyProperty = exports.AnyIndex = exports.PathMatcher = void 0;
const Path_js_1 = require("./Path.js");
const matchers_js_1 = require("./matchers.js");
Object.defineProperty(exports, "Node", { enumerable: true, get: function () { return matchers_js_1.Node; } });
Object.defineProperty(exports, "PropertyMatcher", { enumerable: true, get: function () { return matchers_js_1.PropertyMatcher; } });

@@ -23,11 +22,10 @@ Object.defineProperty(exports, "IndexMatcher", { enumerable: true, get: function () { return matchers_js_1.IndexMatcher; } });

}
find(root, first) {
find(root, collector) {
if (this.expressions.length === 0) {
return [new matchers_js_1.Node(Path_js_1.Path.ROOT, root)];
collector(Path_js_1.Path.ROOT, root);
}
if (typeof root !== 'object') {
return [];
return;
}
const currentPath = [];
const results = [];
const handlers = [];

@@ -39,3 +37,2 @@ for (let i = 0; i < this.expressions.length - 1; i++) {

this.expressions[0].find(root, handlers[0]);
return results;
function intermediateHandler(index, expressions) {

@@ -49,40 +46,56 @@ return (value, component) => {

return (value, component) => {
results.push(new matchers_js_1.Node(Path_js_1.Path.of(...currentPath, component), value));
return !first;
return collector(Path_js_1.Path.of(...currentPath, component), value);
};
}
}
findFirst(root) {
return this.find(root, true)[0];
findAll(root, acceptUndefined) {
const results = [];
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
results.push({ path, value });
return true;
}
return true;
});
return results;
}
findValues(root, first) {
if (this.expressions.length === 0) {
return [root];
}
if (typeof root !== 'object') {
return [];
}
findFirst(root, acceptUndefined) {
let result = undefined;
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
result = { path, value };
return false;
}
return true;
});
return result;
}
findValues(root, acceptUndefined) {
const results = [];
const handlers = [];
for (let i = 0; i < this.expressions.length - 1; i++) {
handlers[i] = intermediateHandler(i, this.expressions);
}
handlers[this.expressions.length - 1] = resultHandler();
this.expressions[0].find(root, handlers[0]);
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
results.push(value);
return true;
}
return true;
});
return results;
function intermediateHandler(index, expressions) {
return (value) => {
return expressions[index + 1].find(value, handlers[index + 1]);
};
}
function resultHandler() {
return (value) => {
results.push(value);
return !first;
};
}
}
findFirstValue(root) {
return this.findValues(root, true)[0];
findFirstValue(root, acceptUndefined) {
let result = undefined;
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
result = value;
return false;
}
return true;
});
return result;
}
/**
* Exact match: path length must match the number of expressions and all expressions must match. Only sibling paths match.
*
* @param path
* @returns true if path is an exact match to expressions
*/
match(path) {

@@ -92,4 +105,3 @@ if (path.length !== this.expressions.length) {

}
let index = 0;
for (; index < this.expressions.length; index++) {
for (let index = 0; index < this.expressions.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {

@@ -101,2 +113,8 @@ return false;

}
/**
* Prefix match: path length must be equal or longer than the number of expressions and all expressions must match. All sibling and child paths match.
*
* @param path
* @returns true the start the path matches
*/
prefixMatch(path) {

@@ -106,4 +124,3 @@ if (path.length < this.expressions.length) {

}
let index = 0;
for (; index < this.expressions.length; index++) {
for (let index = 0; index < this.expressions.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {

@@ -115,5 +132,10 @@ return false;

}
/**
* Partial match: path length can be less than or more than the number of expressions, but all corresponding expressions must match. All parent, sibling and child paths match.
*
* @param path
* @returns true if all path components match
*/
partialMatch(path) {
let index = 0;
for (; index < this.expressions.length && index < path.length; index++) {
for (let index = 0; index < this.expressions.length && index < path.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {

@@ -120,0 +142,0 @@ return false;

@@ -70,6 +70,6 @@ "use strict";

function include(input, matcher, output) {
matcher.find(input).forEach(node => node.path.set(output, node.value));
matcher.find(input, (path, value) => path.set(output, value));
}
function exclude(output, matcher) {
matcher.find(output).forEach(node => node.path.unset(output));
matcher.find(output, (path) => path.unset(output));
}

@@ -76,0 +76,0 @@ function jsonClone(value) {

@@ -13,6 +13,5 @@ import { Path, PathComponent } from './Path.js';

export declare function isPathExpression(component: PathComponent | PathExpression): component is PathExpression;
export declare class Node {
export interface Node {
readonly path: Path;
readonly value: any;
constructor(path: Path, value: any);
}

@@ -19,0 +18,0 @@ export declare class IndexMatcher implements PathExpression {

@@ -5,10 +5,2 @@ import { Path } from './Path.js';

}
export class Node {
path;
value;
constructor(path, value) {
this.path = path;
this.value = value;
}
}
export class IndexMatcher {

@@ -15,0 +7,0 @@ index;

import { Path, PathComponent } from './Path.js';
import { Node, PathExpression, PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher } from './matchers.js';
export interface ResultCollector {
/**
* Collect a matching path and value. Return true to continue matching or false to stop.
*/
(path: Path, value: any): boolean;
}
export declare class PathMatcher {

@@ -7,8 +13,27 @@ private readonly expressions;

private constructor();
find(root: any, first?: boolean): Node[];
findFirst(root: any): undefined | Node;
findValues(root: any, first?: boolean): any[];
findFirstValue(root: any): any;
find(root: any, collector: ResultCollector): void;
findAll(root: any, acceptUndefined?: boolean): Node[];
findFirst(root: any, acceptUndefined?: boolean): undefined | Node;
findValues(root: any, acceptUndefined?: boolean): any[];
findFirstValue(root: any, acceptUndefined?: boolean): any;
/**
* Exact match: path length must match the number of expressions and all expressions must match. Only sibling paths match.
*
* @param path
* @returns true if path is an exact match to expressions
*/
match(path: Path): boolean;
/**
* Prefix match: path length must be equal or longer than the number of expressions and all expressions must match. All sibling and child paths match.
*
* @param path
* @returns true the start the path matches
*/
prefixMatch(path: Path): boolean;
/**
* Partial match: path length can be less than or more than the number of expressions, but all corresponding expressions must match. All parent, sibling and child paths match.
*
* @param path
* @returns true if all path components match
*/
partialMatch(path: Path): boolean;

@@ -15,0 +40,0 @@ toJSON(): string;

import { Path } from './Path.js';
import { Node, PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher, isPathExpression } from './matchers.js';
import { PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher, isPathExpression } from './matchers.js';
export class PathMatcher {

@@ -14,11 +14,10 @@ expressions;

}
find(root, first) {
find(root, collector) {
if (this.expressions.length === 0) {
return [new Node(Path.ROOT, root)];
collector(Path.ROOT, root);
}
if (typeof root !== 'object') {
return [];
return;
}
const currentPath = [];
const results = [];
const handlers = [];

@@ -30,3 +29,2 @@ for (let i = 0; i < this.expressions.length - 1; i++) {

this.expressions[0].find(root, handlers[0]);
return results;
function intermediateHandler(index, expressions) {

@@ -40,40 +38,56 @@ return (value, component) => {

return (value, component) => {
results.push(new Node(Path.of(...currentPath, component), value));
return !first;
return collector(Path.of(...currentPath, component), value);
};
}
}
findFirst(root) {
return this.find(root, true)[0];
findAll(root, acceptUndefined) {
const results = [];
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
results.push({ path, value });
return true;
}
return true;
});
return results;
}
findValues(root, first) {
if (this.expressions.length === 0) {
return [root];
}
if (typeof root !== 'object') {
return [];
}
findFirst(root, acceptUndefined) {
let result = undefined;
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
result = { path, value };
return false;
}
return true;
});
return result;
}
findValues(root, acceptUndefined) {
const results = [];
const handlers = [];
for (let i = 0; i < this.expressions.length - 1; i++) {
handlers[i] = intermediateHandler(i, this.expressions);
}
handlers[this.expressions.length - 1] = resultHandler();
this.expressions[0].find(root, handlers[0]);
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
results.push(value);
return true;
}
return true;
});
return results;
function intermediateHandler(index, expressions) {
return (value) => {
return expressions[index + 1].find(value, handlers[index + 1]);
};
}
function resultHandler() {
return (value) => {
results.push(value);
return !first;
};
}
}
findFirstValue(root) {
return this.findValues(root, true)[0];
findFirstValue(root, acceptUndefined) {
let result = undefined;
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
result = value;
return false;
}
return true;
});
return result;
}
/**
* Exact match: path length must match the number of expressions and all expressions must match. Only sibling paths match.
*
* @param path
* @returns true if path is an exact match to expressions
*/
match(path) {

@@ -83,4 +97,3 @@ if (path.length !== this.expressions.length) {

}
let index = 0;
for (; index < this.expressions.length; index++) {
for (let index = 0; index < this.expressions.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {

@@ -92,2 +105,8 @@ return false;

}
/**
* Prefix match: path length must be equal or longer than the number of expressions and all expressions must match. All sibling and child paths match.
*
* @param path
* @returns true the start the path matches
*/
prefixMatch(path) {

@@ -97,4 +116,3 @@ if (path.length < this.expressions.length) {

}
let index = 0;
for (; index < this.expressions.length; index++) {
for (let index = 0; index < this.expressions.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {

@@ -106,5 +124,10 @@ return false;

}
/**
* Partial match: path length can be less than or more than the number of expressions, but all corresponding expressions must match. All parent, sibling and child paths match.
*
* @param path
* @returns true if all path components match
*/
partialMatch(path) {
let index = 0;
for (; index < this.expressions.length && index < path.length; index++) {
for (let index = 0; index < this.expressions.length && index < path.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {

@@ -135,2 +158,2 @@ return false;

}
export { AnyIndex, AnyProperty, PropertyMatcher, IndexMatcher, Node, UnionMatcher };
export { AnyIndex, AnyProperty, PropertyMatcher, IndexMatcher, UnionMatcher };

@@ -65,6 +65,6 @@ import { PathMatcher } from './PathMatcher.js';

function include(input, matcher, output) {
matcher.find(input).forEach(node => node.path.set(output, node.value));
matcher.find(input, (path, value) => path.set(output, value));
}
function exclude(output, matcher) {
matcher.find(output).forEach(node => node.path.unset(output));
matcher.find(output, (path) => path.unset(output));
}

@@ -71,0 +71,0 @@ function jsonClone(value) {

{
"name": "@finnair/path",
"version": "5.4.0",
"version": "6.0.0",
"private": false,

@@ -40,3 +40,3 @@ "description": "Simple object path as array of strings and numbers",

},
"gitHead": "8c3d3213f21cef7c27ed0d70bf8596d484a3d9b1"
"gitHead": "c1075c7dc35e540949abc4e220547e1146523682"
}

@@ -13,3 +13,3 @@ # Path

Install v-validation using [`yarn`](https://yarnpkg.com/en/package/jest):
Install v-validation using [`yarn`](https://yarnpkg.com):

@@ -28,45 +28,10 @@ ```bash

### Report Validation Errors
### Validation
Path can be used to point a location of invalid value (see [`v-validation`](../core/README.md). Path's immutability and fluent API makes it easy and safe to use.
### Analyze Changes
### Diff, Versioning & Patching
Path can be used to report changes made to an object:
Analyze changes and trigger logic based on what has changed (see [`diff`](../diff/README.md).
```typescript
// NOTE: This is pseudocode!
const originalValue = fetchResource(); // No need for long lasting, e.g. optimistic, locking
const updatedValue = await doUpdate(originalValue); // Edit in e.g. UI
// Map from Path to new value
const changes: Map<Path, any> = analyzeChanges(updatedValue, originalValue);
// analyzeChanges implementation is not in this library's scope
```
### Detect Interesting Changes
In message based systems, consumers may be interested only in specific changes. Once a change is analyzed, `PathMatcher` can be used to check if it's of interest to a particular consumer.
```typescript
const subscription = [PathMatcher.of('interestingProperty'), PathMatcher.of('interestingArray', AnyIndex, 'someProperty')];
const isInteresting = Array.from(changes.keys()).some(path => subscription.some(pathMatcher => pathMatcher.prefixMatch(path)));
```
### Apply Changes (Patch)
Changes can also be applied to another (newer) version of the same object safely. This is important in systems where there
```typescript
// Fetch and lock (pessimistic or optimistic) latest value for the duration of the actual update
const latestValue = fetchAndLockResource();
changes.forEach((newValue, path) => {
path.set(latestValue, newValue); // Unsets value if newValue is undefined
});
updateResource(latestValue);
```
NOTE: Updating an array with `Path.set` will truncate possible undefined values from the end of the array. This allows
removing trailing elements without leaving undefined elements in place.
### Include/Exclude Projection

@@ -73,0 +38,0 @@

Sorry, the diff of this file is not supported yet

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