assign-gingerly 0.0.79 → 0.0.81
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 +67 -18
- package/DX/paths.ts +162 -92
- package/README.md +9 -8
- 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/NewCustomElement.md +20 -440
- package/inferencer/types/NewHTMLFirstCustomElement.md +431 -0
- package/inferencer/types/NewJSFirstCustomElement.md +278 -0
- 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,34 @@
|
|
|
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
|
+
}
|
|
33
|
+
const COMMAND_TOKEN_SUFFIXES = {
|
|
34
|
+
Each: '?.@each',
|
|
35
|
+
EqNot: ' =!',
|
|
36
|
+
PlusEq: ' +=',
|
|
37
|
+
QMEq: ' ?=',
|
|
38
|
+
YEq: ' Y=',
|
|
39
|
+
MinusEq: ' -=',
|
|
40
|
+
Arrow: ' =>',
|
|
41
|
+
};
|
|
42
|
+
function serializePath(prefix) {
|
|
43
|
+
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
44
|
+
}
|
|
45
|
+
function appendPathSegment(prefix, segment) {
|
|
46
|
+
return prefix ? `${prefix}?.${segment}` : segment;
|
|
47
|
+
}
|
|
48
|
+
function appendCommandSuffix(prefix, token) {
|
|
49
|
+
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
50
|
+
}
|
|
51
|
+
function getReservedToken(prop) {
|
|
52
|
+
if (prop === 'Path' || prop === 'path')
|
|
53
|
+
return prop;
|
|
54
|
+
if (prop in COMMAND_TOKEN_SUFFIXES)
|
|
55
|
+
return prop;
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
30
58
|
/**
|
|
31
59
|
* Create a proxy for id-ref paths (#[varName]).
|
|
32
60
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
@@ -34,15 +62,23 @@ const PATH_SYMBOL = Symbol('assign-gingerly-path');
|
|
|
34
62
|
*/
|
|
35
63
|
function createIdRefProxy(idRef, options) {
|
|
36
64
|
function handler() { }
|
|
65
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
37
66
|
return new Proxy(handler, {
|
|
38
67
|
get(_, prop) {
|
|
39
|
-
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
68
|
+
if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
|
|
40
69
|
return idRef;
|
|
41
70
|
}
|
|
42
71
|
if (typeof prop === 'symbol')
|
|
43
72
|
return undefined;
|
|
73
|
+
const reservedToken = getReservedToken(prop);
|
|
74
|
+
if (reservedToken === 'Each') {
|
|
75
|
+
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
76
|
+
}
|
|
77
|
+
if (reservedToken && reservedToken !== 'Path' && reservedToken !== 'path') {
|
|
78
|
+
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
79
|
+
}
|
|
44
80
|
// Chain further path segments after the id ref
|
|
45
|
-
const chained =
|
|
81
|
+
const chained = appendPathSegment(idRef, String(prop));
|
|
46
82
|
return createIdRefProxy(chained, options);
|
|
47
83
|
},
|
|
48
84
|
apply(_, __, args) {
|
|
@@ -53,7 +89,7 @@ function createIdRefProxy(idRef, options) {
|
|
|
53
89
|
argStr = 'true';
|
|
54
90
|
else if (arg === false)
|
|
55
91
|
argStr = 'false';
|
|
56
|
-
else if (arg
|
|
92
|
+
else if (isPathProxy(arg)) {
|
|
57
93
|
const fullPath = arg[PATH_SYMBOL];
|
|
58
94
|
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
59
95
|
}
|
|
@@ -77,14 +113,25 @@ function createPathProxy(prefix, options) {
|
|
|
77
113
|
const aliasMap = options?.aka;
|
|
78
114
|
// Use a function as the target to enable the apply trap
|
|
79
115
|
function handler() { }
|
|
116
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
80
117
|
return new Proxy(handler, {
|
|
81
118
|
get(_, prop) {
|
|
82
|
-
if (prop === 'path' || prop === PATH_SYMBOL) {
|
|
83
|
-
return prefix
|
|
119
|
+
if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
|
|
120
|
+
return serializePath(prefix);
|
|
84
121
|
}
|
|
85
122
|
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
86
123
|
if (typeof prop === 'symbol')
|
|
87
124
|
return undefined;
|
|
125
|
+
const reservedToken = getReservedToken(String(prop));
|
|
126
|
+
if (reservedToken === 'Path' || reservedToken === 'path') {
|
|
127
|
+
return serializePath(prefix);
|
|
128
|
+
}
|
|
129
|
+
if (reservedToken === 'Each') {
|
|
130
|
+
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
131
|
+
}
|
|
132
|
+
if (reservedToken) {
|
|
133
|
+
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
134
|
+
}
|
|
88
135
|
let segment = String(prop);
|
|
89
136
|
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
90
137
|
if (segment.startsWith('#')) {
|
|
@@ -102,7 +149,7 @@ function createPathProxy(prefix, options) {
|
|
|
102
149
|
}
|
|
103
150
|
}
|
|
104
151
|
}
|
|
105
|
-
const newPath = prefix
|
|
152
|
+
const newPath = appendPathSegment(prefix, segment);
|
|
106
153
|
return createPathProxy(newPath, options);
|
|
107
154
|
},
|
|
108
155
|
apply(_, __, args) {
|
|
@@ -114,14 +161,14 @@ function createPathProxy(prefix, options) {
|
|
|
114
161
|
argStr = 'true';
|
|
115
162
|
else if (arg === false)
|
|
116
163
|
argStr = 'false';
|
|
117
|
-
else if (arg
|
|
164
|
+
else if (isPathProxy(arg)) {
|
|
118
165
|
// Proxy arg — extract path without '?.' prefix
|
|
119
166
|
const fullPath = arg[PATH_SYMBOL];
|
|
120
167
|
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
121
168
|
}
|
|
122
169
|
else
|
|
123
170
|
argStr = String(arg);
|
|
124
|
-
const newPath = prefix
|
|
171
|
+
const newPath = appendPathSegment(prefix, argStr);
|
|
125
172
|
return createPathProxy(newPath, options);
|
|
126
173
|
}
|
|
127
174
|
// No args — method called with no arguments, return self
|
|
@@ -167,12 +214,12 @@ export function paths(options) {
|
|
|
167
214
|
* }
|
|
168
215
|
*/
|
|
169
216
|
export function set(lhs) {
|
|
170
|
-
const lhsStr = lhs
|
|
217
|
+
const lhsStr = isPathProxy(lhs)
|
|
171
218
|
? lhs[PATH_SYMBOL]
|
|
172
219
|
: String(lhs);
|
|
173
220
|
return {
|
|
174
221
|
to(rhs) {
|
|
175
|
-
const rhsStr = rhs
|
|
222
|
+
const rhsStr = isPathProxy(rhs)
|
|
176
223
|
? rhs[PATH_SYMBOL]
|
|
177
224
|
: rhs;
|
|
178
225
|
return { [lhsStr]: rhsStr };
|
|
@@ -198,7 +245,7 @@ export function set(lhs) {
|
|
|
198
245
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
199
246
|
*/
|
|
200
247
|
export function smoothOver(value) {
|
|
201
|
-
if (value
|
|
248
|
+
if (isPathProxy(value)) {
|
|
202
249
|
return value[PATH_SYMBOL];
|
|
203
250
|
}
|
|
204
251
|
if (Array.isArray(value)) {
|
|
@@ -294,13 +341,13 @@ export function sp(strings, ...values) {
|
|
|
294
341
|
result.push(strings[i]);
|
|
295
342
|
if (i < values.length) {
|
|
296
343
|
const v = values[i];
|
|
297
|
-
if (v
|
|
344
|
+
if (isPathProxy(v)) {
|
|
298
345
|
// Auto-extract path from proxy object
|
|
299
346
|
result.push(v[PATH_SYMBOL]);
|
|
300
347
|
}
|
|
301
348
|
else if (Array.isArray(v)) {
|
|
302
349
|
// Nested array — recursively extract paths from proxy elements
|
|
303
|
-
result.push(v.map(el => el
|
|
350
|
+
result.push(v.map(el => isPathProxy(el) ? el[PATH_SYMBOL] : el));
|
|
304
351
|
}
|
|
305
352
|
else {
|
|
306
353
|
result.push(v);
|
|
@@ -314,8 +361,10 @@ export function sp(strings, ...values) {
|
|
|
314
361
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
315
362
|
*/
|
|
316
363
|
function extractPropName(pathStr) {
|
|
317
|
-
const
|
|
318
|
-
|
|
364
|
+
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
365
|
+
const parts = withoutCommand.split('?.');
|
|
366
|
+
const last = parts[parts.length - 1];
|
|
367
|
+
return last === '@each' ? 'Each' : last;
|
|
319
368
|
}
|
|
320
369
|
/**
|
|
321
370
|
* Tagged template literal that produces an array of {prop, val} objects + literal strings.
|
|
@@ -349,7 +398,7 @@ export function md(strings, ...values) {
|
|
|
349
398
|
result.push(strings[i]);
|
|
350
399
|
if (i < values.length) {
|
|
351
400
|
const v = values[i];
|
|
352
|
-
if (v
|
|
401
|
+
if (isPathProxy(v)) {
|
|
353
402
|
// Proxy object → {prop, val}
|
|
354
403
|
const pathStr = v[PATH_SYMBOL];
|
|
355
404
|
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
@@ -357,7 +406,7 @@ export function md(strings, ...values) {
|
|
|
357
406
|
else if (Array.isArray(v)) {
|
|
358
407
|
// Nested array — recursively convert proxy elements to {prop, val}
|
|
359
408
|
result.push(v.map(el => {
|
|
360
|
-
if (el
|
|
409
|
+
if (isPathProxy(el)) {
|
|
361
410
|
const pathStr = el[PATH_SYMBOL];
|
|
362
411
|
return { prop: extractPropName(pathStr), val: pathStr };
|
|
363
412
|
}
|
|
@@ -367,7 +416,7 @@ export function md(strings, ...values) {
|
|
|
367
416
|
else if (v && typeof v === 'object' && 'prop' in v) {
|
|
368
417
|
// Developer override object — extract val from proxy if present
|
|
369
418
|
const processed = { ...v };
|
|
370
|
-
if (processed.val
|
|
419
|
+
if (isPathProxy(processed.val)) {
|
|
371
420
|
processed.val = processed.val[PATH_SYMBOL];
|
|
372
421
|
}
|
|
373
422
|
result.push(processed);
|
package/DX/paths.ts
CHANGED
|
@@ -27,21 +27,68 @@
|
|
|
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
|
+
}
|
|
35
|
+
|
|
36
|
+
const COMMAND_TOKEN_SUFFIXES = {
|
|
37
|
+
Each: '?.@each',
|
|
38
|
+
EqNot: ' =!',
|
|
39
|
+
PlusEq: ' +=',
|
|
40
|
+
QMEq: ' ?=',
|
|
41
|
+
YEq: ' Y=',
|
|
42
|
+
MinusEq: ' -=',
|
|
43
|
+
Arrow: ' =>',
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
type CommandToken = keyof typeof COMMAND_TOKEN_SUFFIXES;
|
|
47
|
+
|
|
48
|
+
function serializePath(prefix: string): string {
|
|
49
|
+
return prefix.length > 0 ? `?.${prefix}` : '?.';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function appendPathSegment(prefix: string, segment: string): string {
|
|
53
|
+
return prefix ? `${prefix}?.${segment}` : segment;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function appendCommandSuffix(prefix: string, token: CommandToken): string {
|
|
57
|
+
return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getReservedToken(prop: string): CommandToken | 'Path' | 'path' | undefined {
|
|
61
|
+
if (prop === 'Path' || prop === 'path') return prop;
|
|
62
|
+
if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
31
65
|
|
|
32
66
|
/**
|
|
33
|
-
* Type that maps an object type to a proxy where every property access
|
|
34
|
-
* returns either a deeper proxy (for object properties) or a terminal
|
|
35
|
-
* with a
|
|
36
|
-
* Enhanced: also callable (for method call syntax)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
* Type that maps an object type to a proxy where every property access
|
|
68
|
+
* returns either a deeper proxy (for object properties) or a terminal
|
|
69
|
+
* with a serialized path string accessor — while providing full autocomplete.
|
|
70
|
+
* Enhanced: also callable (for method call syntax) and includes reserved
|
|
71
|
+
* command markers such as `Each`, `EqNot`, and `PlusEq`.
|
|
72
|
+
*/
|
|
73
|
+
export type PathProxyCore = {
|
|
74
|
+
readonly Path: string;
|
|
75
|
+
readonly path: string;
|
|
76
|
+
readonly Each: PathProxy<any>;
|
|
77
|
+
readonly EqNot: PathProxy<any>;
|
|
78
|
+
readonly PlusEq: PathProxy<any>;
|
|
79
|
+
readonly QMEq: PathProxy<any>;
|
|
80
|
+
readonly YEq: PathProxy<any>;
|
|
81
|
+
readonly MinusEq: PathProxy<any>;
|
|
82
|
+
readonly Arrow: PathProxy<any>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type PathProxy<T> = {
|
|
86
|
+
[K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
|
|
87
|
+
? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
|
|
88
|
+
: T[K] extends (object | undefined | null)
|
|
89
|
+
? PathProxy<NonNullable<T[K]>> & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore)
|
|
90
|
+
: PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
91
|
+
} & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
|
|
45
92
|
|
|
46
93
|
/**
|
|
47
94
|
* Options for paths proxy creation.
|
|
@@ -58,26 +105,35 @@ export interface PathsOptions {
|
|
|
58
105
|
* After the initial #[varName], further property access chains with ?. from the resolved element.
|
|
59
106
|
* .path returns the #[varName] prefix (optionally with further ?. path).
|
|
60
107
|
*/
|
|
61
|
-
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
62
|
-
function handler() {}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
108
|
+
function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
109
|
+
function handler() {}
|
|
110
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
|
|
111
|
+
return new Proxy(handler, {
|
|
112
|
+
get(_, prop: string | symbol) {
|
|
113
|
+
if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
|
|
114
|
+
return idRef;
|
|
115
|
+
}
|
|
116
|
+
if (typeof prop === 'symbol') return undefined;
|
|
117
|
+
|
|
118
|
+
const reservedToken = getReservedToken(prop);
|
|
119
|
+
if (reservedToken === 'Each') {
|
|
120
|
+
return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
|
|
121
|
+
}
|
|
122
|
+
if (reservedToken && reservedToken !== 'Path' && reservedToken !== 'path') {
|
|
123
|
+
return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Chain further path segments after the id ref
|
|
127
|
+
const chained = appendPathSegment(idRef, String(prop));
|
|
128
|
+
return createIdRefProxy(chained, options);
|
|
129
|
+
},
|
|
130
|
+
apply(_, __, args) {
|
|
131
|
+
if (args.length > 0) {
|
|
132
|
+
const arg = args[0];
|
|
77
133
|
let argStr: string;
|
|
78
134
|
if (arg === true) argStr = 'true';
|
|
79
135
|
else if (arg === false) argStr = 'false';
|
|
80
|
-
else if (arg
|
|
136
|
+
else if (isPathProxy(arg)) {
|
|
81
137
|
const fullPath = arg[PATH_SYMBOL] as string;
|
|
82
138
|
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
83
139
|
}
|
|
@@ -98,60 +154,72 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
|
|
|
98
154
|
* When `aka` is provided, property names that match an alias *value* are output
|
|
99
155
|
* using the alias *key* instead (reverse alias).
|
|
100
156
|
*/
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
157
|
+
function createPathProxy(prefix: string, options?: PathsOptions): any {
|
|
158
|
+
const aliasMap = options?.aka;
|
|
159
|
+
|
|
160
|
+
// Use a function as the target to enable the apply trap
|
|
161
|
+
function handler() {}
|
|
162
|
+
Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
|
|
163
|
+
|
|
164
|
+
return new Proxy(handler, {
|
|
165
|
+
get(_, prop: string | symbol) {
|
|
166
|
+
if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
|
|
167
|
+
return serializePath(prefix);
|
|
168
|
+
}
|
|
169
|
+
// Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
|
|
170
|
+
if (typeof prop === 'symbol') return undefined;
|
|
171
|
+
|
|
172
|
+
const reservedToken = getReservedToken(String(prop));
|
|
173
|
+
if (reservedToken === 'Path' || reservedToken === 'path') {
|
|
174
|
+
return serializePath(prefix);
|
|
175
|
+
}
|
|
176
|
+
if (reservedToken === 'Each') {
|
|
177
|
+
return createPathProxy(appendPathSegment(prefix, '@each'), options);
|
|
178
|
+
}
|
|
179
|
+
if (reservedToken) {
|
|
180
|
+
return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let segment = String(prop);
|
|
184
|
+
|
|
185
|
+
// #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
|
|
186
|
+
if (segment.startsWith('#')) {
|
|
187
|
+
const varName = segment.substring(1);
|
|
188
|
+
const idRef = `#[${varName}]`;
|
|
121
189
|
// Return a proxy that starts from this id ref (can chain further with ?.)
|
|
122
190
|
return createIdRefProxy(idRef, options);
|
|
123
191
|
}
|
|
124
192
|
|
|
125
193
|
// Apply reverse alias: if prop matches an alias value, use the alias key
|
|
126
|
-
if (aliasMap) {
|
|
127
|
-
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
128
|
-
if (target === segment) { segment = alias; break; }
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const newPath = prefix
|
|
133
|
-
return createPathProxy(newPath, options);
|
|
134
|
-
},
|
|
135
|
-
apply(_, __, args) {
|
|
136
|
-
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
137
|
-
if (args.length > 0) {
|
|
194
|
+
if (aliasMap) {
|
|
195
|
+
for (const [alias, target] of Object.entries(aliasMap)) {
|
|
196
|
+
if (target === segment) { segment = alias; break; }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const newPath = appendPathSegment(prefix, segment);
|
|
201
|
+
return createPathProxy(newPath, options);
|
|
202
|
+
},
|
|
203
|
+
apply(_, __, args) {
|
|
204
|
+
// Method call syntax: $.querySelector('.username') → extends path with the argument
|
|
205
|
+
if (args.length > 0) {
|
|
138
206
|
const arg = args[0];
|
|
139
207
|
let argStr: string;
|
|
140
208
|
if (arg === true) argStr = 'true';
|
|
141
209
|
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);
|
|
148
|
-
|
|
149
|
-
const newPath = prefix
|
|
150
|
-
return createPathProxy(newPath, options);
|
|
151
|
-
}
|
|
152
|
-
// No args — method called with no arguments, return self
|
|
153
|
-
return createPathProxy(prefix, options);
|
|
154
|
-
}
|
|
210
|
+
else if (isPathProxy(arg)) {
|
|
211
|
+
// Proxy arg — extract path without '?.' prefix
|
|
212
|
+
const fullPath = arg[PATH_SYMBOL] as string;
|
|
213
|
+
argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
|
|
214
|
+
}
|
|
215
|
+
else argStr = String(arg);
|
|
216
|
+
|
|
217
|
+
const newPath = appendPathSegment(prefix, argStr);
|
|
218
|
+
return createPathProxy(newPath, options);
|
|
219
|
+
}
|
|
220
|
+
// No args — method called with no arguments, return self
|
|
221
|
+
return createPathProxy(prefix, options);
|
|
222
|
+
}
|
|
155
223
|
});
|
|
156
224
|
}
|
|
157
225
|
|
|
@@ -193,13 +261,13 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
|
|
|
193
261
|
* count: 1
|
|
194
262
|
* }
|
|
195
263
|
*/
|
|
196
|
-
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
197
|
-
const lhsStr = lhs
|
|
198
|
-
? lhs[PATH_SYMBOL]
|
|
199
|
-
: String(lhs);
|
|
264
|
+
export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
265
|
+
const lhsStr = isPathProxy(lhs)
|
|
266
|
+
? lhs[PATH_SYMBOL]
|
|
267
|
+
: String(lhs);
|
|
200
268
|
return {
|
|
201
269
|
to(rhs: any): Record<string, any> {
|
|
202
|
-
const rhsStr = rhs
|
|
270
|
+
const rhsStr = isPathProxy(rhs)
|
|
203
271
|
? rhs[PATH_SYMBOL]
|
|
204
272
|
: rhs;
|
|
205
273
|
return { [lhsStr]: rhsStr };
|
|
@@ -225,9 +293,9 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
|
|
|
225
293
|
* });
|
|
226
294
|
* // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
|
|
227
295
|
*/
|
|
228
|
-
export function smoothOver(value: any): any {
|
|
229
|
-
if (value
|
|
230
|
-
return value[PATH_SYMBOL];
|
|
296
|
+
export function smoothOver(value: any): any {
|
|
297
|
+
if (isPathProxy(value)) {
|
|
298
|
+
return value[PATH_SYMBOL];
|
|
231
299
|
}
|
|
232
300
|
if (Array.isArray(value)) {
|
|
233
301
|
return value.map(smoothOver);
|
|
@@ -328,13 +396,13 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
328
396
|
if (strings[i]) result.push(strings[i]);
|
|
329
397
|
if (i < values.length) {
|
|
330
398
|
const v = values[i];
|
|
331
|
-
if (v
|
|
399
|
+
if (isPathProxy(v)) {
|
|
332
400
|
// Auto-extract path from proxy object
|
|
333
401
|
result.push(v[PATH_SYMBOL]);
|
|
334
402
|
} else if (Array.isArray(v)) {
|
|
335
403
|
// Nested array — recursively extract paths from proxy elements
|
|
336
404
|
result.push(v.map(el =>
|
|
337
|
-
el
|
|
405
|
+
isPathProxy(el) ? el[PATH_SYMBOL] : el
|
|
338
406
|
));
|
|
339
407
|
} else {
|
|
340
408
|
result.push(v);
|
|
@@ -348,10 +416,12 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
348
416
|
* Extract the last segment from a `?.`-prefixed path string.
|
|
349
417
|
* e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
|
|
350
418
|
*/
|
|
351
|
-
function extractPropName(pathStr: string): string {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
419
|
+
function extractPropName(pathStr: string): string {
|
|
420
|
+
const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
|
|
421
|
+
const parts = withoutCommand.split('?.');
|
|
422
|
+
const last = parts[parts.length - 1];
|
|
423
|
+
return last === '@each' ? 'Each' : last;
|
|
424
|
+
}
|
|
355
425
|
|
|
356
426
|
/**
|
|
357
427
|
* Tagged template literal that produces an array of {prop, val} objects + literal strings.
|
|
@@ -384,14 +454,14 @@ export function md(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
384
454
|
if (strings[i]) result.push(strings[i]);
|
|
385
455
|
if (i < values.length) {
|
|
386
456
|
const v = values[i];
|
|
387
|
-
if (v
|
|
457
|
+
if (isPathProxy(v)) {
|
|
388
458
|
// Proxy object → {prop, val}
|
|
389
459
|
const pathStr = v[PATH_SYMBOL] as string;
|
|
390
460
|
result.push({ prop: extractPropName(pathStr), val: pathStr });
|
|
391
461
|
} else if (Array.isArray(v)) {
|
|
392
462
|
// Nested array — recursively convert proxy elements to {prop, val}
|
|
393
463
|
result.push(v.map(el => {
|
|
394
|
-
if (el
|
|
464
|
+
if (isPathProxy(el)) {
|
|
395
465
|
const pathStr = el[PATH_SYMBOL] as string;
|
|
396
466
|
return { prop: extractPropName(pathStr), val: pathStr };
|
|
397
467
|
}
|
|
@@ -400,7 +470,7 @@ export function md(strings: TemplateStringsArray, ...values: any[]): any[] {
|
|
|
400
470
|
} else if (v && typeof v === 'object' && 'prop' in v) {
|
|
401
471
|
// Developer override object — extract val from proxy if present
|
|
402
472
|
const processed = { ...v };
|
|
403
|
-
if (processed.val
|
|
473
|
+
if (isPathProxy(processed.val)) {
|
|
404
474
|
processed.val = processed.val[PATH_SYMBOL];
|
|
405
475
|
}
|
|
406
476
|
result.push(processed);
|
package/README.md
CHANGED
|
@@ -4400,9 +4400,10 @@ export default {
|
|
|
4400
4400
|
**How `paths` works:**
|
|
4401
4401
|
|
|
4402
4402
|
- `paths<T>()` creates a deeply-proxied object typed as `T`
|
|
4403
|
-
- Every property access returns a deeper proxy (e.g., `$.address.city`)
|
|
4404
|
-
- `.
|
|
4405
|
-
-
|
|
4403
|
+
- Every property access returns a deeper proxy (e.g., `$.address.city`)
|
|
4404
|
+
- Capitalized reserved tokens like `.Each` and `.EqNot` normalize into command syntax
|
|
4405
|
+
- `.Path` extracts the `?.`-prefixed string: `$.address.city.Path` → `'?.address?.city'`
|
|
4406
|
+
- Inside `sp` template literals, `.Path` is not needed — proxy objects are auto-detected
|
|
4406
4407
|
|
|
4407
4408
|
**How `sp` works:**
|
|
4408
4409
|
|
|
@@ -4421,19 +4422,19 @@ const value = sp`${$.lastName}${[', ', $.middleName]}, ${$.firstName}`;
|
|
|
4421
4422
|
// ['?.lastName', [', ', '?.middleName'], ', ', '?.firstName']
|
|
4422
4423
|
```
|
|
4423
4424
|
|
|
4424
|
-
**Using `.
|
|
4425
|
+
**Using `.Path` outside of `sp`:**
|
|
4425
4426
|
|
|
4426
4427
|
When you need the path string in a non-`sp` context (object keys, plain arrays, other expressions):
|
|
4427
4428
|
|
|
4428
4429
|
```TypeScript
|
|
4429
4430
|
const $ = paths<Person>();
|
|
4430
4431
|
|
|
4431
|
-
$.lastName.
|
|
4432
|
-
$.address.city.
|
|
4432
|
+
$.lastName.Path // '?.lastName'
|
|
4433
|
+
$.address.city.Path // '?.address?.city'
|
|
4433
4434
|
|
|
4434
4435
|
// As an object key:
|
|
4435
4436
|
const pattern = {
|
|
4436
|
-
[$.textContent.
|
|
4437
|
+
[$.textContent.Path]: '?.firstName' // '?.textContent': '?.firstName'
|
|
4437
4438
|
};
|
|
4438
4439
|
```
|
|
4439
4440
|
|
|
@@ -4473,7 +4474,7 @@ md`${$.firstName} ${{ prop: 'birthDate', val: $.birthDT, format: 'long' }}`
|
|
|
4473
4474
|
| `sp` | `'?.firstName'` (path string) | `builtIns.join` |
|
|
4474
4475
|
| `md` | `{ prop: 'firstName', val: '?.firstName' }` | `builtIns.microDataJoin` |
|
|
4475
4476
|
|
|
4476
|
-
Both auto-detect path proxies (no `.
|
|
4477
|
+
Both auto-detect path proxies (no `.Path` needed inside template literals) and preserve nested arrays for optional segments.
|
|
4477
4478
|
|
|
4478
4479
|
## Cached Element Resolution with `#[x]` and `pin`
|
|
4479
4480
|
|