@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.
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/index.js +160 -251
- package/package.json +10 -7
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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.
|
|
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
|
|
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.
|
|
37
|
-
"@thi.ng/checks": "^3.4.
|
|
38
|
-
"@thi.ng/errors": "^2.4.
|
|
39
|
-
"@thi.ng/paths": "^5.1.
|
|
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": "
|
|
80
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
78
81
|
}
|