@microsoft/fast-html 1.0.0-alpha.19 → 1.0.0-alpha.20

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.
@@ -1,4 +1,5 @@
1
1
  import { Observable } from "@microsoft/fast-element/observable.js";
2
+ import { defsPropertyName, refPropertyName, } from "./schema.js";
2
3
  const openClientSideBinding = "{";
3
4
  const closeClientSideBinding = "}";
4
5
  const openContentBinding = "{{";
@@ -140,6 +141,11 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
140
141
  function getContentDataBindingConfig(config) {
141
142
  return Object.assign(Object.assign({}, config), { subtype: "content" });
142
143
  }
144
+ /**
145
+ * Finds the next data binding in innerHTML and determines its type and indices
146
+ * @param innerHTML - The innerHTML string to search for data bindings
147
+ * @returns NextDataBindingBehaviorConfig containing the binding type and start indices
148
+ */
143
149
  function getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML) {
144
150
  // {{{}}} binding
145
151
  const openingUnescapedStartIndex = innerHTML.indexOf(openUnescapedBinding);
@@ -214,36 +220,6 @@ export function getNextBehavior(innerHTML) {
214
220
  }
215
221
  return getNextDataBindingBehavior(innerHTML);
216
222
  }
217
- /**
218
- * Gets all the partials with their IDs
219
- * @param innerHTML - The innerHTML string to evaluate
220
- * @param offset - The index offset from the innerHTML
221
- * @param partials - The partials found
222
- * @returns {[key: string]: PartialTemplateConfig}
223
- */
224
- export function getAllPartials(innerHTML, offset = 0, partials = {}) {
225
- const openingTag = `${openTagStart}partial`;
226
- const openingTagStartIndex = innerHTML.indexOf(openingTag);
227
- if (openingTagStartIndex >= 0) {
228
- const openingTagStartSlice = innerHTML.slice(openingTagStartIndex);
229
- const closingTag = `${closeTagStart}partial${tagEnd}`;
230
- const closingTagLength = closingTag.length;
231
- const matchingCloseTagIndex = getIndexOfNextMatchingTag(openingTagStartSlice, openingTag, closingTag, openingTagStartIndex) + closingTagLength;
232
- const startId = openingTagStartIndex + ' id="'.length + openingTag.length;
233
- const endId = innerHTML.slice(startId).indexOf('"') + startId;
234
- const id = innerHTML.slice(startId, endId);
235
- const openingTagEndIndex = openingTagStartSlice.indexOf(tagEnd) + 1 + openingTagStartIndex;
236
- const closingTagStartIndex = matchingCloseTagIndex - closingTagLength;
237
- partials[id] = {
238
- innerHTML: innerHTML.slice(openingTagEndIndex, closingTagStartIndex),
239
- startIndex: openingTagEndIndex + offset,
240
- endIndex: closingTagStartIndex + offset,
241
- };
242
- offset += matchingCloseTagIndex;
243
- return getAllPartials(innerHTML.slice(matchingCloseTagIndex), offset, partials);
244
- }
245
- return partials;
246
- }
247
223
  /**
248
224
  * Create a function to resolve a value from an object using a path with dot syntax.
249
225
  * e.g. "foo.bar"
@@ -251,30 +227,38 @@ export function getAllPartials(innerHTML, offset = 0, partials = {}) {
251
227
  * @param self - Where the first item in the path path refers to the item itself (used by repeat).
252
228
  * @returns A function to access the value from a given path.
253
229
  */
254
- export function pathResolver(path, self = false) {
255
- let splitPath = [];
256
- path.split("../").forEach((pathItem, index) => {
257
- if (pathItem === "") {
258
- splitPath.unshift("../");
230
+ export function pathResolver(path, contextPath, level, rootSchema) {
231
+ var _a, _b;
232
+ let splitPath = path.split(".");
233
+ let levelCount = level;
234
+ let self = splitPath[0] === contextPath;
235
+ const parentContexts = [];
236
+ if (level > 0 &&
237
+ ((_b = (_a = rootSchema === null || rootSchema === void 0 ? void 0 : rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[contextPath]) === null || _b === void 0 ? void 0 : _b.$fast_context) ===
238
+ splitPath.at(-1)) {
239
+ self = true;
240
+ }
241
+ while (levelCount > 0 && !self) {
242
+ if (levelCount !== 1) {
243
+ parentContexts.push("parentContext");
259
244
  }
260
245
  else {
261
- splitPath.push(...pathItem.split("."));
246
+ parentContexts.push("parent");
262
247
  }
263
- });
264
- splitPath = splitPath.map((pathItem, index) => {
265
- if (pathItem === "../") {
266
- if (splitPath[index + 1] === "../") {
267
- return "parentContext";
268
- }
269
- return "parent";
270
- }
271
- return pathItem;
272
- });
248
+ levelCount--;
249
+ }
250
+ splitPath = [...parentContexts, ...splitPath];
273
251
  return pathWithContextResolver(splitPath, self);
274
252
  }
253
+ /**
254
+ * Creates a resolver function that can access properties from an object using a split path array
255
+ * @param splitPath - The dot syntax path split into an array of property names
256
+ * @param self - Whether the first item in the path refers to the item itself
257
+ * @returns A function that resolves the value from the given path on an accessible object
258
+ */
275
259
  function pathWithContextResolver(splitPath, self) {
276
- const usesContext = splitPath[0] === "parent" || splitPath[0] === "parentContext";
277
- if (self && !usesContext) {
260
+ const isInPreviousContext = splitPath[0] === "parent" || splitPath[0] === "parentContext";
261
+ if (self && !isInPreviousContext) {
278
262
  if (splitPath.length > 1) {
279
263
  splitPath = splitPath.slice(1);
280
264
  }
@@ -284,7 +268,7 @@ function pathWithContextResolver(splitPath, self) {
284
268
  };
285
269
  }
286
270
  }
287
- if (usesContext) {
271
+ if (isInPreviousContext) {
288
272
  return (accessibleObject, context) => {
289
273
  return splitPath.reduce((previousAccessors, pathItem) => {
290
274
  return previousAccessors === null || previousAccessors === void 0 ? void 0 : previousAccessors[pathItem];
@@ -297,40 +281,23 @@ function pathWithContextResolver(splitPath, self) {
297
281
  }, accessibleObject);
298
282
  };
299
283
  }
300
- export function bindingResolver(path, self = false, parentContext, type, observerMap, contextPath, level) {
301
- // Cache path during template processing when ObserverMap is provided
302
- if (observerMap) {
303
- observerMap.cachePathWithContext({
304
- path,
305
- self,
306
- parentContext,
307
- contextPath,
308
- type,
309
- level,
310
- rootPath: null,
311
- context: null,
312
- });
313
- }
314
- return pathResolver(path, self);
315
- }
316
- export function expressionResolver(self, expression, parentContext, level, observerMap) {
317
- // Cache paths from expression during template processing when ObserverMap is provided
318
- if (observerMap) {
319
- const paths = extractPathsFromChainedExpression(expression);
320
- paths.forEach(path => {
321
- observerMap.cachePathWithContext({
322
- path,
323
- self,
284
+ export function bindingResolver(rootPropertyName, path, parentContext, type, schema, currentContext, level) {
285
+ rootPropertyName = getRootPropertyName(rootPropertyName, path, currentContext, type);
286
+ if (type !== "event" && rootPropertyName !== null) {
287
+ schema.addPath({
288
+ pathConfig: {
289
+ type,
290
+ currentContext,
324
291
  parentContext,
325
- contextPath: null,
326
- type: "access",
327
- level,
328
- rootPath: null,
329
- context: null,
330
- });
292
+ path,
293
+ },
294
+ rootPropertyName,
331
295
  });
332
296
  }
333
- return (x, c) => resolveChainedExpression(x, c, self, expression);
297
+ return pathResolver(path, currentContext, level, schema.getSchema(rootPropertyName));
298
+ }
299
+ export function expressionResolver(rootPropertyName, expression, parentContext, level, schema) {
300
+ return (x, c) => resolveChainedExpression(x, c, level, parentContext || null, expression, schema.getSchema(rootPropertyName));
334
301
  }
335
302
  /**
336
303
  * Extracts all paths from a ChainedExpression, including nested expressions
@@ -358,7 +325,8 @@ export function extractPathsFromChainedExpression(chainedExpression) {
358
325
  }
359
326
  /**
360
327
  * Determine if the operand is a value (boolean, number, string) or an accessor.
361
- * @param operand
328
+ * @param operand - The string to evaluate as either a literal value or property accessor
329
+ * @returns An object containing the parsed value and whether it represents a literal value
362
330
  */
363
331
  function isOperandValue(operand) {
364
332
  try {
@@ -377,10 +345,10 @@ function isOperandValue(operand) {
377
345
  }
378
346
  }
379
347
  /**
380
- * Evaluates parts of an expression chain
381
- * @param parts Each part of an expression chain
382
- * @param operator The operator between expressions
383
- * @returns
348
+ * Evaluates parts of an expression chain and chains them with the specified operator
349
+ * @param parts - Each part of an expression chain to be evaluated
350
+ * @param operator - The logical operator used to chain the expression parts
351
+ * @returns A ChainedExpression object representing the linked expressions, or void if no valid expressions found
384
352
  */
385
353
  function evaluatePartsInExpressionChain(parts, operator) {
386
354
  // Process each part recursively and chain them with ||
@@ -441,6 +409,11 @@ export function getExpressionChain(value) {
441
409
  }
442
410
  return void 0;
443
411
  }
412
+ /**
413
+ * Parses a binding value string into an Expression object
414
+ * @param value - The binding string value to parse (e.g., "!condition", "foo == bar", "property")
415
+ * @returns An Expression object containing the operator, operands, and whether operands are literal values
416
+ */
444
417
  function getExpression(value) {
445
418
  if (value[0] === "!") {
446
419
  const left = value.slice(1);
@@ -477,62 +450,77 @@ function getExpression(value) {
477
450
  };
478
451
  }
479
452
  /**
480
- * Resolve a single expression
481
- * @param x - The context
482
- * @param c - The parent context
483
- * @param self - Where the first item in the path path refers to the item itself (used by repeat).
484
- * @param expression - The expression being evaluated
485
- * @returns - A function resolving the binding for this expression
453
+ * Resolve a single expression by evaluating its operator and operands
454
+ * @param x - The current data context
455
+ * @param c - The parent context for accessing parent scope data
456
+ * @param level - The nesting level for context resolution
457
+ * @param contextPath - The current context path for property resolution
458
+ * @param expression - The expression object to evaluate
459
+ * @param rootSchema - The root JSON schema for data validation and navigation
460
+ * @returns The resolved value of the expression
486
461
  */
487
- function resolveExpression(x, c, self, expression) {
462
+ function resolveExpression(x, c, level, contextPath, expression, rootSchema) {
488
463
  const { operator, left, right, rightIsValue } = expression;
489
464
  switch (operator) {
490
465
  case "!":
491
- return !pathResolver(left, self)(x, c);
466
+ return !pathResolver(left, contextPath, level, rootSchema)(x, c);
492
467
  case "==":
493
- return (pathResolver(left, self)(x, c) ==
494
- (rightIsValue ? right : pathResolver(right, self)(x, c)));
468
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) ==
469
+ (rightIsValue
470
+ ? right
471
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
495
472
  case "!=":
496
- return (pathResolver(left, self)(x, c) !=
497
- (rightIsValue ? right : pathResolver(right, self)(x, c)));
473
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) !=
474
+ (rightIsValue
475
+ ? right
476
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
498
477
  case ">=":
499
- return (pathResolver(left, self)(x, c) >=
500
- (rightIsValue ? right : pathResolver(right, self)(x, c)));
478
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) >=
479
+ (rightIsValue
480
+ ? right
481
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
501
482
  case ">":
502
- return (pathResolver(left, self)(x, c) >
503
- (rightIsValue ? right : pathResolver(right, self)(x, c)));
483
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) >
484
+ (rightIsValue
485
+ ? right
486
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
504
487
  case "<=":
505
- return (pathResolver(left, self)(x, c) <=
506
- (rightIsValue ? right : pathResolver(right, self)(x, c)));
488
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) <=
489
+ (rightIsValue
490
+ ? right
491
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
507
492
  case "<":
508
- return (pathResolver(left, self)(x, c) <
509
- (rightIsValue ? right : pathResolver(right, self)(x, c)));
493
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) <
494
+ (rightIsValue
495
+ ? right
496
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
510
497
  default:
511
- return pathResolver(left, self)(x, c);
498
+ return pathResolver(left, contextPath, level, rootSchema)(x, c);
512
499
  }
513
500
  }
514
501
  /**
515
- * Resolve a chained expression
516
- * @param x - The context
517
- * @param c - The parent context
518
- * @param self - Where the first item in the path path refers to the item itself (used by repeat).
519
- * @param expression - The expression being evaluated
520
- * @param next - The next expression to be chained
521
- * @returns - A resolved return value for a binding
502
+ * Resolve a chained expression by evaluating expressions linked with logical operators
503
+ * @param x - The current data context
504
+ * @param c - The parent context for accessing parent scope data
505
+ * @param level - The nesting level for context resolution
506
+ * @param contextPath - The current context path for property resolution
507
+ * @param expression - The chained expression object containing linked expressions
508
+ * @param rootSchema - The root JSON schema for data validation and navigation
509
+ * @returns The resolved boolean result of the chained expression
522
510
  */
523
- function resolveChainedExpression(x, c, self, expression) {
511
+ function resolveChainedExpression(x, c, level, contextPath, expression, rootSchema) {
524
512
  if (expression.next) {
525
513
  switch (expression.next.operator) {
526
514
  case "&&":
527
515
  case "&amp;&amp;":
528
- return (resolveExpression(x, c, self, expression.expression) &&
529
- resolveChainedExpression(x, c, self, expression.next));
516
+ return (resolveExpression(x, c, level, contextPath, expression.expression, rootSchema) &&
517
+ resolveChainedExpression(x, c, level, contextPath, expression.next, rootSchema));
530
518
  case "||":
531
- return (resolveExpression(x, c, self, expression.expression) ||
532
- resolveChainedExpression(x, c, self, expression.next));
519
+ return (resolveExpression(x, c, level, contextPath, expression.expression, rootSchema) ||
520
+ resolveChainedExpression(x, c, level, contextPath, expression.next, rootSchema));
533
521
  }
534
522
  }
535
- return resolveExpression(x, c, self, expression.expression);
523
+ return resolveExpression(x, c, level, contextPath, expression.expression, rootSchema);
536
524
  }
537
525
  /**
538
526
  * This is the transform utility for rationalizing declarative HTML syntax
@@ -568,44 +556,35 @@ export function transformInnerHTML(innerHTML, index = 0) {
568
556
  * @param self - Where the first item in the path path refers to the item itself (used by repeat).
569
557
  * @param chainedExpression - The chained expression which includes the expression and the next expression
570
558
  * if there is another in the chain
571
- * @param observerMap - Optional ObserverMap instance for caching paths during template processing
572
559
  * @returns - A binding that resolves the chained expression logic
573
560
  */
574
- export function resolveWhen(self, expression, parentContext, level, observerMap) {
575
- const binding = expressionResolver(self, expression, parentContext, level, observerMap);
561
+ export function resolveWhen(rootPropertyName, expression, parentContext, level, schema) {
562
+ const binding = expressionResolver(rootPropertyName, expression, parentContext, level, schema);
576
563
  return (x, c) => binding(x, c);
577
564
  }
578
565
  /**
579
- * Helper function to determine the data type of an object property
566
+ * Determines the data type of the provided data
567
+ * @param data - The data to analyze
568
+ * @returns "array" for arrays, "object" for non-null objects, "primitive" for other types
580
569
  */
581
- function getDataType(object) {
582
- if (Array.isArray(object))
570
+ function getDataType(data) {
571
+ if (Array.isArray(data))
583
572
  return "array";
584
- if (typeof object === "object" && object !== null)
573
+ if (typeof data === "object" && data !== null)
585
574
  return "object";
586
575
  return "primitive";
587
576
  }
588
577
  /**
589
- * Get the next property
590
- * @param path The dot syntax data path
591
- * @returns The next property
578
+ * Assigns Observable properties to items in an array and sets up change notifications
579
+ * @param proxiedData - The array data to make observable
580
+ * @param schema - The schema defining the structure of array items
581
+ * @param rootSchema - The root schema for the entire data structure
582
+ * @returns The array with observable properties and change notifications
592
583
  */
593
- export function getNextProperty(path) {
594
- return path.split(".")[0];
595
- }
596
- function sortByDeepestNestingItem(first, second) {
597
- const firstRelativePathLength = first.length;
598
- const secondRelativePathLength = second.length;
599
- return firstRelativePathLength > secondRelativePathLength
600
- ? -1
601
- : secondRelativePathLength > firstRelativePathLength
602
- ? 1
603
- : 0;
604
- }
605
- function assignObservablesToArray(proxiedData, cachePath) {
584
+ function assignObservablesToArray(proxiedData, schema, rootSchema) {
606
585
  const data = proxiedData.map((item) => {
607
586
  const originalItem = Object.assign({}, item);
608
- assignProxyToItemsInArray(item, originalItem, cachePath);
587
+ assignProxyToItemsInArray(item, originalItem, schema, rootSchema);
609
588
  return Object.assign(item, originalItem);
610
589
  });
611
590
  Observable.getNotifier(data).subscribe({
@@ -615,7 +594,7 @@ function assignObservablesToArray(proxiedData, cachePath) {
615
594
  for (let i = arg.addedCount - 1; i >= 0; i--) {
616
595
  const item = subject[arg.index + i];
617
596
  const originalItem = Object.assign({}, item);
618
- assignProxyToItemsInArray(item, originalItem, cachePath);
597
+ assignProxyToItemsInArray(item, originalItem, schema, rootSchema);
619
598
  return Object.assign(item, originalItem);
620
599
  }
621
600
  }
@@ -624,73 +603,104 @@ function assignObservablesToArray(proxiedData, cachePath) {
624
603
  });
625
604
  return data;
626
605
  }
627
- export function assignObservables(cachePath, data, target, rootProperty) {
606
+ /**
607
+ * Extracts the definition name from a JSON Schema $ref property
608
+ * @param defName - The $ref string (e.g., "#/$defs/MyType")
609
+ * @returns The definition name (e.g., "MyType")
610
+ */
611
+ function getDefFromRef(defName) {
612
+ const splitName = defName.split("/");
613
+ return splitName.at(-1);
614
+ }
615
+ /**
616
+ * Assign observables to data
617
+ * @param schema - The schema
618
+ * @param rootSchema - The root schema mapping to the root property
619
+ * @param data - The data
620
+ * @param target - The target custom element
621
+ * @param rootProperty - The root property
622
+ * @returns
623
+ */
624
+ export function assignObservables(schema, rootSchema, data, target, rootProperty) {
625
+ var _a;
628
626
  const dataType = getDataType(data);
629
627
  let proxiedData = data;
630
628
  switch (dataType) {
631
629
  case "array": {
632
- if (cachePath.type === "repeat") {
633
- proxiedData = assignObservablesToArray(proxiedData, cachePath);
634
- }
630
+ const context = getDefFromRef(schema[refPropertyName]);
631
+ proxiedData = assignObservablesToArray(proxiedData, (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context], rootSchema);
635
632
  break;
636
633
  }
637
634
  case "object": {
638
- if (cachePath.type === "default") {
639
- const relativePaths = Object.values(cachePath.paths)
640
- .map((value) => {
641
- return value.relativePath.split(".").slice(1); // the first item is the root path
642
- })
643
- .sort(sortByDeepestNestingItem);
644
- for (const relativePath of relativePaths) {
645
- proxiedData = assignProxyToItemsInObject(relativePath, target, rootProperty, proxiedData, cachePath);
646
- }
647
- }
635
+ proxiedData = assignProxyToItemsInObject(target, rootProperty, proxiedData, schema, rootSchema);
648
636
  break;
649
637
  }
650
638
  }
651
639
  return proxiedData;
652
640
  }
653
- function assignProxyToItemsInArray(item, originalItem, cachePath) {
641
+ /**
642
+ * Assign a proxy to items in an array
643
+ * @param item - The array item to proxy
644
+ * @param originalItem - The original array item
645
+ * @param schema - The schema mapping to the items in the array
646
+ * @param rootSchema - The root schema assigned to the root property
647
+ */
648
+ function assignProxyToItemsInArray(item, originalItem, schema, rootSchema) {
654
649
  const itemProperties = Object.keys(item);
655
650
  itemProperties.forEach(key => {
656
651
  Observable.defineProperty(item, key);
657
- const relativePaths = Object.values(cachePath.paths).filter(pathItem => {
658
- if (pathItem.type === "access") {
659
- return pathItem.relativePath.startsWith(`${cachePath.context}.${key}.`);
660
- }
661
- return false;
662
- })
663
- .map(value => {
664
- return value.relativePath.split(".").slice(2); // the first item is the context, the next is the property
665
- })
666
- .sort(sortByDeepestNestingItem);
667
- for (const relativePath of relativePaths) {
668
- originalItem[key] = assignProxyToItemsInObject(relativePath, item, key, originalItem[key], cachePath);
652
+ if (originalItem[key] && schema && schema.properties) {
653
+ originalItem[key] = assignProxyToItemsInObject(item, key, originalItem[key], schema.properties[key], rootSchema);
669
654
  }
670
655
  });
671
656
  }
672
- function assignProxyToItemsInObject(paths, target, rootProperty, data, cachePath) {
657
+ /**
658
+ * Assign a proxy to items in an object
659
+ * @param target - The target custom element
660
+ * @param rootProperty - The root property
661
+ * @param data - The data to proxy
662
+ * @param schema - The schema for the data
663
+ * @param rootSchema - The root schema for the root property
664
+ * @returns a Proxy
665
+ */
666
+ function assignProxyToItemsInObject(target, rootProperty, data, schema, rootSchema) {
667
+ var _a;
673
668
  const type = getDataType(data);
674
669
  let proxiedData = data;
675
- if (type === "object") {
670
+ if (type === "object" && (schema === null || schema === void 0 ? void 0 : schema.properties)) {
676
671
  // navigate through all items in the object
677
- proxiedData[paths[0]] = assignProxyToItemsInObject(paths.slice(1), target, rootProperty, proxiedData[paths[0]], cachePath);
672
+ Object.keys(schema.properties).forEach(property => {
673
+ if (proxiedData[property] && schema && schema.properties) {
674
+ proxiedData[property] = assignProxyToItemsInObject(target, rootProperty, proxiedData[property], schema.properties[property], rootSchema);
675
+ }
676
+ });
678
677
  // assign a Proxy to the object
679
- proxiedData = assignProxy(cachePath, target, rootProperty, data);
678
+ proxiedData = assignProxy(schema, rootSchema, target, rootProperty, data);
680
679
  }
681
680
  else if (type === "array") {
682
- if (cachePath.type === "repeat") {
683
- proxiedData = assignObservablesToArray(proxiedData, cachePath.paths[rootProperty]);
681
+ const context = getDefFromRef(schema.items[refPropertyName]);
682
+ const definition = (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context];
683
+ if ((definition === null || definition === void 0 ? void 0 : definition.type) === "object") {
684
+ proxiedData = assignObservablesToArray(proxiedData, definition, rootSchema);
684
685
  }
685
686
  }
686
687
  return proxiedData;
687
688
  }
688
- export function assignProxy(cachePath, target, rootProperty, object) {
689
+ /**
690
+ * Assign a proxy to an object
691
+ * @param schema - The current schema
692
+ * @param rootSchema - The root schema for the root property
693
+ * @param target - The target custom element
694
+ * @param rootProperty - The root property
695
+ * @param object - The object to assign the proxy to
696
+ * @returns Proxy object
697
+ */
698
+ export function assignProxy(schema, rootSchema, target, rootProperty, object) {
689
699
  if (object.$isProxy === undefined) {
690
700
  // Create a proxy for the object that triggers Observable.notify on mutations
691
701
  return new Proxy(object, {
692
702
  set: (obj, prop, value) => {
693
- obj[prop] = assignObservables(cachePath, value, target, rootProperty);
703
+ obj[prop] = assignObservables(schema, rootSchema, value, target, rootProperty);
694
704
  // Trigger notification for property changes
695
705
  Observable.notify(target, rootProperty);
696
706
  return true;
@@ -714,3 +724,16 @@ export function assignProxy(cachePath, target, rootProperty, object) {
714
724
  }
715
725
  return object;
716
726
  }
727
+ /**
728
+ * Get the root property name
729
+ * @param rootPropertyName - The root property
730
+ * @param path - The dot syntax path
731
+ * @param context - The context created by a repeat
732
+ * @param type - The type of path binding
733
+ * @returns
734
+ */
735
+ export function getRootPropertyName(rootPropertyName, path, context, type) {
736
+ return (rootPropertyName === null || context === null) && type !== "event"
737
+ ? path.split(".")[0]
738
+ : rootPropertyName;
739
+ }
@@ -1,6 +1,6 @@
1
1
  import { __awaiter } from "tslib";
2
2
  import { expect, test } from "@playwright/test";
3
- import { getNextBehavior, getAllPartials, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML, getExpressionChain, extractPathsFromChainedExpression, } from "./utilities.js";
3
+ import { getNextBehavior, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML, getExpressionChain, extractPathsFromChainedExpression, } from "./utilities.js";
4
4
  test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* () {
5
5
  test.describe("content", () => __awaiter(void 0, void 0, void 0, function* () {
6
6
  test("get the next content binding", () => __awaiter(void 0, void 0, void 0, function* () {
@@ -108,29 +108,6 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
108
108
  expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(21);
109
109
  }));
110
110
  }));
111
- test.describe("partials", () => __awaiter(void 0, void 0, void 0, function* () {
112
- test("get a single partial", () => __awaiter(void 0, void 0, void 0, function* () {
113
- const partialContent = "{{text}}";
114
- const partial = `<f-partial id="foo">${partialContent}</f-partial>`;
115
- const allPartials = getAllPartials(partial);
116
- expect(allPartials.foo.innerHTML).toEqual(partialContent);
117
- expect(allPartials.foo.startIndex).toEqual(20);
118
- expect(allPartials.foo.endIndex).toEqual(28);
119
- }));
120
- test("get multiple partials", () => __awaiter(void 0, void 0, void 0, function* () {
121
- const partial1Content = "{{text}}";
122
- const partial2Content = "{{othertext}}";
123
- const partial1 = `<f-partial id="foo">${partial1Content}</f-partial>`;
124
- const partial2 = `<f-partial id="foobar">${partial2Content}</f-partial>`;
125
- const allPartials = getAllPartials(`${partial1}${partial2}`);
126
- expect(allPartials.foo.innerHTML).toEqual(partial1Content);
127
- expect(allPartials.foo.startIndex).toEqual(20);
128
- expect(allPartials.foo.endIndex).toEqual(28);
129
- expect(allPartials.foobar.innerHTML).toEqual(partial2Content);
130
- expect(allPartials.foobar.startIndex).toEqual(63);
131
- expect(allPartials.foobar.endIndex).toEqual(76);
132
- }));
133
- }));
134
111
  test.describe("getIndexOfNextMatchingTag", () => __awaiter(void 0, void 0, void 0, function* () {
135
112
  test("should resolve a single tag", () => __awaiter(void 0, void 0, void 0, function* () {
136
113
  const index = getIndexOfNextMatchingTag(`<div>Hello world</div>`, `<div`, `</div>`, 0);
@@ -151,19 +128,19 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
151
128
  }));
152
129
  test.describe("pathResolver", () => __awaiter(void 0, void 0, void 0, function* () {
153
130
  test("should resolve a path with no nesting", () => __awaiter(void 0, void 0, void 0, function* () {
154
- expect(pathResolver("foo")({ foo: "bar" }, {})).toEqual("bar");
131
+ expect(pathResolver("foo", null, 0, {})({ foo: "bar" }, {})).toEqual("bar");
155
132
  }));
156
133
  test("should resolve a path with nesting", () => __awaiter(void 0, void 0, void 0, function* () {
157
- expect(pathResolver("foo.bar.bat")({ foo: { bar: { bat: "baz" } } }, {})).toEqual("baz");
134
+ expect(pathResolver("foo.bar.bat", null, 0, {})({ foo: { bar: { bat: "baz" } } }, {})).toEqual("baz");
158
135
  }));
159
136
  test("should resolve a path with no nesting and self reference", () => __awaiter(void 0, void 0, void 0, function* () {
160
- expect(pathResolver("foo", true)("bar", {})).toEqual("bar");
137
+ expect(pathResolver("foo", "foo", 0, {})("bar", {})).toEqual("bar");
161
138
  }));
162
139
  test("should resolve a path with nesting and self reference", () => __awaiter(void 0, void 0, void 0, function* () {
163
- expect(pathResolver("foo.bar.bat", true)({ bar: { bat: "baz" } }, {})).toEqual("baz");
140
+ expect(pathResolver("foo.bar.bat", "foo", 0, {})({ bar: { bat: "baz" } }, {})).toEqual("baz");
164
141
  }));
165
142
  test("should resolve a path with context", () => __awaiter(void 0, void 0, void 0, function* () {
166
- expect(pathResolver("../foo")({}, { parent: { foo: "bar" } })).toEqual("bar");
143
+ expect(pathResolver("foo", "parent", 1, {})({}, { parent: { foo: "bar" } })).toEqual("bar");
167
144
  }));
168
145
  }));
169
146
  test.describe("transformInnerHTML", () => __awaiter(void 0, void 0, void 0, function* () {
@@ -5,7 +5,7 @@ class TestElement extends FASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.list = ["Foo", "Bar"];
8
- this.parent = "Bat";
8
+ this.item_parent = "Bat";
9
9
  }
10
10
  }
11
11
  __decorate([