assign-gingerly 0.0.90 → 0.0.91
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 +42 -0
- package/assignFrom.js +50 -4
- package/assignFrom.ts +49 -4
- package/assignFromAsync.js +24 -2
- package/assignFromAsync.ts +24 -2
- package/package.json +1 -1
- package/resolve/getValues.js +87 -44
- package/resolve/getValues.ts +106 -38
package/README.md
CHANGED
|
@@ -102,6 +102,8 @@ assignFrom adds support for:
|
|
|
102
102
|
5. Looped substitution with `where_x_in` / `where_y_in` / `where_z_in` for expanding template patterns into multiple concrete assignments.
|
|
103
103
|
6. Dynamic substitutions via the `substitutions` option for injecting runtime string values into path segments. See [docs/substitutions.md](docs/substitutions.md).
|
|
104
104
|
7. Spread merging via the `"..."` key.
|
|
105
|
+
8. Boolean coercion / negation of a resolved value via a leading `!` marker: `'!!?.isOpen'` resolves `?.isOpen` then coerces to a real boolean, `'!?.isOpen'` negates it (`!!!` negates, and so on). Only honored in front of a `?.` path, `$0` reference, or protocol — a plain literal like `'!important'` is left untouched. Nullish coerces to `false`, which makes `!!` the safe way to feed an optional source value into a boolean sink (`hidden`, `disabled`, `classList.toggle`'s force argument, …).
|
|
106
|
+
9. Multiple invocations of one `withMethods` method via the ` =*` operator — see [docs/multi-invoke.md](docs/multi-invoke.md).
|
|
105
107
|
|
|
106
108
|
Example:
|
|
107
109
|
|
|
@@ -853,6 +855,7 @@ While we are in the business of passing values of object A into object B, we mig
|
|
|
853
855
|
| ` ?=` | Ternary | Conditional assignment (assignFrom only) — [details](docs/ternary-assignment.md) | `'?.text ?=': ['?.cond', 'yes', 'no']` |
|
|
854
856
|
| ` =>` | Handler | Invoke a handler plugin (assignFrom only) | `'?.el =>': { do: 'builtIns.lazyLoad', ... }` |
|
|
855
857
|
| ` =&` | Sync op | Compute a value synchronously and assign it back (assignFrom only) — no dynamic import, no await, ever | `'?.text =&': { join: ['?.first', ' ', '?.last'] }` |
|
|
858
|
+
| ` =*` | Multi-invoke | Call a `withMethods` method once per argument-list (assignFrom only) — [details](docs/multi-invoke.md) | `` '?.classList?.toggle =*': [['a', '!!?.x'], ['b', '!!?.y']] `` |
|
|
856
859
|
|
|
857
860
|
All operators use a space before the suffix to distinguish them from property names. They compose with `?.` nested paths and `withMethods`.
|
|
858
861
|
|
|
@@ -957,6 +960,45 @@ The `=!` command syntax is `<path> =!` where the path uses the `?.` nested notat
|
|
|
957
960
|
|
|
958
961
|
For existing values, the toggle is performed using JavaScript's logical NOT operator (`!value`), regardless of what type it is.
|
|
959
962
|
|
|
963
|
+
## Example 5a - Conditional class lists with the `!!` marker and `=*` command
|
|
964
|
+
|
|
965
|
+
The imperative pattern
|
|
966
|
+
|
|
967
|
+
```JS
|
|
968
|
+
if (vm.isOpen) el.classList.add('isOpenCls');
|
|
969
|
+
else el.classList.remove('isOpenCls');
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
is just `el.classList.toggle('isOpenCls', vm.isOpen)`. In `assignFrom` that is a `toggle`
|
|
973
|
+
call in `withMethods` with a resolved force argument — and the `!!` marker coerces the
|
|
974
|
+
resolved value to a real boolean so a missing source value *removes* the class instead of
|
|
975
|
+
flipping it:
|
|
976
|
+
|
|
977
|
+
```TypeScript
|
|
978
|
+
assignFrom(el, {
|
|
979
|
+
'?.classList?.toggle': ['isOpenCls', '!!?.isOpen']
|
|
980
|
+
}, { from: vm, withMethods: ['toggle'] });
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
For several classes at once, the `=*` operator runs the method once per argument-list:
|
|
984
|
+
|
|
985
|
+
```TypeScript
|
|
986
|
+
const vm = { isOpen: true, isLoading: false, hasError: true };
|
|
987
|
+
|
|
988
|
+
assignFrom(el, {
|
|
989
|
+
'?.classList?.toggle =*': [
|
|
990
|
+
['isOpenCls', '!!?.isOpen'],
|
|
991
|
+
['loadingCls', '!!?.isLoading'],
|
|
992
|
+
['errorCls', '!!?.hasError'],
|
|
993
|
+
]
|
|
994
|
+
}, { from: vm, withMethods: ['toggle'] });
|
|
995
|
+
|
|
996
|
+
// toggle('isOpenCls', true); toggle('loadingCls', false); toggle('errorCls', true)
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
See [docs/multi-invoke.md](docs/multi-invoke.md) for the full `=*` semantics, including its
|
|
1000
|
+
interaction with `where_x_in`.
|
|
1001
|
+
|
|
960
1002
|
## Example 6 - Deleting properties with -= command
|
|
961
1003
|
|
|
962
1004
|
The `-=` command allows us to delete properties from objects:
|
package/assignFrom.js
CHANGED
|
@@ -57,6 +57,21 @@ export function parseSyncOpCommand(key) {
|
|
|
57
57
|
return null;
|
|
58
58
|
return key.substring(0, key.length - 3); // Remove ' =&' suffix
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a key ends with the multi-invoke operator ' =*'.
|
|
62
|
+
*/
|
|
63
|
+
export function isMultiInvokeCommand(key) {
|
|
64
|
+
return key.endsWith(' =*');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a =* multi-invoke command and extract the base LHS path — the path a
|
|
68
|
+
* `withMethods` method sits at, to be called once per argument-list on the RHS.
|
|
69
|
+
*/
|
|
70
|
+
export function parseMultiInvokeCommand(key) {
|
|
71
|
+
if (!isMultiInvokeCommand(key))
|
|
72
|
+
return null;
|
|
73
|
+
return key.substring(0, key.length - 3); // Remove ' =*' suffix
|
|
74
|
+
}
|
|
60
75
|
/**
|
|
61
76
|
* Throws if a resolved sync-op value (or anything nested inside it) is a thenable.
|
|
62
77
|
*
|
|
@@ -338,12 +353,19 @@ export function expandSubstitutions(pattern, options) {
|
|
|
338
353
|
return mergeHandlerDuplicates(entries);
|
|
339
354
|
}
|
|
340
355
|
/**
|
|
341
|
-
* Convert entries to an object
|
|
356
|
+
* Convert entries to an object. Duplicate keys — which `where_x_in` expansion can
|
|
357
|
+
* produce — are collapsed with last-wins, except:
|
|
358
|
+
* - ` =>` handler keys merge into the Multiple Handlers array form.
|
|
359
|
+
* - ` =*` multi-invoke keys concatenate their argument-list arrays, so an
|
|
360
|
+
* expanded pattern still invokes the method once per expansion.
|
|
342
361
|
*/
|
|
343
362
|
export function mergeHandlerDuplicates(entries) {
|
|
344
363
|
const result = {};
|
|
345
364
|
for (const [key, value] of entries) {
|
|
346
|
-
if (key.endsWith('
|
|
365
|
+
if (key in result && key.endsWith(' =*') && Array.isArray(result[key]) && Array.isArray(value)) {
|
|
366
|
+
result[key] = result[key].concat(value);
|
|
367
|
+
}
|
|
368
|
+
else if (key.endsWith(' =>') && key in result) {
|
|
347
369
|
const existing = result[key];
|
|
348
370
|
if (Array.isArray(existing)) {
|
|
349
371
|
existing.push(value);
|
|
@@ -389,6 +411,7 @@ export function categorizeKeys(expandedPattern) {
|
|
|
389
411
|
const idRefHandlerKeys = [];
|
|
390
412
|
const ternaryKeys = [];
|
|
391
413
|
const syncOpKeys = [];
|
|
414
|
+
const multiInvokeKeys = [];
|
|
392
415
|
for (const key of Object.keys(expandedPattern)) {
|
|
393
416
|
if (isHandlerCommand(key)) {
|
|
394
417
|
if (key.startsWith('#[')) {
|
|
@@ -404,6 +427,9 @@ export function categorizeKeys(expandedPattern) {
|
|
|
404
427
|
else if (isSyncOpCommand(key)) {
|
|
405
428
|
syncOpKeys.push(key);
|
|
406
429
|
}
|
|
430
|
+
else if (isMultiInvokeCommand(key)) {
|
|
431
|
+
multiInvokeKeys.push(key);
|
|
432
|
+
}
|
|
407
433
|
else if (key.startsWith('#[')) {
|
|
408
434
|
idRefNormalKeys.push(key);
|
|
409
435
|
}
|
|
@@ -411,7 +437,7 @@ export function categorizeKeys(expandedPattern) {
|
|
|
411
437
|
normalPattern[key] = expandedPattern[key];
|
|
412
438
|
}
|
|
413
439
|
}
|
|
414
|
-
return { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys };
|
|
440
|
+
return { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys, multiInvokeKeys };
|
|
415
441
|
}
|
|
416
442
|
/**
|
|
417
443
|
* Merge pin and at into a single lookup map for resolveIdVariable.
|
|
@@ -469,7 +495,7 @@ export function assignFrom(target, pattern, options, permissionProcessor) {
|
|
|
469
495
|
// Expand looped substitution variables
|
|
470
496
|
const expandedPattern = expandSubstitutions(pattern, options);
|
|
471
497
|
// Categorize keys
|
|
472
|
-
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys } = categorizeKeys(expandedPattern);
|
|
498
|
+
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys, multiInvokeKeys } = categorizeKeys(expandedPattern);
|
|
473
499
|
const resolveOptions = { ...options, root: target, permissionProcessor };
|
|
474
500
|
// Process ?= ternary keys (sync)
|
|
475
501
|
if (ternaryKeys.length > 0) {
|
|
@@ -517,6 +543,26 @@ export function assignFrom(target, pattern, options, permissionProcessor) {
|
|
|
517
543
|
}
|
|
518
544
|
}
|
|
519
545
|
}
|
|
546
|
+
// Process =* multi-invoke keys (sync): call a withMethods method sitting at the
|
|
547
|
+
// base path once per argument-list on the RHS. Each argument-list is resolved
|
|
548
|
+
// against `from` (so `?.` paths and `!!` markers work), then delegated to
|
|
549
|
+
// assignGingerly as a single array-valued key — which spreads it as call args.
|
|
550
|
+
if (multiInvokeKeys.length > 0) {
|
|
551
|
+
for (const key of multiInvokeKeys) {
|
|
552
|
+
const basePath = parseMultiInvokeCommand(key);
|
|
553
|
+
if (basePath === null)
|
|
554
|
+
continue;
|
|
555
|
+
const callList = expandedPattern[key];
|
|
556
|
+
if (!Array.isArray(callList)) {
|
|
557
|
+
throw new Error(`assignFrom: multi-invoke command "${key}" requires an array of argument-lists`);
|
|
558
|
+
}
|
|
559
|
+
for (const rawArgs of callList) {
|
|
560
|
+
const argList = Array.isArray(rawArgs) ? rawArgs : [rawArgs];
|
|
561
|
+
const resolved = getValues({ __args: argList }, options.from, resolveOptions).__args;
|
|
562
|
+
assignGingerly(target, { [basePath]: resolved }, options, permissionProcessor);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
520
566
|
// Process normal keys via getValues (sync) + assignGingerly
|
|
521
567
|
if (Object.keys(normalPattern).length > 0) {
|
|
522
568
|
// Resolve #[x] references on RHS values before getValues
|
package/assignFrom.ts
CHANGED
|
@@ -67,6 +67,22 @@ export function parseSyncOpCommand(key: string): string | null {
|
|
|
67
67
|
return key.substring(0, key.length - 3); // Remove ' =&' suffix
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Check if a key ends with the multi-invoke operator ' =*'.
|
|
72
|
+
*/
|
|
73
|
+
export function isMultiInvokeCommand(key: string): boolean {
|
|
74
|
+
return key.endsWith(' =*');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse a =* multi-invoke command and extract the base LHS path — the path a
|
|
79
|
+
* `withMethods` method sits at, to be called once per argument-list on the RHS.
|
|
80
|
+
*/
|
|
81
|
+
export function parseMultiInvokeCommand(key: string): string | null {
|
|
82
|
+
if (!isMultiInvokeCommand(key)) return null;
|
|
83
|
+
return key.substring(0, key.length - 3); // Remove ' =*' suffix
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
/**
|
|
71
87
|
* Throws if a resolved sync-op value (or anything nested inside it) is a thenable.
|
|
72
88
|
*
|
|
@@ -349,12 +365,18 @@ export function expandSubstitutions(
|
|
|
349
365
|
}
|
|
350
366
|
|
|
351
367
|
/**
|
|
352
|
-
* Convert entries to an object
|
|
368
|
+
* Convert entries to an object. Duplicate keys — which `where_x_in` expansion can
|
|
369
|
+
* produce — are collapsed with last-wins, except:
|
|
370
|
+
* - ` =>` handler keys merge into the Multiple Handlers array form.
|
|
371
|
+
* - ` =*` multi-invoke keys concatenate their argument-list arrays, so an
|
|
372
|
+
* expanded pattern still invokes the method once per expansion.
|
|
353
373
|
*/
|
|
354
374
|
export function mergeHandlerDuplicates(entries: [string, any][]): Record<string, any> {
|
|
355
375
|
const result: Record<string, any> = {};
|
|
356
376
|
for (const [key, value] of entries) {
|
|
357
|
-
if (key.endsWith('
|
|
377
|
+
if (key in result && key.endsWith(' =*') && Array.isArray(result[key]) && Array.isArray(value)) {
|
|
378
|
+
result[key] = (result[key] as any[]).concat(value);
|
|
379
|
+
} else if (key.endsWith(' =>') && key in result) {
|
|
358
380
|
const existing = result[key];
|
|
359
381
|
if (Array.isArray(existing)) {
|
|
360
382
|
existing.push(value);
|
|
@@ -400,6 +422,7 @@ export function categorizeKeys(expandedPattern: Record<string, any>) {
|
|
|
400
422
|
const idRefHandlerKeys: string[] = [];
|
|
401
423
|
const ternaryKeys: string[] = [];
|
|
402
424
|
const syncOpKeys: string[] = [];
|
|
425
|
+
const multiInvokeKeys: string[] = [];
|
|
403
426
|
|
|
404
427
|
for (const key of Object.keys(expandedPattern)) {
|
|
405
428
|
if (isHandlerCommand(key)) {
|
|
@@ -412,6 +435,8 @@ export function categorizeKeys(expandedPattern: Record<string, any>) {
|
|
|
412
435
|
ternaryKeys.push(key);
|
|
413
436
|
} else if (isSyncOpCommand(key)) {
|
|
414
437
|
syncOpKeys.push(key);
|
|
438
|
+
} else if (isMultiInvokeCommand(key)) {
|
|
439
|
+
multiInvokeKeys.push(key);
|
|
415
440
|
} else if (key.startsWith('#[')) {
|
|
416
441
|
idRefNormalKeys.push(key);
|
|
417
442
|
} else {
|
|
@@ -419,7 +444,7 @@ export function categorizeKeys(expandedPattern: Record<string, any>) {
|
|
|
419
444
|
}
|
|
420
445
|
}
|
|
421
446
|
|
|
422
|
-
return { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys };
|
|
447
|
+
return { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys, multiInvokeKeys };
|
|
423
448
|
}
|
|
424
449
|
|
|
425
450
|
/**
|
|
@@ -495,7 +520,7 @@ export function assignFrom(
|
|
|
495
520
|
const expandedPattern = expandSubstitutions(pattern, options);
|
|
496
521
|
|
|
497
522
|
// Categorize keys
|
|
498
|
-
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys } = categorizeKeys(expandedPattern);
|
|
523
|
+
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, ternaryKeys, syncOpKeys, multiInvokeKeys } = categorizeKeys(expandedPattern);
|
|
499
524
|
|
|
500
525
|
const resolveOptions = { ...options, root: target, permissionProcessor } as any;
|
|
501
526
|
|
|
@@ -547,6 +572,26 @@ export function assignFrom(
|
|
|
547
572
|
}
|
|
548
573
|
}
|
|
549
574
|
|
|
575
|
+
// Process =* multi-invoke keys (sync): call a withMethods method sitting at the
|
|
576
|
+
// base path once per argument-list on the RHS. Each argument-list is resolved
|
|
577
|
+
// against `from` (so `?.` paths and `!!` markers work), then delegated to
|
|
578
|
+
// assignGingerly as a single array-valued key — which spreads it as call args.
|
|
579
|
+
if (multiInvokeKeys.length > 0) {
|
|
580
|
+
for (const key of multiInvokeKeys) {
|
|
581
|
+
const basePath = parseMultiInvokeCommand(key);
|
|
582
|
+
if (basePath === null) continue;
|
|
583
|
+
const callList = expandedPattern[key];
|
|
584
|
+
if (!Array.isArray(callList)) {
|
|
585
|
+
throw new Error(`assignFrom: multi-invoke command "${key}" requires an array of argument-lists`);
|
|
586
|
+
}
|
|
587
|
+
for (const rawArgs of callList) {
|
|
588
|
+
const argList = Array.isArray(rawArgs) ? rawArgs : [rawArgs];
|
|
589
|
+
const resolved = getValues({ __args: argList }, options.from, resolveOptions).__args;
|
|
590
|
+
assignGingerly(target, { [basePath]: resolved }, options, permissionProcessor);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
550
595
|
// Process normal keys via getValues (sync) + assignGingerly
|
|
551
596
|
if (Object.keys(normalPattern).length > 0) {
|
|
552
597
|
// Resolve #[x] references on RHS values before getValues
|
package/assignFromAsync.js
CHANGED
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { resolveValues } from './resolve/resolveValues.js';
|
|
23
23
|
import assignGingerly from './assignGingerly.js';
|
|
24
|
-
import { expandSubstitutions, categorizeKeys, handleSpreads } from './assignFrom.js';
|
|
24
|
+
import { expandSubstitutions, categorizeKeys, handleSpreads, parseMultiInvokeCommand } from './assignFrom.js';
|
|
25
25
|
// Module cache for processHandlerCommands — avoids await on dynamic import after first call
|
|
26
26
|
let _processHandlerCommands;
|
|
27
27
|
export async function assignFromAsync(target, pattern, options, permissionProcessor) {
|
|
28
28
|
// First: expand looped substitution variables (${x}, ${y}, ${z})
|
|
29
29
|
const expandedPattern = expandSubstitutions(pattern, options);
|
|
30
30
|
// Categorize keys
|
|
31
|
-
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys } = categorizeKeys(expandedPattern);
|
|
31
|
+
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, multiInvokeKeys } = categorizeKeys(expandedPattern);
|
|
32
32
|
// Process normal keys via resolveValues + assignGingerly
|
|
33
33
|
if (Object.keys(normalPattern).length > 0) {
|
|
34
34
|
const resolved = await resolveValues(normalPattern, options.from, {
|
|
@@ -44,6 +44,28 @@ export async function assignFromAsync(target, pattern, options, permissionProces
|
|
|
44
44
|
handleSpreads(resolved);
|
|
45
45
|
assignGingerly(target, resolved, options, permissionProcessor);
|
|
46
46
|
}
|
|
47
|
+
// Process =* multi-invoke keys: call a withMethods method sitting at the base
|
|
48
|
+
// path once per argument-list on the RHS (see assignFrom for the sync form).
|
|
49
|
+
if (multiInvokeKeys && multiInvokeKeys.length > 0) {
|
|
50
|
+
const { withMethods, aka, akaMethods, substitutions, protocols, from } = options;
|
|
51
|
+
for (const key of multiInvokeKeys) {
|
|
52
|
+
const basePath = parseMultiInvokeCommand(key);
|
|
53
|
+
if (basePath === null)
|
|
54
|
+
continue;
|
|
55
|
+
const callList = expandedPattern[key];
|
|
56
|
+
if (!Array.isArray(callList)) {
|
|
57
|
+
throw new Error(`assignFrom: multi-invoke command "${key}" requires an array of argument-lists`);
|
|
58
|
+
}
|
|
59
|
+
for (const rawArgs of callList) {
|
|
60
|
+
const argList = Array.isArray(rawArgs) ? rawArgs : [rawArgs];
|
|
61
|
+
const resolved = await resolveValues({ __args: argList }, from, {
|
|
62
|
+
withMethods, aka, akaMethods, substitutions, protocols,
|
|
63
|
+
root: target, permissionProcessor
|
|
64
|
+
});
|
|
65
|
+
assignGingerly(target, { [basePath]: resolved.__args }, options, permissionProcessor);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
47
69
|
// Process #[x] normal keys — resolve element, then apply remaining path + value
|
|
48
70
|
if (idRefNormalKeys.length > 0 && (options.pin || options.at)) {
|
|
49
71
|
const ids = { ...options.pin, ...options.at };
|
package/assignFromAsync.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {IAssignGingerlyOptions} from './types/assign-gingerly/types.js';
|
|
|
24
24
|
import assignGingerly from './assignGingerly.js';
|
|
25
25
|
import type { PermissionProcessor, AssignFromHandler, AssignFromHandlerConstructor } from './types/assign-gingerly/types.js';
|
|
26
26
|
import {
|
|
27
|
-
expandSubstitutions, categorizeKeys, handleSpreads, isHandlerCommand
|
|
27
|
+
expandSubstitutions, categorizeKeys, handleSpreads, isHandlerCommand, parseMultiInvokeCommand
|
|
28
28
|
} from './assignFrom.js';
|
|
29
29
|
|
|
30
30
|
export interface AssignFromOptions extends IAssignGingerlyOptions {
|
|
@@ -61,7 +61,7 @@ export async function assignFromAsync(
|
|
|
61
61
|
const expandedPattern = expandSubstitutions(pattern, options);
|
|
62
62
|
|
|
63
63
|
// Categorize keys
|
|
64
|
-
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys } = categorizeKeys(expandedPattern);
|
|
64
|
+
const { handlerKeys, normalPattern, idRefNormalKeys, idRefHandlerKeys, multiInvokeKeys } = categorizeKeys(expandedPattern);
|
|
65
65
|
|
|
66
66
|
// Process normal keys via resolveValues + assignGingerly
|
|
67
67
|
if (Object.keys(normalPattern).length > 0) {
|
|
@@ -81,6 +81,28 @@ export async function assignFromAsync(
|
|
|
81
81
|
assignGingerly(target, resolved, options, permissionProcessor);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// Process =* multi-invoke keys: call a withMethods method sitting at the base
|
|
85
|
+
// path once per argument-list on the RHS (see assignFrom for the sync form).
|
|
86
|
+
if (multiInvokeKeys && multiInvokeKeys.length > 0) {
|
|
87
|
+
const { withMethods, aka, akaMethods, substitutions, protocols, from } = options;
|
|
88
|
+
for (const key of multiInvokeKeys) {
|
|
89
|
+
const basePath = parseMultiInvokeCommand(key);
|
|
90
|
+
if (basePath === null) continue;
|
|
91
|
+
const callList = expandedPattern[key];
|
|
92
|
+
if (!Array.isArray(callList)) {
|
|
93
|
+
throw new Error(`assignFrom: multi-invoke command "${key}" requires an array of argument-lists`);
|
|
94
|
+
}
|
|
95
|
+
for (const rawArgs of callList) {
|
|
96
|
+
const argList = Array.isArray(rawArgs) ? rawArgs : [rawArgs];
|
|
97
|
+
const resolved = await resolveValues({ __args: argList }, from, {
|
|
98
|
+
withMethods, aka, akaMethods, substitutions, protocols,
|
|
99
|
+
root: target, permissionProcessor
|
|
100
|
+
});
|
|
101
|
+
assignGingerly(target, { [basePath]: resolved.__args }, options, permissionProcessor);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
// Process #[x] normal keys — resolve element, then apply remaining path + value
|
|
85
107
|
if (idRefNormalKeys.length > 0 && (options.pin || options.at)) {
|
|
86
108
|
const ids = { ...options.pin, ...options.at };
|
package/package.json
CHANGED
package/resolve/getValues.js
CHANGED
|
@@ -188,6 +188,80 @@ function navigatePath(source, parts, withMethods, permissionProcessor) {
|
|
|
188
188
|
function hasProtocol(value) {
|
|
189
189
|
return value.includes('://');
|
|
190
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Detect a leading run of `!` characters used as a boolean coercion / negation
|
|
193
|
+
* marker (`!` = negate, `!!` = coerce to boolean, `!!!` = negate, …). Returns the
|
|
194
|
+
* count and the remaining string, or null when the value does not start with `!`.
|
|
195
|
+
*
|
|
196
|
+
* The marker is only *honored* when the remainder is itself a resolvable
|
|
197
|
+
* reference (see `looksLikeReference`); otherwise the original string is a plain
|
|
198
|
+
* literal — e.g. a CSS `!important` passes through untouched.
|
|
199
|
+
*/
|
|
200
|
+
function parseNegationMarker(value) {
|
|
201
|
+
let count = 0;
|
|
202
|
+
while (count < value.length && value.charCodeAt(count) === 33 /* '!' */)
|
|
203
|
+
count++;
|
|
204
|
+
if (count === 0)
|
|
205
|
+
return null;
|
|
206
|
+
return { count, rest: value.slice(count) };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Whether a string should be resolved as a value reference rather than kept as a
|
|
210
|
+
* literal: a `?.` path, a `$0` root reference, or a recognized protocol.
|
|
211
|
+
*/
|
|
212
|
+
function looksLikeReference(value, protocols) {
|
|
213
|
+
return value.startsWith('?.')
|
|
214
|
+
|| value.startsWith('$0')
|
|
215
|
+
|| (!!protocols && hasProtocol(value));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Resolve a single reference string (`?.` path, `$0` root ref, or protocol) to
|
|
219
|
+
* its value. Callers must confirm `looksLikeReference(value)` first.
|
|
220
|
+
*/
|
|
221
|
+
function resolveReferenceString(value, source, aliasMap, withMethods, protocols, options, substitutionMap) {
|
|
222
|
+
const permissionProcessor = options?.permissionProcessor;
|
|
223
|
+
if (value.startsWith('?.')) {
|
|
224
|
+
const substituted = applySubstitutions(value, substitutionMap);
|
|
225
|
+
const aliased = applyAliases(substituted, aliasMap);
|
|
226
|
+
const parts = parseCachedPath(aliased);
|
|
227
|
+
return parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor);
|
|
228
|
+
}
|
|
229
|
+
if (value.startsWith('$0')) {
|
|
230
|
+
const rootRef = resolveRootReference(value, source, options?.root);
|
|
231
|
+
if (rootRef === null)
|
|
232
|
+
return source;
|
|
233
|
+
const substitutedPath = applySubstitutions(rootRef.path, substitutionMap);
|
|
234
|
+
const aliased = applyAliases(substitutedPath, aliasMap);
|
|
235
|
+
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
236
|
+
const parts = parseCachedPath(normalizedPath);
|
|
237
|
+
return parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor);
|
|
238
|
+
}
|
|
239
|
+
// protocol — looksLikeReference already ensured protocols is defined
|
|
240
|
+
return getProtocolValue(value, protocols, options);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Resolve a string RHS value: a reference (`?.` / `$0` / protocol), a
|
|
244
|
+
* `!`-prefixed coercion/negation of a reference, or a plain literal.
|
|
245
|
+
*/
|
|
246
|
+
function resolveStringValue(value, source, aliasMap, withMethods, protocols, options, substitutionMap) {
|
|
247
|
+
// A leading `!` run can only be a coercion/negation marker or a literal —
|
|
248
|
+
// no `?.` path, `$0` ref, or protocol name starts with `!`. Check it first so
|
|
249
|
+
// `hasProtocol` inside `looksLikeReference` can't misread `!!proto://x`.
|
|
250
|
+
if (value.charCodeAt(0) === 33 /* '!' */) {
|
|
251
|
+
const neg = parseNegationMarker(value);
|
|
252
|
+
if (neg !== null && looksLikeReference(neg.rest, protocols)) {
|
|
253
|
+
let resolved = resolveReferenceString(neg.rest, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
254
|
+
for (let i = 0; i < neg.count; i++)
|
|
255
|
+
resolved = !resolved;
|
|
256
|
+
return resolved;
|
|
257
|
+
}
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
if (looksLikeReference(value, protocols)) {
|
|
261
|
+
return resolveReferenceString(value, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
262
|
+
}
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
191
265
|
/**
|
|
192
266
|
* Resolve a protocol-prefixed value synchronously.
|
|
193
267
|
*/
|
|
@@ -212,30 +286,10 @@ function getProtocolValue(value, protocols, options) {
|
|
|
212
286
|
* Recurses into nested arrays and plain objects.
|
|
213
287
|
*/
|
|
214
288
|
function getArray(arr, source, aliasMap, withMethods, protocols, options, substitutionMap) {
|
|
215
|
-
const permissionProcessor = options?.permissionProcessor;
|
|
216
289
|
const result = [];
|
|
217
290
|
for (const item of arr) {
|
|
218
|
-
if (typeof item === 'string'
|
|
219
|
-
|
|
220
|
-
const aliased = applyAliases(substituted, aliasMap);
|
|
221
|
-
const parts = parseCachedPath(aliased);
|
|
222
|
-
result.push(parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor));
|
|
223
|
-
}
|
|
224
|
-
else if (typeof item === 'string' && item.startsWith('$0')) {
|
|
225
|
-
const rootRef = resolveRootReference(item, source, options?.root);
|
|
226
|
-
if (rootRef === null) {
|
|
227
|
-
result.push(source);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
const substitutedPath = applySubstitutions(rootRef.path, substitutionMap);
|
|
231
|
-
const aliased = applyAliases(substitutedPath, aliasMap);
|
|
232
|
-
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
233
|
-
const parts = parseCachedPath(normalizedPath);
|
|
234
|
-
result.push(parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor));
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
else if (typeof item === 'string' && protocols && hasProtocol(item)) {
|
|
238
|
-
result.push(getProtocolValue(item, protocols, options));
|
|
291
|
+
if (typeof item === 'string') {
|
|
292
|
+
result.push(resolveStringValue(item, source, aliasMap, withMethods, protocols, options, substitutionMap));
|
|
239
293
|
}
|
|
240
294
|
else if (Array.isArray(item)) {
|
|
241
295
|
result.push(getArray(item, source, aliasMap, withMethods, protocols, options, substitutionMap));
|
|
@@ -271,30 +325,10 @@ export function getValues(pattern, source, options) {
|
|
|
271
325
|
const { aliasMap, withMethods } = normalizeAliasOptions(options);
|
|
272
326
|
const substitutionMap = resolveSubstitutions(options?.substitutions, source, options);
|
|
273
327
|
const protocols = options?.protocols;
|
|
274
|
-
const permissionProcessor = options?.permissionProcessor;
|
|
275
328
|
const result = {};
|
|
276
329
|
for (const [key, value] of Object.entries(pattern)) {
|
|
277
|
-
if (typeof value === 'string'
|
|
278
|
-
|
|
279
|
-
const aliased = applyAliases(substituted, aliasMap);
|
|
280
|
-
const parts = parseCachedPath(aliased);
|
|
281
|
-
result[key] = parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor);
|
|
282
|
-
}
|
|
283
|
-
else if (typeof value === 'string' && value.startsWith('$0')) {
|
|
284
|
-
const rootRef = resolveRootReference(value, source, options?.root);
|
|
285
|
-
if (rootRef === null) {
|
|
286
|
-
result[key] = source;
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
const substitutedPath = applySubstitutions(rootRef.path, substitutionMap);
|
|
290
|
-
const aliased = applyAliases(substitutedPath, aliasMap);
|
|
291
|
-
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
292
|
-
const parts = parseCachedPath(normalizedPath);
|
|
293
|
-
result[key] = parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else if (typeof value === 'string' && protocols && hasProtocol(value)) {
|
|
297
|
-
result[key] = getProtocolValue(value, protocols, options);
|
|
330
|
+
if (typeof value === 'string') {
|
|
331
|
+
result[key] = resolveStringValue(value, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
298
332
|
}
|
|
299
333
|
else if (Array.isArray(value)) {
|
|
300
334
|
result[key] = getArray(value, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
@@ -323,6 +357,15 @@ export function getValues(pattern, source, options) {
|
|
|
323
357
|
* @returns The resolved value, or undefined if any segment is nullish
|
|
324
358
|
*/
|
|
325
359
|
export function getValue(path, source, options) {
|
|
360
|
+
// Leading `!` run: coerce/negate the resolved reference (`!` negate, `!!` boolean, …).
|
|
361
|
+
// Only honored in front of a `?.` path or `$0` root reference — otherwise literal.
|
|
362
|
+
const neg = parseNegationMarker(path);
|
|
363
|
+
if (neg !== null && (neg.rest.startsWith('?.') || neg.rest.startsWith('$0'))) {
|
|
364
|
+
let resolved = getValue(neg.rest, source, options);
|
|
365
|
+
for (let i = 0; i < neg.count; i++)
|
|
366
|
+
resolved = !resolved;
|
|
367
|
+
return resolved;
|
|
368
|
+
}
|
|
326
369
|
const rootRef = resolveRootReference(path, source, options?.root);
|
|
327
370
|
if (rootRef) {
|
|
328
371
|
path = rootRef.path;
|
package/resolve/getValues.ts
CHANGED
|
@@ -227,6 +227,99 @@ function hasProtocol(value: string): boolean {
|
|
|
227
227
|
return value.includes('://');
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Detect a leading run of `!` characters used as a boolean coercion / negation
|
|
232
|
+
* marker (`!` = negate, `!!` = coerce to boolean, `!!!` = negate, …). Returns the
|
|
233
|
+
* count and the remaining string, or null when the value does not start with `!`.
|
|
234
|
+
*
|
|
235
|
+
* The marker is only *honored* when the remainder is itself a resolvable
|
|
236
|
+
* reference (see `looksLikeReference`); otherwise the original string is a plain
|
|
237
|
+
* literal — e.g. a CSS `!important` passes through untouched.
|
|
238
|
+
*/
|
|
239
|
+
function parseNegationMarker(value: string): { count: number; rest: string } | null {
|
|
240
|
+
let count = 0;
|
|
241
|
+
while (count < value.length && value.charCodeAt(count) === 33 /* '!' */) count++;
|
|
242
|
+
if (count === 0) return null;
|
|
243
|
+
return { count, rest: value.slice(count) };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Whether a string should be resolved as a value reference rather than kept as a
|
|
248
|
+
* literal: a `?.` path, a `$0` root reference, or a recognized protocol.
|
|
249
|
+
*/
|
|
250
|
+
function looksLikeReference(
|
|
251
|
+
value: string,
|
|
252
|
+
protocols: Record<string, (key: string) => any> | undefined
|
|
253
|
+
): boolean {
|
|
254
|
+
return value.startsWith('?.')
|
|
255
|
+
|| value.startsWith('$0')
|
|
256
|
+
|| (!!protocols && hasProtocol(value));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Resolve a single reference string (`?.` path, `$0` root ref, or protocol) to
|
|
261
|
+
* its value. Callers must confirm `looksLikeReference(value)` first.
|
|
262
|
+
*/
|
|
263
|
+
function resolveReferenceString(
|
|
264
|
+
value: string,
|
|
265
|
+
source: any,
|
|
266
|
+
aliasMap: Map<string, string>,
|
|
267
|
+
withMethods: Set<string> | undefined,
|
|
268
|
+
protocols: Record<string, (key: string) => any> | undefined,
|
|
269
|
+
options: GetValuesOptions | undefined,
|
|
270
|
+
substitutionMap: Map<string, string> | undefined
|
|
271
|
+
): any {
|
|
272
|
+
const permissionProcessor = options?.permissionProcessor;
|
|
273
|
+
if (value.startsWith('?.')) {
|
|
274
|
+
const substituted = applySubstitutions(value, substitutionMap);
|
|
275
|
+
const aliased = applyAliases(substituted, aliasMap);
|
|
276
|
+
const parts = parseCachedPath(aliased);
|
|
277
|
+
return parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor);
|
|
278
|
+
}
|
|
279
|
+
if (value.startsWith('$0')) {
|
|
280
|
+
const rootRef = resolveRootReference(value, source, options?.root);
|
|
281
|
+
if (rootRef === null) return source;
|
|
282
|
+
const substitutedPath = applySubstitutions(rootRef.path, substitutionMap);
|
|
283
|
+
const aliased = applyAliases(substitutedPath, aliasMap);
|
|
284
|
+
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
285
|
+
const parts = parseCachedPath(normalizedPath);
|
|
286
|
+
return parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor);
|
|
287
|
+
}
|
|
288
|
+
// protocol — looksLikeReference already ensured protocols is defined
|
|
289
|
+
return getProtocolValue(value, protocols!, options);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Resolve a string RHS value: a reference (`?.` / `$0` / protocol), a
|
|
294
|
+
* `!`-prefixed coercion/negation of a reference, or a plain literal.
|
|
295
|
+
*/
|
|
296
|
+
function resolveStringValue(
|
|
297
|
+
value: string,
|
|
298
|
+
source: any,
|
|
299
|
+
aliasMap: Map<string, string>,
|
|
300
|
+
withMethods: Set<string> | undefined,
|
|
301
|
+
protocols: Record<string, (key: string) => any> | undefined,
|
|
302
|
+
options: GetValuesOptions | undefined,
|
|
303
|
+
substitutionMap: Map<string, string> | undefined
|
|
304
|
+
): any {
|
|
305
|
+
// A leading `!` run can only be a coercion/negation marker or a literal —
|
|
306
|
+
// no `?.` path, `$0` ref, or protocol name starts with `!`. Check it first so
|
|
307
|
+
// `hasProtocol` inside `looksLikeReference` can't misread `!!proto://x`.
|
|
308
|
+
if (value.charCodeAt(0) === 33 /* '!' */) {
|
|
309
|
+
const neg = parseNegationMarker(value);
|
|
310
|
+
if (neg !== null && looksLikeReference(neg.rest, protocols)) {
|
|
311
|
+
let resolved = resolveReferenceString(neg.rest, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
312
|
+
for (let i = 0; i < neg.count; i++) resolved = !resolved;
|
|
313
|
+
return resolved;
|
|
314
|
+
}
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
if (looksLikeReference(value, protocols)) {
|
|
318
|
+
return resolveReferenceString(value, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
319
|
+
}
|
|
320
|
+
return value;
|
|
321
|
+
}
|
|
322
|
+
|
|
230
323
|
/**
|
|
231
324
|
* Resolve a protocol-prefixed value synchronously.
|
|
232
325
|
*/
|
|
@@ -268,27 +361,10 @@ function getArray(
|
|
|
268
361
|
options?: GetValuesOptions,
|
|
269
362
|
substitutionMap?: Map<string, string>
|
|
270
363
|
): any[] {
|
|
271
|
-
const permissionProcessor = options?.permissionProcessor;
|
|
272
364
|
const result: any[] = [];
|
|
273
365
|
for (const item of arr) {
|
|
274
|
-
if (typeof item === 'string'
|
|
275
|
-
|
|
276
|
-
const aliased = applyAliases(substituted, aliasMap);
|
|
277
|
-
const parts = parseCachedPath(aliased);
|
|
278
|
-
result.push(parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor));
|
|
279
|
-
} else if (typeof item === 'string' && item.startsWith('$0')) {
|
|
280
|
-
const rootRef = resolveRootReference(item, source, options?.root);
|
|
281
|
-
if (rootRef === null) {
|
|
282
|
-
result.push(source);
|
|
283
|
-
} else {
|
|
284
|
-
const substitutedPath = applySubstitutions(rootRef.path, substitutionMap);
|
|
285
|
-
const aliased = applyAliases(substitutedPath, aliasMap);
|
|
286
|
-
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
287
|
-
const parts = parseCachedPath(normalizedPath);
|
|
288
|
-
result.push(parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor));
|
|
289
|
-
}
|
|
290
|
-
} else if (typeof item === 'string' && protocols && hasProtocol(item)) {
|
|
291
|
-
result.push(getProtocolValue(item, protocols, options));
|
|
366
|
+
if (typeof item === 'string') {
|
|
367
|
+
result.push(resolveStringValue(item, source, aliasMap, withMethods, protocols, options, substitutionMap));
|
|
292
368
|
} else if (Array.isArray(item)) {
|
|
293
369
|
result.push(getArray(item, source, aliasMap, withMethods, protocols, options, substitutionMap));
|
|
294
370
|
} else if (item && typeof item === 'object') {
|
|
@@ -326,28 +402,11 @@ export function getValues(
|
|
|
326
402
|
const substitutionMap = resolveSubstitutions(options?.substitutions, source, options);
|
|
327
403
|
|
|
328
404
|
const protocols = options?.protocols;
|
|
329
|
-
const permissionProcessor = options?.permissionProcessor;
|
|
330
405
|
|
|
331
406
|
const result: Record<string, any> = {};
|
|
332
407
|
for (const [key, value] of Object.entries(pattern)) {
|
|
333
|
-
if (typeof value === 'string'
|
|
334
|
-
|
|
335
|
-
const aliased = applyAliases(substituted, aliasMap);
|
|
336
|
-
const parts = parseCachedPath(aliased);
|
|
337
|
-
result[key] = parts.length === 0 ? source : navigatePath(source, parts, withMethods, permissionProcessor);
|
|
338
|
-
} else if (typeof value === 'string' && value.startsWith('$0')) {
|
|
339
|
-
const rootRef = resolveRootReference(value, source, options?.root);
|
|
340
|
-
if (rootRef === null) {
|
|
341
|
-
result[key] = source;
|
|
342
|
-
} else {
|
|
343
|
-
const substitutedPath = applySubstitutions(rootRef.path, substitutionMap);
|
|
344
|
-
const aliased = applyAliases(substitutedPath, aliasMap);
|
|
345
|
-
const normalizedPath = aliased.startsWith('?.') ? aliased : (aliased ? `?.${aliased}` : '?.');
|
|
346
|
-
const parts = parseCachedPath(normalizedPath);
|
|
347
|
-
result[key] = parts.length === 0 ? rootRef.source : navigatePath(rootRef.source, parts, withMethods, permissionProcessor);
|
|
348
|
-
}
|
|
349
|
-
} else if (typeof value === 'string' && protocols && hasProtocol(value)) {
|
|
350
|
-
result[key] = getProtocolValue(value, protocols, options);
|
|
408
|
+
if (typeof value === 'string') {
|
|
409
|
+
result[key] = resolveStringValue(value, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
351
410
|
} else if (Array.isArray(value)) {
|
|
352
411
|
result[key] = getArray(value, source, aliasMap, withMethods, protocols, options, substitutionMap);
|
|
353
412
|
} else if (typeof value === 'object' && value !== null) {
|
|
@@ -377,6 +436,15 @@ export function getValue(
|
|
|
377
436
|
source: any,
|
|
378
437
|
options?: GetValuesOptions
|
|
379
438
|
): any {
|
|
439
|
+
// Leading `!` run: coerce/negate the resolved reference (`!` negate, `!!` boolean, …).
|
|
440
|
+
// Only honored in front of a `?.` path or `$0` root reference — otherwise literal.
|
|
441
|
+
const neg = parseNegationMarker(path);
|
|
442
|
+
if (neg !== null && (neg.rest.startsWith('?.') || neg.rest.startsWith('$0'))) {
|
|
443
|
+
let resolved = getValue(neg.rest, source, options);
|
|
444
|
+
for (let i = 0; i < neg.count; i++) resolved = !resolved;
|
|
445
|
+
return resolved;
|
|
446
|
+
}
|
|
447
|
+
|
|
380
448
|
const rootRef = resolveRootReference(path, source, options?.root);
|
|
381
449
|
if (rootRef) {
|
|
382
450
|
path = rootRef.path;
|