assign-gingerly 0.0.74 → 0.0.76
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/DX/installForwarding.js +1 -1
- package/DX/installForwarding.ts +1 -1
- package/README.md +2 -0
- package/assignFrom.js +18 -17
- package/assignFrom.ts +23 -23
- package/assignFromAsync.js +14 -13
- package/assignFromAsync.ts +15 -15
- package/assignGingerly.js +46 -42
- package/assignGingerly.ts +131 -124
- package/assignPermissions/PermissionProcessor.js +166 -0
- package/assignPermissions/PermissionProcessor.ts +210 -0
- package/assignPermissions/isAllowedImportPath.js +2 -6
- package/assignPermissions/isAllowedImportPath.ts +3 -5
- package/assignPermissions/isAllowedUrl.js +18 -0
- package/assignPermissions/isAllowedUrl.ts +15 -0
- package/assignTentatively.js +9 -11
- package/assignTentatively.ts +11 -13
- package/beVigilant.js +1 -1
- package/beVigilant.ts +1 -1
- package/buildCSSQuery.js +1 -1
- package/buildCSSQuery.ts +1 -1
- package/eachTime.js +5 -6
- package/eachTime.ts +12 -15
- package/enhanceAll.js +3 -3
- package/enhanceAll.ts +4 -4
- package/evaluatePathWithAsyncMethods.js +12 -8
- package/evaluatePathWithAsyncMethods.ts +129 -117
- package/handlers/addEventListener.js +11 -11
- package/handlers/addEventListener.ts +12 -13
- package/handlers/lazyLoad.ts +7 -7
- package/handlers/lazyLoadSwitch.ts +3 -3
- package/handlers/manageTemplateList.js +10 -10
- package/handlers/manageTemplateList.ts +12 -12
- package/handlers/rangeSelector.ts +3 -3
- package/index.js +2 -3
- package/index.ts +2 -3
- package/inferencer/types/assign-gingerly/types.d.ts +37 -23
- package/inferencer/types/el-maker/types.d.ts +33 -0
- package/inferencer/types/scratch-box/types.d.ts +3 -0
- package/inferencer/types/truth-sourcer/types.d.ts +4 -0
- package/package.json +25 -23
- package/parseWithAttrs.js +1 -1
- package/parseWithAttrs.ts +1 -1
- package/processHandlerCommands.js +5 -7
- package/processHandlerCommands.ts +12 -15
- package/{getValues.js → resolve/getValues.js} +19 -8
- package/{getValues.ts → resolve/getValues.ts} +331 -314
- package/{resolveIdRef.js → resolve/resolveIdRef.js} +1 -1
- package/{resolveIdRef.ts → resolve/resolveIdRef.ts} +1 -1
- package/{resolveValues.ts → resolve/resolveValues.ts} +1 -1
- package/types/assign-gingerly/types.d.ts +37 -8
- package/utils/findClassPrototypeInPath.js +57 -0
- package/utils/findClassPrototypeInPath.ts +62 -0
- package/utils/isAsyncSpawn.js +20 -0
- package/utils/isAsyncSpawn.ts +17 -0
- package/assignPermissions/restrictedProps.js +0 -43
- package/assignPermissions/restrictedProps.ts +0 -53
- package/resolveAndAssignFeatures.js +0 -36
- package/resolveAndAssignFeatures.ts +0 -43
- /package/{resolveTemplate.js → resolve/resolveTemplate.js} +0 -0
- /package/{resolveTemplate.ts → resolve/resolveTemplate.ts} +0 -0
- /package/{resolveValues.js → resolve/resolveValues.js} +0 -0
|
@@ -1,314 +1,331 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* getValues.ts — Synchronous value resolution for path strings.
|
|
3
|
-
*
|
|
4
|
-
* The synchronous counterpart to resolveValues. Resolves `?.`-prefixed path
|
|
5
|
-
* strings against a source object, with support for withMethods, aka aliases,
|
|
6
|
-
* synchronous protocols, arrays, and nested plain objects.
|
|
7
|
-
*
|
|
8
|
-
* For async protocol handlers, use resolveValues instead.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* import { getValues, getValue } from 'assign-gingerly/getValues.js';
|
|
12
|
-
*
|
|
13
|
-
* const result = getValues({
|
|
14
|
-
* name: '?.user?.name',
|
|
15
|
-
* greeting: '?.messages?.hello',
|
|
16
|
-
* count: 42
|
|
17
|
-
* }, source, { withMethods: ['querySelector'], aka: { q: 'querySelector' } });
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import type { GetValuesOptions } from '
|
|
21
|
-
|
|
22
|
-
export function normalizeAliasOptions(options?: {
|
|
23
|
-
aka?: Record<string, string>;
|
|
24
|
-
akaMethods?: Record<string, string>;
|
|
25
|
-
withMethods?: string[] | Set<string>;
|
|
26
|
-
}): { aliasMap: Map<string, string>; withMethods: Set<string> | undefined } {
|
|
27
|
-
const aliasMap = new Map<string, string>();
|
|
28
|
-
|
|
29
|
-
if (options?.aka) {
|
|
30
|
-
for (const [alias, target] of Object.entries(options.aka)) {
|
|
31
|
-
if (alias.includes(' ') || alias.includes('`')) {
|
|
32
|
-
throw new Error(`Invalid alias '${alias}': aliases cannot contain space or backtick characters`);
|
|
33
|
-
}
|
|
34
|
-
aliasMap.set(alias, target);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (options?.akaMethods) {
|
|
39
|
-
for (const [alias, target] of Object.entries(options.akaMethods)) {
|
|
40
|
-
if (alias.includes(' ') || alias.includes('`')) {
|
|
41
|
-
throw new Error(`Invalid alias '${alias}': aliases cannot contain space or backtick characters`);
|
|
42
|
-
}
|
|
43
|
-
aliasMap.set(alias, target);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const withMethods = options?.withMethods
|
|
48
|
-
? options.withMethods instanceof Set
|
|
49
|
-
? new Set(options.withMethods)
|
|
50
|
-
: new Set(options.withMethods)
|
|
51
|
-
: options?.akaMethods
|
|
52
|
-
? new Set<string>()
|
|
53
|
-
: undefined;
|
|
54
|
-
|
|
55
|
-
if (options?.akaMethods) {
|
|
56
|
-
for (const target of Object.values(options.akaMethods)) {
|
|
57
|
-
withMethods?.add(target);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return { aliasMap, withMethods };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*
|
|
106
|
-
*/
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
parts
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
result[key] =
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
1
|
+
/**
|
|
2
|
+
* getValues.ts — Synchronous value resolution for path strings.
|
|
3
|
+
*
|
|
4
|
+
* The synchronous counterpart to resolveValues. Resolves `?.`-prefixed path
|
|
5
|
+
* strings against a source object, with support for withMethods, aka aliases,
|
|
6
|
+
* synchronous protocols, arrays, and nested plain objects.
|
|
7
|
+
*
|
|
8
|
+
* For async protocol handlers, use resolveValues instead.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { getValues, getValue } from 'assign-gingerly/getValues.js';
|
|
12
|
+
*
|
|
13
|
+
* const result = getValues({
|
|
14
|
+
* name: '?.user?.name',
|
|
15
|
+
* greeting: '?.messages?.hello',
|
|
16
|
+
* count: 42
|
|
17
|
+
* }, source, { withMethods: ['querySelector'], aka: { q: 'querySelector' } });
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { GetValuesOptions, PermissionProcessor } from '../types/assign-gingerly/types.js';
|
|
21
|
+
|
|
22
|
+
export function normalizeAliasOptions(options?: {
|
|
23
|
+
aka?: Record<string, string>;
|
|
24
|
+
akaMethods?: Record<string, string>;
|
|
25
|
+
withMethods?: string[] | Set<string>;
|
|
26
|
+
}): { aliasMap: Map<string, string>; withMethods: Set<string> | undefined } {
|
|
27
|
+
const aliasMap = new Map<string, string>();
|
|
28
|
+
|
|
29
|
+
if (options?.aka) {
|
|
30
|
+
for (const [alias, target] of Object.entries(options.aka)) {
|
|
31
|
+
if (alias.includes(' ') || alias.includes('`')) {
|
|
32
|
+
throw new Error(`Invalid alias '${alias}': aliases cannot contain space or backtick characters`);
|
|
33
|
+
}
|
|
34
|
+
aliasMap.set(alias, target);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (options?.akaMethods) {
|
|
39
|
+
for (const [alias, target] of Object.entries(options.akaMethods)) {
|
|
40
|
+
if (alias.includes(' ') || alias.includes('`')) {
|
|
41
|
+
throw new Error(`Invalid alias '${alias}': aliases cannot contain space or backtick characters`);
|
|
42
|
+
}
|
|
43
|
+
aliasMap.set(alias, target);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const withMethods = options?.withMethods
|
|
48
|
+
? options.withMethods instanceof Set
|
|
49
|
+
? new Set(options.withMethods)
|
|
50
|
+
: new Set(options.withMethods)
|
|
51
|
+
: options?.akaMethods
|
|
52
|
+
? new Set<string>()
|
|
53
|
+
: undefined;
|
|
54
|
+
|
|
55
|
+
if (options?.akaMethods) {
|
|
56
|
+
for (const target of Object.values(options.akaMethods)) {
|
|
57
|
+
withMethods?.add(target);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { aliasMap, withMethods };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check whether a method name is listed in withMethods and is not restricted
|
|
66
|
+
* by the permission processor. Restricted methods are treated as non-method
|
|
67
|
+
* property names so they fall through to normal access.
|
|
68
|
+
*/
|
|
69
|
+
function isAllowedMethod(
|
|
70
|
+
methodName: string,
|
|
71
|
+
withMethods: Set<string> | undefined,
|
|
72
|
+
permissionProcessor?: PermissionProcessor
|
|
73
|
+
): boolean {
|
|
74
|
+
return !!withMethods && withMethods.has(methodName) && !permissionProcessor?.checkRestrictedMethod(methodName);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Apply alias substitutions to a path string.
|
|
79
|
+
* Replaces complete tokens between `?.` delimiters with their aliased values.
|
|
80
|
+
*/
|
|
81
|
+
function applyAliases(path: string, aliasMap: Map<string, string>): string {
|
|
82
|
+
if (aliasMap.size === 0) return path;
|
|
83
|
+
const parts = path.split('?.');
|
|
84
|
+
const substituted = parts.map(part => aliasMap.get(part) ?? part);
|
|
85
|
+
return substituted.join('?.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a special root-reference token at the start of a string.
|
|
90
|
+
* '$0' refers to the first argument passed to assignFrom / resolveValues.
|
|
91
|
+
*/
|
|
92
|
+
function resolveRootReference(path: string, source: any, root: any): { source: any; path: string } | null {
|
|
93
|
+
if (path === '$0') return { source: root ?? source, path: '' };
|
|
94
|
+
if (path.startsWith('$0?.')) return { source: root ?? source, path: path.substring(4) };
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Path cache for parsed path strings.
|
|
100
|
+
* Avoids re-splitting the same path on repeated calls.
|
|
101
|
+
*/
|
|
102
|
+
const pathCache = new Map<string, string[]>();
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Parse a `?.`-delimited path string into segments, with caching.
|
|
106
|
+
*/
|
|
107
|
+
function parseCachedPath(path: string): string[] {
|
|
108
|
+
let parts = pathCache.get(path);
|
|
109
|
+
if (!parts) {
|
|
110
|
+
parts = path.split('?.').filter(p => p.length > 0);
|
|
111
|
+
pathCache.set(path, parts);
|
|
112
|
+
}
|
|
113
|
+
return parts;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Navigate a path against a source object, optionally calling methods.
|
|
118
|
+
* Returns the resolved value at the end of the path.
|
|
119
|
+
*/
|
|
120
|
+
function navigatePath(
|
|
121
|
+
source: any,
|
|
122
|
+
parts: string[],
|
|
123
|
+
withMethods: Set<string> | undefined,
|
|
124
|
+
permissionProcessor?: PermissionProcessor
|
|
125
|
+
): any {
|
|
126
|
+
let current = source;
|
|
127
|
+
let i = 0;
|
|
128
|
+
|
|
129
|
+
while (i < parts.length) {
|
|
130
|
+
if (current == null) return current;
|
|
131
|
+
|
|
132
|
+
const part = parts[i];
|
|
133
|
+
|
|
134
|
+
if (isAllowedMethod(part, withMethods, permissionProcessor)) {
|
|
135
|
+
const method = current[part];
|
|
136
|
+
if (typeof method === 'function') {
|
|
137
|
+
const nextPart = parts[i + 1];
|
|
138
|
+
if (nextPart !== undefined && !isAllowedMethod(nextPart, withMethods, permissionProcessor)) {
|
|
139
|
+
current = method.call(current, nextPart);
|
|
140
|
+
i += 2;
|
|
141
|
+
} else {
|
|
142
|
+
current = method.call(current);
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
current = current[part];
|
|
147
|
+
i++;
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
current = current[part];
|
|
151
|
+
i++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return current;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Checks if a string value looks like a protocol reference.
|
|
160
|
+
*/
|
|
161
|
+
function hasProtocol(value: string): boolean {
|
|
162
|
+
return value.includes('://');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a protocol-prefixed value synchronously.
|
|
167
|
+
*/
|
|
168
|
+
function getProtocolValue(
|
|
169
|
+
value: string,
|
|
170
|
+
protocols: Record<string, (key: string) => any>,
|
|
171
|
+
options?: GetValuesOptions
|
|
172
|
+
): any {
|
|
173
|
+
const protoEnd = value.indexOf('://');
|
|
174
|
+
const protocol = value.substring(0, protoEnd);
|
|
175
|
+
|
|
176
|
+
const handler = protocols[protocol];
|
|
177
|
+
if (!handler) return value; // not a recognized protocol
|
|
178
|
+
|
|
179
|
+
const rest = value.substring(protoEnd + 3);
|
|
180
|
+
|
|
181
|
+
const pathStart = rest.indexOf('?.');
|
|
182
|
+
const key = pathStart === -1 ? rest : rest.substring(0, pathStart);
|
|
183
|
+
const path = pathStart === -1 ? null : rest.substring(pathStart);
|
|
184
|
+
|
|
185
|
+
const resolved = handler(key);
|
|
186
|
+
|
|
187
|
+
if (path) {
|
|
188
|
+
return getValue(path, resolved, options);
|
|
189
|
+
}
|
|
190
|
+
return resolved;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Resolve path strings and protocols within an array (synchronous).
|
|
195
|
+
* Recurses into nested arrays and plain objects.
|
|
196
|
+
*/
|
|
197
|
+
function getArray(
|
|
198
|
+
arr: any[],
|
|
199
|
+
source: any,
|
|
200
|
+
aliasMap: Map<string, string>,
|
|
201
|
+
withMethods: Set<string> | undefined,
|
|
202
|
+
protocols: Record<string, (key: string) => any> | undefined,
|
|
203
|
+
options?: GetValuesOptions
|
|
204
|
+
): any[] {
|
|
205
|
+
const permissionProcessor = options?.permissionProcessor;
|
|
206
|
+
const result: any[] = [];
|
|
207
|
+
for (const item of arr) {
|
|
208
|
+
if (typeof item === 'string' && item.startsWith('?.')) {
|
|
209
|
+
const aliased = applyAliases(item, aliasMap);
|
|
210
|
+
const parts = parseCachedPath(aliased);
|
|
211
|
+
result.push(parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor));
|
|
212
|
+
} else if (typeof item === 'string' && item.startsWith('$0')) {
|
|
213
|
+
const rootRef = resolveRootReference(item, source, options?.root);
|
|
214
|
+
if (rootRef === null) {
|
|
215
|
+
result.push(source);
|
|
216
|
+
} else {
|
|
217
|
+
const aliased = applyAliases(rootRef.path, aliasMap);
|
|
218
|
+
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
219
|
+
const parts = parseCachedPath(normalizedPath);
|
|
220
|
+
result.push(parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor));
|
|
221
|
+
}
|
|
222
|
+
} else if (typeof item === 'string' && protocols && hasProtocol(item)) {
|
|
223
|
+
result.push(getProtocolValue(item, protocols, options));
|
|
224
|
+
} else if (Array.isArray(item)) {
|
|
225
|
+
result.push(getArray(item, source, aliasMap, withMethods, protocols, options));
|
|
226
|
+
} else if (item && typeof item === 'object') {
|
|
227
|
+
const proto = Object.getPrototypeOf(item);
|
|
228
|
+
if (proto === Object.prototype || proto === null) {
|
|
229
|
+
result.push(getValues(item, source, options));
|
|
230
|
+
} else {
|
|
231
|
+
result.push(item);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
result.push(item);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Synchronously resolve RHS path strings in a pattern object against a source object.
|
|
242
|
+
*
|
|
243
|
+
* Any value that is a string starting with `?.` is treated as a path
|
|
244
|
+
* and resolved against the source object. Non-string values and strings
|
|
245
|
+
* not starting with `?.` pass through unchanged.
|
|
246
|
+
*
|
|
247
|
+
* @param pattern - Object whose RHS values may contain `?.` path strings
|
|
248
|
+
* @param source - Object to resolve paths against
|
|
249
|
+
* @param options - Optional withMethods, aka, and synchronous protocols
|
|
250
|
+
* @returns New object with path strings replaced by resolved values
|
|
251
|
+
*/
|
|
252
|
+
export function getValues(
|
|
253
|
+
pattern: Record<string, any>,
|
|
254
|
+
source: any,
|
|
255
|
+
options?: GetValuesOptions
|
|
256
|
+
): Record<string, any> {
|
|
257
|
+
const { aliasMap, withMethods } = normalizeAliasOptions(options);
|
|
258
|
+
|
|
259
|
+
const protocols = options?.protocols;
|
|
260
|
+
const permissionProcessor = options?.permissionProcessor;
|
|
261
|
+
|
|
262
|
+
const result: Record<string, any> = {};
|
|
263
|
+
for (const [key, value] of Object.entries(pattern)) {
|
|
264
|
+
if (typeof value === 'string' && value.startsWith('?.')) {
|
|
265
|
+
const aliased = applyAliases(value, aliasMap);
|
|
266
|
+
const parts = parseCachedPath(aliased);
|
|
267
|
+
result[key] = parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor);
|
|
268
|
+
} else if (typeof value === 'string' && value.startsWith('$0')) {
|
|
269
|
+
const rootRef = resolveRootReference(value, source, options?.root);
|
|
270
|
+
if (rootRef === null) {
|
|
271
|
+
result[key] = source;
|
|
272
|
+
} else {
|
|
273
|
+
const aliased = applyAliases(rootRef.path, aliasMap);
|
|
274
|
+
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
275
|
+
const parts = parseCachedPath(normalizedPath);
|
|
276
|
+
result[key] = parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor);
|
|
277
|
+
}
|
|
278
|
+
} else if (typeof value === 'string' && protocols && hasProtocol(value)) {
|
|
279
|
+
result[key] = getProtocolValue(value, protocols, options);
|
|
280
|
+
} else if (Array.isArray(value)) {
|
|
281
|
+
result[key] = getArray(value, source, aliasMap, withMethods, protocols, options);
|
|
282
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
283
|
+
const proto = Object.getPrototypeOf(value);
|
|
284
|
+
if (proto === Object.prototype || proto === null) {
|
|
285
|
+
result[key] = getValues(value, source, options);
|
|
286
|
+
} else {
|
|
287
|
+
result[key] = value;
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
result[key] = value;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Synchronously resolve a single `?.`-delimited path string against a source object.
|
|
298
|
+
*
|
|
299
|
+
* @param path - A `?.`-delimited path string (e.g., '?.user?.name')
|
|
300
|
+
* @param source - Object to resolve the path against
|
|
301
|
+
* @param options - Optional withMethods and aka
|
|
302
|
+
* @returns The resolved value, or undefined if any segment is nullish
|
|
303
|
+
*/
|
|
304
|
+
export function getValue(
|
|
305
|
+
path: string,
|
|
306
|
+
source: any,
|
|
307
|
+
options?: GetValuesOptions
|
|
308
|
+
): any {
|
|
309
|
+
const rootRef = resolveRootReference(path, source, options?.root);
|
|
310
|
+
if (rootRef) {
|
|
311
|
+
path = rootRef.path;
|
|
312
|
+
source = rootRef.source;
|
|
313
|
+
} else if (!path.startsWith('?.')) {
|
|
314
|
+
return path;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
let aliased = path;
|
|
318
|
+
const { aliasMap } = normalizeAliasOptions(options);
|
|
319
|
+
if (aliasMap.size > 0) {
|
|
320
|
+
aliased = applyAliases(path, aliasMap);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
324
|
+
const parts = parseCachedPath(normalizedPath);
|
|
325
|
+
if (parts.length === 0) return source;
|
|
326
|
+
|
|
327
|
+
const { withMethods } = normalizeAliasOptions(options);
|
|
328
|
+
const permissionProcessor = options?.permissionProcessor;
|
|
329
|
+
|
|
330
|
+
return navigatePath(source, parts, withMethods, permissionProcessor);
|
|
331
|
+
}
|