assign-gingerly 0.0.79 → 0.0.80
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/paths.js +15 -10
- package/DX/paths.ts +96 -90
- package/assignGingerly.js +143 -54
- package/assignGingerly.ts +172 -56
- package/handleIshProperty.js +1 -1
- package/handleIshProperty.ts +1 -1
- package/handlers/manageTemplateList.js +1 -1
- package/handlers/manageTemplateList.ts +1 -1
- package/index.js +1 -1
- package/index.ts +1 -1
- package/inferencer/types/roundabout/types.d.ts +1 -0
- package/object-extension.js +1 -1
- package/object-extension.ts +1 -1
- package/package.json +7 -6
- /package/{waitForEvent.js → utils/waitForEvent.js} +0 -0
- /package/{waitForEvent.ts → utils/waitForEvent.ts} +0 -0
- /package/{waitForSettled.js → utils/waitForSettled.js} +0 -0
- /package/{waitForSettled.ts → utils/waitForSettled.ts} +0 -0
package/DX/paths.js
CHANGED
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
* The sp tag function uses this to auto-extract path strings from proxies.
|
|
28
28
|
*/
|
|
29
29
|
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
30
|
+
function isPathProxy(value) {
|
|
31
|
+
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
32
|
+
}
|
|
30
33
|
/**
|
|
31
34
|
* Create a proxy for id-ref paths (#[varName]).
|
|
32
35
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
@@ -34,6 +37,7 @@ const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
|
34
37
|
*/
|
|
35
38
|
function createIdRefProxy(idRef, options) {
|
|
36
39
|
function handler() { }
|
|
40
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
37
41
|
return new Proxy(handler, {
|
|
38
42
|
get(_, prop) {
|
|
39
43
|
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
@@ -53,7 +57,7 @@ function createIdRefProxy(idRef, options) {
|
|
|
53
57
|
argStr = 'true';
|
|
54
58
|
else if (arg === false)
|
|
55
59
|
argStr = 'false';
|
|
56
|
-
else if (arg
|
|
60
|
+
else if (isPathProxy(arg)) {
|
|
57
61
|
const fullPath = arg[PATH_SYMBOL];
|
|
58
62
|
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
59
63
|
}
|
|
@@ -77,6 +81,7 @@ function createPathProxy(prefix, options) {
|
|
|
77
81
|
const aliasMap = options?.aka;
|
|
78
82
|
// Use a function as the target to enable the apply trap
|
|
79
83
|
function handler() { }
|
|
84
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
80
85
|
return new Proxy(handler, {
|
|
81
86
|
get(_, prop) {
|
|
82
87
|
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
@@ -114,7 +119,7 @@ function createPathProxy(prefix, options) {
|
|
|
114
119
|
argStr = 'true';
|
|
115
120
|
else if (arg === false)
|
|
116
121
|
argStr = 'false';
|
|
117
|
-
else if (arg
|
|
122
|
+
else if (isPathProxy(arg)) {
|
|
118
123
|
// Proxy arg — extract path without '?.' prefix
|
|
119
124
|
const fullPath = arg[PATH_SYMBOL];
|
|
120
125
|
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
@@ -167,12 +172,12 @@ export function paths(options) {
|
|
|
167
172
|
* }
|
|
168
173
|
*/
|
|
169
174
|
export function set(lhs) {
|
|
170
|
-
const lhsStr = lhs
|
|
175
|
+
const lhsStr = isPathProxy(lhs)
|
|
171
176
|
? lhs[PATH_SYMBOL]
|
|
172
177
|
: String(lhs);
|
|
173
178
|
return {
|
|
174
179
|
to(rhs) {
|
|
175
|
-
const rhsStr = rhs
|
|
180
|
+
const rhsStr = isPathProxy(rhs)
|
|
176
181
|
? rhs[PATH_SYMBOL]
|
|
177
182
|
: rhs;
|
|
178
183
|
return { [lhsStr]: rhsStr };
|
|
@@ -198,7 +203,7 @@ export function set(lhs) {
|
|
|
198
203
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
199
204
|
*/
|
|
200
205
|
export function smoothOver(value) {
|
|
201
|
-
if (value
|
|
206
|
+
if (isPathProxy(value)) {
|
|
202
207
|
return value[PATH_SYMBOL];
|
|
203
208
|
}
|
|
204
209
|
if (Array.isArray(value)) {
|
|
@@ -294,13 +299,13 @@ export function sp(strings, ...values) {
|
|
|
294
299
|
result.push(strings[i]);
|
|
295
300
|
if (i < values.length) {
|
|
296
301
|
const v = values[i];
|
|
297
|
-
if (v
|
|
302
|
+
if (isPathProxy(v)) {
|
|
298
303
|
// Auto-extract path from proxy object
|
|
299
304
|
result.push(v[PATH_SYMBOL]);
|
|
300
305
|
}
|
|
301
306
|
else if (Array.isArray(v)) {
|
|
302
307
|
// Nested array — recursively extract paths from proxy elements
|
|
303
|
-
result.push(v.map(el => el
|
|
308
|
+
result.push(v.map(el => isPathProxy(el) ? el[PATH_SYMBOL] : el));
|
|
304
309
|
}
|
|
305
310
|
else {
|
|
306
311
|
result.push(v);
|
|
@@ -349,7 +354,7 @@ export function md(strings, ...values) {
|
|
|
349
354
|
result.push(strings[i]);
|
|
350
355
|
if (i < values.length) {
|
|
351
356
|
const v = values[i];
|
|
352
|
-
if (v
|
|
357
|
+
if (isPathProxy(v)) {
|
|
353
358
|
// Proxy object → {prop, val}
|
|
354
359
|
const pathStr = v[PATH_SYMBOL];
|
|
355
360
|
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
@@ -357,7 +362,7 @@ export function md(strings, ...values) {
|
|
|
357
362
|
else if (Array.isArray(v)) {
|
|
358
363
|
// Nested array — recursively convert proxy elements to {prop, val}
|
|
359
364
|
result.push(v.map(el => {
|
|
360
|
-
if (el
|
|
365
|
+
if (isPathProxy(el)) {
|
|
361
366
|
const pathStr = el[PATH_SYMBOL];
|
|
362
367
|
return { prop: extractPropName(pathStr), val: pathStr };
|
|
363
368
|
}
|
|
@@ -367,7 +372,7 @@ export function md(strings, ...values) {
|
|
|
367
372
|
else if (v && typeof v === 'object' && 'prop' in v) {
|
|
368
373
|
// Developer override object — extract val from proxy if present
|
|
369
374
|
const processed = { ...v };
|
|
370
|
-
if (processed.val
|
|
375
|
+
if (isPathProxy(processed.val)) {
|
|
371
376
|
processed.val = processed.val[PATH_SYMBOL];
|
|
372
377
|
}
|
|
373
378
|
result.push(processed);
|
package/DX/paths.ts
CHANGED
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
* Symbol used internally to detect path proxy objects.
|
|
28
28
|
* The sp tag function uses this to auto-extract path strings from proxies.
|
|
29
29
|
*/
|
|
30
|
-
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
30
|
+
const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
31
|
+
|
|
32
|
+
function isPathProxy(value: unknown): value is { [PATH_SYMBOL]: string } {
|
|
33
|
+
return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
|
|
34
|
+
}
|
|
31
35
|
|
|
32
36
|
/**
|
|
33
37
|
* Type that maps an object type to a proxy where every property access
|
|
@@ -58,13 +62,14 @@ export interface PathsOptions {
|
|
|
58
62
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
59
63
|
* .path returns the #[varName] prefix (optionally with further ?. path).
|
|
60
64
|
*/
|
|
61
|
-
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
62
|
-
function handler() {}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
66
|
+
function handler() {}
|
|
67
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
68
|
+
return new Proxy(handler, {
|
|
69
|
+
get(_, prop: string | symbol) {
|
|
70
|
+
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
71
|
+
return idRef;
|
|
72
|
+
}
|
|
68
73
|
if (typeof prop === 'symbol') return undefined;
|
|
69
74
|
|
|
70
75
|
// Chain further path segments after the id ref
|
|
@@ -73,15 +78,15 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
73
78
|
},
|
|
74
79
|
apply(_, __, args) {
|
|
75
80
|
if (args.length > 0) {
|
|
76
|
-
const arg = args[0];
|
|
77
|
-
let argStr: string;
|
|
78
|
-
if (arg === true) argStr = 'true';
|
|
79
|
-
else if (arg === false) argStr = 'false';
|
|
80
|
-
else if (arg
|
|
81
|
-
const fullPath = arg[PATH_SYMBOL] as string;
|
|
82
|
-
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
83
|
-
}
|
|
84
|
-
else argStr = String(arg);
|
|
81
|
+
const arg = args[0];
|
|
82
|
+
let argStr: string;
|
|
83
|
+
if (arg === true) argStr = 'true';
|
|
84
|
+
else if (arg === false) argStr = 'false';
|
|
85
|
+
else if (isPathProxy(arg)) {
|
|
86
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
87
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
88
|
+
}
|
|
89
|
+
else argStr = String(arg);
|
|
85
90
|
|
|
86
91
|
const chained = `${idRef}?.${argStr}`;
|
|
87
92
|
return createIdRefProxy(chained, options);
|
|
@@ -98,16 +103,17 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
98
103
|
* When `aka` is provided, property names that match an alias *value* are output
|
|
99
104
|
* using the alias *key* instead (reverse alias).
|
|
100
105
|
*/
|
|
101
|
-
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
102
|
-
const aliasMap = options?.aka;
|
|
103
|
-
|
|
104
|
-
// Use a function as the target to enable the apply trap
|
|
105
|
-
function handler() {}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
107
|
+
const aliasMap = options?.aka;
|
|
108
|
+
|
|
109
|
+
// Use a function as the target to enable the apply trap
|
|
110
|
+
function handler() {}
|
|
111
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
112
|
+
|
|
113
|
+
return new Proxy(handler, {
|
|
114
|
+
get(_, prop: string | symbol) {
|
|
115
|
+
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
116
|
+
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
111
117
|
}
|
|
112
118
|
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
113
119
|
if (typeof prop === 'symbol') return undefined;
|
|
@@ -135,16 +141,16 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
|
135
141
|
apply(_, __, args) {
|
|
136
142
|
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
137
143
|
if (args.length > 0) {
|
|
138
|
-
const arg = args[0];
|
|
139
|
-
let argStr: string;
|
|
140
|
-
if (arg === true) argStr = 'true';
|
|
141
|
-
else if (arg === false) argStr = 'false';
|
|
142
|
-
else if (arg
|
|
143
|
-
// Proxy arg — extract path without '?.' prefix
|
|
144
|
-
const fullPath = arg[PATH_SYMBOL] as string;
|
|
145
|
-
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
146
|
-
}
|
|
147
|
-
else argStr = String(arg);
|
|
144
|
+
const arg = args[0];
|
|
145
|
+
let argStr: string;
|
|
146
|
+
if (arg === true) argStr = 'true';
|
|
147
|
+
else if (arg === false) argStr = 'false';
|
|
148
|
+
else if (isPathProxy(arg)) {
|
|
149
|
+
// Proxy arg — extract path without '?.' prefix
|
|
150
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
151
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
152
|
+
}
|
|
153
|
+
else argStr = String(arg);
|
|
148
154
|
|
|
149
155
|
const newPath = prefix ? `${prefix}?.${argStr}` : argStr;
|
|
150
156
|
return createPathProxy(newPath, options);
|
|
@@ -193,18 +199,18 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
193
199
|
* count: 1
|
|
194
200
|
* }
|
|
195
201
|
*/
|
|
196
|
-
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
197
|
-
const lhsStr = lhs
|
|
198
|
-
? lhs[PATH_SYMBOL]
|
|
199
|
-
: String(lhs);
|
|
200
|
-
return {
|
|
201
|
-
to(rhs: any): Record<string, any> {
|
|
202
|
-
const rhsStr = rhs
|
|
203
|
-
? rhs[PATH_SYMBOL]
|
|
204
|
-
: rhs;
|
|
205
|
-
return { [lhsStr]: rhsStr };
|
|
206
|
-
}
|
|
207
|
-
};
|
|
202
|
+
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
203
|
+
const lhsStr = isPathProxy(lhs)
|
|
204
|
+
? lhs[PATH_SYMBOL]
|
|
205
|
+
: String(lhs);
|
|
206
|
+
return {
|
|
207
|
+
to(rhs: any): Record<string, any> {
|
|
208
|
+
const rhsStr = isPathProxy(rhs)
|
|
209
|
+
? rhs[PATH_SYMBOL]
|
|
210
|
+
: rhs;
|
|
211
|
+
return { [lhsStr]: rhsStr };
|
|
212
|
+
}
|
|
213
|
+
};
|
|
208
214
|
}
|
|
209
215
|
|
|
210
216
|
/**
|
|
@@ -225,19 +231,19 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
225
231
|
* });
|
|
226
232
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
227
233
|
*/
|
|
228
|
-
export function smoothOver(value: any): any {
|
|
229
|
-
if (value
|
|
230
|
-
return value[PATH_SYMBOL];
|
|
231
|
-
}
|
|
234
|
+
export function smoothOver(value: any): any {
|
|
235
|
+
if (isPathProxy(value)) {
|
|
236
|
+
return value[PATH_SYMBOL];
|
|
237
|
+
}
|
|
232
238
|
if (Array.isArray(value)) {
|
|
233
239
|
return value.map(smoothOver);
|
|
234
240
|
}
|
|
235
241
|
if (value && typeof value === 'object') {
|
|
236
242
|
const proto = Object.getPrototypeOf(value);
|
|
237
243
|
if (proto === Object.prototype || proto === null) {
|
|
238
|
-
const result: Record<string, any> = {};
|
|
239
|
-
for (const [k, v] of Object.entries(value)) {
|
|
240
|
-
result[k] = smoothOver(v);
|
|
244
|
+
const result: Record<string, any> = {};
|
|
245
|
+
for (const [k, v] of Object.entries(value)) {
|
|
246
|
+
result[k] = smoothOver(v);
|
|
241
247
|
}
|
|
242
248
|
return result;
|
|
243
249
|
}
|
|
@@ -322,23 +328,23 @@ export function forEachKeyIn<T>(
|
|
|
322
328
|
* sp`${$.lastName}${[', ', $.middleName]}, ${$.firstName}`
|
|
323
329
|
* // ['?.lastName', [', ', '?.middleName'], ', ', '?.firstName']
|
|
324
330
|
*/
|
|
325
|
-
export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
331
|
+
export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
326
332
|
const result: any[] = [];
|
|
327
333
|
for (let i = 0; i < strings.length; i++) {
|
|
328
334
|
if (strings[i]) result.push(strings[i]);
|
|
329
335
|
if (i < values.length) {
|
|
330
|
-
const v = values[i];
|
|
331
|
-
if (v
|
|
332
|
-
// Auto-extract path from proxy object
|
|
333
|
-
result.push(v[PATH_SYMBOL]);
|
|
334
|
-
} else if (Array.isArray(v)) {
|
|
335
|
-
// Nested array — recursively extract paths from proxy elements
|
|
336
|
-
result.push(v.map(el =>
|
|
337
|
-
el
|
|
338
|
-
));
|
|
339
|
-
} else {
|
|
340
|
-
result.push(v);
|
|
341
|
-
}
|
|
336
|
+
const v = values[i];
|
|
337
|
+
if (isPathProxy(v)) {
|
|
338
|
+
// Auto-extract path from proxy object
|
|
339
|
+
result.push(v[PATH_SYMBOL]);
|
|
340
|
+
} else if (Array.isArray(v)) {
|
|
341
|
+
// Nested array — recursively extract paths from proxy elements
|
|
342
|
+
result.push(v.map(el =>
|
|
343
|
+
isPathProxy(el) ? el[PATH_SYMBOL] : el
|
|
344
|
+
));
|
|
345
|
+
} else {
|
|
346
|
+
result.push(v);
|
|
347
|
+
}
|
|
342
348
|
}
|
|
343
349
|
}
|
|
344
350
|
return result;
|
|
@@ -383,27 +389,27 @@ export function md(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
383
389
|
for (let i = 0; i < strings.length; i++) {
|
|
384
390
|
if (strings[i]) result.push(strings[i]);
|
|
385
391
|
if (i < values.length) {
|
|
386
|
-
const v = values[i];
|
|
387
|
-
if (v
|
|
388
|
-
// Proxy object → {prop, val}
|
|
389
|
-
const pathStr = v[PATH_SYMBOL] as string;
|
|
390
|
-
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
391
|
-
} else if (Array.isArray(v)) {
|
|
392
|
-
// Nested array — recursively convert proxy elements to {prop, val}
|
|
393
|
-
result.push(v.map(el => {
|
|
394
|
-
if (el
|
|
395
|
-
const pathStr = el[PATH_SYMBOL] as string;
|
|
396
|
-
return { prop: extractPropName(pathStr), val: pathStr };
|
|
397
|
-
}
|
|
398
|
-
return el;
|
|
399
|
-
}));
|
|
400
|
-
} else if (v && typeof v === 'object' && 'prop' in v) {
|
|
401
|
-
// Developer override object — extract val from proxy if present
|
|
402
|
-
const processed = { ...v };
|
|
403
|
-
if (processed.val
|
|
404
|
-
processed.val = processed.val[PATH_SYMBOL];
|
|
405
|
-
}
|
|
406
|
-
result.push(processed);
|
|
392
|
+
const v = values[i];
|
|
393
|
+
if (isPathProxy(v)) {
|
|
394
|
+
// Proxy object → {prop, val}
|
|
395
|
+
const pathStr = v[PATH_SYMBOL] as string;
|
|
396
|
+
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
397
|
+
} else if (Array.isArray(v)) {
|
|
398
|
+
// Nested array — recursively convert proxy elements to {prop, val}
|
|
399
|
+
result.push(v.map(el => {
|
|
400
|
+
if (isPathProxy(el)) {
|
|
401
|
+
const pathStr = el[PATH_SYMBOL] as string;
|
|
402
|
+
return { prop: extractPropName(pathStr), val: pathStr };
|
|
403
|
+
}
|
|
404
|
+
return el;
|
|
405
|
+
}));
|
|
406
|
+
} else if (v && typeof v === 'object' && 'prop' in v) {
|
|
407
|
+
// Developer override object — extract val from proxy if present
|
|
408
|
+
const processed = { ...v };
|
|
409
|
+
if (isPathProxy(processed.val)) {
|
|
410
|
+
processed.val = processed.val[PATH_SYMBOL];
|
|
411
|
+
}
|
|
412
|
+
result.push(processed);
|
|
407
413
|
} else {
|
|
408
414
|
result.push(v);
|
|
409
415
|
}
|
package/assignGingerly.js
CHANGED
|
@@ -420,6 +420,7 @@ export function isAllowedMethod(methodName, withMethods, permissionProcessor) {
|
|
|
420
420
|
export function evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor) {
|
|
421
421
|
let current = target;
|
|
422
422
|
let i = 0;
|
|
423
|
+
let lastSegmentConsumed = false;
|
|
423
424
|
// Process all segments except the last one
|
|
424
425
|
while (i < pathParts.length - 1) {
|
|
425
426
|
const part = pathParts[i];
|
|
@@ -442,6 +443,9 @@ export function evaluatePathWithMethods(target, pathParts, value, withMethods, p
|
|
|
442
443
|
// Only current is method - call with next part as string arg
|
|
443
444
|
current = method.call(current, nextPart, ...appendArgs);
|
|
444
445
|
i++; // Skip next part since we consumed it as argument
|
|
446
|
+
if (i === pathParts.length - 1) {
|
|
447
|
+
lastSegmentConsumed = true;
|
|
448
|
+
}
|
|
445
449
|
}
|
|
446
450
|
}
|
|
447
451
|
else {
|
|
@@ -470,7 +474,8 @@ export function evaluatePathWithMethods(target, pathParts, value, withMethods, p
|
|
|
470
474
|
target: current,
|
|
471
475
|
lastKey,
|
|
472
476
|
isMethod: isAllowedMethod(lastKey, withMethods, permissionProcessor),
|
|
473
|
-
isZeroArg
|
|
477
|
+
isZeroArg,
|
|
478
|
+
lastSegmentConsumed
|
|
474
479
|
};
|
|
475
480
|
}
|
|
476
481
|
/**
|
|
@@ -623,6 +628,95 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
|
|
|
623
628
|
}
|
|
624
629
|
}
|
|
625
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Resolve the RHS of a toggle (=!) command to the value that should be negated.
|
|
633
|
+
* Supports:
|
|
634
|
+
* - '?.path' nested path strings resolved against target (returns the resolved value, or true if missing)
|
|
635
|
+
* - plain key strings looked up on target (returns the value, or true if missing)
|
|
636
|
+
* - resolved literal values (e.g., booleans from assignFrom) returned as-is
|
|
637
|
+
*/
|
|
638
|
+
function resolveValueToNegate(rhsPath, target) {
|
|
639
|
+
if (typeof rhsPath === 'string' && isNestedPath(rhsPath)) {
|
|
640
|
+
const rhsPathParts = parsePath(rhsPath);
|
|
641
|
+
let current = target;
|
|
642
|
+
let exists = true;
|
|
643
|
+
for (const part of rhsPathParts) {
|
|
644
|
+
if (current && typeof current === 'object' && part in current) {
|
|
645
|
+
current = current[part];
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
exists = false;
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return exists ? current : true;
|
|
653
|
+
}
|
|
654
|
+
if (typeof rhsPath === 'string') {
|
|
655
|
+
return (rhsPath in target) ? target[rhsPath] : true;
|
|
656
|
+
}
|
|
657
|
+
// Non-string resolved value (e.g., boolean, number, null from assignFrom)
|
|
658
|
+
return rhsPath;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Navigate a path that leads to an iterable, returning the iterable value or undefined.
|
|
662
|
+
* Handles withMethods and the evaluatePathWithMethods semantics.
|
|
663
|
+
*/
|
|
664
|
+
function getIterableAtPath(target, pathParts, value, withMethods, permissionProcessor) {
|
|
665
|
+
let current = target;
|
|
666
|
+
if (pathParts.length > 0) {
|
|
667
|
+
if (withMethods && withMethods.size > 0) {
|
|
668
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor);
|
|
669
|
+
// evaluatePathWithMethods returns the container object + last key by default.
|
|
670
|
+
// If the last segment was consumed as a method argument, or the last segment
|
|
671
|
+
// is a zero-arg method marked with |, result.target is already the value.
|
|
672
|
+
if (result.lastSegmentConsumed) {
|
|
673
|
+
current = result.target;
|
|
674
|
+
}
|
|
675
|
+
else if (result.isMethod) {
|
|
676
|
+
const method = result.target[result.lastKey];
|
|
677
|
+
if (typeof method === 'function') {
|
|
678
|
+
const appendArgs = permissionProcessor?.getMethodAppendArgs(result.lastKey) ?? [];
|
|
679
|
+
current = method.call(result.target, ...appendArgs);
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
current = method;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
current = result.target[result.lastKey];
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
for (const part of pathParts) {
|
|
691
|
+
current = current[part];
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return isIterable(current) ? current : undefined;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Apply an operator command (+=, =!, -=, Y=) to each item in an iterable.
|
|
699
|
+
* Detects @each in the path, navigates to the iterable, then builds a synthetic
|
|
700
|
+
* command key for the remaining path and delegates to assignGingerly per item.
|
|
701
|
+
* Nested @each is handled recursively through assignGingerly.
|
|
702
|
+
*/
|
|
703
|
+
function applyCommandToEach(target, pathParts, commandSuffix, value, withMethods, aliasMap, options, permissionProcessor) {
|
|
704
|
+
const forEachIndex = pathParts.findIndex(part => isForEachSymbol(part, aliasMap));
|
|
705
|
+
if (forEachIndex === -1)
|
|
706
|
+
return;
|
|
707
|
+
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
708
|
+
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
709
|
+
const iterable = getIterableAtPath(target, pathToForEach, value, withMethods, permissionProcessor);
|
|
710
|
+
if (!iterable)
|
|
711
|
+
return;
|
|
712
|
+
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
713
|
+
const syntheticKey = pathAfterForEach.length > 0
|
|
714
|
+
? `?.${pathAfterForEach.join('?.')}${commandSuffix}`
|
|
715
|
+
: commandSuffix;
|
|
716
|
+
for (const item of items) {
|
|
717
|
+
assignGingerly(item, { [syntheticKey]: value }, options, permissionProcessor);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
626
720
|
/**
|
|
627
721
|
* Apply alias substitutions to a key string.
|
|
628
722
|
* Replaces complete tokens between `?.` delimiters with their aliased values.
|
|
@@ -727,6 +821,11 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
727
821
|
if (path) {
|
|
728
822
|
if (isNestedPath(path)) {
|
|
729
823
|
const pathParts = parsePath(path);
|
|
824
|
+
// Check for @each in path
|
|
825
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
826
|
+
applyCommandToEach(target, pathParts, ' +=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
730
829
|
// Check for withMethods path evaluation
|
|
731
830
|
let lhsValue;
|
|
732
831
|
let lhsParent;
|
|
@@ -795,54 +894,47 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
795
894
|
const lhsPath = parseToggleCommand(key);
|
|
796
895
|
if (lhsPath) {
|
|
797
896
|
const rhsPath = value;
|
|
798
|
-
// Resolve LHS
|
|
799
|
-
let lhsParent;
|
|
800
|
-
let lhsLastKey;
|
|
801
897
|
if (isNestedPath(lhsPath)) {
|
|
802
898
|
const lhsPathParts = parsePath(lhsPath);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
//
|
|
814
|
-
|
|
815
|
-
|
|
899
|
+
// Check for @each in the LHS path
|
|
900
|
+
if (lhsPathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
901
|
+
// Resolve non-self-referencing RHS paths against the original target
|
|
902
|
+
// before iterating, so each item negates the same root value.
|
|
903
|
+
const resolvedValue = (rhsPath === '.' || typeof rhsPath !== 'string')
|
|
904
|
+
? rhsPath
|
|
905
|
+
: resolveValueToNegate(rhsPath, target);
|
|
906
|
+
applyCommandToEach(target, lhsPathParts, ' =!', resolvedValue, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
// No @each in path - standard toggle
|
|
910
|
+
const lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
911
|
+
const lhsParent = ensureNestedPath(target, lhsPathParts);
|
|
912
|
+
// Determine what to negate
|
|
913
|
+
let valueToNegate;
|
|
914
|
+
if (rhsPath === '.') {
|
|
915
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
816
916
|
}
|
|
817
917
|
else {
|
|
818
|
-
valueToNegate =
|
|
918
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
919
|
+
}
|
|
920
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
921
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
819
922
|
}
|
|
820
923
|
}
|
|
821
924
|
else {
|
|
822
|
-
//
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (current && typeof current === 'object' && part in current) {
|
|
829
|
-
current = current[part];
|
|
830
|
-
}
|
|
831
|
-
else {
|
|
832
|
-
exists = false;
|
|
833
|
-
break;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
valueToNegate = exists ? current : true;
|
|
925
|
+
// Plain key LHS
|
|
926
|
+
const lhsLastKey = lhsPath;
|
|
927
|
+
const lhsParent = target;
|
|
928
|
+
let valueToNegate;
|
|
929
|
+
if (rhsPath === '.') {
|
|
930
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
837
931
|
}
|
|
838
932
|
else {
|
|
839
|
-
|
|
840
|
-
|
|
933
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
934
|
+
}
|
|
935
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
936
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
841
937
|
}
|
|
842
|
-
}
|
|
843
|
-
// Apply negation to LHS — check restriction first
|
|
844
|
-
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
845
|
-
lhsParent[lhsLastKey] = !valueToNegate;
|
|
846
938
|
}
|
|
847
939
|
}
|
|
848
940
|
continue;
|
|
@@ -856,6 +948,11 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
856
948
|
let canDelete = true;
|
|
857
949
|
if (isNestedPath(path)) {
|
|
858
950
|
const pathParts = parsePath(path);
|
|
951
|
+
// Check for @each in path
|
|
952
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
953
|
+
applyCommandToEach(target, pathParts, ' -=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
859
956
|
if (pathParts.length === 0) {
|
|
860
957
|
parent = target;
|
|
861
958
|
}
|
|
@@ -901,6 +998,11 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
901
998
|
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
902
999
|
continue;
|
|
903
1000
|
}
|
|
1001
|
+
// Check for @each in path
|
|
1002
|
+
if (isNestedPath(path) && pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1003
|
+
applyCommandToEach(target, pathParts, ' Y=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
904
1006
|
// Navigate to the target sub-object
|
|
905
1007
|
let mergeTarget;
|
|
906
1008
|
if (isNestedPath(path)) {
|
|
@@ -956,22 +1058,9 @@ export function assignGingerly(target, source, options, permissionProcessor) {
|
|
|
956
1058
|
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
957
1059
|
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
958
1060
|
// Navigate to the iterable
|
|
959
|
-
|
|
960
|
-
if (pathToForEach.length > 0) {
|
|
961
|
-
if (withMethodsSet) {
|
|
962
|
-
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
963
|
-
// The result.target is the current position after evaluating the path
|
|
964
|
-
// This is already the iterable we want
|
|
965
|
-
current = result.target;
|
|
966
|
-
}
|
|
967
|
-
else {
|
|
968
|
-
for (const part of pathToForEach) {
|
|
969
|
-
current = current[part];
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
1061
|
+
const current = getIterableAtPath(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
973
1062
|
// Apply to each item in the iterable
|
|
974
|
-
if (
|
|
1063
|
+
if (current) {
|
|
975
1064
|
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options, permissionProcessor);
|
|
976
1065
|
}
|
|
977
1066
|
// If not iterable, let JavaScript throw error naturally when trying to iterate
|
package/assignGingerly.ts
CHANGED
|
@@ -506,9 +506,10 @@ export function evaluatePathWithMethods(
|
|
|
506
506
|
value: any,
|
|
507
507
|
withMethods: Set<string>,
|
|
508
508
|
permissionProcessor?: PermissionProcessor
|
|
509
|
-
): { target: any; lastKey: string; isMethod: boolean; isZeroArg: boolean } {
|
|
509
|
+
): { target: any; lastKey: string; isMethod: boolean; isZeroArg: boolean; lastSegmentConsumed: boolean } {
|
|
510
510
|
let current = target;
|
|
511
511
|
let i = 0;
|
|
512
|
+
let lastSegmentConsumed = false;
|
|
512
513
|
|
|
513
514
|
// Process all segments except the last one
|
|
514
515
|
while (i < pathParts.length - 1) {
|
|
@@ -533,6 +534,9 @@ export function evaluatePathWithMethods(
|
|
|
533
534
|
// Only current is method - call with next part as string arg
|
|
534
535
|
current = method.call(current, nextPart, ...appendArgs);
|
|
535
536
|
i++; // Skip next part since we consumed it as argument
|
|
537
|
+
if (i === pathParts.length - 1) {
|
|
538
|
+
lastSegmentConsumed = true;
|
|
539
|
+
}
|
|
536
540
|
}
|
|
537
541
|
} else {
|
|
538
542
|
// Not a function - just access property (create if needed)
|
|
@@ -561,7 +565,8 @@ export function evaluatePathWithMethods(
|
|
|
561
565
|
target: current,
|
|
562
566
|
lastKey,
|
|
563
567
|
isMethod: isAllowedMethod(lastKey, withMethods, permissionProcessor),
|
|
564
|
-
isZeroArg
|
|
568
|
+
isZeroArg,
|
|
569
|
+
lastSegmentConsumed
|
|
565
570
|
};
|
|
566
571
|
}
|
|
567
572
|
|
|
@@ -724,6 +729,110 @@ function applyToEach(
|
|
|
724
729
|
}
|
|
725
730
|
}
|
|
726
731
|
|
|
732
|
+
/**
|
|
733
|
+
* Resolve the RHS of a toggle (=!) command to the value that should be negated.
|
|
734
|
+
* Supports:
|
|
735
|
+
* - '?.path' nested path strings resolved against target (returns the resolved value, or true if missing)
|
|
736
|
+
* - plain key strings looked up on target (returns the value, or true if missing)
|
|
737
|
+
* - resolved literal values (e.g., booleans from assignFrom) returned as-is
|
|
738
|
+
*/
|
|
739
|
+
function resolveValueToNegate(rhsPath: any, target: any): any {
|
|
740
|
+
if (typeof rhsPath === 'string' && isNestedPath(rhsPath)) {
|
|
741
|
+
const rhsPathParts = parsePath(rhsPath);
|
|
742
|
+
let current = target;
|
|
743
|
+
let exists = true;
|
|
744
|
+
for (const part of rhsPathParts) {
|
|
745
|
+
if (current && typeof current === 'object' && part in current) {
|
|
746
|
+
current = current[part];
|
|
747
|
+
} else {
|
|
748
|
+
exists = false;
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return exists ? current : true;
|
|
753
|
+
}
|
|
754
|
+
if (typeof rhsPath === 'string') {
|
|
755
|
+
return (rhsPath in target) ? target[rhsPath] : true;
|
|
756
|
+
}
|
|
757
|
+
// Non-string resolved value (e.g., boolean, number, null from assignFrom)
|
|
758
|
+
return rhsPath;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Navigate a path that leads to an iterable, returning the iterable value or undefined.
|
|
763
|
+
* Handles withMethods and the evaluatePathWithMethods semantics.
|
|
764
|
+
*/
|
|
765
|
+
function getIterableAtPath(
|
|
766
|
+
target: any,
|
|
767
|
+
pathParts: string[],
|
|
768
|
+
value: any,
|
|
769
|
+
withMethods: Set<string> | undefined,
|
|
770
|
+
permissionProcessor?: PermissionProcessor
|
|
771
|
+
): any {
|
|
772
|
+
let current = target;
|
|
773
|
+
if (pathParts.length > 0) {
|
|
774
|
+
if (withMethods && withMethods.size > 0) {
|
|
775
|
+
const result = evaluatePathWithMethods(target, pathParts, value, withMethods, permissionProcessor);
|
|
776
|
+
// evaluatePathWithMethods returns the container object + last key by default.
|
|
777
|
+
// If the last segment was consumed as a method argument, or the last segment
|
|
778
|
+
// is a zero-arg method marked with |, result.target is already the value.
|
|
779
|
+
if (result.lastSegmentConsumed) {
|
|
780
|
+
current = result.target;
|
|
781
|
+
} else if (result.isMethod) {
|
|
782
|
+
const method = result.target[result.lastKey];
|
|
783
|
+
if (typeof method === 'function') {
|
|
784
|
+
const appendArgs = permissionProcessor?.getMethodAppendArgs(result.lastKey) ?? [];
|
|
785
|
+
current = method.call(result.target, ...appendArgs);
|
|
786
|
+
} else {
|
|
787
|
+
current = method;
|
|
788
|
+
}
|
|
789
|
+
} else {
|
|
790
|
+
current = result.target[result.lastKey];
|
|
791
|
+
}
|
|
792
|
+
} else {
|
|
793
|
+
for (const part of pathParts) {
|
|
794
|
+
current = current[part];
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return isIterable(current) ? current : undefined;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Apply an operator command (+=, =!, -=, Y=) to each item in an iterable.
|
|
803
|
+
* Detects @each in the path, navigates to the iterable, then builds a synthetic
|
|
804
|
+
* command key for the remaining path and delegates to assignGingerly per item.
|
|
805
|
+
* Nested @each is handled recursively through assignGingerly.
|
|
806
|
+
*/
|
|
807
|
+
function applyCommandToEach(
|
|
808
|
+
target: any,
|
|
809
|
+
pathParts: string[],
|
|
810
|
+
commandSuffix: string,
|
|
811
|
+
value: any,
|
|
812
|
+
withMethods: Set<string> | undefined,
|
|
813
|
+
aliasMap: Map<string, string>,
|
|
814
|
+
options?: IAssignGingerlyOptions,
|
|
815
|
+
permissionProcessor?: PermissionProcessor
|
|
816
|
+
): void {
|
|
817
|
+
const forEachIndex = pathParts.findIndex(part => isForEachSymbol(part, aliasMap));
|
|
818
|
+
if (forEachIndex === -1) return;
|
|
819
|
+
|
|
820
|
+
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
821
|
+
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
822
|
+
|
|
823
|
+
const iterable = getIterableAtPath(target, pathToForEach, value, withMethods, permissionProcessor);
|
|
824
|
+
if (!iterable) return;
|
|
825
|
+
|
|
826
|
+
const items = Array.isArray(iterable) ? iterable : Array.from(iterable);
|
|
827
|
+
const syntheticKey = pathAfterForEach.length > 0
|
|
828
|
+
? `?.${pathAfterForEach.join('?.')}${commandSuffix}`
|
|
829
|
+
: commandSuffix;
|
|
830
|
+
|
|
831
|
+
for (const item of items) {
|
|
832
|
+
assignGingerly(item, { [syntheticKey]: value }, options, permissionProcessor);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
727
836
|
/**
|
|
728
837
|
* Apply alias substitutions to a key string.
|
|
729
838
|
* Replaces complete tokens between `?.` delimiters with their aliased values.
|
|
@@ -845,6 +954,12 @@ export function assignGingerly(
|
|
|
845
954
|
if (isNestedPath(path)) {
|
|
846
955
|
const pathParts = parsePath(path);
|
|
847
956
|
|
|
957
|
+
// Check for @each in path
|
|
958
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
959
|
+
applyCommandToEach(target, pathParts, ' +=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
|
|
848
963
|
// Check for withMethods path evaluation
|
|
849
964
|
let lhsValue: any;
|
|
850
965
|
let lhsParent: any;
|
|
@@ -915,52 +1030,51 @@ export function assignGingerly(
|
|
|
915
1030
|
const lhsPath = parseToggleCommand(key);
|
|
916
1031
|
if (lhsPath) {
|
|
917
1032
|
const rhsPath = value;
|
|
918
|
-
|
|
919
|
-
// Resolve LHS
|
|
920
|
-
let lhsParent: any;
|
|
921
|
-
let lhsLastKey: string;
|
|
1033
|
+
|
|
922
1034
|
if (isNestedPath(lhsPath)) {
|
|
923
1035
|
const lhsPathParts = parsePath(lhsPath);
|
|
924
|
-
lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
925
|
-
lhsParent = ensureNestedPath(target, lhsPathParts);
|
|
926
|
-
} else {
|
|
927
|
-
lhsLastKey = lhsPath;
|
|
928
|
-
lhsParent = target;
|
|
929
|
-
}
|
|
930
1036
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1037
|
+
// Check for @each in the LHS path
|
|
1038
|
+
if (lhsPathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1039
|
+
// Resolve non-self-referencing RHS paths against the original target
|
|
1040
|
+
// before iterating, so each item negates the same root value.
|
|
1041
|
+
const resolvedValue = (rhsPath === '.' || typeof rhsPath !== 'string')
|
|
1042
|
+
? rhsPath
|
|
1043
|
+
: resolveValueToNegate(rhsPath, target);
|
|
1044
|
+
applyCommandToEach(target, lhsPathParts, ' =!', resolvedValue, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// No @each in path - standard toggle
|
|
1049
|
+
const lhsLastKey = lhsPathParts[lhsPathParts.length - 1];
|
|
1050
|
+
const lhsParent = ensureNestedPath(target, lhsPathParts);
|
|
1051
|
+
|
|
1052
|
+
// Determine what to negate
|
|
1053
|
+
let valueToNegate: any;
|
|
1054
|
+
if (rhsPath === '.') {
|
|
1055
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
937
1056
|
} else {
|
|
938
|
-
valueToNegate =
|
|
1057
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
1061
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
939
1062
|
}
|
|
940
1063
|
} else {
|
|
941
|
-
//
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
current = current[part];
|
|
949
|
-
} else {
|
|
950
|
-
exists = false;
|
|
951
|
-
break;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
valueToNegate = exists ? current : true;
|
|
1064
|
+
// Plain key LHS
|
|
1065
|
+
const lhsLastKey = lhsPath;
|
|
1066
|
+
const lhsParent = target;
|
|
1067
|
+
|
|
1068
|
+
let valueToNegate: any;
|
|
1069
|
+
if (rhsPath === '.') {
|
|
1070
|
+
valueToNegate = (lhsLastKey in lhsParent) ? lhsParent[lhsLastKey] : undefined;
|
|
955
1071
|
} else {
|
|
956
|
-
|
|
957
|
-
|
|
1072
|
+
valueToNegate = resolveValueToNegate(rhsPath, target);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
1076
|
+
lhsParent[lhsLastKey] = !valueToNegate;
|
|
958
1077
|
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// Apply negation to LHS — check restriction first
|
|
962
|
-
if (!permissionProcessor?.checkRestrictedProp(lhsLastKey)) {
|
|
963
|
-
lhsParent[lhsLastKey] = !valueToNegate;
|
|
964
1078
|
}
|
|
965
1079
|
}
|
|
966
1080
|
continue;
|
|
@@ -976,6 +1090,13 @@ export function assignGingerly(
|
|
|
976
1090
|
|
|
977
1091
|
if (isNestedPath(path)) {
|
|
978
1092
|
const pathParts = parsePath(path);
|
|
1093
|
+
|
|
1094
|
+
// Check for @each in path
|
|
1095
|
+
if (pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1096
|
+
applyCommandToEach(target, pathParts, ' -=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
979
1100
|
if (pathParts.length === 0) {
|
|
980
1101
|
parent = target;
|
|
981
1102
|
} else {
|
|
@@ -1019,6 +1140,13 @@ export function assignGingerly(
|
|
|
1019
1140
|
if (permissionProcessor?.checkRestrictedProp(lastKey)) {
|
|
1020
1141
|
continue;
|
|
1021
1142
|
}
|
|
1143
|
+
|
|
1144
|
+
// Check for @each in path
|
|
1145
|
+
if (isNestedPath(path) && pathParts.some(part => isForEachSymbol(part, aliasMap))) {
|
|
1146
|
+
applyCommandToEach(target, pathParts, ' Y=', value, withMethodsSet, aliasMap, options, permissionProcessor);
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1022
1150
|
// Navigate to the target sub-object
|
|
1023
1151
|
let mergeTarget: any;
|
|
1024
1152
|
if (isNestedPath(path)) {
|
|
@@ -1086,24 +1214,12 @@ export function assignGingerly(
|
|
|
1086
1214
|
// Static forEach (@each) - existing logic
|
|
1087
1215
|
const pathToForEach = pathParts.slice(0, forEachIndex);
|
|
1088
1216
|
const pathAfterForEach = pathParts.slice(forEachIndex + 1);
|
|
1089
|
-
|
|
1217
|
+
|
|
1090
1218
|
// Navigate to the iterable
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (withMethodsSet) {
|
|
1094
|
-
const result = evaluatePathWithMethods(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
1095
|
-
// The result.target is the current position after evaluating the path
|
|
1096
|
-
// This is already the iterable we want
|
|
1097
|
-
current = result.target;
|
|
1098
|
-
} else {
|
|
1099
|
-
for (const part of pathToForEach) {
|
|
1100
|
-
current = current[part];
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1219
|
+
const current = getIterableAtPath(target, pathToForEach, value, withMethodsSet, permissionProcessor);
|
|
1220
|
+
|
|
1105
1221
|
// Apply to each item in the iterable
|
|
1106
|
-
if (
|
|
1222
|
+
if (current) {
|
|
1107
1223
|
applyToEach(current, pathAfterForEach, value, withMethodsSet || new Set(), aliasMap, options, permissionProcessor);
|
|
1108
1224
|
}
|
|
1109
1225
|
// If not iterable, let JavaScript throw error naturally when trying to iterate
|
package/handleIshProperty.js
CHANGED
|
@@ -47,7 +47,7 @@ async function defineIshProperty(element, managerName, options, assignGingerlyFn
|
|
|
47
47
|
let config = registry.get(managerName);
|
|
48
48
|
// If not registered, wait for registration
|
|
49
49
|
if (!config) {
|
|
50
|
-
const { waitForEvent } = await import('./waitForEvent.js');
|
|
50
|
+
const { waitForEvent } = await import('./utils/waitForEvent.js');
|
|
51
51
|
await waitForEvent(registry, managerName);
|
|
52
52
|
config = registry.get(managerName);
|
|
53
53
|
if (!config) {
|
package/handleIshProperty.ts
CHANGED
|
@@ -66,7 +66,7 @@ async function defineIshProperty(
|
|
|
66
66
|
|
|
67
67
|
// If not registered, wait for registration
|
|
68
68
|
if (!config) {
|
|
69
|
-
const { waitForEvent } = await import('./waitForEvent.js');
|
|
69
|
+
const { waitForEvent } = await import('./utils/waitForEvent.js');
|
|
70
70
|
await waitForEvent(registry, managerName);
|
|
71
71
|
config = registry.get(managerName);
|
|
72
72
|
|
|
@@ -224,7 +224,7 @@ export class ManageTemplateListHandler {
|
|
|
224
224
|
if (fragment.childNodes.length > 0) {
|
|
225
225
|
const waitOpt = resolvedParams.waitForSettled;
|
|
226
226
|
if (waitOpt) {
|
|
227
|
-
const { waitForSettled } = await import('../waitForSettled.js');
|
|
227
|
+
const { waitForSettled } = await import('../utils/waitForSettled.js');
|
|
228
228
|
const idleMs = typeof waitOpt === 'object' ? waitOpt.idleMs : 100;
|
|
229
229
|
const timeout = typeof waitOpt === 'object' ? waitOpt.timeout : undefined;
|
|
230
230
|
try {
|
|
@@ -263,7 +263,7 @@ export class ManageTemplateListHandler implements AssignFromHandler {
|
|
|
263
263
|
if (fragment.childNodes.length > 0) {
|
|
264
264
|
const waitOpt = resolvedParams.waitForSettled;
|
|
265
265
|
if (waitOpt) {
|
|
266
|
-
const { waitForSettled } = await import('../waitForSettled.js');
|
|
266
|
+
const { waitForSettled } = await import('../utils/waitForSettled.js');
|
|
267
267
|
const idleMs = typeof waitOpt === 'object' ? waitOpt.idleMs : 100;
|
|
268
268
|
const timeout = typeof waitOpt === 'object' ? waitOpt.timeout : undefined;
|
|
269
269
|
try {
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { assignGingerly } from './assignGingerly.js';
|
|
2
2
|
export { assignTentatively } from './assignTentatively.js';
|
|
3
3
|
export { EnhancementRegistry, ItemscopeRegistry, EnhancementRegisteredEvent } from './assignGingerly.js';
|
|
4
|
-
export { waitForEvent } from './waitForEvent.js';
|
|
4
|
+
export { waitForEvent } from './utils/waitForEvent.js';
|
|
5
5
|
export { ParserRegistry, globalParserRegistry } from './parserRegistry.js';
|
|
6
6
|
export { parseWithAttrs } from './parseWithAttrs.js';
|
|
7
7
|
export { buildCSSQuery } from './buildCSSQuery.js';
|
package/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export {assignGingerly} from './assignGingerly.js';
|
|
2
2
|
export {assignTentatively} from './assignTentatively.js';
|
|
3
3
|
export {EnhancementRegistry, ItemscopeRegistry, EnhancementRegisteredEvent} from './assignGingerly.js';
|
|
4
|
-
export {waitForEvent} from './waitForEvent.js';
|
|
4
|
+
export {waitForEvent} from './utils/waitForEvent.js';
|
|
5
5
|
export {ParserRegistry, globalParserRegistry} from './parserRegistry.js';
|
|
6
6
|
export {parseWithAttrs} from './parseWithAttrs.js';
|
|
7
7
|
export {buildCSSQuery} from './buildCSSQuery.js';
|
|
@@ -53,6 +53,7 @@ export type Compacts<TProps = any, TActions = TProps, TEvents extends string = s
|
|
|
53
53
|
| Partial<{[key in `when_${keyof TProps & string}_changes_dispatch`]: string}>
|
|
54
54
|
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_inc_${keyof TProps & string}_by`]: number}>
|
|
55
55
|
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_set_${keyof TProps & string}_to`]: any}>
|
|
56
|
+
| Partial<{[key in `on_${TEvents}_of_${keyof TProps & string}_assign`]: Record<string, any>}>
|
|
56
57
|
;
|
|
57
58
|
|
|
58
59
|
export type Hitches<TProps = any, TActions = TProps> =
|
package/object-extension.js
CHANGED
|
@@ -250,7 +250,7 @@ class ElementEnhancementContainer {
|
|
|
250
250
|
throw new Error('Instance must be an EventTarget to use whenResolved');
|
|
251
251
|
}
|
|
252
252
|
// Lazy load waitForEvent
|
|
253
|
-
const { waitForEvent } = await import('./waitForEvent.js');
|
|
253
|
+
const { waitForEvent } = await import('./utils/waitForEvent.js');
|
|
254
254
|
// Wait for the resolved event (use resolvedKey as event name)
|
|
255
255
|
// Note: When symbols are supported as event names, this will work with symbol keys too
|
|
256
256
|
await waitForEvent(spawnedInstance, resolvedKey);
|
package/object-extension.ts
CHANGED
|
@@ -359,7 +359,7 @@ class ElementEnhancementContainer {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
// Lazy load waitForEvent
|
|
362
|
-
const { waitForEvent } = await import('./waitForEvent.js');
|
|
362
|
+
const { waitForEvent } = await import('./utils/waitForEvent.js');
|
|
363
363
|
|
|
364
364
|
// Wait for the resolved event (use resolvedKey as event name)
|
|
365
365
|
// Note: When symbols are supported as event names, this will work with symbol keys too
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assign-gingerly",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.80",
|
|
4
4
|
"description": "This package provides a utility function for carefully merging one object into another.",
|
|
5
5
|
"homepage": "https://github.com/bahrus/assign-gingerly#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -159,9 +159,9 @@
|
|
|
159
159
|
"default": "./markerUtils.js",
|
|
160
160
|
"types": "./markerUtils.ts"
|
|
161
161
|
},
|
|
162
|
-
"./waitForSettled.js": {
|
|
163
|
-
"default": "./waitForSettled.js",
|
|
164
|
-
"types": "./waitForSettled.ts"
|
|
162
|
+
"./utils/waitForSettled.js": {
|
|
163
|
+
"default": "./utils/waitForSettled.js",
|
|
164
|
+
"types": "./utils/waitForSettled.ts"
|
|
165
165
|
},
|
|
166
166
|
"./inferredAssignments.js": {
|
|
167
167
|
"default": "./inferredAssignments.js",
|
|
@@ -199,8 +199,9 @@
|
|
|
199
199
|
"default": "./evaluatePathWithAsyncMethods.js",
|
|
200
200
|
"types": "./evaluatePathWithAsyncMethods.ts"
|
|
201
201
|
},
|
|
202
|
-
"./waitForEvent.js": {
|
|
203
|
-
"default": "./waitForEvent.js"
|
|
202
|
+
"./utils/waitForEvent.js": {
|
|
203
|
+
"default": "./utils/waitForEvent.js",
|
|
204
|
+
"types": "./utils/waitForEvent.ts"
|
|
204
205
|
}
|
|
205
206
|
},
|
|
206
207
|
"main": "index.js",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|