@thi.ng/resolve-map 6.1.1 → 7.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
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2022-06-09T16:14:01Z
3
+ - **Last updated**: 2022-07-07T13:36:28Z
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,28 @@ 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.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@7.0.0) (2022-07-07)
13
+
14
+ #### 🛑 Breaking changes
15
+
16
+ - add ResolveOpts, conditional unwrapping ([a23308b](https://github.com/thi-ng/umbrella/commit/a23308b))
17
+ - BREAKING CHANGE: update resolve() signature, use new `ResolveOpts`
18
+ - this change has only downstream impact on use cases requiring custom
19
+ prefixes to indicate lookup paths
20
+ - add new option to control value unwrapping in final result
21
+ - update docs/readme
22
+ - add new tests
23
+
24
+ ## [6.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.2.0) (2022-07-07)
25
+
26
+ #### 🚀 Features
27
+
28
+ - unwrap all resolved() values in result ([888fa33](https://github.com/thi-ng/umbrella/commit/888fa33))
29
+ - add unwrapResolved() to unwrap any values wrapped via `resolved()`
30
+ - update resolveMap/Array()
31
+ - update doc strings
32
+ - update tests
33
+
12
34
  ## [6.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/resolve-map@6.1.0) (2022-05-23)
13
35
 
14
36
  #### 🚀 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.11 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,23 @@ 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
+ ```
309
322
 
310
323
  ## Authors
311
324
 
package/index.d.ts CHANGED
@@ -4,6 +4,22 @@ 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.
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
+ }
7
23
  /**
8
24
  * Visits all key-value pairs or array items in depth-first order, expands any
9
25
  * reference values, mutates the original object and returns it. Cyclic
@@ -20,9 +36,13 @@ export declare type LookupPath = NumOrString[];
20
36
  * level (the original object passed to this function).
21
37
  *
22
38
  * 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.
39
+ * {@link resolved}. By default (unless `unwrap` is set to `false`), these
40
+ * wrapped values are only used during the resolution phase and the final result
41
+ * object/array will only contain the original, unwrapped values. In any way,
42
+ * unwrapped values will also be supplied to any lookup functions, no `.deref()`
43
+ * necessary there.
44
+ *
45
+ * See {@link ResolveOpts} for further details.
26
46
  *
27
47
  * @example
28
48
  * ```ts
@@ -88,10 +108,10 @@ export declare type LookupPath = NumOrString[];
88
108
  * ```
89
109
  *
90
110
  * @param root -
91
- * @param prefix -
111
+ * @param opts -
92
112
  */
93
- export declare function resolve<T>(root: Unresolved<T>, prefix?: string): T;
94
- export declare function resolve<T>(root: Unresolved<T[]>, prefix?: string): T[];
113
+ export declare function resolve<T>(root: Unresolved<T>, opts?: Partial<ResolveOpts>): T;
114
+ export declare function resolve<T>(root: Unresolved<T[]>, opts?: Partial<ResolveOpts>): T[];
95
115
  /**
96
116
  * Takes the path for the current key and a lookup path string. Converts
97
117
  * 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,19 @@ 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 (isString(v) && v.startsWith(opts.prefix)) {
72
+ res = _resolve(root, absPath(path, v, opts.prefix.length), resolved, stack, opts);
66
73
  }
67
74
  else if (isFunction(v)) {
68
- res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack, prefix), pathID, resolved);
75
+ res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack, opts), pathID, resolved);
69
76
  }
70
77
  else if (!exists(root, path)) {
71
- v = resolvePath(root, path, resolved, stack, prefix);
78
+ v = resolvePath(root, path, resolved, stack, opts);
72
79
  }
73
80
  if (res !== SEMAPHORE) {
74
81
  mutInUnsafe(root, path, res);
@@ -95,13 +102,15 @@ const _resolve = (root, path, resolved, stack, prefix) => {
95
102
  * @param root -
96
103
  * @param path -
97
104
  * @param resolved -
105
+ *
106
+ * @internal
98
107
  */
99
- const resolvePath = (root, path, resolved, stack, prefix) => {
108
+ const resolvePath = (root, path, resolved, stack, opts) => {
100
109
  // temporarily remove current path to avoid cycle detection
101
110
  let pathID = stack.pop();
102
111
  let v;
103
112
  for (let i = 1, n = path.length; i <= n; i++) {
104
- v = _resolve(root, path.slice(0, i), resolved, stack, prefix);
113
+ v = _resolve(root, path.slice(0, i), resolved, stack, opts);
105
114
  }
106
115
  // restore
107
116
  stack.push(pathID);
@@ -123,6 +132,8 @@ const resolvePath = (root, path, resolved, stack, prefix) => {
123
132
  * @param resolve -
124
133
  * @param pathID - current base path for marking
125
134
  * @param resolved -
135
+ *
136
+ * @internal
126
137
  */
127
138
  const resolveFunction = (fn, resolve, pathID, resolved) => {
128
139
  const match = RE_ARGS.exec(fn.toString());
@@ -142,6 +153,7 @@ const resolveFunction = (fn, resolve, pathID, resolved) => {
142
153
  markResolved(res, pathID, resolved);
143
154
  return res;
144
155
  };
156
+ /** @internal */
145
157
  const markResolved = (v, path, resolved) => {
146
158
  resolved[path] = true;
147
159
  if (isPlainObject(v)) {
@@ -151,6 +163,7 @@ const markResolved = (v, path, resolved) => {
151
163
  markArrayResolved(v, path, resolved);
152
164
  }
153
165
  };
166
+ /** @internal */
154
167
  const markObjResolved = (obj, path, resolved) => {
155
168
  let v, p;
156
169
  for (let k in obj) {
@@ -159,6 +172,7 @@ const markObjResolved = (obj, path, resolved) => {
159
172
  markResolved(v, p, resolved);
160
173
  }
161
174
  };
175
+ /** @internal */
162
176
  const markArrayResolved = (arr, path, resolved) => {
163
177
  let v, p;
164
178
  for (let i = 0, n = arr.length; i < n; i++) {
@@ -237,3 +251,19 @@ const getInUnsafe = (obj, path) => {
237
251
  }
238
252
  return [res, isResolved];
239
253
  };
254
+ /**
255
+ * Unwraps all known values wrapped using {@link Resolved} in-place.
256
+ *
257
+ * @param root
258
+ * @param resolved
259
+ *
260
+ * @internal
261
+ */
262
+ const unwrapResolved = (root, resolved) => {
263
+ for (let path in resolved) {
264
+ const $path = path.split("/");
265
+ const val = getInUnsafe(root, $path);
266
+ val[1] && mutInUnsafe(root, $path, val[0]);
267
+ }
268
+ return root;
269
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/resolve-map",
3
- "version": "6.1.1",
3
+ "version": "7.0.0",
4
4
  "description": "DAG resolution of vanilla objects & arrays with internally linked values",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -35,9 +35,9 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@thi.ng/api": "^8.3.7",
38
- "@thi.ng/checks": "^3.2.0",
38
+ "@thi.ng/checks": "^3.2.1",
39
39
  "@thi.ng/errors": "^2.1.7",
40
- "@thi.ng/paths": "^5.1.7"
40
+ "@thi.ng/paths": "^5.1.8"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@microsoft/api-extractor": "^7.25.0",
@@ -76,5 +76,5 @@
76
76
  ],
77
77
  "year": 2018
78
78
  },
79
- "gitHead": "9e516d30a1a537e027a6b3d78bf9121bc5831d31\n"
79
+ "gitHead": "4c4aeb0672852c319408c92b56444a5e42826996\n"
80
80
  }