assign-gingerly 0.0.80 → 0.0.82

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/emojis.js CHANGED
@@ -47,7 +47,8 @@ export const akaMethods = {
47
47
  '📝': 'log',
48
48
  '⚠️': 'warn',
49
49
  '🚨': 'error',
50
- '🤱': 'appendChild'
50
+ '🤱': 'appendChild',
51
+ '😣': 'focus',
51
52
  //'🧩': 'includes'
52
53
  };
53
54
  export const aka = {
package/DX/emojis.ts CHANGED
@@ -49,7 +49,8 @@ export const akaMethods = {
49
49
  '📝': 'log',
50
50
  '⚠️': 'warn',
51
51
  '🚨': 'error',
52
- '🤱': 'appendChild'
52
+ '🤱': 'appendChild',
53
+ '😣': 'focus',
53
54
  //'🧩': 'includes'
54
55
  };
55
56
 
package/DX/paths.js CHANGED
@@ -19,8 +19,8 @@
19
19
  * const value = sp`${$.lastName}, ${$.firstName}`;
20
20
  * // ['?.lastName', ', ', '?.firstName']
21
21
  *
22
- * // Use .path for raw string contexts (object keys, plain arrays):
23
- * const key = $.textContent.path; // '?.textContent'
22
+ * // Use .Path for raw string contexts (object keys, plain arrays):
23
+ * const key = $.textContent.Path; // '?.textContent'
24
24
  */
25
25
  /**
26
26
  * Symbol used internally to detect path proxy objects.
@@ -30,23 +30,55 @@ const PATH_SYMBOL = Symbol('assign-gingerly-path');
30
30
  function isPathProxy(value) {
31
31
  return !!value && (typeof value === 'object' || typeof value === 'function') && PATH_SYMBOL in value;
32
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')
53
+ return prop;
54
+ if (prop in COMMAND_TOKEN_SUFFIXES)
55
+ return prop;
56
+ return undefined;
57
+ }
33
58
  /**
34
59
  * Create a proxy for id-ref paths (#[varName]).
35
60
  * After the initial #[varName], further property access chains with ?. from the resolved element.
36
- * .path returns the #[varName] prefix (optionally with further ?. path).
61
+ * .Path returns the #[varName] prefix (optionally with further ?. path).
37
62
  */
38
63
  function createIdRefProxy(idRef, options) {
39
64
  function handler() { }
40
65
  Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
41
66
  return new Proxy(handler, {
42
67
  get(_, prop) {
43
- if (prop === 'path' || prop === PATH_SYMBOL) {
68
+ if (prop === 'Path' || prop === PATH_SYMBOL) {
44
69
  return idRef;
45
70
  }
46
71
  if (typeof prop === 'symbol')
47
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') {
78
+ return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
79
+ }
48
80
  // Chain further path segments after the id ref
49
- const chained = `${idRef}?.${String(prop)}`;
81
+ const chained = appendPathSegment(idRef, String(prop));
50
82
  return createIdRefProxy(chained, options);
51
83
  },
52
84
  apply(_, __, args) {
@@ -84,12 +116,22 @@ function createPathProxy(prefix, options) {
84
116
  Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
85
117
  return new Proxy(handler, {
86
118
  get(_, prop) {
87
- if (prop === 'path' || prop === PATH_SYMBOL) {
88
- return prefix.length > 0 ? `?.${prefix}` : '?.';
119
+ if (prop === 'Path' || prop === PATH_SYMBOL) {
120
+ return serializePath(prefix);
89
121
  }
90
122
  // Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
91
123
  if (typeof prop === 'symbol')
92
124
  return undefined;
125
+ const reservedToken = getReservedToken(String(prop));
126
+ if (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
+ }
93
135
  let segment = String(prop);
94
136
  // #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
95
137
  if (segment.startsWith('#')) {
@@ -107,7 +149,7 @@ function createPathProxy(prefix, options) {
107
149
  }
108
150
  }
109
151
  }
110
- const newPath = prefix ? `${prefix}?.${segment}` : segment;
152
+ const newPath = appendPathSegment(prefix, segment);
111
153
  return createPathProxy(newPath, options);
112
154
  },
113
155
  apply(_, __, args) {
@@ -126,7 +168,7 @@ function createPathProxy(prefix, options) {
126
168
  }
127
169
  else
128
170
  argStr = String(arg);
129
- const newPath = prefix ? `${prefix}?.${argStr}` : argStr;
171
+ const newPath = appendPathSegment(prefix, argStr);
130
172
  return createPathProxy(newPath, options);
131
173
  }
132
174
  // No args — method called with no arguments, return self
@@ -143,21 +185,21 @@ function createPathProxy(prefix, options) {
143
185
  *
144
186
  * @example
145
187
  * const $ = paths<Person>();
146
- * $.lastName.path // '?.lastName'
147
- * $.address.city.path // '?.address?.city'
188
+ * $.lastName.Path // '?.lastName'
189
+ * $.address.city.Path // '?.address?.city'
148
190
  *
149
191
  * // With aka (reverse alias applied):
150
192
  * const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
151
- * $.querySelector('.user').textContent.path // '?.q?..user?.textContent'
193
+ * $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
152
194
  *
153
- * // Inside sp template literals, .path is not needed:
195
+ * // Inside sp template literals, .Path is not needed:
154
196
  * sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
155
197
  */
156
198
  export function paths(options) {
157
199
  return createPathProxy('', options);
158
200
  }
159
201
  /**
160
- * Create an assignment pair: { [lhs.path]: rhs.path }.
202
+ * Create an assignment pair: { [lhs.Path]: rhs.Path }.
161
203
  * Used to express "set this target to this source value" in a spreadable form.
162
204
  *
163
205
  * @example
@@ -276,7 +318,7 @@ export function forEachKeyIn(keys, factory, options) {
276
318
  * Interleaves static string segments with interpolated values.
277
319
  *
278
320
  * Path proxy objects are auto-detected and converted to their `?.`-prefixed
279
- * string representation — no `.path` call needed inside sp template literals.
321
+ * string representation — no `.Path` call needed inside sp template literals.
280
322
  *
281
323
  * Arrays passed as interpolations are preserved as nested arrays (for
282
324
  * all-or-nothing optional segments in builtIns.join).
@@ -319,8 +361,10 @@ export function sp(strings, ...values) {
319
361
  * e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
320
362
  */
321
363
  function extractPropName(pathStr) {
322
- const parts = pathStr.split('?.');
323
- return parts[parts.length - 1];
364
+ const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
365
+ const parts = withoutCommand.split('?.');
366
+ const last = parts[parts.length - 1];
367
+ return last === '@each' ? 'Each' : last;
324
368
  }
325
369
  /**
326
370
  * Tagged template literal that produces an array of {prop, val} objects + literal strings.
package/DX/paths.ts CHANGED
@@ -19,33 +19,75 @@
19
19
  * const value = sp`${$.lastName}, ${$.firstName}`;
20
20
  * // ['?.lastName', ', ', '?.firstName']
21
21
  *
22
- * // Use .path for raw string contexts (object keys, plain arrays):
23
- * const key = $.textContent.path; // '?.textContent'
22
+ * // Use .Path for raw string contexts (object keys, plain arrays):
23
+ * const key = $.textContent.Path; // '?.textContent'
24
24
  */
25
25
 
26
26
  /**
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');
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
- }
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' | undefined {
61
+ if (prop === 'Path') return prop;
62
+ if (prop in COMMAND_TOKEN_SUFFIXES) return prop as CommandToken;
63
+ return undefined;
64
+ }
35
65
 
36
66
  /**
37
67
  * Type that maps an object type to a proxy where every property access
38
68
  * returns either a deeper proxy (for object properties) or a terminal
39
- * with a `.path` string accessor — while providing full autocomplete.
40
- * Enhanced: also callable (for method call syntax).
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`.
41
72
  */
73
+ export type PathProxyCore = {
74
+ readonly Path: string;
75
+ readonly Each: PathProxy<any>;
76
+ readonly EqNot: PathProxy<any>;
77
+ readonly PlusEq: PathProxy<any>;
78
+ readonly QMEq: PathProxy<any>;
79
+ readonly YEq: PathProxy<any>;
80
+ readonly MinusEq: PathProxy<any>;
81
+ readonly Arrow: PathProxy<any>;
82
+ };
83
+
42
84
  export type PathProxy<T> = {
43
85
  [K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
44
- ? ((...args: any[]) => PathProxy<NonNullable<R>> & { readonly path: string }) & PathProxy<NonNullable<R>> & { readonly path: string }
86
+ ? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
45
87
  : T[K] extends (object | undefined | null)
46
- ? PathProxy<NonNullable<T[K]>> & { readonly path: string } & ((...args: any[]) => PathProxy<any> & { readonly path: string })
47
- : { readonly path: string } & ((...args: any[]) => PathProxy<any> & { readonly path: string });
48
- } & { readonly path: string } & ((...args: any[]) => PathProxy<any> & { readonly path: string });
88
+ ? PathProxy<NonNullable<T[K]>> & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore)
89
+ : PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
90
+ } & PathProxyCore & ((...args: any[]) => PathProxy<any> & PathProxyCore);
49
91
 
50
92
  /**
51
93
  * Options for paths proxy creation.
@@ -60,33 +102,41 @@ export interface PathsOptions {
60
102
  /**
61
103
  * Create a proxy for id-ref paths (#[varName]).
62
104
  * After the initial #[varName], further property access chains with ?. from the resolved element.
63
- * .path returns the #[varName] prefix (optionally with further ?. path).
105
+ * .Path returns the #[varName] prefix (optionally with further ?. path).
64
106
  */
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) {
107
+ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
108
+ function handler() {}
109
+ Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
110
+ return new Proxy(handler, {
111
+ get(_, prop: string | symbol) {
112
+ if (prop === 'Path' || prop === PATH_SYMBOL) {
71
113
  return idRef;
72
114
  }
73
115
  if (typeof prop === 'symbol') return undefined;
74
116
 
117
+ const reservedToken = getReservedToken(prop);
118
+ if (reservedToken === 'Each') {
119
+ return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
120
+ }
121
+ if (reservedToken && reservedToken !== 'Path') {
122
+ return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
123
+ }
124
+
75
125
  // Chain further path segments after the id ref
76
- const chained = `${idRef}?.${String(prop)}`;
126
+ const chained = appendPathSegment(idRef, String(prop));
77
127
  return createIdRefProxy(chained, options);
78
128
  },
79
129
  apply(_, __, args) {
80
130
  if (args.length > 0) {
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);
131
+ const arg = args[0];
132
+ let argStr: string;
133
+ if (arg === true) argStr = 'true';
134
+ else if (arg === false) argStr = 'false';
135
+ else if (isPathProxy(arg)) {
136
+ const fullPath = arg[PATH_SYMBOL] as string;
137
+ argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
138
+ }
139
+ else argStr = String(arg);
90
140
 
91
141
  const chained = `${idRef}?.${argStr}`;
92
142
  return createIdRefProxy(chained, options);
@@ -103,21 +153,32 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
103
153
  * When `aka` is provided, property names that match an alias *value* are output
104
154
  * using the alias *key* instead (reverse alias).
105
155
  */
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}` : '?.';
117
- }
156
+ function createPathProxy(prefix: string, options?: PathsOptions): any {
157
+ const aliasMap = options?.aka;
158
+
159
+ // Use a function as the target to enable the apply trap
160
+ function handler() {}
161
+ Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
162
+
163
+ return new Proxy(handler, {
164
+ get(_, prop: string | symbol) {
165
+ if (prop === 'Path' || prop === PATH_SYMBOL) {
166
+ return serializePath(prefix);
167
+ }
118
168
  // Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
119
169
  if (typeof prop === 'symbol') return undefined;
120
170
 
171
+ const reservedToken = getReservedToken(String(prop));
172
+ if (reservedToken === 'Path') {
173
+ return serializePath(prefix);
174
+ }
175
+ if (reservedToken === 'Each') {
176
+ return createPathProxy(appendPathSegment(prefix, '@each'), options);
177
+ }
178
+ if (reservedToken) {
179
+ return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
180
+ }
181
+
121
182
  let segment = String(prop);
122
183
 
123
184
  // #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
@@ -135,24 +196,24 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
135
196
  }
136
197
  }
137
198
 
138
- const newPath = prefix ? `${prefix}?.${segment}` : segment;
199
+ const newPath = appendPathSegment(prefix, segment);
139
200
  return createPathProxy(newPath, options);
140
201
  },
141
202
  apply(_, __, args) {
142
203
  // Method call syntax: $.querySelector('.username') → extends path with the argument
143
204
  if (args.length > 0) {
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);
154
-
155
- const newPath = prefix ? `${prefix}?.${argStr}` : argStr;
205
+ const arg = args[0];
206
+ let argStr: string;
207
+ if (arg === true) argStr = 'true';
208
+ else if (arg === false) argStr = 'false';
209
+ else if (isPathProxy(arg)) {
210
+ // Proxy arg — extract path without '?.' prefix
211
+ const fullPath = arg[PATH_SYMBOL] as string;
212
+ argStr = fullPath.startsWith('?.') ? fullPath.substring(2) : fullPath;
213
+ }
214
+ else argStr = String(arg);
215
+
216
+ const newPath = appendPathSegment(prefix, argStr);
156
217
  return createPathProxy(newPath, options);
157
218
  }
158
219
  // No args — method called with no arguments, return self
@@ -170,14 +231,14 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
170
231
  *
171
232
  * @example
172
233
  * const $ = paths<Person>();
173
- * $.lastName.path // '?.lastName'
174
- * $.address.city.path // '?.address?.city'
234
+ * $.lastName.Path // '?.lastName'
235
+ * $.address.city.Path // '?.address?.city'
175
236
  *
176
237
  * // With aka (reverse alias applied):
177
238
  * const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
178
- * $.querySelector('.user').textContent.path // '?.q?..user?.textContent'
239
+ * $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
179
240
  *
180
- * // Inside sp template literals, .path is not needed:
241
+ * // Inside sp template literals, .Path is not needed:
181
242
  * sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
182
243
  */
183
244
  export function paths<T>(options?: PathsOptions): PathProxy<T> {
@@ -185,7 +246,7 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
185
246
  }
186
247
 
187
248
  /**
188
- * Create an assignment pair: { [lhs.path]: rhs.path }.
249
+ * Create an assignment pair: { [lhs.Path]: rhs.Path }.
189
250
  * Used to express "set this target to this source value" in a spreadable form.
190
251
  *
191
252
  * @example
@@ -199,18 +260,18 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
199
260
  * count: 1
200
261
  * }
201
262
  */
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
- };
263
+ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
264
+ const lhsStr = isPathProxy(lhs)
265
+ ? lhs[PATH_SYMBOL]
266
+ : String(lhs);
267
+ return {
268
+ to(rhs: any): Record<string, any> {
269
+ const rhsStr = isPathProxy(rhs)
270
+ ? rhs[PATH_SYMBOL]
271
+ : rhs;
272
+ return { [lhsStr]: rhsStr };
273
+ }
274
+ };
214
275
  }
215
276
 
216
277
  /**
@@ -231,19 +292,19 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
231
292
  * });
232
293
  * // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
233
294
  */
234
- export function smoothOver(value: any): any {
235
- if (isPathProxy(value)) {
236
- return value[PATH_SYMBOL];
237
- }
295
+ export function smoothOver(value: any): any {
296
+ if (isPathProxy(value)) {
297
+ return value[PATH_SYMBOL];
298
+ }
238
299
  if (Array.isArray(value)) {
239
300
  return value.map(smoothOver);
240
301
  }
241
302
  if (value && typeof value === 'object') {
242
303
  const proto = Object.getPrototypeOf(value);
243
304
  if (proto === Object.prototype || proto === null) {
244
- const result: Record<string, any> = {};
245
- for (const [k, v] of Object.entries(value)) {
246
- result[k] = smoothOver(v);
305
+ const result: Record<string, any> = {};
306
+ for (const [k, v] of Object.entries(value)) {
307
+ result[k] = smoothOver(v);
247
308
  }
248
309
  return result;
249
310
  }
@@ -312,7 +373,7 @@ export function forEachKeyIn<T>(
312
373
  * Interleaves static string segments with interpolated values.
313
374
  *
314
375
  * Path proxy objects are auto-detected and converted to their `?.`-prefixed
315
- * string representation — no `.path` call needed inside sp template literals.
376
+ * string representation — no `.Path` call needed inside sp template literals.
316
377
  *
317
378
  * Arrays passed as interpolations are preserved as nested arrays (for
318
379
  * all-or-nothing optional segments in builtIns.join).
@@ -328,23 +389,23 @@ export function forEachKeyIn<T>(
328
389
  * sp`${$.lastName}${[', ', $.middleName]}, ${$.firstName}`
329
390
  * // ['?.lastName', [', ', '?.middleName'], ', ', '?.firstName']
330
391
  */
331
- export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
392
+ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
332
393
  const result: any[] = [];
333
394
  for (let i = 0; i < strings.length; i++) {
334
395
  if (strings[i]) result.push(strings[i]);
335
396
  if (i < values.length) {
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
- }
397
+ const v = values[i];
398
+ if (isPathProxy(v)) {
399
+ // Auto-extract path from proxy object
400
+ result.push(v[PATH_SYMBOL]);
401
+ } else if (Array.isArray(v)) {
402
+ // Nested array — recursively extract paths from proxy elements
403
+ result.push(v.map(el =>
404
+ isPathProxy(el) ? el[PATH_SYMBOL] : el
405
+ ));
406
+ } else {
407
+ result.push(v);
408
+ }
348
409
  }
349
410
  }
350
411
  return result;
@@ -355,8 +416,10 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
355
416
  * e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
356
417
  */
357
418
  function extractPropName(pathStr: string): string {
358
- const parts = pathStr.split('?.');
359
- return parts[parts.length - 1];
419
+ const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
420
+ const parts = withoutCommand.split('?.');
421
+ const last = parts[parts.length - 1];
422
+ return last === '@each' ? 'Each' : last;
360
423
  }
361
424
 
362
425
  /**
@@ -389,27 +452,27 @@ export function md(strings: TemplateStringsArray, ...values: any[]): any[] {
389
452
  for (let i = 0; i < strings.length; i++) {
390
453
  if (strings[i]) result.push(strings[i]);
391
454
  if (i < values.length) {
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);
455
+ const v = values[i];
456
+ if (isPathProxy(v)) {
457
+ // Proxy object → {prop, val}
458
+ const pathStr = v[PATH_SYMBOL] as string;
459
+ result.push({ prop: extractPropName(pathStr), val: pathStr });
460
+ } else if (Array.isArray(v)) {
461
+ // Nested array — recursively convert proxy elements to {prop, val}
462
+ result.push(v.map(el => {
463
+ if (isPathProxy(el)) {
464
+ const pathStr = el[PATH_SYMBOL] as string;
465
+ return { prop: extractPropName(pathStr), val: pathStr };
466
+ }
467
+ return el;
468
+ }));
469
+ } else if (v && typeof v === 'object' && 'prop' in v) {
470
+ // Developer override object — extract val from proxy if present
471
+ const processed = { ...v };
472
+ if (isPathProxy(processed.val)) {
473
+ processed.val = processed.val[PATH_SYMBOL];
474
+ }
475
+ result.push(processed);
413
476
  } else {
414
477
  result.push(v);
415
478
  }
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
- - `.path` extracts the `?.`-prefixed string: `$.address.city.path` → `'?.address?.city'`
4405
- - Inside `sp` template literals, `.path` is not needed proxy objects are auto-detected
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 `.path` outside of `sp`:**
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.path // '?.lastName'
4432
- $.address.city.path // '?.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.path]: '?.firstName' // '?.textContent': '?.firstName'
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 `.path` needed inside template literals) and preserve nested arrays for optional segments.
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