assign-gingerly 0.0.64 → 0.0.65

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/resolveValues.js CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Re-exports ResolveValuesOptions for backward compatibility.
8
8
  */
9
- import { getValue } from './getValues.js';
9
+ import { getValue, getValues } from './getValues.js';
10
10
  // Re-export getValue as resolveValue for backward compatibility
11
11
  export { getValue as resolveValue };
12
12
  /**
@@ -15,63 +15,6 @@ export { getValue as resolveValue };
15
15
  function hasProtocol(value) {
16
16
  return value.includes('://');
17
17
  }
18
- /**
19
- * Apply alias substitutions to a path string.
20
- */
21
- function applyAliases(path, aliasMap) {
22
- if (aliasMap.size === 0)
23
- return path;
24
- const parts = path.split('?.');
25
- const substituted = parts.map(part => aliasMap.get(part) ?? part);
26
- return substituted.join('?.');
27
- }
28
- /**
29
- * Path cache for parsed path strings.
30
- */
31
- const pathCache = new Map();
32
- function parseCachedPath(path) {
33
- let parts = pathCache.get(path);
34
- if (!parts) {
35
- parts = path.split('?.').filter(p => p.length > 0);
36
- pathCache.set(path, parts);
37
- }
38
- return parts;
39
- }
40
- /**
41
- * Navigate a path against a source object, optionally calling methods.
42
- */
43
- function navigatePath(source, parts, withMethods) {
44
- let current = source;
45
- let i = 0;
46
- while (i < parts.length) {
47
- if (current == null)
48
- return current;
49
- const part = parts[i];
50
- if (withMethods && withMethods.has(part)) {
51
- const method = current[part];
52
- if (typeof method === 'function') {
53
- const nextPart = parts[i + 1];
54
- if (nextPart !== undefined && !(withMethods.has(nextPart))) {
55
- current = method.call(current, nextPart);
56
- i += 2;
57
- }
58
- else {
59
- current = method.call(current);
60
- i++;
61
- }
62
- }
63
- else {
64
- current = current[part];
65
- i++;
66
- }
67
- }
68
- else {
69
- current = current[part];
70
- i++;
71
- }
72
- }
73
- return current;
74
- }
75
18
  /**
76
19
  * Resolves a protocol-prefixed value asynchronously.
77
20
  */
@@ -94,24 +37,24 @@ async function resolveProtocolValue(value, protocols, options) {
94
37
  /**
95
38
  * Resolve path strings and protocol references within an array (async).
96
39
  */
97
- async function resolveArray(arr, source, aliasMap, withMethods, protocols, options) {
40
+ async function resolveArray(arr, source, protocols, options) {
98
41
  const result = [];
99
42
  for (const item of arr) {
100
- if (typeof item === 'string' && item.startsWith('?.')) {
101
- const aliased = applyAliases(item, aliasMap);
102
- const parts = parseCachedPath(aliased);
103
- result.push(parts.length === 0 ? source : navigatePath(source, parts, withMethods));
104
- }
105
- else if (typeof item === 'string' && protocols && hasProtocol(item)) {
106
- result.push(await resolveProtocolValue(item, protocols, options));
43
+ if (typeof item === 'string') {
44
+ if (protocols && hasProtocol(item)) {
45
+ result.push(await resolveProtocolValue(item, protocols, options));
46
+ }
47
+ else {
48
+ result.push(getValue(item, source, options));
49
+ }
107
50
  }
108
51
  else if (Array.isArray(item)) {
109
- result.push(await resolveArray(item, source, aliasMap, withMethods, protocols, options));
52
+ result.push(await resolveArray(item, source, protocols, options));
110
53
  }
111
54
  else if (item && typeof item === 'object') {
112
55
  const proto = Object.getPrototypeOf(item);
113
56
  if (proto === Object.prototype || proto === null) {
114
- result.push(await resolveValues(item, source, options));
57
+ result.push(options?.protocols ? await resolveValues(item, source, options) : getValues(item, source, options));
115
58
  }
116
59
  else {
117
60
  result.push(item);
@@ -135,35 +78,24 @@ async function resolveArray(arr, source, aliasMap, withMethods, protocols, optio
135
78
  * @returns New object with path strings replaced by resolved values
136
79
  */
137
80
  export async function resolveValues(pattern, source, options) {
138
- const aliasMap = new Map();
139
- if (options?.aka) {
140
- for (const [alias, target] of Object.entries(options.aka)) {
141
- aliasMap.set(alias, target);
142
- }
143
- }
144
- const withMethods = options?.withMethods
145
- ? options.withMethods instanceof Set
146
- ? options.withMethods
147
- : new Set(options.withMethods)
148
- : undefined;
149
81
  const protocols = options?.protocols;
150
82
  const result = {};
151
83
  for (const [key, value] of Object.entries(pattern)) {
152
- if (typeof value === 'string' && value.startsWith('?.')) {
153
- const aliased = applyAliases(value, aliasMap);
154
- const parts = parseCachedPath(aliased);
155
- result[key] = parts.length === 0 ? source : navigatePath(source, parts, withMethods);
156
- }
157
- else if (typeof value === 'string' && protocols && hasProtocol(value)) {
158
- result[key] = await resolveProtocolValue(value, protocols, options);
84
+ if (typeof value === 'string') {
85
+ if (protocols && hasProtocol(value)) {
86
+ result[key] = await resolveProtocolValue(value, protocols, options);
87
+ }
88
+ else {
89
+ result[key] = getValue(value, source, options);
90
+ }
159
91
  }
160
92
  else if (Array.isArray(value)) {
161
- result[key] = await resolveArray(value, source, aliasMap, withMethods, protocols, options);
93
+ result[key] = await resolveArray(value, source, protocols, options);
162
94
  }
163
95
  else if (typeof value === 'object' && value !== null) {
164
96
  const proto = Object.getPrototypeOf(value);
165
97
  if (proto === Object.prototype || proto === null) {
166
- result[key] = await resolveValues(value, source, options);
98
+ result[key] = options?.protocols ? await resolveValues(value, source, options) : getValues(value, source, options);
167
99
  }
168
100
  else {
169
101
  result[key] = value;
package/resolveValues.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * Re-exports ResolveValuesOptions for backward compatibility.
8
8
  */
9
9
 
10
- import { getValue } from './getValues.js';
10
+ import { getValue, getValues } from './getValues.js';
11
11
  import type { ResolveValuesOptions } from './types/assign-gingerly/types.js';
12
12
 
13
13
  export type { ResolveValuesOptions };
@@ -22,65 +22,6 @@ function hasProtocol(value: string): boolean {
22
22
  return value.includes('://');
23
23
  }
24
24
 
25
- /**
26
- * Apply alias substitutions to a path string.
27
- */
28
- function applyAliases(path: string, aliasMap: Map<string, string>): string {
29
- if (aliasMap.size === 0) return path;
30
- const parts = path.split('?.');
31
- const substituted = parts.map(part => aliasMap.get(part) ?? part);
32
- return substituted.join('?.');
33
- }
34
-
35
- /**
36
- * Path cache for parsed path strings.
37
- */
38
- const pathCache = new Map<string, string[]>();
39
-
40
- function parseCachedPath(path: string): string[] {
41
- let parts = pathCache.get(path);
42
- if (!parts) {
43
- parts = path.split('?.').filter(p => p.length > 0);
44
- pathCache.set(path, parts);
45
- }
46
- return parts;
47
- }
48
-
49
- /**
50
- * Navigate a path against a source object, optionally calling methods.
51
- */
52
- function navigatePath(
53
- source: any,
54
- parts: string[],
55
- withMethods: Set<string> | undefined
56
- ): any {
57
- let current = source;
58
- let i = 0;
59
- while (i < parts.length) {
60
- if (current == null) return current;
61
- const part = parts[i];
62
- if (withMethods && withMethods.has(part)) {
63
- const method = current[part];
64
- if (typeof method === 'function') {
65
- const nextPart = parts[i + 1];
66
- if (nextPart !== undefined && !(withMethods.has(nextPart))) {
67
- current = method.call(current, nextPart);
68
- i += 2;
69
- } else {
70
- current = method.call(current);
71
- i++;
72
- }
73
- } else {
74
- current = current[part];
75
- i++;
76
- }
77
- } else {
78
- current = current[part];
79
- i++;
80
- }
81
- }
82
- return current;
83
- }
84
25
 
85
26
  /**
86
27
  * Resolves a protocol-prefixed value asynchronously.
@@ -115,25 +56,23 @@ async function resolveProtocolValue(
115
56
  async function resolveArray(
116
57
  arr: any[],
117
58
  source: any,
118
- aliasMap: Map<string, string>,
119
- withMethods: Set<string> | undefined,
120
59
  protocols: Record<string, (key: string) => any | Promise<any>> | undefined,
121
60
  options?: ResolveValuesOptions
122
61
  ): Promise<any[]> {
123
62
  const result: any[] = [];
124
63
  for (const item of arr) {
125
- if (typeof item === 'string' && item.startsWith('?.')) {
126
- const aliased = applyAliases(item, aliasMap);
127
- const parts = parseCachedPath(aliased);
128
- result.push(parts.length === 0 ? source : navigatePath(source, parts, withMethods));
129
- } else if (typeof item === 'string' && protocols && hasProtocol(item)) {
130
- result.push(await resolveProtocolValue(item, protocols, options));
64
+ if (typeof item === 'string') {
65
+ if (protocols && hasProtocol(item)) {
66
+ result.push(await resolveProtocolValue(item, protocols, options));
67
+ } else {
68
+ result.push(getValue(item, source, options));
69
+ }
131
70
  } else if (Array.isArray(item)) {
132
- result.push(await resolveArray(item, source, aliasMap, withMethods, protocols, options));
71
+ result.push(await resolveArray(item, source, protocols, options));
133
72
  } else if (item && typeof item === 'object') {
134
73
  const proto = Object.getPrototypeOf(item);
135
74
  if (proto === Object.prototype || proto === null) {
136
- result.push(await resolveValues(item, source, options));
75
+ result.push(options?.protocols ? await resolveValues(item, source, options) : getValues(item, source, options));
137
76
  } else {
138
77
  result.push(item);
139
78
  }
@@ -160,35 +99,22 @@ export async function resolveValues(
160
99
  source: any,
161
100
  options?: ResolveValuesOptions
162
101
  ): Promise<Record<string, any>> {
163
- const aliasMap = new Map<string, string>();
164
- if (options?.aka) {
165
- for (const [alias, target] of Object.entries(options.aka)) {
166
- aliasMap.set(alias, target);
167
- }
168
- }
169
-
170
- const withMethods = options?.withMethods
171
- ? options.withMethods instanceof Set
172
- ? options.withMethods
173
- : new Set(options.withMethods)
174
- : undefined;
175
-
176
102
  const protocols = options?.protocols;
177
103
 
178
104
  const result: Record<string, any> = {};
179
105
  for (const [key, value] of Object.entries(pattern)) {
180
- if (typeof value === 'string' && value.startsWith('?.')) {
181
- const aliased = applyAliases(value, aliasMap);
182
- const parts = parseCachedPath(aliased);
183
- result[key] = parts.length === 0 ? source : navigatePath(source, parts, withMethods);
184
- } else if (typeof value === 'string' && protocols && hasProtocol(value)) {
185
- result[key] = await resolveProtocolValue(value, protocols, options);
106
+ if (typeof value === 'string') {
107
+ if (protocols && hasProtocol(value)) {
108
+ result[key] = await resolveProtocolValue(value, protocols, options);
109
+ } else {
110
+ result[key] = getValue(value, source, options);
111
+ }
186
112
  } else if (Array.isArray(value)) {
187
- result[key] = await resolveArray(value, source, aliasMap, withMethods, protocols, options);
113
+ result[key] = await resolveArray(value, source, protocols, options);
188
114
  } else if (typeof value === 'object' && value !== null) {
189
115
  const proto = Object.getPrototypeOf(value);
190
116
  if (proto === Object.prototype || proto === null) {
191
- result[key] = await resolveValues(value, source, options);
117
+ result[key] = options?.protocols ? await resolveValues(value, source, options) : getValues(value, source, options);
192
118
  } else {
193
119
  result[key] = value;
194
120
  }
@@ -250,8 +250,21 @@ export type IEnhancementRegistryItem<T = any> = EnhancementConfig<T>;
250
250
  export interface IAssignGingerlyOptions {
251
251
  registry?: typeof EnhancementRegistry | EnhancementRegistry;
252
252
  bypassChecks?: boolean;
253
+ /**
254
+ * Method names to call during path evaluation (e.g., for `?.method()` calls)
255
+ */
253
256
  withMethods?: string[] | Set<string>;
257
+ /**
258
+ * Alias mappings for property and method names.
259
+ */
254
260
  aka?: Record<string, string>;
261
+
262
+ /**
263
+ * Shorthand for binding method aliases from the source object.
264
+ * Each entry maps an alias to a method name and is normalized into
265
+ * the existing withMethods + aka behavior.
266
+ */
267
+ akaMethods?: Record<string, string>;
255
268
 
256
269
  /**
257
270
  * AbortSignal for cleaning up reactive subscriptions (@eachTime)
@@ -289,11 +302,89 @@ export interface IAssignTentativelyOptions {
289
302
  aka?: Record<string, string>;
290
303
  }
291
304
 
305
+ /**
306
+ * Options for assignFrom / assignFromAsync.
307
+ */
308
+ export interface AssignFromOptions {
309
+ /** Source object to resolve RHS path strings against */
310
+ from: any;
311
+
312
+ /** Protocol handlers (sync or async) */
313
+ protocols?: Record<string, (key: string) => any | Promise<any>>;
314
+
315
+ /** Method names to call during path evaluation */
316
+ withMethods?: string[] | Set<string>;
317
+
318
+ /** Alias mappings for path segments */
319
+ aka?: Record<string, string>;
320
+
321
+ /** AbortSignal for cleanup */
322
+ signal?: AbortSignal;
323
+
324
+ /** Loop variable bindings — expand pattern entries containing ${x} */
325
+ where_x_in?: string[];
326
+ /** Loop variable bindings — expand pattern entries containing ${y} */
327
+ where_y_in?: string[];
328
+ /** Loop variable bindings — expand pattern entries containing ${z} */
329
+ where_z_in?: string[];
330
+
331
+ /**
332
+ * Pin element references by variable name.
333
+ * Used with `#[varName]` syntax in LHS keys for fast repeated element access.
334
+ *
335
+ * - String value: existing element ID (uses getElementById)
336
+ * - Object value: { qry: 'selector' } — finds element via querySelector on target, auto-assigns an ID
337
+ * - Object value: { path: [...], expect?, fallback? } — child index path + auto-ID + optional validation
338
+ */
339
+ pin?: Record<string, string | { qry: string } | { path: number[]; expect?: string; fallback?: boolean }>;
340
+
341
+ /**
342
+ * Positional element references for use with `#[varName]` syntax.
343
+ * Resolves elements by child index path — no IDs assigned, no caching.
344
+ */
345
+ at?: Record<string, number[] | { path: number[]; expect?: string; fallback?: boolean }>;
346
+
347
+ /**
348
+ * Handler implementations scoped to this call.
349
+ * Key: the `do` name referenced in handler configs.
350
+ * Value: a class constructor, an import path, or a builtIns.* alias string.
351
+ */
352
+ handlers?: Record<string, AssignFromHandlerConstructor | string>;
353
+
354
+ /**
355
+ * Inferred assignments — automatically distribute source values to matching
356
+ * DOM elements based on structural conventions (itemprop, name, etc.).
357
+ */
358
+ infer?: {
359
+ byItemprop?: string[] | true;
360
+ '|'?: string[] | true;
361
+ byName?: string[] | true | { props: string[] | true; outside: string };
362
+ '@'?: string[] | true | { props: string[] | true; outside: string };
363
+ beVigilant?: boolean;
364
+ };
365
+
366
+ /**
367
+ * Bulk enhancement application via EMC JSON configs.
368
+ */
369
+ enhance?: Array<{ emc: string; matching?: string; parse?: boolean }>;
370
+
371
+ /** Registry for enhancement dependency injection (inherited from IAssignGingerlyOptions) */
372
+ registry?: any;
373
+
374
+ [key: string]: any;
375
+ }
376
+
292
377
  /**
293
378
  * Options for synchronous value resolution (getValues / getValue).
294
379
  * Extends IAssignGingerlyOptions with synchronous protocol handlers.
295
380
  */
296
381
  export interface GetValuesOptions extends IAssignGingerlyOptions {
382
+ /**
383
+ * Internal root object for special $0 references.
384
+ * This is used by assignFrom to resolve values relative to the target object.
385
+ */
386
+ root?: any;
387
+
297
388
  /**
298
389
  * Synchronous protocol handlers for resolving protocol-prefixed values.
299
390
  * Each handler receives the key portion and MUST return synchronously.
@@ -313,6 +404,12 @@ export interface GetValuesOptions extends IAssignGingerlyOptions {
313
404
  * Same as GetValuesOptions but protocol handlers may return Promises.
314
405
  */
315
406
  export interface ResolveValuesOptions extends IAssignGingerlyOptions {
407
+ /**
408
+ * Internal root object for special $0 references.
409
+ * This is used by assignFrom to resolve values relative to the target object.
410
+ */
411
+ root?: any;
412
+
316
413
  /**
317
414
  * Protocol handlers for resolving protocol-prefixed values (e.g., 'globalThis://key').
318
415
  * Each handler receives the key portion and returns the resolved value (sync or async).
@@ -697,6 +794,20 @@ export interface LazyLoadSwitchResolvedParams extends Omit<LazyLoadResolvedParam
697
794
  rhs: any;
698
795
  }
699
796
 
797
+ export interface FromEachItemConfig {
798
+ assignToFragment?: Record<string, any>;
799
+ withOptions?: AssignFromOptions;
800
+ resolve?: {
801
+ key?: string;
802
+ }
803
+ }
804
+
805
+ export interface ManageTemplateListConfig extends HandlerConfig {
806
+ do: 'builtIns.manageTemplateList';
807
+ resolve: ManageTemplateListResolvedParams;
808
+ fromEachItem: FromEachItemConfig;
809
+ }
810
+
700
811
  /**
701
812
  * Resolved parameters received by ManageTemplateListHandler.assign().
702
813
  */
@@ -740,3 +851,32 @@ export declare class LazyLoadHandler implements AssignFromHandler {
740
851
  assign(lhsTarget: any, resolvedParams: Record<string, any>, options?: any): Promise<void>;
741
852
  protected onCloneInserted(nodes: Node[], lhsTarget: Element, resolvedParams: Record<string, any>): Promise<void>;
742
853
  }
854
+
855
+ //#region Event Handler
856
+
857
+ export interface AssignVector {
858
+ assignToTarget?: Record<string, any>,
859
+ assignToSource?: Record<string, any>,
860
+ assignToLHS?: Record<string, any>,
861
+ withOptions?: AssignFromOptions,
862
+ withAssignToTargetOptions?: AssignFromOptions,
863
+ withAssignToSourceOptions?: AssignFromOptions,
864
+ withAssignToLHSOptions?: AssignFromOptions,
865
+ }
866
+
867
+
868
+ export interface AddEventListenerConfig extends AssignVector {
869
+ get?: {
870
+ abortController?: string | AbortController,
871
+ on?: string,
872
+ nudge?: boolean,
873
+ options?: AddEventListenerOptions,
874
+ },
875
+
876
+ fromLHS?: AssignVector,
877
+ fromSource?: AssignVector,
878
+ fromEvent?: AssignVector,
879
+
880
+
881
+ }
882
+ //#endregion