@thi.ng/resolve-map 7.1.45 → 7.1.46

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +1 -1
  3. package/index.js +160 -251
  4. package/package.json +10 -7
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-09T19:12:03Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
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.
package/README.md CHANGED
@@ -74,7 +74,7 @@ For Node.js REPL:
74
74
  const resolveMap = await import("@thi.ng/resolve-map");
75
75
  ```
76
76
 
77
- Package sizes (brotli'd, pre-treeshake): ESM: 1.11 KB
77
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.01 KB
78
78
 
79
79
  ## Dependencies
80
80
 
package/index.js CHANGED
@@ -7,268 +7,177 @@ 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, 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;
10
+ function resolve(root, opts) {
11
+ const $opts = { prefix: "@", unwrap: true, ...opts };
12
+ return isPlainObject(root) ? resolveMap(root, $opts) : isArray(root) ? resolveArray(root, $opts) : root;
17
13
  }
18
- /** @internal */
19
- const resolveMap = (obj, opts, root, path = [], resolved = {}, stack = []) => {
20
- root = root || obj;
21
- for (let k in obj) {
22
- _resolve(root, [...path, k], resolved, stack, opts);
23
- }
24
- return !opts.unwrap || path.length
25
- ? obj
26
- : unwrapResolved(obj, resolved);
27
- };
28
- /** @internal */
29
- const resolveArray = (arr, opts, root, path = [], resolved = {}, stack = []) => {
30
- root = root || arr;
31
- for (let k = 0, n = arr.length; k < n; k++) {
32
- _resolve(root, [...path, k], resolved, stack, opts);
33
- }
34
- return !opts.unwrap || path.length
35
- ? arr
36
- : unwrapResolved(arr, resolved);
14
+ const resolveMap = (obj, opts, root, path = [], resolved2 = {}, stack = []) => {
15
+ root = root || obj;
16
+ for (let k in obj) {
17
+ _resolve(root, [...path, k], resolved2, stack, opts);
18
+ }
19
+ return !opts.unwrap || path.length ? obj : unwrapResolved(obj, resolved2);
37
20
  };
38
- /**
39
- * The actual recursive resolution mechanism. Takes root object, key
40
- * path, helper object for marking visited keys and a stack of currently
41
- * active lookups. The latter is used for cycle detection and `_resolve`
42
- * will throw an error if a cycle has been detected.
43
- *
44
- * @param root -
45
- * @param path -
46
- * @param resolved -
47
- * @param stack -
48
- *
49
- * @internal
50
- */
51
- const _resolve = (root, path, resolved, stack, opts) => {
52
- const pathID = path.join("/");
53
- if (stack.indexOf(pathID) >= 0) {
54
- illegalArgs(`cyclic references not allowed: ${pathID}`);
55
- }
56
- // console.log(pp, resolved[pp], stack);
57
- let [v, isResolved] = getInUnsafe(root, path);
58
- if (!resolved[pathID]) {
59
- if (isResolved) {
60
- resolved[pathID] = true;
61
- return v;
62
- }
63
- let res = SEMAPHORE;
64
- stack.push(pathID);
65
- if (isPlainObject(v)) {
66
- resolveMap(v, { ...opts, unwrap: false }, root, path, resolved, stack);
67
- }
68
- else if (isArray(v)) {
69
- resolveArray(v, { ...opts, unwrap: false }, root, path, resolved, stack);
70
- }
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);
75
- }
76
- else if (isFunction(v)) {
77
- res = resolveFunction(v, (p) => _resolve(root, absPath(path, p, 0), resolved, stack, opts), pathID, resolved);
78
- }
79
- else if (!exists(root, path)) {
80
- v = resolvePath(root, path, resolved, stack, opts);
81
- }
82
- if (res !== SEMAPHORE) {
83
- mutInUnsafe(root, path, res);
84
- v = res;
85
- }
86
- resolved[pathID] = true;
87
- stack.pop();
88
- }
89
- return v;
21
+ const resolveArray = (arr, opts, root, path = [], resolved2 = {}, stack = []) => {
22
+ root = root || arr;
23
+ for (let k = 0, n = arr.length; k < n; k++) {
24
+ _resolve(root, [...path, k], resolved2, stack, opts);
25
+ }
26
+ return !opts.unwrap || path.length ? arr : unwrapResolved(arr, resolved2);
90
27
  };
91
- /**
92
- * Repeatedly calls `_resolve` by stepwise descending along given path
93
- * and returns final value. This is to ensure full resolution of deeper
94
- * values created by functions at intermediate tree levels.
95
- *
96
- * E.g. given:
97
- *
98
- * ```
99
- * {a: () => ({b: {c: 1}}), d: "@/a/b/c" }
100
- * =>
101
- * { a: { b: { c: 1 } }, d: 1 }
102
- * ```
103
- *
104
- * @param root -
105
- * @param path -
106
- * @param resolved -
107
- *
108
- * @internal
109
- */
110
- const resolvePath = (root, path, resolved, stack, opts) => {
111
- // temporarily remove current path to avoid cycle detection
112
- let pathID = stack.pop();
113
- let v;
114
- for (let i = 1, n = path.length; i <= n; i++) {
115
- v = _resolve(root, path.slice(0, i), resolved, stack, opts);
116
- }
117
- // restore
28
+ const _resolve = (root, path, resolved2, stack, opts) => {
29
+ const pathID = path.join("/");
30
+ if (stack.indexOf(pathID) >= 0) {
31
+ illegalArgs(`cyclic references not allowed: ${pathID}`);
32
+ }
33
+ let [v, isResolved] = getInUnsafe(root, path);
34
+ if (!resolved2[pathID]) {
35
+ if (isResolved) {
36
+ resolved2[pathID] = true;
37
+ return v;
38
+ }
39
+ let res = SEMAPHORE;
118
40
  stack.push(pathID);
119
- return v;
41
+ if (isPlainObject(v)) {
42
+ resolveMap(
43
+ v,
44
+ { ...opts, unwrap: false },
45
+ root,
46
+ path,
47
+ resolved2,
48
+ stack
49
+ );
50
+ } else if (isArray(v)) {
51
+ resolveArray(
52
+ v,
53
+ { ...opts, unwrap: false },
54
+ root,
55
+ path,
56
+ resolved2,
57
+ stack
58
+ );
59
+ } else if (!opts.onlyFnRefs && isString(v) && v.startsWith(opts.prefix)) {
60
+ res = _resolve(
61
+ root,
62
+ absPath(path, v, opts.prefix.length),
63
+ resolved2,
64
+ stack,
65
+ opts
66
+ );
67
+ } else if (isFunction(v)) {
68
+ res = resolveFunction(
69
+ v,
70
+ (p) => _resolve(root, absPath(path, p, 0), resolved2, stack, opts),
71
+ pathID,
72
+ resolved2
73
+ );
74
+ } else if (!exists(root, path)) {
75
+ v = resolvePath(root, path, resolved2, stack, opts);
76
+ }
77
+ if (res !== SEMAPHORE) {
78
+ mutInUnsafe(root, path, res);
79
+ v = res;
80
+ }
81
+ resolved2[pathID] = true;
82
+ stack.pop();
83
+ }
84
+ return v;
120
85
  };
121
- /**
122
- * Resolution helper for function values. Checks if the user function
123
- * uses ES6 object destructuring for its first argument and if so
124
- * resolves the given keys before calling the function and provides
125
- * their values as first arg. If no de-structure form is found, calls
126
- * function only with `resolve` as argument.
127
- *
128
- * If the user function returns an array or plain object, all of its
129
- * nested values are marked as resolved.
130
- *
131
- * See `resolve` comments for further details.
132
- *
133
- * @param fn -
134
- * @param resolve -
135
- * @param pathID - current base path for marking
136
- * @param resolved -
137
- *
138
- * @internal
139
- */
140
- const resolveFunction = (fn, resolve, pathID, resolved) => {
141
- const match = RE_ARGS.exec(fn.toString());
142
- let res;
143
- if (match) {
144
- const args = match[2]
145
- // remove white space and trailing comma
146
- .replace(/\s|(,\s*$)/g, "")
147
- .split(/,/g)
148
- .map((k) => k.split(":")[0])
149
- .reduce((acc, k) => ((acc[k] = resolve(k)), acc), {});
150
- res = fn(args, resolve);
151
- }
152
- else {
153
- res = fn(resolve);
154
- }
155
- markResolved(res, pathID, resolved);
156
- return res;
86
+ const resolvePath = (root, path, resolved2, stack, opts) => {
87
+ let pathID = stack.pop();
88
+ let v;
89
+ for (let i = 1, n = path.length; i <= n; i++) {
90
+ v = _resolve(root, path.slice(0, i), resolved2, stack, opts);
91
+ }
92
+ stack.push(pathID);
93
+ return v;
157
94
  };
158
- /** @internal */
159
- const markResolved = (v, path, resolved) => {
160
- resolved[path] = true;
161
- if (isPlainObject(v)) {
162
- markObjResolved(v, path, resolved);
163
- }
164
- else if (isArray(v)) {
165
- markArrayResolved(v, path, resolved);
166
- }
95
+ const resolveFunction = (fn, resolve2, pathID, resolved2) => {
96
+ const match = RE_ARGS.exec(fn.toString());
97
+ let res;
98
+ if (match) {
99
+ const args = match[2].replace(/\s|(,\s*$)/g, "").split(/,/g).map((k) => k.split(":")[0]).reduce((acc, k) => (acc[k] = resolve2(k), acc), {});
100
+ res = fn(args, resolve2);
101
+ } else {
102
+ res = fn(resolve2);
103
+ }
104
+ markResolved(res, pathID, resolved2);
105
+ return res;
167
106
  };
168
- /** @internal */
169
- const markObjResolved = (obj, path, resolved) => {
170
- let v, p;
171
- for (let k in obj) {
172
- v = obj[k];
173
- p = path + "/" + k;
174
- markResolved(v, p, resolved);
175
- }
107
+ const markResolved = (v, path, resolved2) => {
108
+ resolved2[path] = true;
109
+ if (isPlainObject(v)) {
110
+ markObjResolved(v, path, resolved2);
111
+ } else if (isArray(v)) {
112
+ markArrayResolved(v, path, resolved2);
113
+ }
176
114
  };
177
- /** @internal */
178
- const markArrayResolved = (arr, path, resolved) => {
179
- let v, p;
180
- for (let i = 0, n = arr.length; i < n; i++) {
181
- v = arr[i];
182
- p = path + "/" + i;
183
- markResolved(v, p, resolved);
184
- }
115
+ const markObjResolved = (obj, path, resolved2) => {
116
+ let v, p;
117
+ for (let k in obj) {
118
+ v = obj[k];
119
+ p = path + "/" + k;
120
+ markResolved(v, p, resolved2);
121
+ }
185
122
  };
186
- /**
187
- * Takes the path for the current key and a lookup path string. Converts
188
- * the possibly relative lookup path into its absolute form.
189
- *
190
- * @param curr -
191
- * @param path -
192
- * @param idx -
193
- */
194
- export const absPath = (curr, path, idx = 1) => {
195
- if (path.charAt(idx) === "/") {
196
- return path.substring(idx + 1).split("/");
197
- }
198
- curr = curr.slice(0, curr.length - 1);
199
- const sub = path.substring(idx).split("/");
200
- for (let i = 0, n = sub.length; i < n; i++) {
201
- if (sub[i] === "..") {
202
- !curr.length && illegalArgs(`invalid lookup path: ${path}`);
203
- curr.pop();
204
- }
205
- else {
206
- return curr.concat(sub.slice(i));
207
- }
208
- }
209
- !curr.length && illegalArgs(`invalid lookup path: ${path}`);
210
- return curr;
123
+ const markArrayResolved = (arr, path, resolved2) => {
124
+ let v, p;
125
+ for (let i = 0, n = arr.length; i < n; i++) {
126
+ v = arr[i];
127
+ p = path + "/" + i;
128
+ markResolved(v, p, resolved2);
129
+ }
211
130
  };
212
- /**
213
- * Value wrapper to protect from future recursive resolution attempts. See
214
- * {@link resolved} for further details.
215
- */
216
- export class Resolved {
217
- _value;
218
- constructor(_value) {
219
- this._value = _value;
220
- }
221
- deref() {
222
- return this._value;
223
- }
131
+ const absPath = (curr, path, idx = 1) => {
132
+ if (path.charAt(idx) === "/") {
133
+ return path.substring(idx + 1).split("/");
134
+ }
135
+ curr = curr.slice(0, curr.length - 1);
136
+ const sub = path.substring(idx).split("/");
137
+ for (let i = 0, n = sub.length; i < n; i++) {
138
+ if (sub[i] === "..") {
139
+ !curr.length && illegalArgs(`invalid lookup path: ${path}`);
140
+ curr.pop();
141
+ } else {
142
+ return curr.concat(sub.slice(i));
143
+ }
144
+ }
145
+ !curr.length && illegalArgs(`invalid lookup path: ${path}`);
146
+ return curr;
147
+ };
148
+ class Resolved {
149
+ constructor(_value) {
150
+ this._value = _value;
151
+ }
152
+ deref() {
153
+ return this._value;
154
+ }
224
155
  }
225
- /**
226
- * Factory function for {@link Resolved} to wrap & protect values from further
227
- * resolution attempts. The wrapped value can be later obtained via the standard
228
- * [`IDeref`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html)
229
- * interface/mechanism. In lookup functions, the unwrapped value will be
230
- * supplied, no `.deref()` necessary there.
231
- *
232
- * @param val
233
- */
234
- export const resolved = (val) => new Resolved(val);
235
- /**
236
- * Special version of
237
- * [`getInUnsafe()`](https://docs.thi.ng/umbrella/paths/functions/getInUnsafe.html)
238
- * with extra support for intermediate wrapped {@link Resolved} values and
239
- * returning tuple of: `[val,isResolved]`.
240
- *
241
- * @param obj
242
- * @param path
243
- *
244
- * @internal
245
- */
156
+ const resolved = (val) => new Resolved(val);
246
157
  const getInUnsafe = (obj, path) => {
247
- const n = path.length - 1;
248
- let res = obj;
249
- let isResolved = obj instanceof Resolved;
250
- for (let i = 0; res != null && i <= n; i++) {
251
- res = res[path[i]];
252
- if (res instanceof Resolved) {
253
- isResolved = true;
254
- res = res.deref();
255
- }
256
- }
257
- return [res, isResolved];
158
+ const n = path.length - 1;
159
+ let res = obj;
160
+ let isResolved = obj instanceof Resolved;
161
+ for (let i = 0; res != null && i <= n; i++) {
162
+ res = res[path[i]];
163
+ if (res instanceof Resolved) {
164
+ isResolved = true;
165
+ res = res.deref();
166
+ }
167
+ }
168
+ return [res, isResolved];
258
169
  };
259
- /**
260
- * Unwraps all known values wrapped using {@link Resolved} in-place.
261
- *
262
- * @param root
263
- * @param resolved
264
- *
265
- * @internal
266
- */
267
- const unwrapResolved = (root, resolved) => {
268
- for (let path in resolved) {
269
- const $path = path.split("/");
270
- const val = getInUnsafe(root, $path);
271
- val[1] && mutInUnsafe(root, $path, val[0]);
272
- }
273
- return root;
170
+ const unwrapResolved = (root, resolved2) => {
171
+ for (let path in resolved2) {
172
+ const $path = path.split("/");
173
+ const val = getInUnsafe(root, $path);
174
+ val[1] && mutInUnsafe(root, $path, val[0]);
175
+ }
176
+ return root;
177
+ };
178
+ export {
179
+ Resolved,
180
+ absPath,
181
+ resolve,
182
+ resolved
274
183
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/resolve-map",
3
- "version": "7.1.45",
3
+ "version": "7.1.46",
4
4
  "description": "DAG resolution of vanilla objects & arrays with internally linked values",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -24,7 +24,9 @@
24
24
  "author": "Karsten Schmidt (https://thi.ng)",
25
25
  "license": "Apache-2.0",
26
26
  "scripts": {
27
- "build": "yarn clean && tsc --declaration",
27
+ "build": "yarn build:esbuild && yarn build:decl",
28
+ "build:decl": "tsc --declaration --emitDeclarationOnly",
29
+ "build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
28
30
  "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
29
31
  "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
30
32
  "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
@@ -33,13 +35,14 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/api": "^8.9.11",
37
- "@thi.ng/checks": "^3.4.11",
38
- "@thi.ng/errors": "^2.4.5",
39
- "@thi.ng/paths": "^5.1.52"
38
+ "@thi.ng/api": "^8.9.12",
39
+ "@thi.ng/checks": "^3.4.12",
40
+ "@thi.ng/errors": "^2.4.6",
41
+ "@thi.ng/paths": "^5.1.53"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@microsoft/api-extractor": "^7.38.3",
45
+ "esbuild": "^0.19.8",
43
46
  "rimraf": "^5.0.5",
44
47
  "tools": "^0.0.1",
45
48
  "typedoc": "^0.25.4",
@@ -74,5 +77,5 @@
74
77
  ],
75
78
  "year": 2018
76
79
  },
77
- "gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n"
80
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
78
81
  }