@thi.ng/resolve-map 6.1.2 → 7.1.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
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2022-06-09T16:14:01Z
3
+ - **Last updated**: 2022-07-12T19:50:54Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,34 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [7.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@7.1.0) (2022-07-12)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add `onlyFnRefs` option ([a23fc98](https://github.com/thi-ng/umbrella/commit/a23fc98))
17
+
18
+ # [7.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@7.0.0) (2022-07-07)
19
+
20
+ #### 🛑 Breaking changes
21
+
22
+ - add ResolveOpts, conditional unwrapping ([a23308b](https://github.com/thi-ng/umbrella/commit/a23308b))
23
+ - BREAKING CHANGE: update resolve() signature, use new `ResolveOpts`
24
+ - this change has only downstream impact on use cases requiring custom
25
+ prefixes to indicate lookup paths
26
+ - add new option to control value unwrapping in final result
27
+ - update docs/readme
28
+ - add new tests
29
+
30
+ ## [6.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.2.0) (2022-07-07)
31
+
32
+ #### 🚀 Features
33
+
34
+ - unwrap all resolved() values in result ([888fa33](https://github.com/thi-ng/umbrella/commit/888fa33))
35
+ - add unwrapResolved() to unwrap any values wrapped via `resolved()`
36
+ - update resolveMap/Array()
37
+ - update doc strings
38
+ - update tests
39
+
12
40
  ## [6.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.1.0) (2022-05-23)
13
41
 
14
42
  #### 🚀 Features
package/README.md CHANGED
@@ -76,7 +76,7 @@ node --experimental-repl-await
76
76
  > const resolveMap = await import("@thi.ng/resolve-map");
77
77
  ```
78
78
 
79
- Package sizes (gzipped, pre-treeshake): ESM: 1.03 KB
79
+ Package sizes (gzipped, pre-treeshake): ESM: 1.13 KB
80
80
 
81
81
  ## Dependencies
82
82
 
@@ -237,7 +237,7 @@ resolve({ a: 1, b: { c: "@d", d: "@/a"} })
237
237
  // { a: 1, b: { c: 1, d: 1 } }
238
238
 
239
239
  // same with custom lookup prefix
240
- resolve({ a: 1, b: { c: ">>>d", d: ">>>/a"} }, ">>>")
240
+ resolve({ a: 1, b: { c: ">>>d", d: ">>>/a"} }, { prefix: ">>>" })
241
241
  // { a: 1, b: { c: 1, d: 1 } }
242
242
  ```
243
243
 
@@ -256,8 +256,8 @@ the 2nd (legacy) form. Also, since ES6 var names can't contain special
256
256
  characters, destructured keys can ALWAYS only be looked up as siblings
257
257
  of the currently processed key.
258
258
 
259
- The `resolve` function provided as arg to the user function accepts a
260
- path (**without `@` prefix**) to look up any other values in the root
259
+ The `resolve` function provided as arg to the user function accepts a path
260
+ (**without `@` (or custom) prefix**) to look up any other values in the root
261
261
  object.
262
262
 
263
263
  ```ts
@@ -302,10 +302,37 @@ res.e(2);
302
302
 
303
303
  Values can be protected from further resolution attempts by wrapping them via
304
304
  [`resolved()`](https://docs.thi.ng/umbrella/resolve-map/modules.html#resolved).
305
- The wrapped value can be later obtained via the standard [`IDeref`
306
- interface/mechanism](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html).
307
- In lookup/resolution functions, the unwrapped value will be supplied, no
308
- `.deref()` necessary there.
305
+ By default, these wrapped values are only used during the resolution phase and
306
+ the final result object/array will only contain the original, unwrapped values.
307
+ Unwrapped values will also be supplied to any lookup functions, no
308
+ [`.deref()`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html) necessary
309
+ there.
310
+
311
+ ```ts
312
+ resolve({ a: 42, b: ({a}) => resolved(a) });
313
+ // { a: 42, b: 42 }
314
+
315
+ const res = resolve({ a: 42, b: ({a}) => resolved(a) }, { unwrap: false });
316
+ // { a: 42, b: Resolved { _value: 42 } }
317
+
318
+ // obtain b's value via .deref()
319
+ res.b.deref()
320
+ // 42
321
+ ```
322
+
323
+ Since v7.1.0 a new `onlyFnRefs` option has been added which changes the
324
+ resolution behavior to **not** consider string values for resolution at all
325
+ anymore and instead requires the use of function values to trigger resolution.
326
+
327
+ ```ts
328
+ // default behavior
329
+ resolve({ a: "@c", b: ({a}) => a, c: 42 })
330
+ // { a: 42, b: 42, c: 42 }
331
+
332
+ // with option enabled
333
+ resolve({ a: "@c", b: ({a}) => a, c: 42 }, { onlyFnRefs: true })
334
+ // { a: '@c', b: '@c', c: 42 }
335
+ ```
309
336
 
310
337
  ## Authors
311
338
 
package/index.d.ts CHANGED
@@ -4,6 +4,27 @@ export declare type Unresolved<T> = {
4
4
  };
5
5
  export declare type ResolveFn = (path: string) => any;
6
6
  export declare type LookupPath = NumOrString[];
7
+ export interface ResolveOpts {
8
+ /**
9
+ * Prefix for auto-recognizing & interpreting embedded string values as
10
+ * lookup paths (only if {@link ResolveOpts.onlyFnRefs} is false, default)
11
+ *
12
+ * @defaultValue `@`
13
+ */
14
+ prefix: string;
15
+ /**
16
+ * If true (default), all known values wrapped using {@link resolved} will
17
+ * be unwrapped in the final result.
18
+ *
19
+ * @defaultValue true
20
+ */
21
+ unwrap: boolean;
22
+ /**
23
+ * If true, only function values (not strings!) will be considered for
24
+ * resolution.
25
+ */
26
+ onlyFnRefs: boolean;
27
+ }
7
28
  /**
8
29
  * Visits all key-value pairs or array items in depth-first order, expands any
9
30
  * reference values, mutates the original object and returns it. Cyclic
@@ -19,10 +40,17 @@ export declare type LookupPath = NumOrString[];
19
40
  * access any parent levels. Absolute refs are always resolved from the root
20
41
  * level (the original object passed to this function).
21
42
  *
22
- * Values can be protected from further resolution attempts by wrapping them via
23
- * {@link resolved}. The wrapped value can be later obtained via the standard
24
- * {@link @thi.ng/api#IDeref} interface/mechanism. In lookup functions, the
25
- * unwrapped value will be supplied, no `.deref()` necessary there.
43
+ * Values can be protected from (further) resolution attempts in two ways:
44
+ *
45
+ * 1) by wrapping them via {@link resolved}. By default (unless `unwrap` is set
46
+ * to `false`), these wrapped values are only used during the resolution
47
+ * phase and the final result object/array will only contain the original,
48
+ * unwrapped values. In any way, unwrapped values will also be supplied to
49
+ * any lookup functions, no `.deref()` necessary there.
50
+ * 2) Enabling the `onlyFnRefs` option, only function values will be considered
51
+ * for resolution and strings (regardless of prefix) will be ignored.
52
+ *
53
+ * See {@link ResolveOpts} and package readme for further details.
26
54
  *
27
55
  * @example
28
56
  * ```ts
@@ -88,10 +116,10 @@ export declare type LookupPath = NumOrString[];
88
116
  * ```
89
117
  *
90
118
  * @param root -
91
- * @param prefix -
119
+ * @param opts -
92
120
  */
93
- export declare function resolve<T>(root: Unresolved<T>, prefix?: string): T;
94
- export declare function resolve<T>(root: Unresolved<T[]>, prefix?: string): T[];
121
+ export declare function resolve<T>(root: Unresolved<T>, opts?: Partial<ResolveOpts>): T;
122
+ export declare function resolve<T>(root: Unresolved<T[]>, opts?: Partial<ResolveOpts>): T[];
95
123
  /**
96
124
  * Takes the path for the current key and a lookup path string. Converts
97
125
  * the possibly relative lookup path into its absolute form.
package/index.js CHANGED
@@ -7,28 +7,33 @@ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
7
7
  import { mutInUnsafe } from "@thi.ng/paths/mut-in";
8
8
  import { exists } from "@thi.ng/paths/path";
9
9
  const RE_ARGS = /^(function\s+\w+)?\s*\(\{([\w\s,:]+)\}/;
10
- export function resolve(root, prefix = "@") {
11
- if (isPlainObject(root)) {
12
- return resolveMap(root, prefix);
13
- }
14
- else if (isArray(root)) {
15
- return resolveArray(root, prefix);
16
- }
17
- return root;
10
+ export function resolve(root, opts) {
11
+ const $opts = { prefix: "@", unwrap: true, ...opts };
12
+ return isPlainObject(root)
13
+ ? resolveMap(root, $opts)
14
+ : isArray(root)
15
+ ? resolveArray(root, $opts)
16
+ : root;
18
17
  }
19
- const resolveMap = (obj, prefix, root, path = [], resolved = {}, stack = []) => {
18
+ /** @internal */
19
+ const resolveMap = (obj, opts, root, path = [], resolved = {}, stack = []) => {
20
20
  root = root || obj;
21
21
  for (let k in obj) {
22
- _resolve(root, [...path, k], resolved, stack, prefix);
22
+ _resolve(root, [...path, k], resolved, stack, opts);
23
23
  }
24
- return obj;
24
+ return !opts.unwrap || path.length
25
+ ? obj
26
+ : unwrapResolved(obj, resolved);
25
27
  };
26
- const resolveArray = (arr, prefix, root, path = [], resolved = {}, stack = []) => {
28
+ /** @internal */
29
+ const resolveArray = (arr, opts, root, path = [], resolved = {}, stack = []) => {
27
30
  root = root || arr;
28
31
  for (let k = 0, n = arr.length; k < n; k++) {
29
- _resolve(root, [...path, k], resolved, stack, prefix);
32
+ _resolve(root, [...path, k], resolved, stack, opts);
30
33
  }
31
- return arr;
34
+ return !opts.unwrap || path.length
35
+ ? arr
36
+ : unwrapResolved(arr, resolved);
32
37
  };
33
38
  /**
34
39
  * The actual recursive resolution mechanism. Takes root object, key
@@ -40,8 +45,10 @@ const resolveArray = (arr, prefix, root, path = [], resolved = {}, stack = []) =
40
45
  * @param path -
41
46
  * @param resolved -
42
47
  * @param stack -
48
+ *
49
+ * @internal
43
50
  */
44
- const _resolve = (root, path, resolved, stack, prefix) => {
51
+ const _resolve = (root, path, resolved, stack, opts) => {
45
52
  const pathID = path.join("/");
46
53
  if (stack.indexOf(pathID) >= 0) {
47
54
  illegalArgs(`cyclic references not allowed: ${pathID}`);
@@ -56,19 +63,21 @@ const _resolve = (root, path, resolved, stack, prefix) => {
56
63
  let res = SEMAPHORE;
57
64
  stack.push(pathID);
58
65
  if (isPlainObject(v)) {
59
- resolveMap(v, prefix, root, path, resolved, stack);
66
+ resolveMap(v, { ...opts, unwrap: false }, root, path, resolved, stack);
60
67
  }
61
68
  else if (isArray(v)) {
62
- resolveArray(v, prefix, root, path, resolved, stack);
69
+ resolveArray(v, { ...opts, unwrap: false }, root, path, resolved, stack);
63
70
  }
64
- else if (isString(v) && v.startsWith(prefix)) {
65
- res = _resolve(root, absPath(path, v, prefix.length), resolved, stack, prefix);
71
+ else if (!opts.onlyFnRefs &&
72
+ isString(v) &&
73
+ v.startsWith(opts.prefix)) {
74
+ res = _resolve(root, absPath(path, v, opts.prefix.length), resolved, stack, opts);
66
75
  }
67
76
  else if (isFunction(v)) {
68
- res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack, prefix), pathID, resolved);
77
+ res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack, opts), pathID, resolved);
69
78
  }
70
79
  else if (!exists(root, path)) {
71
- v = resolvePath(root, path, resolved, stack, prefix);
80
+ v = resolvePath(root, path, resolved, stack, opts);
72
81
  }
73
82
  if (res !== SEMAPHORE) {
74
83
  mutInUnsafe(root, path, res);
@@ -95,13 +104,15 @@ const _resolve = (root, path, resolved, stack, prefix) => {
95
104
  * @param root -
96
105
  * @param path -
97
106
  * @param resolved -
107
+ *
108
+ * @internal
98
109
  */
99
- const resolvePath = (root, path, resolved, stack, prefix) => {
110
+ const resolvePath = (root, path, resolved, stack, opts) => {
100
111
  // temporarily remove current path to avoid cycle detection
101
112
  let pathID = stack.pop();
102
113
  let v;
103
114
  for (let i = 1, n = path.length; i <= n; i++) {
104
- v = _resolve(root, path.slice(0, i), resolved, stack, prefix);
115
+ v = _resolve(root, path.slice(0, i), resolved, stack, opts);
105
116
  }
106
117
  // restore
107
118
  stack.push(pathID);
@@ -123,6 +134,8 @@ const resolvePath = (root, path, resolved, stack, prefix) => {
123
134
  * @param resolve -
124
135
  * @param pathID - current base path for marking
125
136
  * @param resolved -
137
+ *
138
+ * @internal
126
139
  */
127
140
  const resolveFunction = (fn, resolve, pathID, resolved) => {
128
141
  const match = RE_ARGS.exec(fn.toString());
@@ -142,6 +155,7 @@ const resolveFunction = (fn, resolve, pathID, resolved) => {
142
155
  markResolved(res, pathID, resolved);
143
156
  return res;
144
157
  };
158
+ /** @internal */
145
159
  const markResolved = (v, path, resolved) => {
146
160
  resolved[path] = true;
147
161
  if (isPlainObject(v)) {
@@ -151,6 +165,7 @@ const markResolved = (v, path, resolved) => {
151
165
  markArrayResolved(v, path, resolved);
152
166
  }
153
167
  };
168
+ /** @internal */
154
169
  const markObjResolved = (obj, path, resolved) => {
155
170
  let v, p;
156
171
  for (let k in obj) {
@@ -159,6 +174,7 @@ const markObjResolved = (obj, path, resolved) => {
159
174
  markResolved(v, p, resolved);
160
175
  }
161
176
  };
177
+ /** @internal */
162
178
  const markArrayResolved = (arr, path, resolved) => {
163
179
  let v, p;
164
180
  for (let i = 0, n = arr.length; i < n; i++) {
@@ -237,3 +253,19 @@ const getInUnsafe = (obj, path) => {
237
253
  }
238
254
  return [res, isResolved];
239
255
  };
256
+ /**
257
+ * Unwraps all known values wrapped using {@link Resolved} in-place.
258
+ *
259
+ * @param root
260
+ * @param resolved
261
+ *
262
+ * @internal
263
+ */
264
+ const unwrapResolved = (root, resolved) => {
265
+ for (let path in resolved) {
266
+ const $path = path.split("/");
267
+ const val = getInUnsafe(root, $path);
268
+ val[1] && mutInUnsafe(root, $path, val[0]);
269
+ }
270
+ return root;
271
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/resolve-map",
3
- "version": "6.1.2",
3
+ "version": "7.1.0",
4
4
  "description": "DAG resolution of vanilla objects & arrays with internally linked values",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -76,5 +76,5 @@
76
76
  ],
77
77
  "year": 2018
78
78
  },
79
- "gitHead": "ab0188234419f2d9f471de80871df930e5555bd6\n"
79
+ "gitHead": "21abd94ba9e13d4636f3e9a85d3a7d4e9ea269ba\n"
80
80
  }