assign-gingerly 0.0.57 → 0.0.59
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/README.md +243 -131
- package/assignFrom.js +124 -129
- package/assignFrom.ts +146 -233
- package/assignFromAsync.js +118 -0
- package/assignFromAsync.ts +240 -0
- package/getValues.js +223 -0
- package/getValues.ts +255 -0
- package/handlers/join.ts +1 -1
- package/handlers/lazyLoad.js +2 -2
- package/handlers/lazyLoad.ts +3 -3
- package/handlers/lazyLoadSwitch.ts +1 -1
- package/handlers/manageTemplateList.js +203 -0
- package/handlers/manageTemplateList.ts +240 -0
- package/handlers/microDataJoin.ts +1 -1
- package/index.js +1 -0
- package/index.ts +2 -0
- package/inferredAssignments.js +1 -1
- package/inferredAssignments.ts +2 -2
- package/markerUtils.js +136 -127
- package/package.json +18 -1
- package/processHandlerCommands.js +30 -3
- package/processHandlerCommands.ts +31 -7
- package/resolveValues.js +41 -125
- package/resolveValues.ts +131 -255
- package/transitionHelper.js +11 -5
- package/transitionHelper.ts +11 -5
- package/types/assign-gingerly/types.d.ts +51 -0
- package/waitForSettled.js +57 -0
- package/waitForSettled.ts +65 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Dynamically imported only when ` =>` keys are detected in the pattern.
|
|
5
5
|
*/
|
|
6
6
|
import { resolveValues } from './resolveValues.js';
|
|
7
|
+
import { getValues } from './getValues.js';
|
|
7
8
|
import { evaluatePathWithMethods } from './assignGingerly.js';
|
|
8
9
|
import { isAllowedImportPath } from './isAllowedImportPath.js';
|
|
9
10
|
/**
|
|
@@ -15,6 +16,7 @@ const BUILT_IN_MAP = {
|
|
|
15
16
|
'builtIns.lazyLoadSwitch': './handlers/lazyLoadSwitch.js',
|
|
16
17
|
'builtIns.join': './handlers/join.js',
|
|
17
18
|
'builtIns.microDataJoin': './handlers/microDataJoin.js',
|
|
19
|
+
'builtIns.manageTemplateList': './handlers/manageTemplateList.js',
|
|
18
20
|
};
|
|
19
21
|
/**
|
|
20
22
|
* Find a handler class in a dynamically imported module.
|
|
@@ -35,16 +37,27 @@ function findHandlerInModule(module) {
|
|
|
35
37
|
}
|
|
36
38
|
return undefined;
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Cache for loaded built-in handler classes — avoids await on subsequent calls.
|
|
42
|
+
*/
|
|
43
|
+
const handlerCache = new Map();
|
|
38
44
|
/**
|
|
39
45
|
* Dynamically load a built-in handler by name.
|
|
40
46
|
* Returns the handler constructor, or undefined if the name isn't a recognized built-in.
|
|
47
|
+
* Cached after first load — subsequent calls are synchronous.
|
|
41
48
|
*/
|
|
42
49
|
async function loadBuiltIn(name) {
|
|
50
|
+
const cached = handlerCache.get(name);
|
|
51
|
+
if (cached)
|
|
52
|
+
return cached;
|
|
43
53
|
const path = BUILT_IN_MAP[name];
|
|
44
54
|
if (!path)
|
|
45
55
|
return undefined;
|
|
46
56
|
const module = await import(path);
|
|
47
|
-
|
|
57
|
+
const cls = findHandlerInModule(module);
|
|
58
|
+
if (cls)
|
|
59
|
+
handlerCache.set(name, cls);
|
|
60
|
+
return cls;
|
|
48
61
|
}
|
|
49
62
|
/**
|
|
50
63
|
* Resolve a handler from options.handlers (class constructor or import path).
|
|
@@ -157,27 +170,41 @@ export async function processHandlerCommands(target, handlerKeys, pattern, optio
|
|
|
157
170
|
}
|
|
158
171
|
// Execute handlers sequentially, sharing the same lhsTarget
|
|
159
172
|
for (const config of configs) {
|
|
173
|
+
//return; //1.3ms
|
|
160
174
|
// 1. Check options.handlers (local, per-call)
|
|
161
175
|
let HandlerClass = await resolveFromHandlers(config.do, options.handlers, permissions);
|
|
176
|
+
//return; // 1.4
|
|
162
177
|
// 2. Fallback to built-in auto-load
|
|
163
178
|
if (!HandlerClass && config.do.startsWith('builtIns.')) {
|
|
164
179
|
HandlerClass = await loadBuiltIn(config.do);
|
|
165
180
|
}
|
|
181
|
+
//return; //1.4
|
|
166
182
|
if (!HandlerClass) {
|
|
167
183
|
throw new Error(`assignFrom: unknown handler "${config.do}". Provide it in options.handlers.`);
|
|
168
184
|
}
|
|
169
|
-
// Resolve '
|
|
185
|
+
// Resolve 'get' map synchronously (no thread yield)
|
|
170
186
|
let resolvedParams = {};
|
|
187
|
+
if (config.get) {
|
|
188
|
+
resolvedParams = getValues(config.get, options.from, {
|
|
189
|
+
withMethods: options.withMethods,
|
|
190
|
+
aka: options.aka,
|
|
191
|
+
protocols: options.protocols
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// Resolve 'resolve' map asynchronously (yields to microtask queue)
|
|
171
195
|
if (config.resolve) {
|
|
172
|
-
|
|
196
|
+
const asyncResolved = await resolveValues(config.resolve, options.from, {
|
|
173
197
|
withMethods: options.withMethods,
|
|
174
198
|
aka: options.aka,
|
|
175
199
|
protocols: options.protocols
|
|
176
200
|
});
|
|
201
|
+
Object.assign(resolvedParams, asyncResolved);
|
|
177
202
|
}
|
|
178
203
|
// Instantiate and invoke the handler
|
|
179
204
|
const handler = new HandlerClass(config);
|
|
205
|
+
//return; //1.5ms
|
|
180
206
|
const result = await handler.assign(lhsTarget, resolvedParams, options);
|
|
207
|
+
//return; //1.5ms
|
|
181
208
|
// Return-value protocol: if handler returns a non-undefined value,
|
|
182
209
|
// assign it back to the LHS path
|
|
183
210
|
if (result !== undefined && lhsParent != null && lhsKey != null) {
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { resolveValues } from './resolveValues.js';
|
|
8
|
+
import { getValues } from './getValues.js';
|
|
8
9
|
import { evaluatePathWithMethods } from './assignGingerly.js';
|
|
9
10
|
import { isAllowedImportPath } from './isAllowedImportPath.js';
|
|
10
11
|
import type { AssignPermissions } from './isAllowedImportPath.js';
|
|
11
|
-
import type { AssignFromOptions, AssignFromHandlerConstructor } from './
|
|
12
|
+
import type { AssignFromOptions, AssignFromHandlerConstructor } from './assignFromAsync.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Map of built-in handler names to their module paths.
|
|
@@ -19,6 +20,7 @@ const BUILT_IN_MAP: Record<string, string> = {
|
|
|
19
20
|
'builtIns.lazyLoadSwitch': './handlers/lazyLoadSwitch.js',
|
|
20
21
|
'builtIns.join': './handlers/join.js',
|
|
21
22
|
'builtIns.microDataJoin': './handlers/microDataJoin.js',
|
|
23
|
+
'builtIns.manageTemplateList': './handlers/manageTemplateList.js',
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -41,15 +43,25 @@ function findHandlerInModule(module: any): AssignFromHandlerConstructor | undefi
|
|
|
41
43
|
return undefined;
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Cache for loaded built-in handler classes — avoids await on subsequent calls.
|
|
48
|
+
*/
|
|
49
|
+
const handlerCache = new Map<string, AssignFromHandlerConstructor>();
|
|
50
|
+
|
|
44
51
|
/**
|
|
45
52
|
* Dynamically load a built-in handler by name.
|
|
46
53
|
* Returns the handler constructor, or undefined if the name isn't a recognized built-in.
|
|
54
|
+
* Cached after first load — subsequent calls are synchronous.
|
|
47
55
|
*/
|
|
48
56
|
async function loadBuiltIn(name: string): Promise<AssignFromHandlerConstructor | undefined> {
|
|
57
|
+
const cached = handlerCache.get(name);
|
|
58
|
+
if (cached) return cached;
|
|
49
59
|
const path = BUILT_IN_MAP[name];
|
|
50
60
|
if (!path) return undefined;
|
|
51
61
|
const module = await import(path);
|
|
52
|
-
|
|
62
|
+
const cls = findHandlerInModule(module);
|
|
63
|
+
if (cls) handlerCache.set(name, cls);
|
|
64
|
+
return cls;
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
/**
|
|
@@ -107,6 +119,7 @@ export async function processHandlerCommands(
|
|
|
107
119
|
options: AssignFromOptions,
|
|
108
120
|
permissions?: AssignPermissions
|
|
109
121
|
): Promise<void> {
|
|
122
|
+
|
|
110
123
|
for (const key of handlerKeys) {
|
|
111
124
|
const lhsPath = key.substring(0, key.length - 3); // Remove ' =>'
|
|
112
125
|
const rhs = pattern[key];
|
|
@@ -178,35 +191,46 @@ export async function processHandlerCommands(
|
|
|
178
191
|
} else {
|
|
179
192
|
lhsTarget = target;
|
|
180
193
|
}
|
|
181
|
-
|
|
182
194
|
// Execute handlers sequentially, sharing the same lhsTarget
|
|
183
195
|
for (const config of configs) {
|
|
196
|
+
//return; //1.3ms
|
|
184
197
|
// 1. Check options.handlers (local, per-call)
|
|
185
198
|
let HandlerClass = await resolveFromHandlers(config.do, options.handlers, permissions);
|
|
186
|
-
|
|
199
|
+
//return; // 1.4
|
|
187
200
|
// 2. Fallback to built-in auto-load
|
|
188
201
|
if (!HandlerClass && config.do.startsWith('builtIns.')) {
|
|
189
202
|
HandlerClass = await loadBuiltIn(config.do);
|
|
190
203
|
}
|
|
204
|
+
//return; //1.4
|
|
191
205
|
|
|
192
206
|
if (!HandlerClass) {
|
|
193
207
|
throw new Error(`assignFrom: unknown handler "${config.do}". Provide it in options.handlers.`);
|
|
194
208
|
}
|
|
195
209
|
|
|
196
|
-
// Resolve '
|
|
210
|
+
// Resolve 'get' map synchronously (no thread yield)
|
|
197
211
|
let resolvedParams: Record<string, any> = {};
|
|
212
|
+
if (config.get) {
|
|
213
|
+
resolvedParams = getValues(config.get, options.from, {
|
|
214
|
+
withMethods: options.withMethods,
|
|
215
|
+
aka: options.aka,
|
|
216
|
+
protocols: options.protocols
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// Resolve 'resolve' map asynchronously (yields to microtask queue)
|
|
198
220
|
if (config.resolve) {
|
|
199
|
-
|
|
221
|
+
const asyncResolved = await resolveValues(config.resolve, options.from, {
|
|
200
222
|
withMethods: options.withMethods,
|
|
201
223
|
aka: options.aka,
|
|
202
224
|
protocols: options.protocols
|
|
203
225
|
});
|
|
226
|
+
Object.assign(resolvedParams, asyncResolved);
|
|
204
227
|
}
|
|
205
228
|
|
|
206
229
|
// Instantiate and invoke the handler
|
|
207
230
|
const handler = new HandlerClass(config);
|
|
231
|
+
//return; //1.5ms
|
|
208
232
|
const result = await handler.assign(lhsTarget, resolvedParams, options);
|
|
209
|
-
|
|
233
|
+
//return; //1.5ms
|
|
210
234
|
// Return-value protocol: if handler returns a non-undefined value,
|
|
211
235
|
// assign it back to the LHS path
|
|
212
236
|
if (result !== undefined && lhsParent != null && lhsKey != null) {
|
package/resolveValues.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveValues.ts — Async value resolution for path strings.
|
|
3
|
+
*
|
|
4
|
+
* Thin async wrapper around getValues that adds support for async protocol handlers.
|
|
5
|
+
* For synchronous-only use cases, import getValues/getValue directly for better performance.
|
|
6
|
+
*
|
|
7
|
+
* Re-exports ResolveValuesOptions for backward compatibility.
|
|
8
|
+
*/
|
|
9
|
+
import { getValue } from './getValues.js';
|
|
10
|
+
// Re-export getValue as resolveValue for backward compatibility
|
|
11
|
+
export { getValue as resolveValue };
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a string value looks like a protocol reference.
|
|
14
|
+
*/
|
|
15
|
+
function hasProtocol(value) {
|
|
16
|
+
return value.includes('://');
|
|
17
|
+
}
|
|
1
18
|
/**
|
|
2
19
|
* Apply alias substitutions to a path string.
|
|
3
|
-
* Replaces complete tokens between `?.` delimiters with their aliased values.
|
|
4
20
|
*/
|
|
5
21
|
function applyAliases(path, aliasMap) {
|
|
6
22
|
if (aliasMap.size === 0)
|
|
@@ -11,12 +27,8 @@ function applyAliases(path, aliasMap) {
|
|
|
11
27
|
}
|
|
12
28
|
/**
|
|
13
29
|
* Path cache for parsed path strings.
|
|
14
|
-
* Avoids re-splitting the same path on repeated calls.
|
|
15
30
|
*/
|
|
16
31
|
const pathCache = new Map();
|
|
17
|
-
/**
|
|
18
|
-
* Parse a `?.`-delimited path string into segments, with caching.
|
|
19
|
-
*/
|
|
20
32
|
function parseCachedPath(path) {
|
|
21
33
|
let parts = pathCache.get(path);
|
|
22
34
|
if (!parts) {
|
|
@@ -25,44 +37,8 @@ function parseCachedPath(path) {
|
|
|
25
37
|
}
|
|
26
38
|
return parts;
|
|
27
39
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Resolves a protocol-prefixed value (e.g., 'globalThis://key?.path').
|
|
30
|
-
*
|
|
31
|
-
* 1. Extracts the protocol name (before '://')
|
|
32
|
-
* 2. If the protocol isn't in the protocols map, returns the value unchanged (false positive)
|
|
33
|
-
* 3. Extracts the key (between '://' and first '?.' or end of string)
|
|
34
|
-
* 4. Calls the protocol handler with the key
|
|
35
|
-
* 5. If there's a remaining '?.' path, resolves it against the handler's result
|
|
36
|
-
*/
|
|
37
|
-
async function resolveProtocolValue(value, protocols, options) {
|
|
38
|
-
// Extract protocol name (before ://)
|
|
39
|
-
const protoEnd = value.indexOf('://');
|
|
40
|
-
const protocol = value.substring(0, protoEnd);
|
|
41
|
-
// Resolve via protocol handler
|
|
42
|
-
const handler = protocols[protocol];
|
|
43
|
-
if (!handler)
|
|
44
|
-
return value; // false flag — coincidentally looks like a protocol
|
|
45
|
-
const rest = value.substring(protoEnd + 3);
|
|
46
|
-
// Split at first ?. to separate key from path
|
|
47
|
-
const pathStart = rest.indexOf('?.');
|
|
48
|
-
const key = pathStart === -1 ? rest : rest.substring(0, pathStart);
|
|
49
|
-
const path = pathStart === -1 ? null : rest.substring(pathStart);
|
|
50
|
-
const resolved = await handler(key);
|
|
51
|
-
// If there's a remaining path, resolve it against the result
|
|
52
|
-
if (path) {
|
|
53
|
-
return resolveValue(path, resolved, options);
|
|
54
|
-
}
|
|
55
|
-
return resolved;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Checks if a string value looks like a protocol reference.
|
|
59
|
-
*/
|
|
60
|
-
function hasProtocol(value) {
|
|
61
|
-
return value.includes('://');
|
|
62
|
-
}
|
|
63
40
|
/**
|
|
64
41
|
* Navigate a path against a source object, optionally calling methods.
|
|
65
|
-
* Returns the resolved value at the end of the path.
|
|
66
42
|
*/
|
|
67
43
|
function navigatePath(source, parts, withMethods) {
|
|
68
44
|
let current = source;
|
|
@@ -76,12 +52,10 @@ function navigatePath(source, parts, withMethods) {
|
|
|
76
52
|
if (typeof method === 'function') {
|
|
77
53
|
const nextPart = parts[i + 1];
|
|
78
54
|
if (nextPart !== undefined && !(withMethods.has(nextPart))) {
|
|
79
|
-
// Call method with next segment as argument, consume it
|
|
80
55
|
current = method.call(current, nextPart);
|
|
81
56
|
i += 2;
|
|
82
57
|
}
|
|
83
58
|
else {
|
|
84
|
-
// Consecutive methods or last segment — call with no args
|
|
85
59
|
current = method.call(current);
|
|
86
60
|
i++;
|
|
87
61
|
}
|
|
@@ -99,9 +73,26 @@ function navigatePath(source, parts, withMethods) {
|
|
|
99
73
|
return current;
|
|
100
74
|
}
|
|
101
75
|
/**
|
|
102
|
-
*
|
|
103
|
-
|
|
104
|
-
|
|
76
|
+
* Resolves a protocol-prefixed value asynchronously.
|
|
77
|
+
*/
|
|
78
|
+
async function resolveProtocolValue(value, protocols, options) {
|
|
79
|
+
const protoEnd = value.indexOf('://');
|
|
80
|
+
const protocol = value.substring(0, protoEnd);
|
|
81
|
+
const handler = protocols[protocol];
|
|
82
|
+
if (!handler)
|
|
83
|
+
return value;
|
|
84
|
+
const rest = value.substring(protoEnd + 3);
|
|
85
|
+
const pathStart = rest.indexOf('?.');
|
|
86
|
+
const key = pathStart === -1 ? rest : rest.substring(0, pathStart);
|
|
87
|
+
const path = pathStart === -1 ? null : rest.substring(pathStart);
|
|
88
|
+
const resolved = await handler(key);
|
|
89
|
+
if (path) {
|
|
90
|
+
return getValue(path, resolved, options);
|
|
91
|
+
}
|
|
92
|
+
return resolved;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve path strings and protocol references within an array (async).
|
|
105
96
|
*/
|
|
106
97
|
async function resolveArray(arr, source, aliasMap, withMethods, protocols, options) {
|
|
107
98
|
const result = [];
|
|
@@ -133,47 +124,23 @@ async function resolveArray(arr, source, aliasMap, withMethods, protocols, optio
|
|
|
133
124
|
return result;
|
|
134
125
|
}
|
|
135
126
|
/**
|
|
136
|
-
*
|
|
127
|
+
* Async resolve RHS path strings in a pattern object against a source object.
|
|
137
128
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* Non-string values and strings not starting with `?.` pass through unchanged.
|
|
141
|
-
*
|
|
142
|
-
* Supports `withMethods` for calling methods during resolution and `aka` for
|
|
143
|
-
* alias substitution, consistent with assignGingerly's LHS path handling.
|
|
144
|
-
*
|
|
145
|
-
* Special case: `'?.'` (empty path) resolves to the source object itself.
|
|
129
|
+
* Supports async protocol handlers (e.g., fetch, IndexedDB).
|
|
130
|
+
* For synchronous-only patterns, use `getValues` from 'assign-gingerly/getValues.js' instead.
|
|
146
131
|
*
|
|
147
132
|
* @param pattern - Object whose RHS values may contain `?.` path strings
|
|
148
133
|
* @param source - Object to resolve paths against
|
|
149
|
-
* @param options - Optional withMethods
|
|
134
|
+
* @param options - Optional withMethods, aka, and protocol handlers
|
|
150
135
|
* @returns New object with path strings replaced by resolved values
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* const result = resolveValues({
|
|
154
|
-
* hello: '?.myPropContainer?.stringProp',
|
|
155
|
-
* foo: '?.myFooString',
|
|
156
|
-
* literal: 42
|
|
157
|
-
* }, source);
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* // With methods and aliases
|
|
161
|
-
* const result = resolveValues({
|
|
162
|
-
* text: '?.q?..username?.textContent'
|
|
163
|
-
* }, source, {
|
|
164
|
-
* withMethods: ['querySelector'],
|
|
165
|
-
* aka: { 'q': 'querySelector' }
|
|
166
|
-
* });
|
|
167
136
|
*/
|
|
168
137
|
export async function resolveValues(pattern, source, options) {
|
|
169
|
-
// Build alias map
|
|
170
138
|
const aliasMap = new Map();
|
|
171
139
|
if (options?.aka) {
|
|
172
140
|
for (const [alias, target] of Object.entries(options.aka)) {
|
|
173
141
|
aliasMap.set(alias, target);
|
|
174
142
|
}
|
|
175
143
|
}
|
|
176
|
-
// Build methods set
|
|
177
144
|
const withMethods = options?.withMethods
|
|
178
145
|
? options.withMethods instanceof Set
|
|
179
146
|
? options.withMethods
|
|
@@ -183,24 +150,17 @@ export async function resolveValues(pattern, source, options) {
|
|
|
183
150
|
const result = {};
|
|
184
151
|
for (const [key, value] of Object.entries(pattern)) {
|
|
185
152
|
if (typeof value === 'string' && value.startsWith('?.')) {
|
|
186
|
-
// Apply aliases to the RHS path
|
|
187
153
|
const aliased = applyAliases(value, aliasMap);
|
|
188
|
-
// Parse path with caching
|
|
189
154
|
const parts = parseCachedPath(aliased);
|
|
190
|
-
// Navigate with method support
|
|
191
155
|
result[key] = parts.length === 0 ? source : navigatePath(source, parts, withMethods);
|
|
192
156
|
}
|
|
193
157
|
else if (typeof value === 'string' && protocols && hasProtocol(value)) {
|
|
194
|
-
// Protocol-prefixed value — resolve asynchronously
|
|
195
158
|
result[key] = await resolveProtocolValue(value, protocols, options);
|
|
196
159
|
}
|
|
197
160
|
else if (Array.isArray(value)) {
|
|
198
|
-
// Resolve path strings and protocols within arrays (recursing into nested arrays)
|
|
199
161
|
result[key] = await resolveArray(value, source, aliasMap, withMethods, protocols, options);
|
|
200
162
|
}
|
|
201
163
|
else if (typeof value === 'object' && value !== null) {
|
|
202
|
-
// Recursively resolve nested plain objects (e.g., headers: { "...": "globalThis://key" })
|
|
203
|
-
// Only recurse into plain objects — skip DOM elements, class instances, etc.
|
|
204
164
|
const proto = Object.getPrototypeOf(value);
|
|
205
165
|
if (proto === Object.prototype || proto === null) {
|
|
206
166
|
result[key] = await resolveValues(value, source, options);
|
|
@@ -215,47 +175,3 @@ export async function resolveValues(pattern, source, options) {
|
|
|
215
175
|
}
|
|
216
176
|
return result;
|
|
217
177
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Resolve a single `?.`-delimited path string against a source object.
|
|
220
|
-
*
|
|
221
|
-
* This is a lighter-weight alternative to `resolveValues` when you only need
|
|
222
|
-
* to resolve one path and don't want the overhead of creating wrapper objects.
|
|
223
|
-
*
|
|
224
|
-
* @param path - A `?.`-delimited path string (e.g., '?.behaviors?.command')
|
|
225
|
-
* @param source - Object to resolve the path against
|
|
226
|
-
* @param options - Optional withMethods and aka for method calls and aliases
|
|
227
|
-
* @returns The resolved value, or undefined if any segment is nullish
|
|
228
|
-
*
|
|
229
|
-
* @example
|
|
230
|
-
* const value = resolveValue('?.behaviors?.commandBehavior?.command', el);
|
|
231
|
-
*
|
|
232
|
-
* @example
|
|
233
|
-
* const value = resolveValue('?.q?.myEl?.textContent', el, {
|
|
234
|
-
* withMethods: ['querySelector'],
|
|
235
|
-
* aka: { 'q': 'querySelector' }
|
|
236
|
-
* });
|
|
237
|
-
*/
|
|
238
|
-
export function resolveValue(path, source, options) {
|
|
239
|
-
if (!path.startsWith('?.'))
|
|
240
|
-
return path;
|
|
241
|
-
// Build alias map
|
|
242
|
-
let aliased = path;
|
|
243
|
-
if (options?.aka) {
|
|
244
|
-
const aliasMap = new Map();
|
|
245
|
-
for (const [alias, target] of Object.entries(options.aka)) {
|
|
246
|
-
aliasMap.set(alias, target);
|
|
247
|
-
}
|
|
248
|
-
aliased = applyAliases(path, aliasMap);
|
|
249
|
-
}
|
|
250
|
-
// Parse path with caching
|
|
251
|
-
const parts = parseCachedPath(aliased);
|
|
252
|
-
if (parts.length === 0)
|
|
253
|
-
return source;
|
|
254
|
-
// Build methods set
|
|
255
|
-
const withMethods = options?.withMethods
|
|
256
|
-
? options.withMethods instanceof Set
|
|
257
|
-
? options.withMethods
|
|
258
|
-
: new Set(options.withMethods)
|
|
259
|
-
: undefined;
|
|
260
|
-
return navigatePath(source, parts, withMethods);
|
|
261
|
-
}
|