assign-gingerly 0.0.81 → 0.0.83

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.
@@ -49,7 +49,7 @@ function appendCommandSuffix(prefix, token) {
49
49
  return `${prefix}${COMMAND_TOKEN_SUFFIXES[token]}`;
50
50
  }
51
51
  function getReservedToken(prop) {
52
- if (prop === 'Path' || prop === 'path')
52
+ if (prop === 'Path')
53
53
  return prop;
54
54
  if (prop in COMMAND_TOKEN_SUFFIXES)
55
55
  return prop;
@@ -58,14 +58,14 @@ function getReservedToken(prop) {
58
58
  /**
59
59
  * Create a proxy for id-ref paths (#[varName]).
60
60
  * After the initial #[varName], further property access chains with ?. from the resolved element.
61
- * .path returns the #[varName] prefix (optionally with further ?. path).
61
+ * .Path returns the #[varName] prefix (optionally with further ?. path).
62
62
  */
63
63
  function createIdRefProxy(idRef, options) {
64
64
  function handler() { }
65
65
  Object.defineProperty(handler, PATH_SYMBOL, { value: idRef });
66
66
  return new Proxy(handler, {
67
67
  get(_, prop) {
68
- if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
68
+ if (prop === 'Path' || prop === PATH_SYMBOL) {
69
69
  return idRef;
70
70
  }
71
71
  if (typeof prop === 'symbol')
@@ -74,7 +74,7 @@ function createIdRefProxy(idRef, options) {
74
74
  if (reservedToken === 'Each') {
75
75
  return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
76
76
  }
77
- if (reservedToken && reservedToken !== 'Path' && reservedToken !== 'path') {
77
+ if (reservedToken && reservedToken !== 'Path') {
78
78
  return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
79
79
  }
80
80
  // Chain further path segments after the id ref
@@ -116,14 +116,14 @@ function createPathProxy(prefix, options) {
116
116
  Object.defineProperty(handler, PATH_SYMBOL, { value: prefix.length > 0 ? `?.${prefix}` : '?.' });
117
117
  return new Proxy(handler, {
118
118
  get(_, prop) {
119
- if (prop === 'path' || prop === 'Path' || prop === PATH_SYMBOL) {
119
+ if (prop === 'Path' || prop === PATH_SYMBOL) {
120
120
  return serializePath(prefix);
121
121
  }
122
122
  // Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
123
123
  if (typeof prop === 'symbol')
124
124
  return undefined;
125
125
  const reservedToken = getReservedToken(String(prop));
126
- if (reservedToken === 'Path' || reservedToken === 'path') {
126
+ if (reservedToken === 'Path') {
127
127
  return serializePath(prefix);
128
128
  }
129
129
  if (reservedToken === 'Each') {
@@ -185,21 +185,21 @@ function createPathProxy(prefix, options) {
185
185
  *
186
186
  * @example
187
187
  * const $ = paths<Person>();
188
- * $.lastName.path // '?.lastName'
189
- * $.address.city.path // '?.address?.city'
188
+ * $.lastName.Path // '?.lastName'
189
+ * $.address.city.Path // '?.address?.city'
190
190
  *
191
191
  * // With aka (reverse alias applied):
192
192
  * const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
193
- * $.querySelector('.user').textContent.path // '?.q?..user?.textContent'
193
+ * $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
194
194
  *
195
- * // Inside sp template literals, .path is not needed:
195
+ * // Inside sp template literals, .Path is not needed:
196
196
  * sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
197
197
  */
198
198
  export function paths(options) {
199
199
  return createPathProxy('', options);
200
200
  }
201
201
  /**
202
- * Create an assignment pair: { [lhs.path]: rhs.path }.
202
+ * Create an assignment pair: { [lhs.Path]: rhs.Path }.
203
203
  * Used to express "set this target to this source value" in a spreadable form.
204
204
  *
205
205
  * @example
@@ -318,7 +318,7 @@ export function forEachKeyIn(keys, factory, options) {
318
318
  * Interleaves static string segments with interpolated values.
319
319
  *
320
320
  * Path proxy objects are auto-detected and converted to their `?.`-prefixed
321
- * string representation — no `.path` call needed inside sp template literals.
321
+ * string representation — no `.Path` call needed inside sp template literals.
322
322
  *
323
323
  * Arrays passed as interpolations are preserved as nested arrays (for
324
324
  * all-or-nothing optional segments in builtIns.join).
package/DX/paths.ts CHANGED
@@ -19,76 +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
- }
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
- }
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
+ }
65
65
 
66
66
  /**
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);
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 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
+
84
+ export type PathProxy<T> = {
85
+ [K in keyof T]-?: T[K] extends ((...args: any[]) => infer R)
86
+ ? ((...args: any[]) => PathProxy<NonNullable<R>> & PathProxyCore) & PathProxy<NonNullable<R>> & PathProxyCore
87
+ : T[K] extends (object | undefined | null)
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);
92
91
 
93
92
  /**
94
93
  * Options for paths proxy creation.
@@ -103,33 +102,33 @@ export interface PathsOptions {
103
102
  /**
104
103
  * Create a proxy for id-ref paths (#[varName]).
105
104
  * After the initial #[varName], further property access chains with ?. from the resolved element.
106
- * .path returns the #[varName] prefix (optionally with further ?. path).
105
+ * .Path returns the #[varName] prefix (optionally with further ?. path).
107
106
  */
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) {
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) {
114
113
  return idRef;
115
114
  }
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') {
115
+ if (typeof prop === 'symbol') return undefined;
116
+
117
+ const reservedToken = getReservedToken(prop);
118
+ if (reservedToken === 'Each') {
119
+ return createIdRefProxy(appendPathSegment(idRef, '@each'), options);
120
+ }
121
+ if (reservedToken && reservedToken !== 'Path') {
123
122
  return createIdRefProxy(appendCommandSuffix(idRef, reservedToken), options);
124
123
  }
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];
124
+
125
+ // Chain further path segments after the id ref
126
+ const chained = appendPathSegment(idRef, String(prop));
127
+ return createIdRefProxy(chained, options);
128
+ },
129
+ apply(_, __, args) {
130
+ if (args.length > 0) {
131
+ const arg = args[0];
133
132
  let argStr: string;
134
133
  if (arg === true) argStr = 'true';
135
134
  else if (arg === false) argStr = 'false';
@@ -154,72 +153,72 @@ function createIdRefProxy(idRef: string, options?: PathsOptions): any {
154
153
  * When `aka` is provided, property names that match an alias *value* are output
155
154
  * using the alias *key* instead (reverse alias).
156
155
  */
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) {
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) {
167
166
  return serializePath(prefix);
168
167
  }
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') {
168
+ // Ignore symbol access (Symbol.iterator, Symbol.toPrimitive, etc.)
169
+ if (typeof prop === 'symbol') return undefined;
170
+
171
+ const reservedToken = getReservedToken(String(prop));
172
+ if (reservedToken === 'Path') {
174
173
  return serializePath(prefix);
175
174
  }
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}]`;
175
+ if (reservedToken === 'Each') {
176
+ return createPathProxy(appendPathSegment(prefix, '@each'), options);
177
+ }
178
+ if (reservedToken) {
179
+ return createPathProxy(appendCommandSuffix(prefix, reservedToken), options);
180
+ }
181
+
182
+ let segment = String(prop);
183
+
184
+ // #-prefix: $['#firstName'] → '#[firstName]' (cached element ref)
185
+ if (segment.startsWith('#')) {
186
+ const varName = segment.substring(1);
187
+ const idRef = `#[${varName}]`;
189
188
  // Return a proxy that starts from this id ref (can chain further with ?.)
190
189
  return createIdRefProxy(idRef, options);
191
190
  }
192
191
 
193
192
  // Apply reverse alias: if prop matches an alias value, use the alias key
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) {
193
+ if (aliasMap) {
194
+ for (const [alias, target] of Object.entries(aliasMap)) {
195
+ if (target === segment) { segment = alias; break; }
196
+ }
197
+ }
198
+
199
+ const newPath = appendPathSegment(prefix, segment);
200
+ return createPathProxy(newPath, options);
201
+ },
202
+ apply(_, __, args) {
203
+ // Method call syntax: $.querySelector('.username') → extends path with the argument
204
+ if (args.length > 0) {
206
205
  const arg = args[0];
207
206
  let argStr: string;
208
207
  if (arg === true) argStr = 'true';
209
208
  else if (arg === false) argStr = 'false';
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
- }
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);
217
+ return createPathProxy(newPath, options);
218
+ }
219
+ // No args — method called with no arguments, return self
220
+ return createPathProxy(prefix, options);
221
+ }
223
222
  });
224
223
  }
225
224
 
@@ -232,14 +231,14 @@ function createPathProxy(prefix: string, options?: PathsOptions): any {
232
231
  *
233
232
  * @example
234
233
  * const $ = paths<Person>();
235
- * $.lastName.path // '?.lastName'
236
- * $.address.city.path // '?.address?.city'
234
+ * $.lastName.Path // '?.lastName'
235
+ * $.address.city.Path // '?.address?.city'
237
236
  *
238
237
  * // With aka (reverse alias applied):
239
238
  * const $ = paths<MyEl>({ aka: { q: 'querySelector' } });
240
- * $.querySelector('.user').textContent.path // '?.q?..user?.textContent'
239
+ * $.querySelector('.user').textContent.Path // '?.q?..user?.textContent'
241
240
  *
242
- * // Inside sp template literals, .path is not needed:
241
+ * // Inside sp template literals, .Path is not needed:
243
242
  * sp`${$.lastName}, ${$.firstName}` // ['?.lastName', ', ', '?.firstName']
244
243
  */
245
244
  export function paths<T>(options?: PathsOptions): PathProxy<T> {
@@ -247,7 +246,7 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
247
246
  }
248
247
 
249
248
  /**
250
- * Create an assignment pair: { [lhs.path]: rhs.path }.
249
+ * Create an assignment pair: { [lhs.Path]: rhs.Path }.
251
250
  * Used to express "set this target to this source value" in a spreadable form.
252
251
  *
253
252
  * @example
@@ -261,10 +260,10 @@ export function paths<T>(options?: PathsOptions): PathProxy<T> {
261
260
  * count: 1
262
261
  * }
263
262
  */
264
- export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
265
- const lhsStr = isPathProxy(lhs)
266
- ? lhs[PATH_SYMBOL]
267
- : String(lhs);
263
+ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
264
+ const lhsStr = isPathProxy(lhs)
265
+ ? lhs[PATH_SYMBOL]
266
+ : String(lhs);
268
267
  return {
269
268
  to(rhs: any): Record<string, any> {
270
269
  const rhsStr = isPathProxy(rhs)
@@ -293,9 +292,9 @@ export function set(lhs: any): { to: (rhs: any) => Record<string, any> } {
293
292
  * });
294
293
  * // { assign: { incrementButton: '?.clone?.q?..increment', ... } }
295
294
  */
296
- export function smoothOver(value: any): any {
297
- if (isPathProxy(value)) {
298
- return value[PATH_SYMBOL];
295
+ export function smoothOver(value: any): any {
296
+ if (isPathProxy(value)) {
297
+ return value[PATH_SYMBOL];
299
298
  }
300
299
  if (Array.isArray(value)) {
301
300
  return value.map(smoothOver);
@@ -374,7 +373,7 @@ export function forEachKeyIn<T>(
374
373
  * Interleaves static string segments with interpolated values.
375
374
  *
376
375
  * Path proxy objects are auto-detected and converted to their `?.`-prefixed
377
- * string representation — no `.path` call needed inside sp template literals.
376
+ * string representation — no `.Path` call needed inside sp template literals.
378
377
  *
379
378
  * Arrays passed as interpolations are preserved as nested arrays (for
380
379
  * all-or-nothing optional segments in builtIns.join).
@@ -416,12 +415,12 @@ export function sp(strings: TemplateStringsArray, ...values: any[]): any[] {
416
415
  * Extract the last segment from a `?.`-prefixed path string.
417
416
  * e.g., '?.address?.city' → 'city', '?.firstName' → 'firstName'
418
417
  */
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
- }
418
+ function extractPropName(pathStr: string): string {
419
+ const withoutCommand = pathStr.replace(/(?: \+=| =!| \?=| Y=| -=| =>)$/, '');
420
+ const parts = withoutCommand.split('?.');
421
+ const last = parts[parts.length - 1];
422
+ return last === '@each' ? 'Each' : last;
423
+ }
425
424
 
426
425
  /**
427
426
  * Tagged template literal that produces an array of {prop, val} objects + literal strings.
package/README.md CHANGED
@@ -100,7 +100,8 @@ assignFrom adds support for:
100
100
  3. Protocol resolution (`globalThis://`, `localStorage://`, custom sync protocols).
101
101
  4. Handler plugins via the ` =>` operator for custom logic (fire-and-forget in sync mode, awaitable in async mode).
102
102
  5. Looped substitution with `where_x_in` / `where_y_in` / `where_z_in` for expanding template patterns into multiple concrete assignments.
103
- 6. Spread merging via the `"..."` key.
103
+ 6. Dynamic substitutions via the `substitutions` option for injecting runtime string values into path segments. See [docs/substitutions.md](docs/substitutions.md).
104
+ 7. Spread merging via the `"..."` key.
104
105
 
105
106
  Example:
106
107
 
package/assignFrom.js CHANGED
@@ -49,6 +49,7 @@ function resolveTernaryValue(value, source, options) {
49
49
  return getValue(value, source, {
50
50
  withMethods: options.withMethods,
51
51
  aka: options.aka,
52
+ substitutions: options.substitutions,
52
53
  protocols: options.protocols,
53
54
  root: options.root
54
55
  });
@@ -61,6 +62,7 @@ function resolveTernaryValue(value, source, options) {
61
62
  return getValue(value, source, {
62
63
  withMethods: options.withMethods,
63
64
  aka: options.aka,
65
+ substitutions: options.substitutions,
64
66
  protocols: options.protocols,
65
67
  root: options.root
66
68
  });
@@ -381,7 +383,7 @@ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, option
381
383
  const ids = getEffectiveIds(options);
382
384
  if (!ids)
383
385
  return;
384
- const { withMethods, aka, akaMethods, protocols, from } = options;
386
+ const { withMethods, aka, akaMethods, protocols, from, substitutions } = options;
385
387
  for (const key of idRefNormalKeys) {
386
388
  const parsed = parseIdRef(key);
387
389
  if (!parsed)
@@ -391,11 +393,11 @@ function processIdRefNormalKeys(idRefNormalKeys, expandedPattern, target, option
391
393
  continue;
392
394
  const value = expandedPattern[key];
393
395
  if (parsed.remainingPath) {
394
- const resolvedValue = getValues({ __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor });
396
+ const resolvedValue = getValues({ __v: value }, from, { withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor });
395
397
  assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
396
398
  }
397
399
  else {
398
- const resolvedValue = getValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor });
400
+ const resolvedValue = getValues(typeof value === 'object' && value !== null ? value : { __v: value }, from, { withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor });
399
401
  if (!('__v' in resolvedValue)) {
400
402
  assignGingerly(el, resolvedValue, options, permissionProcessor);
401
403
  }
@@ -458,6 +460,7 @@ export function assignFrom(target, pattern, options, permissionProcessor) {
458
460
  withMethods: options.withMethods,
459
461
  aka: options.aka,
460
462
  akaMethods: options.akaMethods,
463
+ substitutions: options.substitutions,
461
464
  protocols: options.protocols,
462
465
  root: target,
463
466
  permissionProcessor
package/assignFrom.ts CHANGED
@@ -58,6 +58,7 @@ function resolveTernaryValue(value: any, source: any, options: AssignFromOptions
58
58
  return getValue(value, source, {
59
59
  withMethods: options.withMethods,
60
60
  aka: options.aka,
61
+ substitutions: options.substitutions,
61
62
  protocols: options.protocols,
62
63
  root: options.root
63
64
  });
@@ -70,6 +71,7 @@ function resolveTernaryValue(value: any, source: any, options: AssignFromOptions
70
71
  return getValue(value, source, {
71
72
  withMethods: options.withMethods,
72
73
  aka: options.aka,
74
+ substitutions: options.substitutions,
73
75
  protocols: options.protocols,
74
76
  root: options.root
75
77
  });
@@ -395,7 +397,7 @@ function processIdRefNormalKeys(
395
397
  const ids = getEffectiveIds(options);
396
398
  if (!ids) return;
397
399
 
398
- const { withMethods, aka, akaMethods, protocols, from } = options;
400
+ const { withMethods, aka, akaMethods, protocols, from, substitutions } = options;
399
401
  for (const key of idRefNormalKeys) {
400
402
  const parsed = parseIdRef(key);
401
403
  if (!parsed) continue;
@@ -407,14 +409,14 @@ function processIdRefNormalKeys(
407
409
  if (parsed.remainingPath) {
408
410
  const resolvedValue = getValues(
409
411
  { __v: value }, from,
410
- { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor }
412
+ { withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor }
411
413
  );
412
414
  assignGingerly(el, { [parsed.remainingPath]: resolvedValue.__v }, options, permissionProcessor);
413
415
  } else {
414
416
  const resolvedValue = getValues(
415
417
  typeof value === 'object' && value !== null ? value : { __v: value },
416
418
  from,
417
- { withMethods, aka, akaMethods, protocols, root: target, permissionProcessor }
419
+ { withMethods, aka, akaMethods, substitutions, protocols, root: target, permissionProcessor }
418
420
  );
419
421
  if (!('__v' in resolvedValue)) {
420
422
  assignGingerly(el, resolvedValue, options, permissionProcessor);
@@ -486,6 +488,7 @@ export function assignFrom(
486
488
  withMethods: options.withMethods,
487
489
  aka: options.aka,
488
490
  akaMethods: options.akaMethods,
491
+ substitutions: options.substitutions,
489
492
  protocols: options.protocols,
490
493
  root: target,
491
494
  permissionProcessor