@finnair/path 3.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,36 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.0.0](https://github.com/finnair/v-validation/compare/v4.3.0...v5.0.0) (2023-10-13)
7
+
8
+
9
+ ### Features
10
+
11
+ * use ECMAScript modules (ESM) instead of CommonJS ([#95](https://github.com/finnair/v-validation/issues/95)) ([92e9118](https://github.com/finnair/v-validation/commit/92e9118235957ec4bc2bcf2de73e195ea940378c))
12
+
13
+
14
+
15
+
16
+
17
+ # [5.0.0](https://github.com/finnair/v-validation/compare/v4.3.0...v5.0.0) (2023-10-13)
18
+
19
+
20
+ ### Features
21
+
22
+ * use ECMAScript modules (ESM) instead of CommonJS ([#95](https://github.com/finnair/v-validation/issues/95)) ([92e9118](https://github.com/finnair/v-validation/commit/92e9118235957ec4bc2bcf2de73e195ea940378c))
23
+
24
+
25
+
26
+
27
+
28
+ # [4.0.0](https://github.com/finnair/v-validation/compare/v3.2.0...v4.0.0) (2022-11-07)
29
+
30
+ **Note:** Version bump only for package @finnair/path
31
+
32
+
33
+
34
+
35
+
6
36
  # [3.1.0](https://github.com/finnair/v-validation/compare/v3.0.0...v3.1.0) (2022-10-24)
7
37
 
8
38
  **Note:** Version bump only for package @finnair/path
package/README.md CHANGED
@@ -64,6 +64,9 @@ changes.forEach((newValue, path) => {
64
64
  updateResource(latestValue);
65
65
  ```
66
66
 
67
+ NOTE: Updating an array with `Path.set` will truncate possible undefined values from the end of the array. This allows
68
+ removing trailing elements without leaving undefined elements in place.
69
+
67
70
  ### Include/Exclude Projection
68
71
 
69
72
  While GraphQL is all about projections, something similar can also be implemented in a REST API with include/exclude parameters. `Projection` and `parsePathMatcher` function provides means to process results safely based on such a user input. This is, of course, very simplified projection compared to what GraphQL has to offer, but it's also... well, simpler.
@@ -92,9 +95,7 @@ Path.of(); // Root object $ - also Path.ROOT
92
95
  Path.of('array', 1, 'property'); // $.array[1].property
93
96
 
94
97
  // Constructing with fluent syntax
95
- Path.of()
96
- .property('array')
97
- .index(0); // $.array[0]
98
+ Path.of().property('array').index(0); // $.array[0]
98
99
 
99
100
  // Concatenating Paths
100
101
  Path.of('parent').concat(Path.of('child', 'name')); // $.parent.child.name
@@ -119,6 +120,9 @@ Path.of('array', 1).unset({ array: [1, 2, 3] }); // { array: [1, undefined, 3] }
119
120
  // ...but unsetting a value doesn't create intermediate objects
120
121
  Path.of('array', 1).unset({}); // {}
121
122
 
123
+ // Trailing undefined elements will be removed from an array (i.e. array is resized)
124
+ Path.of('array', 2).set({ array: [1, undefined, 3] }, undefined); // { array: [1] } where array.length === 1
125
+
122
126
  // toJSON() returns JsonPath compatible serialization
123
127
  Path.of('array', 0, 'property with spaces').toJSON(); // $.array[0]["property with spaces"]
124
128
  ```
package/dist/Path.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare type PathComponent = number | string;
1
+ export type PathComponent = number | string;
2
2
  export declare class Path {
3
3
  static readonly ROOT: Path;
4
4
  private readonly path;
package/dist/Path.js CHANGED
@@ -1,8 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Path = void 0;
4
1
  const identifierPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
5
- class Path {
2
+ export class Path {
3
+ static ROOT = new Path([]);
4
+ path;
6
5
  constructor(path) {
7
6
  this.path = path;
8
7
  Object.freeze(this.path);
@@ -56,22 +55,30 @@ class Path {
56
55
  if (this.path.length === 0) {
57
56
  return value;
58
57
  }
59
- let index = -1;
58
+ let pathIndex = -1;
60
59
  const _root = toObject(root, this.path);
61
60
  let current = _root;
62
- for (index = 0; index < this.path.length - 1 && current; index++) {
63
- const component = this.path[index];
61
+ for (pathIndex = 0; pathIndex < this.path.length - 1 && current; pathIndex++) {
62
+ const component = this.path[pathIndex];
64
63
  const child = toObject(current[component], this.path);
65
64
  current[component] = child;
66
65
  current = child;
67
66
  }
68
67
  if (value === undefined) {
69
68
  if (current !== undefined) {
70
- delete current[this.path[index]];
69
+ delete current[this.path[pathIndex]];
70
+ // Truncate undefined tail of an array
71
+ if (Array.isArray(current)) {
72
+ let i = current.length - 1;
73
+ while (i >= 0 && current[i] === undefined) {
74
+ i--;
75
+ }
76
+ current.length = i + 1;
77
+ }
71
78
  }
72
79
  }
73
80
  else {
74
- current[this.path[index]] = value;
81
+ current[this.path[pathIndex]] = value;
75
82
  }
76
83
  return _root;
77
84
  function toObject(current, path) {
@@ -79,7 +86,7 @@ class Path {
79
86
  return current;
80
87
  }
81
88
  else if (value !== undefined) {
82
- if (typeof path[index + 1] === 'number') {
89
+ if (typeof path[pathIndex + 1] === 'number') {
83
90
  return [];
84
91
  }
85
92
  else {
@@ -152,5 +159,3 @@ class Path {
152
159
  }
153
160
  }
154
161
  }
155
- exports.Path = Path;
156
- Path.ROOT = new Path([]);
@@ -1,5 +1,5 @@
1
- import { Path, PathComponent } from './Path';
2
- import { Node, PathExpression, PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher } from './matchers';
1
+ import { Path, PathComponent } from './Path.js';
2
+ import { Node, PathExpression, PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher } from './matchers.js';
3
3
  export declare class PathMatcher {
4
4
  private readonly expressions;
5
5
  readonly allowGaps: boolean;
@@ -1,15 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UnionMatcher = exports.Node = exports.IndexMatcher = exports.PropertyMatcher = exports.AnyProperty = exports.AnyIndex = exports.PathMatcher = void 0;
4
- const Path_1 = require("./Path");
5
- const matchers_1 = require("./matchers");
6
- Object.defineProperty(exports, "Node", { enumerable: true, get: function () { return matchers_1.Node; } });
7
- Object.defineProperty(exports, "PropertyMatcher", { enumerable: true, get: function () { return matchers_1.PropertyMatcher; } });
8
- Object.defineProperty(exports, "IndexMatcher", { enumerable: true, get: function () { return matchers_1.IndexMatcher; } });
9
- Object.defineProperty(exports, "AnyIndex", { enumerable: true, get: function () { return matchers_1.AnyIndex; } });
10
- Object.defineProperty(exports, "AnyProperty", { enumerable: true, get: function () { return matchers_1.AnyProperty; } });
11
- Object.defineProperty(exports, "UnionMatcher", { enumerable: true, get: function () { return matchers_1.UnionMatcher; } });
12
- class PathMatcher {
1
+ import { Path } from './Path.js';
2
+ import { Node, PropertyMatcher, IndexMatcher, AnyIndex, AnyProperty, UnionMatcher, isPathExpression } from './matchers.js';
3
+ export class PathMatcher {
4
+ expressions;
5
+ allowGaps;
13
6
  constructor(expressions) {
14
7
  this.expressions = expressions;
15
8
  let allowGaps = false;
@@ -20,7 +13,7 @@ class PathMatcher {
20
13
  }
21
14
  find(root, first) {
22
15
  if (this.expressions.length === 0) {
23
- return [new matchers_1.Node(Path_1.Path.ROOT, root)];
16
+ return [new Node(Path.ROOT, root)];
24
17
  }
25
18
  if (typeof root !== 'object') {
26
19
  return [];
@@ -42,7 +35,7 @@ class PathMatcher {
42
35
  }
43
36
  function resultHandler() {
44
37
  return (value, component) => {
45
- results.push(new matchers_1.Node(Path_1.Path.of(...currentPath, component), value));
38
+ results.push(new Node(Path.of(...currentPath, component), value));
46
39
  return !first;
47
40
  };
48
41
  }
@@ -120,16 +113,16 @@ class PathMatcher {
120
113
  return new PathMatcher(path.map(component => {
121
114
  const type = typeof component;
122
115
  if (type === 'number') {
123
- return new matchers_1.IndexMatcher(component);
116
+ return new IndexMatcher(component);
124
117
  }
125
118
  if (type === 'string') {
126
- return new matchers_1.PropertyMatcher(component);
119
+ return new PropertyMatcher(component);
127
120
  }
128
- if ((0, matchers_1.isPathExpression)(component)) {
121
+ if (isPathExpression(component)) {
129
122
  return component;
130
123
  }
131
124
  throw new Error(`Unrecognized PathComponent: ${component} of type ${type}`);
132
125
  }));
133
126
  }
134
127
  }
135
- exports.PathMatcher = PathMatcher;
128
+ export { AnyIndex, AnyProperty, PropertyMatcher, IndexMatcher, Node, UnionMatcher };
@@ -1,5 +1,5 @@
1
- import { PathMatcher } from './PathMatcher';
2
- import { Path } from './Path';
1
+ import { PathMatcher } from './PathMatcher.js';
2
+ import { Path } from './Path.js';
3
3
  export declare class Projection {
4
4
  private readonly includes;
5
5
  private readonly excludes;
@@ -1,8 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.projection = exports.Projection = void 0;
4
- const PathMatcher_1 = require("./PathMatcher");
5
- class Projection {
1
+ import { PathMatcher } from './PathMatcher.js';
2
+ export class Projection {
3
+ includes;
4
+ excludes;
5
+ allowGaps;
6
6
  constructor(includes, excludes, allowGaps) {
7
7
  this.includes = includes;
8
8
  this.excludes = excludes;
@@ -49,14 +49,12 @@ class Projection {
49
49
  return new Projection(includes, excludes, allowGaps);
50
50
  }
51
51
  }
52
- exports.Projection = Projection;
53
- function projection(includes, excludes) {
52
+ export function projection(includes, excludes) {
54
53
  const projection = Projection.of(includes, excludes);
55
54
  return (input) => projection.map(input);
56
55
  }
57
- exports.projection = projection;
58
56
  function validatePathMatcher(value) {
59
- if (value instanceof PathMatcher_1.PathMatcher) {
57
+ if (value instanceof PathMatcher) {
60
58
  return value;
61
59
  }
62
60
  else {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Path, PathComponent } from './Path';
2
- export * from './PathMatcher';
3
- export * from './matchers';
4
- export * from './Projection';
1
+ export { Path, PathComponent } from './Path.js';
2
+ export * from './PathMatcher.js';
3
+ export * from './matchers.js';
4
+ export * from './Projection.js';
package/dist/index.js CHANGED
@@ -1,22 +1,4 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.Path = void 0;
18
- var Path_1 = require("./Path");
19
- Object.defineProperty(exports, "Path", { enumerable: true, get: function () { return Path_1.Path; } });
20
- __exportStar(require("./PathMatcher"), exports);
21
- __exportStar(require("./matchers"), exports);
22
- __exportStar(require("./Projection"), exports);
1
+ export { Path } from './Path.js';
2
+ export * from './PathMatcher.js';
3
+ export * from './matchers.js';
4
+ export * from './Projection.js';
@@ -1,5 +1,5 @@
1
- import { Path, PathComponent } from './Path';
2
- declare type Continue = boolean;
1
+ import { Path, PathComponent } from './Path.js';
2
+ type Continue = boolean;
3
3
  export interface MatchHandler {
4
4
  (value: any, component: PathComponent): Continue;
5
5
  }
package/dist/matchers.js CHANGED
@@ -1,23 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AnyProperty = exports.AnyIndex = exports.UnionMatcher = exports.PropertyMatcher = exports.IndexMatcher = exports.Node = exports.isPathExpression = void 0;
4
- const Path_1 = require("./Path");
5
- function isPathExpression(component) {
1
+ import { Path } from './Path.js';
2
+ export function isPathExpression(component) {
6
3
  return !!component && typeof component.test === 'function' && typeof component.find === 'function';
7
4
  }
8
- exports.isPathExpression = isPathExpression;
9
- class Node {
5
+ export class Node {
6
+ path;
7
+ value;
10
8
  constructor(path, value) {
11
9
  this.path = path;
12
10
  this.value = value;
13
11
  }
14
12
  }
15
- exports.Node = Node;
16
- class IndexMatcher {
13
+ export class IndexMatcher {
14
+ index;
15
+ allowGaps = true;
17
16
  constructor(index) {
18
17
  this.index = index;
19
- this.allowGaps = true;
20
- Path_1.Path.validateIndex(index);
18
+ Path.validateIndex(index);
21
19
  }
22
20
  find(current, callback) {
23
21
  if (Array.isArray(current) && this.index < current.length) {
@@ -29,15 +27,15 @@ class IndexMatcher {
29
27
  return component === this.index;
30
28
  }
31
29
  toString() {
32
- return Path_1.Path.indexToString(this.index);
30
+ return Path.indexToString(this.index);
33
31
  }
34
32
  }
35
- exports.IndexMatcher = IndexMatcher;
36
- class PropertyMatcher {
33
+ export class PropertyMatcher {
34
+ property;
35
+ allowGaps = false;
37
36
  constructor(property) {
38
37
  this.property = property;
39
- this.allowGaps = false;
40
- Path_1.Path.validateProperty(property);
38
+ Path.validateProperty(property);
41
39
  }
42
40
  find(current, callback) {
43
41
  if (typeof current === 'object' && current.hasOwnProperty(this.property)) {
@@ -49,17 +47,17 @@ class PropertyMatcher {
49
47
  return String(component) === this.property;
50
48
  }
51
49
  toString() {
52
- return Path_1.Path.propertyToString(this.property);
50
+ return Path.propertyToString(this.property);
53
51
  }
54
52
  }
55
- exports.PropertyMatcher = PropertyMatcher;
56
- class UnionMatcher {
53
+ export class UnionMatcher {
54
+ components;
57
55
  constructor(components) {
58
56
  this.components = components;
59
57
  if (components.length < 2) {
60
58
  throw new Error('Expected at least 2 properties');
61
59
  }
62
- components.forEach(Path_1.Path.validateComponent);
60
+ components.forEach(Path.validateComponent);
63
61
  }
64
62
  find(current, callback) {
65
63
  if (typeof current === 'object') {
@@ -90,8 +88,7 @@ class UnionMatcher {
90
88
  return JSON.stringify(property);
91
89
  }
92
90
  }
93
- exports.UnionMatcher = UnionMatcher;
94
- exports.AnyIndex = {
91
+ export const AnyIndex = {
95
92
  allowGaps: false,
96
93
  find: (current, callback) => {
97
94
  if (Array.isArray(current)) {
@@ -110,7 +107,7 @@ exports.AnyIndex = {
110
107
  return '[*]';
111
108
  },
112
109
  };
113
- exports.AnyProperty = {
110
+ export const AnyProperty = {
114
111
  allowGaps: false,
115
112
  find: (current, callback) => {
116
113
  if (typeof current === 'object') {
package/package.json CHANGED
@@ -1,10 +1,17 @@
1
1
  {
2
2
  "name": "@finnair/path",
3
- "version": "3.1.0",
3
+ "version": "5.0.0",
4
4
  "private": false,
5
5
  "description": "Simple object path as array of strings and numbers",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "import": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ }
14
+ },
8
15
  "license": "MIT",
9
16
  "homepage": "https://github.com/finnair/v-validation/tree/master/packages/path#readme",
10
17
  "bugs": "https://github.com/finnair/v-validation/issues",
@@ -22,5 +29,5 @@
22
29
  "scripts": {
23
30
  "build": "tsc -b ."
24
31
  },
25
- "gitHead": "5182a63fa61cc1f5c8991e1482c5bbffe14798c3"
32
+ "gitHead": "b20762715a79e2a336f9c8ceb5107a4d84104a14"
26
33
  }