@thi.ng/resolve-map 6.2.0 → 7.1.1

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-07-07T12:39:33Z
3
+ - **Last updated**: 2022-07-19T15:36:12Z
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,24 @@ 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
+
12
30
  ## [6.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.2.0) (2022-07-07)
13
31
 
14
32
  #### 🚀 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.08 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,12 +302,38 @@ 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
- These wrapped values are only used during the resolution phase and the final
306
- result object/array will only contain the original, unwrapped values. Unwrapped
307
- values will also be supplied to any lookup functions, no
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
308
  [`.deref()`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html) necessary
309
309
  there.
310
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
+ ```
336
+
311
337
  ## Authors
312
338
 
313
339
  Karsten Schmidt
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,11 +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}. These wrapped values are only used during the resolution
24
- * phase and the final result object/array will only contain the original,
25
- * unwrapped values. Unwrapped values will also be supplied to any lookup
26
- * functions, 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.
27
54
  *
28
55
  * @example
29
56
  * ```ts
@@ -89,10 +116,10 @@ export declare type LookupPath = NumOrString[];
89
116
  * ```
90
117
  *
91
118
  * @param root -
92
- * @param prefix -
119
+ * @param opts -
93
120
  */
94
- export declare function resolve<T>(root: Unresolved<T>, prefix?: string): T;
95
- 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[];
96
123
  /**
97
124
  * Takes the path for the current key and a lookup path string. Converts
98
125
  * the possibly relative lookup path into its absolute form.
package/index.js CHANGED
@@ -7,30 +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
18
  /** @internal */
20
- const resolveMap = (obj, prefix, root, path = [], resolved = {}, stack = []) => {
19
+ const resolveMap = (obj, opts, root, path = [], resolved = {}, stack = []) => {
21
20
  root = root || obj;
22
21
  for (let k in obj) {
23
- _resolve(root, [...path, k], resolved, stack, prefix);
22
+ _resolve(root, [...path, k], resolved, stack, opts);
24
23
  }
25
- return path.length ? obj : unwrapResolved(obj, resolved);
24
+ return !opts.unwrap || path.length
25
+ ? obj
26
+ : unwrapResolved(obj, resolved);
26
27
  };
27
28
  /** @internal */
28
- const resolveArray = (arr, prefix, root, path = [], resolved = {}, stack = []) => {
29
+ const resolveArray = (arr, opts, root, path = [], resolved = {}, stack = []) => {
29
30
  root = root || arr;
30
31
  for (let k = 0, n = arr.length; k < n; k++) {
31
- _resolve(root, [...path, k], resolved, stack, prefix);
32
+ _resolve(root, [...path, k], resolved, stack, opts);
32
33
  }
33
- return path.length ? arr : unwrapResolved(arr, resolved);
34
+ return !opts.unwrap || path.length
35
+ ? arr
36
+ : unwrapResolved(arr, resolved);
34
37
  };
35
38
  /**
36
39
  * The actual recursive resolution mechanism. Takes root object, key
@@ -45,7 +48,7 @@ const resolveArray = (arr, prefix, root, path = [], resolved = {}, stack = []) =
45
48
  *
46
49
  * @internal
47
50
  */
48
- const _resolve = (root, path, resolved, stack, prefix) => {
51
+ const _resolve = (root, path, resolved, stack, opts) => {
49
52
  const pathID = path.join("/");
50
53
  if (stack.indexOf(pathID) >= 0) {
51
54
  illegalArgs(`cyclic references not allowed: ${pathID}`);
@@ -60,19 +63,21 @@ const _resolve = (root, path, resolved, stack, prefix) => {
60
63
  let res = SEMAPHORE;
61
64
  stack.push(pathID);
62
65
  if (isPlainObject(v)) {
63
- resolveMap(v, prefix, root, path, resolved, stack);
66
+ resolveMap(v, { ...opts, unwrap: false }, root, path, resolved, stack);
64
67
  }
65
68
  else if (isArray(v)) {
66
- resolveArray(v, prefix, root, path, resolved, stack);
69
+ resolveArray(v, { ...opts, unwrap: false }, root, path, resolved, stack);
67
70
  }
68
- else if (isString(v) && v.startsWith(prefix)) {
69
- 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);
70
75
  }
71
76
  else if (isFunction(v)) {
72
- 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);
73
78
  }
74
79
  else if (!exists(root, path)) {
75
- v = resolvePath(root, path, resolved, stack, prefix);
80
+ v = resolvePath(root, path, resolved, stack, opts);
76
81
  }
77
82
  if (res !== SEMAPHORE) {
78
83
  mutInUnsafe(root, path, res);
@@ -102,12 +107,12 @@ const _resolve = (root, path, resolved, stack, prefix) => {
102
107
  *
103
108
  * @internal
104
109
  */
105
- const resolvePath = (root, path, resolved, stack, prefix) => {
110
+ const resolvePath = (root, path, resolved, stack, opts) => {
106
111
  // temporarily remove current path to avoid cycle detection
107
112
  let pathID = stack.pop();
108
113
  let v;
109
114
  for (let i = 1, n = path.length; i <= n; i++) {
110
- v = _resolve(root, path.slice(0, i), resolved, stack, prefix);
115
+ v = _resolve(root, path.slice(0, i), resolved, stack, opts);
111
116
  }
112
117
  // restore
113
118
  stack.push(pathID);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/resolve-map",
3
- "version": "6.2.0",
3
+ "version": "7.1.1",
4
4
  "description": "DAG resolution of vanilla objects & arrays with internally linked values",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -34,18 +34,18 @@
34
34
  "test": "testament test"
35
35
  },
36
36
  "dependencies": {
37
- "@thi.ng/api": "^8.3.7",
38
- "@thi.ng/checks": "^3.2.1",
39
- "@thi.ng/errors": "^2.1.7",
40
- "@thi.ng/paths": "^5.1.8"
37
+ "@thi.ng/api": "^8.3.8",
38
+ "@thi.ng/checks": "^3.2.2",
39
+ "@thi.ng/errors": "^2.1.8",
40
+ "@thi.ng/paths": "^5.1.9"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@microsoft/api-extractor": "^7.25.0",
44
- "@thi.ng/testament": "^0.2.8",
44
+ "@thi.ng/testament": "^0.2.9",
45
45
  "rimraf": "^3.0.2",
46
46
  "tools": "^0.0.1",
47
47
  "typedoc": "^0.22.17",
48
- "typescript": "^4.7.3"
48
+ "typescript": "^4.7.4"
49
49
  },
50
50
  "keywords": [
51
51
  "configuration",
@@ -76,5 +76,5 @@
76
76
  ],
77
77
  "year": 2018
78
78
  },
79
- "gitHead": "04acabdda49dfcfe8cdcf044401f25ce23c872d0\n"
79
+ "gitHead": "108a6357b77d457912d30681d7cc5603ae995209\n"
80
80
  }