@microsoft/fast-html 1.0.0-alpha.3 → 1.0.0-alpha.30

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.
Files changed (58) hide show
  1. package/README.md +255 -18
  2. package/dist/dts/components/element.d.ts +10 -0
  3. package/dist/dts/components/index.d.ts +3 -1
  4. package/dist/dts/components/observer-map.d.ts +26 -0
  5. package/dist/dts/components/request-idle-callback.d.ts +41 -0
  6. package/dist/dts/components/schema.d.ts +144 -0
  7. package/dist/dts/components/template.d.ts +83 -7
  8. package/dist/dts/components/utilities.d.ts +109 -18
  9. package/dist/dts/fixtures/lifecycle-callbacks/lifecycle-callbacks.spec.d.ts +1 -0
  10. package/dist/dts/fixtures/lifecycle-callbacks/main.d.ts +5 -0
  11. package/dist/dts/fixtures/observer-map/main.d.ts +1 -0
  12. package/dist/dts/fixtures/observer-map/observer-map.spec.d.ts +1 -0
  13. package/dist/dts/index.d.ts +1 -1
  14. package/dist/esm/components/element.js +23 -0
  15. package/dist/esm/components/index.js +3 -1
  16. package/dist/esm/components/observer-map.js +53 -0
  17. package/dist/esm/components/observer-map.spec.js +19 -0
  18. package/dist/esm/components/request-idle-callback.js +72 -0
  19. package/dist/esm/components/schema.js +250 -0
  20. package/dist/esm/components/schema.spec.js +485 -0
  21. package/dist/esm/components/template.js +199 -111
  22. package/dist/esm/components/utilities.js +741 -43
  23. package/dist/esm/components/utilities.spec.js +317 -44
  24. package/dist/esm/fixtures/attribute/main.js +3 -2
  25. package/dist/esm/fixtures/binding/binding.spec.js +6 -0
  26. package/dist/esm/fixtures/binding/main.js +13 -2
  27. package/dist/esm/fixtures/children/children.spec.js +4 -0
  28. package/dist/esm/fixtures/children/main.js +3 -2
  29. package/dist/esm/fixtures/dot-syntax/dot-syntax.spec.js +109 -2
  30. package/dist/esm/fixtures/dot-syntax/main.js +30 -4
  31. package/dist/esm/fixtures/event/event.spec.js +28 -5
  32. package/dist/esm/fixtures/event/main.js +21 -5
  33. package/dist/esm/fixtures/lifecycle-callbacks/lifecycle-callbacks.spec.js +166 -0
  34. package/dist/esm/fixtures/lifecycle-callbacks/main.js +126 -0
  35. package/dist/esm/fixtures/observer-map/main.js +375 -0
  36. package/dist/esm/fixtures/observer-map/observer-map.spec.js +251 -0
  37. package/dist/esm/fixtures/ref/main.js +3 -2
  38. package/dist/esm/fixtures/ref/ref.spec.js +2 -6
  39. package/dist/esm/fixtures/repeat/main.js +27 -2
  40. package/dist/esm/fixtures/repeat/repeat.spec.js +16 -6
  41. package/dist/esm/fixtures/slotted/main.js +15 -4
  42. package/dist/esm/fixtures/slotted/slotted.spec.js +18 -19
  43. package/dist/esm/fixtures/when/main.js +139 -2
  44. package/dist/esm/fixtures/when/when.spec.js +64 -1
  45. package/dist/esm/index.js +1 -1
  46. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  47. package/dist/fast-html.api.json +333 -0
  48. package/dist/fast-html.d.ts +282 -6
  49. package/dist/fast-html.untrimmed.d.ts +282 -6
  50. package/package.json +12 -7
  51. package/rules/attribute-directives.yml +38 -0
  52. package/rules/call-expression-with-event-argument.yml +41 -0
  53. package/rules/member-expression.yml +33 -0
  54. package/rules/tag-function-to-template-literal.yml +16 -0
  55. package/dist/esm/fixtures/partial/main.js +0 -31
  56. package/dist/esm/fixtures/partial/partial.spec.js +0 -14
  57. /package/dist/dts/{fixtures/partial/main.d.ts → components/observer-map.spec.d.ts} +0 -0
  58. /package/dist/dts/{fixtures/partial/partial.spec.d.ts → components/schema.spec.d.ts} +0 -0
@@ -1,9 +1,27 @@
1
- const openBinding = "{{";
2
- const closeBinding = "}}";
1
+ import { Observable } from "@microsoft/fast-element/observable.js";
2
+ import { defsPropertyName, fastContextMetaData, refPropertyName, Schema, } from "./schema.js";
3
+ const openClientSideBinding = "{";
4
+ const closeClientSideBinding = "}";
5
+ const openContentBinding = "{{";
6
+ const closeContentBinding = "}}";
7
+ const openUnescapedBinding = "{{{";
8
+ const closeUnescapedBinding = "}}}";
3
9
  const openTagStart = "<f-";
4
10
  const tagEnd = ">";
5
11
  const closeTagStart = "</f-";
6
12
  const attributeDirectivePrefix = "f-";
13
+ const startInnerHTMLDiv = `<div :innerHTML="{{`;
14
+ const startInnerHTMLDivLength = startInnerHTMLDiv.length;
15
+ const endInnerHTMLDiv = `}}"></div>`;
16
+ const endInnerHTMLDivLength = endInnerHTMLDiv.length;
17
+ /**
18
+ * A map of proxied objects
19
+ */
20
+ const objectTargetsMap = new WeakMap();
21
+ /**
22
+ * A map of arrays being observered
23
+ */
24
+ const observedArraysMap = new WeakMap();
7
25
  /**
8
26
  * Get the index of the next matching tag
9
27
  * @param openingTagStartSlice - The slice starting from the opening tag
@@ -56,7 +74,8 @@ export function getIndexOfNextMatchingTag(openingTagStartSlice, openingTag, clos
56
74
  function getNextDirectiveBehavior(innerHTML) {
57
75
  const openingTagStartIndex = innerHTML.indexOf(openTagStart);
58
76
  const openingTagStartSlice = innerHTML.slice(openingTagStartIndex);
59
- const openingTagEndIndex = openingTagStartSlice.indexOf(tagEnd) + openingTagStartIndex + 1;
77
+ const openingTagEndIndex = // account for f-when which may include >= or > as operators, but will always include a condition attr
78
+ openingTagStartSlice.indexOf(`"${tagEnd}`) + openingTagStartIndex + 2;
60
79
  const directiveTag = innerHTML
61
80
  .slice(openingTagStartIndex + 3, openingTagEndIndex - 1)
62
81
  .split(" ")[0];
@@ -130,20 +149,62 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
130
149
  function getContentDataBindingConfig(config) {
131
150
  return Object.assign(Object.assign({}, config), { subtype: "content" });
132
151
  }
152
+ /**
153
+ * Finds the next data binding in innerHTML and determines its type and indices
154
+ * @param innerHTML - The innerHTML string to search for data bindings
155
+ * @returns NextDataBindingBehaviorConfig containing the binding type and start indices
156
+ */
157
+ function getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML) {
158
+ // {{{}}} binding
159
+ const openingUnescapedStartIndex = innerHTML.indexOf(openUnescapedBinding);
160
+ const closingUnescapedStartIndex = innerHTML.indexOf(closeUnescapedBinding);
161
+ // {{}} binding
162
+ const openingContentStartIndex = innerHTML.indexOf(openContentBinding);
163
+ const closingContentStartIndex = innerHTML.indexOf(closeContentBinding);
164
+ // {} binding
165
+ const openingClientStartIndex = innerHTML.indexOf(openClientSideBinding);
166
+ const closingClientStartIndex = innerHTML.indexOf(closeClientSideBinding);
167
+ if (openingUnescapedStartIndex !== -1 &&
168
+ openingUnescapedStartIndex <= openingContentStartIndex &&
169
+ openingUnescapedStartIndex <= openingClientStartIndex) {
170
+ // is unescaped {{{}}}
171
+ return {
172
+ openingStartIndex: openingUnescapedStartIndex,
173
+ closingStartIndex: closingUnescapedStartIndex,
174
+ bindingType: "unescaped",
175
+ };
176
+ }
177
+ else if (openingContentStartIndex !== -1 &&
178
+ openingContentStartIndex <= openingClientStartIndex) {
179
+ // is default {{}}
180
+ return {
181
+ openingStartIndex: openingContentStartIndex,
182
+ closingStartIndex: closingContentStartIndex,
183
+ bindingType: "default",
184
+ };
185
+ }
186
+ // is client {}
187
+ return {
188
+ openingStartIndex: openingClientStartIndex,
189
+ closingStartIndex: closingClientStartIndex,
190
+ bindingType: "client",
191
+ };
192
+ }
133
193
  /**
134
194
  * Get the next data binding
135
195
  * @param innerHTML - The innerHTML string to evaluate
136
196
  * @returns DataBindingBehaviorConfig - A configuration object
137
197
  */
138
198
  function getNextDataBindingBehavior(innerHTML) {
139
- const openingStartIndex = innerHTML.indexOf(openBinding);
140
- const closingStartIndex = innerHTML.indexOf(closeBinding);
199
+ const { openingStartIndex, closingStartIndex, bindingType } = getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML);
200
+ const bindingLength = bindingType === "client" ? 1 : bindingType === "default" ? 2 : 3;
141
201
  const partialConfig = {
142
202
  type: "dataBinding",
203
+ bindingType,
143
204
  openingStartIndex,
144
- openingEndIndex: openingStartIndex + 2,
205
+ openingEndIndex: openingStartIndex + bindingLength,
145
206
  closingStartIndex,
146
- closingEndIndex: closingStartIndex + 2,
207
+ closingEndIndex: closingStartIndex + bindingLength,
147
208
  };
148
209
  return isAttributeDirective(innerHTML, openingStartIndex)
149
210
  ? getAttributeDirectiveDataBindingConfig(innerHTML, partialConfig)
@@ -157,7 +218,7 @@ function getNextDataBindingBehavior(innerHTML) {
157
218
  * @returns DataBindingBehaviorConfig | DirectiveBehaviorConfig | null - A configuration object or null
158
219
  */
159
220
  export function getNextBehavior(innerHTML) {
160
- const dataBindingOpen = innerHTML.indexOf(openBinding);
221
+ const dataBindingOpen = innerHTML.indexOf(openClientSideBinding); // client side binding will capture all bindings starting with "{"
161
222
  const directiveBindingOpen = innerHTML.indexOf(openTagStart);
162
223
  if (dataBindingOpen === -1 && directiveBindingOpen === -1) {
163
224
  return null;
@@ -167,36 +228,6 @@ export function getNextBehavior(innerHTML) {
167
228
  }
168
229
  return getNextDataBindingBehavior(innerHTML);
169
230
  }
170
- /**
171
- * Gets all the partials with their IDs
172
- * @param innerHTML - The innerHTML string to evaluate
173
- * @param offset - The index offset from the innerHTML
174
- * @param partials - The partials found
175
- * @returns {[key: string]: PartialTemplateConfig}
176
- */
177
- export function getAllPartials(innerHTML, offset = 0, partials = {}) {
178
- const openingTag = `${openTagStart}partial`;
179
- const openingTagStartIndex = innerHTML.indexOf(openingTag);
180
- if (openingTagStartIndex >= 0) {
181
- const openingTagStartSlice = innerHTML.slice(openingTagStartIndex);
182
- const closingTag = `${closeTagStart}partial${tagEnd}`;
183
- const closingTagLength = closingTag.length;
184
- const matchingCloseTagIndex = getIndexOfNextMatchingTag(openingTagStartSlice, openingTag, closingTag, openingTagStartIndex) + closingTagLength;
185
- const startId = openingTagStartIndex + ' id="'.length + openingTag.length;
186
- const endId = innerHTML.slice(startId).indexOf('"') + startId;
187
- const id = innerHTML.slice(startId, endId);
188
- const openingTagEndIndex = openingTagStartSlice.indexOf(">") + 1 + openingTagStartIndex;
189
- const closingTagStartIndex = matchingCloseTagIndex - closingTagLength;
190
- partials[id] = {
191
- innerHTML: innerHTML.slice(openingTagEndIndex, closingTagStartIndex),
192
- startIndex: openingTagEndIndex + offset,
193
- endIndex: closingTagStartIndex + offset,
194
- };
195
- offset += matchingCloseTagIndex;
196
- return getAllPartials(innerHTML.slice(matchingCloseTagIndex), offset, partials);
197
- }
198
- return partials;
199
- }
200
231
  /**
201
232
  * Create a function to resolve a value from an object using a path with dot syntax.
202
233
  * e.g. "foo.bar"
@@ -204,9 +235,38 @@ export function getAllPartials(innerHTML, offset = 0, partials = {}) {
204
235
  * @param self - Where the first item in the path path refers to the item itself (used by repeat).
205
236
  * @returns A function to access the value from a given path.
206
237
  */
207
- export function pathResolver(path, self = false) {
238
+ export function pathResolver(path, contextPath, level, rootSchema) {
239
+ var _a, _b;
208
240
  let splitPath = path.split(".");
209
- if (self) {
241
+ let levelCount = level;
242
+ let self = splitPath[0] === contextPath;
243
+ const parentContexts = [];
244
+ if (level > 0 &&
245
+ ((_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[fastContextMetaData]) ===
246
+ splitPath.at(-1)) {
247
+ self = true;
248
+ }
249
+ while (levelCount > 0 && !self) {
250
+ if (levelCount !== 1) {
251
+ parentContexts.push("parentContext");
252
+ }
253
+ else {
254
+ parentContexts.push("parent");
255
+ }
256
+ levelCount--;
257
+ }
258
+ splitPath = [...parentContexts, ...splitPath];
259
+ return pathWithContextResolver(splitPath, self);
260
+ }
261
+ /**
262
+ * Creates a resolver function that can access properties from an object using a split path array
263
+ * @param splitPath - The dot syntax path split into an array of property names
264
+ * @param self - Whether the first item in the path refers to the item itself
265
+ * @returns A function that resolves the value from the given path on an accessible object
266
+ */
267
+ function pathWithContextResolver(splitPath, self) {
268
+ const isInPreviousContext = splitPath[0] === "parent" || splitPath[0] === "parentContext";
269
+ if (self && !isInPreviousContext) {
210
270
  if (splitPath.length > 1) {
211
271
  splitPath = splitPath.slice(1);
212
272
  }
@@ -216,9 +276,11 @@ export function pathResolver(path, self = false) {
216
276
  };
217
277
  }
218
278
  }
219
- if (splitPath.length === 1) {
220
- return (accessibleObject) => {
221
- return accessibleObject === null || accessibleObject === void 0 ? void 0 : accessibleObject[splitPath[0]];
279
+ if (isInPreviousContext) {
280
+ return (accessibleObject, context) => {
281
+ return splitPath.reduce((previousAccessors, pathItem) => {
282
+ return previousAccessors === null || previousAccessors === void 0 ? void 0 : previousAccessors[pathItem];
283
+ }, context);
222
284
  };
223
285
  }
224
286
  return (accessibleObject) => {
@@ -227,3 +289,639 @@ export function pathResolver(path, self = false) {
227
289
  }, accessibleObject);
228
290
  };
229
291
  }
292
+ export function bindingResolver(previousString, rootPropertyName, path, parentContext, type, schema, currentContext, level) {
293
+ rootPropertyName = getRootPropertyName(rootPropertyName, path, currentContext, type);
294
+ if (type !== "event" && rootPropertyName !== null) {
295
+ const childrenMap = getChildrenMap(previousString);
296
+ schema.addPath({
297
+ pathConfig: {
298
+ type,
299
+ currentContext,
300
+ parentContext,
301
+ path,
302
+ },
303
+ rootPropertyName,
304
+ childrenMap,
305
+ });
306
+ }
307
+ return pathResolver(path, currentContext, level, schema.getSchema(rootPropertyName));
308
+ }
309
+ export function expressionResolver(rootPropertyName, expression, parentContext, level, schema) {
310
+ return (x, c) => resolveChainedExpression(x, c, level, parentContext || null, expression, schema.getSchema(rootPropertyName));
311
+ }
312
+ /**
313
+ * Extracts all paths from a ChainedExpression, including nested expressions
314
+ * @param chainedExpression - The chained expression to extract paths from
315
+ * @returns A Set containing all unique paths found in the expression chain
316
+ */
317
+ export function extractPathsFromChainedExpression(chainedExpression) {
318
+ const paths = new Set();
319
+ function processExpression(expr) {
320
+ // Check left operand (only add if it's not a literal value)
321
+ if (typeof expr.left === "string" && !expr.leftIsValue) {
322
+ paths.add(expr.left);
323
+ }
324
+ // Check right operand (only add if it's not a literal value)
325
+ if (typeof expr.right === "string" && !expr.rightIsValue) {
326
+ paths.add(expr.right);
327
+ }
328
+ }
329
+ let current = chainedExpression;
330
+ while (current !== undefined) {
331
+ processExpression(current.expression);
332
+ current = current.next;
333
+ }
334
+ return paths;
335
+ }
336
+ /**
337
+ * Determine if the operand is a value (boolean, number, string) or an accessor.
338
+ * @param operand - The string to evaluate as either a literal value or property accessor
339
+ * @returns An object containing the parsed value and whether it represents a literal value
340
+ */
341
+ function isOperandValue(operand) {
342
+ try {
343
+ operand = operand.replaceAll("'", '"');
344
+ const value = JSON.parse(operand);
345
+ return {
346
+ value,
347
+ isValue: true,
348
+ };
349
+ }
350
+ catch (e) {
351
+ return {
352
+ value: operand,
353
+ isValue: false,
354
+ };
355
+ }
356
+ }
357
+ /**
358
+ * Evaluates parts of an expression chain and chains them with the specified operator
359
+ * @param parts - Each part of an expression chain to be evaluated
360
+ * @param operator - The logical operator used to chain the expression parts
361
+ * @returns A ChainedExpression object representing the linked expressions, or void if no valid expressions found
362
+ */
363
+ function evaluatePartsInExpressionChain(parts, operator) {
364
+ // Process each part recursively and chain them with ||
365
+ const firstPart = getExpressionChain(parts[0]);
366
+ if (firstPart) {
367
+ let current = firstPart;
368
+ for (let i = 1; i < parts.length; i++) {
369
+ const nextPart = getExpressionChain(parts[i]);
370
+ if (nextPart) {
371
+ // Find the end of the current chain
372
+ while (current.next) {
373
+ current = current.next;
374
+ }
375
+ current.next = Object.assign({ operator }, nextPart);
376
+ }
377
+ }
378
+ return firstPart;
379
+ }
380
+ }
381
+ /**
382
+ * Gets the expression chain as a configuration object
383
+ * @param value - The binding string value
384
+ * @returns - A configuration object containing information about the expression
385
+ */
386
+ export function getExpressionChain(value) {
387
+ // Handle operator precedence: || has lower precedence than &&
388
+ // First, split by || (lowest precedence)
389
+ const orParts = value.split(" || ");
390
+ if (orParts.length > 1) {
391
+ const firstPart = evaluatePartsInExpressionChain(orParts, "||");
392
+ if (firstPart) {
393
+ return firstPart;
394
+ }
395
+ }
396
+ // If no ||, check for && (higher precedence)
397
+ const andParts = value.split(" && ");
398
+ if (andParts.length > 1) {
399
+ // Process each part recursively and chain them with &&
400
+ const firstPart = evaluatePartsInExpressionChain(andParts, "&&");
401
+ if (firstPart) {
402
+ return firstPart;
403
+ }
404
+ }
405
+ // Handle HTML entity version of &&
406
+ const ampParts = value.split(" &amp;&amp; ");
407
+ if (ampParts.length > 1) {
408
+ // Process each part recursively and chain them with &amp;&amp;
409
+ const firstPart = evaluatePartsInExpressionChain(ampParts, "&amp;&amp;");
410
+ if (firstPart) {
411
+ return firstPart;
412
+ }
413
+ }
414
+ // No chaining operators found, create a single expression
415
+ if (value.trim()) {
416
+ return {
417
+ expression: getExpression(value.trim()),
418
+ };
419
+ }
420
+ return void 0;
421
+ }
422
+ /**
423
+ * Parses a binding value string into an Expression object
424
+ * @param value - The binding string value to parse (e.g., "!condition", "foo == bar", "property")
425
+ * @returns An Expression object containing the operator, operands, and whether operands are literal values
426
+ */
427
+ function getExpression(value) {
428
+ if (value[0] === "!") {
429
+ const left = value.slice(1);
430
+ const operandValue = isOperandValue(left);
431
+ return {
432
+ operator: "!",
433
+ left,
434
+ leftIsValue: operandValue.isValue,
435
+ right: null,
436
+ rightIsValue: null,
437
+ };
438
+ }
439
+ const split = value.split(" ");
440
+ if (split.length === 3) {
441
+ const operator = split[1];
442
+ const right = split[2];
443
+ const rightOperandValue = isOperandValue(right);
444
+ const left = split[0];
445
+ const leftOperandValue = isOperandValue(left);
446
+ return {
447
+ operator,
448
+ left: split[0],
449
+ leftIsValue: leftOperandValue.isValue,
450
+ right: rightOperandValue.isValue ? rightOperandValue.value : right,
451
+ rightIsValue: rightOperandValue.isValue,
452
+ };
453
+ }
454
+ return {
455
+ operator: "access",
456
+ left: value,
457
+ leftIsValue: false,
458
+ right: null,
459
+ rightIsValue: null,
460
+ };
461
+ }
462
+ /**
463
+ * Resolve a single expression by evaluating its operator and operands
464
+ * @param x - The current data context
465
+ * @param c - The parent context for accessing parent scope data
466
+ * @param level - The nesting level for context resolution
467
+ * @param contextPath - The current context path for property resolution
468
+ * @param expression - The expression object to evaluate
469
+ * @param rootSchema - The root JSON schema for data validation and navigation
470
+ * @returns The resolved value of the expression
471
+ */
472
+ function resolveExpression(x, c, level, contextPath, expression, rootSchema) {
473
+ const { operator, left, right, rightIsValue } = expression;
474
+ switch (operator) {
475
+ case "!":
476
+ return !pathResolver(left, contextPath, level, rootSchema)(x, c);
477
+ case "==":
478
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) ==
479
+ (rightIsValue
480
+ ? right
481
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
482
+ case "!=":
483
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) !=
484
+ (rightIsValue
485
+ ? right
486
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
487
+ case ">=":
488
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) >=
489
+ (rightIsValue
490
+ ? right
491
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
492
+ case ">":
493
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) >
494
+ (rightIsValue
495
+ ? right
496
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
497
+ case "<=":
498
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) <=
499
+ (rightIsValue
500
+ ? right
501
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
502
+ case "<":
503
+ return (pathResolver(left, contextPath, level, rootSchema)(x, c) <
504
+ (rightIsValue
505
+ ? right
506
+ : pathResolver(right, contextPath, level, rootSchema)(x, c)));
507
+ default:
508
+ return pathResolver(left, contextPath, level, rootSchema)(x, c);
509
+ }
510
+ }
511
+ /**
512
+ * Resolve a chained expression by evaluating expressions linked with logical operators
513
+ * @param x - The current data context
514
+ * @param c - The parent context for accessing parent scope data
515
+ * @param level - The nesting level for context resolution
516
+ * @param contextPath - The current context path for property resolution
517
+ * @param expression - The chained expression object containing linked expressions
518
+ * @param rootSchema - The root JSON schema for data validation and navigation
519
+ * @returns The resolved boolean result of the chained expression
520
+ */
521
+ function resolveChainedExpression(x, c, level, contextPath, expression, rootSchema) {
522
+ if (expression.next) {
523
+ switch (expression.next.operator) {
524
+ case "&&":
525
+ case "&amp;&amp;":
526
+ return (resolveExpression(x, c, level, contextPath, expression.expression, rootSchema) &&
527
+ resolveChainedExpression(x, c, level, contextPath, expression.next, rootSchema));
528
+ case "||":
529
+ return (resolveExpression(x, c, level, contextPath, expression.expression, rootSchema) ||
530
+ resolveChainedExpression(x, c, level, contextPath, expression.next, rootSchema));
531
+ }
532
+ }
533
+ return resolveExpression(x, c, level, contextPath, expression.expression, rootSchema);
534
+ }
535
+ /**
536
+ * This is the transform utility for rationalizing declarative HTML syntax
537
+ * with bindings in the ViewTemplate
538
+ * @param innerHTML The innerHTML to transform
539
+ * @param index The index to start the current slice of HTML to evaluate
540
+ */
541
+ export function transformInnerHTML(innerHTML, index = 0) {
542
+ const sliceToEvaluate = innerHTML.slice(index);
543
+ const nextBinding = getNextBehavior(sliceToEvaluate);
544
+ let transformedInnerHTML = innerHTML;
545
+ if (nextBinding && nextBinding.type === "dataBinding") {
546
+ if (nextBinding.bindingType === "unescaped") {
547
+ transformedInnerHTML = `${innerHTML.slice(0, index)}${sliceToEvaluate.slice(0, nextBinding.openingStartIndex)}${startInnerHTMLDiv}${sliceToEvaluate.slice(nextBinding.openingStartIndex + 3, nextBinding.closingStartIndex)}${endInnerHTMLDiv}${sliceToEvaluate.slice(nextBinding.closingStartIndex + 3)}`;
548
+ return transformInnerHTML(transformedInnerHTML, index +
549
+ startInnerHTMLDivLength +
550
+ endInnerHTMLDivLength +
551
+ nextBinding.closingStartIndex -
552
+ 3);
553
+ }
554
+ else if (nextBinding.bindingType === "client") {
555
+ return transformInnerHTML(transformedInnerHTML, index + nextBinding.closingEndIndex);
556
+ }
557
+ return transformInnerHTML(transformedInnerHTML, index + nextBinding.closingEndIndex);
558
+ }
559
+ else if (nextBinding) {
560
+ return transformInnerHTML(transformedInnerHTML, index + nextBinding.closingTagEndIndex);
561
+ }
562
+ return transformedInnerHTML;
563
+ }
564
+ /**
565
+ * Resolves f-when
566
+ * @param self - Where the first item in the path path refers to the item itself (used by repeat).
567
+ * @param chainedExpression - The chained expression which includes the expression and the next expression
568
+ * if there is another in the chain
569
+ * @returns - A binding that resolves the chained expression logic
570
+ */
571
+ export function resolveWhen(rootPropertyName, expression, parentContext, level, schema) {
572
+ const binding = expressionResolver(rootPropertyName, expression, parentContext, level, schema);
573
+ return (x, c) => binding(x, c);
574
+ }
575
+ /**
576
+ * Determines the data type of the provided data
577
+ * @param data - The data to analyze
578
+ * @returns "array" for arrays, "object" for non-null objects, "primitive" for other types
579
+ */
580
+ function getDataType(data) {
581
+ if (Array.isArray(data))
582
+ return "array";
583
+ if (typeof data === "object" && data !== null)
584
+ return "object";
585
+ return "primitive";
586
+ }
587
+ /**
588
+ * Get properties from an anyOf array
589
+ * @param anyOf - The anyOf array in a JSON schema
590
+ * @returns The array item matching a ref if it exists
591
+ */
592
+ function getSchemaPropertiesFromAnyOf(anyOf) {
593
+ let propertiesFromAnyOf = null;
594
+ for (const anyOfItem of anyOf) {
595
+ if (anyOfItem[refPropertyName]) {
596
+ const splitRef = anyOfItem[refPropertyName].split("/");
597
+ const customElement = splitRef.slice(-2)[0];
598
+ const attributeName = splitRef.slice(-1)[0].slice(0, -5);
599
+ if (Schema.jsonSchemaMap.has(customElement)) {
600
+ const customElementSchemaMap = Schema.jsonSchemaMap.get(customElement);
601
+ propertiesFromAnyOf = customElementSchemaMap.get(attributeName);
602
+ }
603
+ }
604
+ }
605
+ return propertiesFromAnyOf;
606
+ }
607
+ /**
608
+ * Gets a properties definition if one exists
609
+ * @param schema - The JSON schema to get properties from
610
+ * @returns A JSON schema with properties or null
611
+ */
612
+ function getSchemaProperties(schema) {
613
+ if (schema === null || schema === void 0 ? void 0 : schema.properties) {
614
+ return schema.properties;
615
+ }
616
+ else if (schema === null || schema === void 0 ? void 0 : schema.anyOf) {
617
+ return getSchemaPropertiesFromAnyOf(schema.anyOf);
618
+ }
619
+ return null;
620
+ }
621
+ /**
622
+ * Assigns Observable properties to items in an array and sets up change notifications
623
+ * @param proxiedData - The array data to make observable
624
+ * @param schema - The schema defining the structure of array items
625
+ * @param rootSchema - The root schema for the entire data structure
626
+ * @returns The array with observable properties and change notifications
627
+ */
628
+ function assignObservablesToArray(proxiedData, schema, rootSchema) {
629
+ const data = proxiedData.map((item) => {
630
+ const originalItem = Object.assign({}, item);
631
+ assignProxyToItemsInArray(item, originalItem, schema, rootSchema);
632
+ return Object.assign(item, originalItem);
633
+ });
634
+ Observable.getNotifier(data).subscribe({
635
+ handleChange(subject, args) {
636
+ args.forEach((arg) => {
637
+ if (arg.addedCount > 0) {
638
+ for (let i = arg.addedCount - 1; i >= 0; i--) {
639
+ const item = subject[arg.index + i];
640
+ const originalItem = Object.assign({}, item);
641
+ assignProxyToItemsInArray(item, originalItem, schema, rootSchema);
642
+ return Object.assign(item, originalItem);
643
+ }
644
+ }
645
+ });
646
+ },
647
+ });
648
+ return data;
649
+ }
650
+ /**
651
+ * Extracts the definition name from a JSON Schema $ref property
652
+ * @param defName - The $ref string (e.g., "#/$defs/MyType")
653
+ * @returns The definition name (e.g., "MyType")
654
+ */
655
+ function getDefFromRef(defName) {
656
+ const splitName = defName.split("/");
657
+ return splitName.at(-1);
658
+ }
659
+ /**
660
+ * Find a definition
661
+ * This may exist as a $ref at the root or as a $ref in any anyOf or not at all
662
+ * if the Observer Map has not been enabled on a child component
663
+ * @param schema - The JSON schema to find the ref in
664
+ * @returns The definition or null
665
+ */
666
+ export function findDef(schema) {
667
+ const defStartingString = "#/$defs";
668
+ if (schema[refPropertyName] &&
669
+ schema[refPropertyName].startsWith(defStartingString)) {
670
+ return getDefFromRef(schema[refPropertyName]);
671
+ }
672
+ if (schema.anyOf) {
673
+ const index = schema.anyOf.findIndex((anyOfItem) => {
674
+ return (!!anyOfItem[refPropertyName] &&
675
+ anyOfItem[refPropertyName].startsWith(defStartingString));
676
+ });
677
+ if (index > -1) {
678
+ const ref = schema.anyOf[index][refPropertyName];
679
+ if (ref.startsWith(defStartingString)) {
680
+ return getDefFromRef(ref);
681
+ }
682
+ }
683
+ }
684
+ return null;
685
+ }
686
+ /**
687
+ * Subscribe to a notifier on data that is an observed array
688
+ * @param data - The array being observed
689
+ * @param updateArrayObservables - The function to call to update the array item
690
+ */
691
+ function assignSubscribeToObservableArray(data, updateArrayObservables) {
692
+ Observable.getNotifier(data).subscribe({
693
+ handleChange(subject, args) {
694
+ args.forEach((arg) => {
695
+ updateArrayObservables();
696
+ });
697
+ },
698
+ });
699
+ }
700
+ /**
701
+ * Assign observables to data
702
+ * @param schema - The schema
703
+ * @param rootSchema - The root schema mapping to the root property
704
+ * @param data - The data
705
+ * @param target - The target custom element
706
+ * @param rootProperty - The root property
707
+ * @returns
708
+ */
709
+ export function assignObservables(schema, rootSchema, data, target, rootProperty) {
710
+ var _a;
711
+ const dataType = getDataType(data);
712
+ let proxiedData = data;
713
+ switch (dataType) {
714
+ case "array": {
715
+ const context = findDef(schema);
716
+ if (context) {
717
+ proxiedData = assignObservablesToArray(proxiedData, (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context], rootSchema);
718
+ if (!observedArraysMap.has(proxiedData)) {
719
+ observedArraysMap.set(proxiedData, assignSubscribeToObservableArray(proxiedData, () => {
720
+ var _a;
721
+ return assignObservablesToArray(proxiedData, (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context], rootSchema);
722
+ }));
723
+ }
724
+ }
725
+ break;
726
+ }
727
+ case "object": {
728
+ proxiedData = assignProxyToItemsInObject(target, rootProperty, proxiedData, schema, rootSchema);
729
+ break;
730
+ }
731
+ }
732
+ return proxiedData;
733
+ }
734
+ /**
735
+ * Assign a proxy to items in an array
736
+ * @param proxiableItem - The array item to proxy
737
+ * @param originalItem - The original array item
738
+ * @param schema - The schema mapping to the items in the array
739
+ * @param rootSchema - The root schema assigned to the root property
740
+ */
741
+ function assignProxyToItemsInArray(proxiableItem, originalItem, schema, rootSchema) {
742
+ const schemaProperties = getSchemaProperties(schema);
743
+ getObjectProperties(proxiableItem, schemaProperties).forEach(key => {
744
+ Observable.defineProperty(proxiableItem, key);
745
+ originalItem[key] = assignProxyToItemsInObject(proxiableItem, key, originalItem[key], schemaProperties[key], rootSchema);
746
+ });
747
+ }
748
+ /**
749
+ * Get an objects properties as agreed upon between the schema and data
750
+ * @param data - The data
751
+ * @param schemaProperties - The schema properties
752
+ * @returns A list of strings the schema properties enumerate and is present in the data
753
+ */
754
+ function getObjectProperties(data, schemaProperties) {
755
+ const dataKeys = Object.keys(data);
756
+ const schemaPropertyKeys = Object.keys(schemaProperties !== null && schemaProperties !== void 0 ? schemaProperties : {});
757
+ return dataKeys.filter(function (key) {
758
+ return schemaPropertyKeys.indexOf(key) !== -1;
759
+ });
760
+ }
761
+ /**
762
+ * Assign a proxy to items in an object
763
+ * @param target - The target custom element
764
+ * @param rootProperty - The root property
765
+ * @param data - The data to proxy
766
+ * @param schema - The schema for the data
767
+ * @param rootSchema - The root schema for the root property
768
+ * @returns a Proxy
769
+ */
770
+ function assignProxyToItemsInObject(target, rootProperty, data, schema, rootSchema) {
771
+ var _a;
772
+ const type = getDataType(data);
773
+ const schemaProperties = getSchemaProperties(schema);
774
+ let proxiedData = data;
775
+ if (type === "object" && schemaProperties) {
776
+ // navigate through all items in the object
777
+ getObjectProperties(proxiedData, schemaProperties).forEach(property => {
778
+ proxiedData[property] = assignProxyToItemsInObject(target, rootProperty, proxiedData[property], schemaProperties[property], rootSchema);
779
+ });
780
+ // assign a Proxy to the object
781
+ proxiedData = assignProxy(schema, rootSchema, target, rootProperty, proxiedData);
782
+ // Add this target to the object's target list
783
+ addTargetToObject(proxiedData, target, rootProperty);
784
+ }
785
+ else if (type === "array") {
786
+ const context = findDef(schema.items);
787
+ if (context) {
788
+ const definition = (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context];
789
+ if ((definition === null || definition === void 0 ? void 0 : definition.type) === "object") {
790
+ proxiedData = assignObservablesToArray(proxiedData, definition, rootSchema);
791
+ if (!observedArraysMap.has(proxiedData)) {
792
+ observedArraysMap.set(proxiedData, assignObservablesToArray(proxiedData, definition, rootSchema));
793
+ }
794
+ }
795
+ }
796
+ }
797
+ return proxiedData;
798
+ }
799
+ /**
800
+ * Add a target to an object's target list
801
+ * @param object - The object to associate with the target
802
+ * @param target - The target custom element
803
+ * @param rootProperty - The root property name
804
+ */
805
+ function addTargetToObject(object, target, rootProperty) {
806
+ if (!objectTargetsMap.has(object)) {
807
+ objectTargetsMap.set(object, []);
808
+ }
809
+ const targets = objectTargetsMap.get(object);
810
+ targets.push({ target, rootProperty });
811
+ }
812
+ /**
813
+ * Get all targets for an object
814
+ * @param object - The object to get targets for
815
+ * @returns Array of target info objects
816
+ */
817
+ function getTargetsForObject(object) {
818
+ return objectTargetsMap.get(object) || [];
819
+ }
820
+ /**
821
+ * Notify any observables mapped to the object
822
+ * @param targetObject The object that is mapped to a target and rootProperty
823
+ */
824
+ function notifyObservables(targetObject) {
825
+ getTargetsForObject(targetObject).forEach((targetItem) => {
826
+ // Trigger notification for property changes
827
+ Observable.notify(targetItem.target, targetItem.rootProperty);
828
+ });
829
+ }
830
+ /**
831
+ * Assign a proxy to an object
832
+ * @param schema - The current schema
833
+ * @param rootSchema - The root schema for the root property
834
+ * @param target - The target custom element
835
+ * @param rootProperty - The root property
836
+ * @param object - The object to assign the proxy to
837
+ * @returns Proxy object
838
+ */
839
+ export function assignProxy(schema, rootSchema, target, rootProperty, object) {
840
+ if (object.$isProxy === undefined) {
841
+ // Create a proxy for the object that triggers Observable.notify on mutations
842
+ const proxy = new Proxy(object, {
843
+ set: (obj, prop, value) => {
844
+ obj[prop] = assignObservables(schema, rootSchema, value, target, rootProperty);
845
+ notifyObservables(proxy);
846
+ return true;
847
+ },
848
+ get: (target, key) => {
849
+ if (key !== "$isProxy") {
850
+ return target[key];
851
+ }
852
+ return true;
853
+ },
854
+ deleteProperty: (obj, prop) => {
855
+ if (prop in obj) {
856
+ delete obj[prop];
857
+ notifyObservables(proxy);
858
+ return true;
859
+ }
860
+ return false;
861
+ },
862
+ });
863
+ return proxy;
864
+ }
865
+ return object;
866
+ }
867
+ /**
868
+ * Get the root property name
869
+ * @param rootPropertyName - The root property
870
+ * @param path - The dot syntax path
871
+ * @param context - The context created by a repeat
872
+ * @param type - The type of path binding
873
+ * @returns
874
+ */
875
+ export function getRootPropertyName(rootPropertyName, path, context, type) {
876
+ return (rootPropertyName === null || context === null) && type !== "event"
877
+ ? path.split(".")[0]
878
+ : rootPropertyName;
879
+ }
880
+ /**
881
+ * Get details of bindings to the attributes of child custom elements
882
+ * @param previousString - The previous string before the binding
883
+ * @returns null, or a custom element name and attribute name
884
+ */
885
+ export function getChildrenMap(previousString) {
886
+ if (typeof previousString === "string" &&
887
+ isAttribute(previousString, previousString.length)) {
888
+ const customElementName = getAttributesCustomElementName(previousString);
889
+ if (customElementName) {
890
+ return {
891
+ customElementName,
892
+ attributeName: getAttributeName(previousString),
893
+ };
894
+ }
895
+ }
896
+ return null;
897
+ }
898
+ /**
899
+ * Get the HTML element that is passing the attribute binding
900
+ * @param previousString - The previous string before the binding
901
+ * @returns null if this is not a custom element, or the custom element that is passing the binding as an attribute
902
+ */
903
+ function getAttributesCustomElementName(previousString) {
904
+ const indexOfElementTagStart = previousString.lastIndexOf("<") + 1;
905
+ const indexOfElementTagEnd = previousString.slice(indexOfElementTagStart).indexOf(" ") +
906
+ indexOfElementTagStart;
907
+ const elementName = previousString.slice(indexOfElementTagStart, indexOfElementTagEnd);
908
+ if (elementName.includes("-")) {
909
+ return elementName;
910
+ }
911
+ return null;
912
+ }
913
+ /**
914
+ * Gets a non-aspected attribute name
915
+ * @param previousString - The previous string before the binding
916
+ * @returns The attribute name with any aspects (:, ?, @) removed
917
+ */
918
+ function getAttributeName(previousString) {
919
+ const indexOfAttributeStart = previousString.lastIndexOf(" ") + 1;
920
+ const indexOfAttributeEnd = previousString.slice(indexOfAttributeStart).indexOf("=") + indexOfAttributeStart;
921
+ const attributeName = previousString.slice(indexOfAttributeStart, indexOfAttributeEnd);
922
+ const potentialAspect = attributeName.charAt(0);
923
+ if (potentialAspect === ":" || potentialAspect === "@" || potentialAspect === "?") {
924
+ return attributeName.slice(1);
925
+ }
926
+ return attributeName;
927
+ }