@microsoft/fast-element 2.0.0-beta.16 → 2.0.0-beta.18

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 (63) hide show
  1. package/CHANGELOG.json +36 -0
  2. package/CHANGELOG.md +18 -1
  3. package/dist/dts/components/element-controller.d.ts +5 -0
  4. package/dist/dts/dom-policy.d.ts +68 -0
  5. package/dist/dts/dom.d.ts +116 -0
  6. package/dist/dts/index.d.ts +3 -2
  7. package/dist/dts/index.rollup.d.ts +0 -1
  8. package/dist/dts/index.rollup.debug.d.ts +0 -1
  9. package/dist/dts/interfaces.d.ts +15 -24
  10. package/dist/dts/polyfills.d.ts +0 -1
  11. package/dist/dts/templating/binding-signal.d.ts +3 -1
  12. package/dist/dts/templating/binding-two-way.d.ts +3 -1
  13. package/dist/dts/templating/binding.d.ts +16 -5
  14. package/dist/dts/templating/compiler.d.ts +11 -13
  15. package/dist/dts/templating/dangerous-html.d.ts +18 -0
  16. package/dist/dts/templating/html-directive.d.ts +54 -118
  17. package/dist/dts/templating/node-observation.d.ts +11 -1
  18. package/dist/dts/templating/ref.d.ts +4 -0
  19. package/dist/dts/templating/render.d.ts +30 -6
  20. package/dist/dts/templating/repeat.d.ts +1 -5
  21. package/dist/dts/templating/template.d.ts +44 -13
  22. package/dist/dts/templating/view.d.ts +8 -3
  23. package/dist/dts/testing/fakes.d.ts +1 -0
  24. package/dist/dts/utilities.d.ts +39 -0
  25. package/dist/esm/components/attributes.js +1 -1
  26. package/dist/esm/components/element-controller.js +6 -1
  27. package/dist/esm/debug.js +4 -1
  28. package/dist/esm/dom-policy.js +337 -0
  29. package/dist/esm/dom.js +117 -0
  30. package/dist/esm/index.js +2 -1
  31. package/dist/esm/index.rollup.debug.js +3 -1
  32. package/dist/esm/index.rollup.js +3 -1
  33. package/dist/esm/observation/observable.js +5 -1
  34. package/dist/esm/platform.js +1 -1
  35. package/dist/esm/polyfills.js +3 -7
  36. package/dist/esm/templating/binding-signal.js +9 -3
  37. package/dist/esm/templating/binding-two-way.js +9 -3
  38. package/dist/esm/templating/binding.js +40 -55
  39. package/dist/esm/templating/children.js +8 -4
  40. package/dist/esm/templating/compiler.js +31 -38
  41. package/dist/esm/templating/dangerous-html.js +23 -0
  42. package/dist/esm/templating/html-directive.js +42 -133
  43. package/dist/esm/templating/node-observation.js +14 -8
  44. package/dist/esm/templating/ref.js +1 -1
  45. package/dist/esm/templating/render.js +17 -6
  46. package/dist/esm/templating/repeat.js +2 -6
  47. package/dist/esm/templating/template.js +86 -56
  48. package/dist/esm/templating/view.js +6 -0
  49. package/dist/esm/testing/fakes.js +2 -0
  50. package/dist/esm/testing/fixture.js +1 -1
  51. package/dist/esm/utilities.js +68 -0
  52. package/dist/fast-element.api.json +1088 -608
  53. package/dist/fast-element.d.ts +194 -147
  54. package/dist/fast-element.debug.js +745 -381
  55. package/dist/fast-element.debug.min.js +1 -1
  56. package/dist/fast-element.js +716 -355
  57. package/dist/fast-element.min.js +1 -1
  58. package/dist/fast-element.untrimmed.d.ts +208 -145
  59. package/docs/api-report.md +74 -56
  60. package/package.json +5 -1
  61. package/yarn-error.log +177 -0
  62. package/dist/dts/templating/dom.d.ts +0 -41
  63. package/dist/esm/templating/dom.js +0 -49
@@ -1,34 +1,3 @@
1
- (function ensureGlobalThis() {
2
- if (typeof globalThis !== "undefined") {
3
- // We're running in a modern environment.
4
- return;
5
- }
6
- if (typeof global !== "undefined") {
7
- // We're running in NodeJS
8
- global.globalThis = global;
9
- }
10
- else if (typeof self !== "undefined") {
11
- self.globalThis = self;
12
- }
13
- else if (typeof window !== "undefined") {
14
- // We're running in the browser's main thread.
15
- window.globalThis = window;
16
- }
17
- else {
18
- // Hopefully we never get here...
19
- // Not all environments allow eval and Function. Use only as a last resort:
20
- // eslint-disable-next-line no-new-func
21
- const result = new Function("return this")();
22
- result.globalThis = result;
23
- }
24
- })();
25
- // API-only Polyfill for trustedTypes
26
- if (!globalThis.trustedTypes) {
27
- globalThis.trustedTypes = {
28
- createPolicy: (n, r) => r,
29
- };
30
- }
31
-
32
1
  if (globalThis.FAST === void 0) {
33
2
  Reflect.defineProperty(globalThis, "FAST", {
34
3
  value: Object.create(null),
@@ -40,11 +9,14 @@ if (globalThis.FAST === void 0) {
40
9
  const FAST$1 = globalThis.FAST;
41
10
  const debugMessages = {
42
11
  [1101 /* needsArrayObservation */]: "Must call enableArrayObservation before observing arrays.",
43
- [1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
12
+ [1201 /* onlySetDOMPolicyOnce */]: "The DOM Policy can only be set once.",
44
13
  [1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
45
14
  [1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
46
15
  [1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
47
16
  [1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
17
+ [1206 /* directCallToHTMLTagNotAllowed */]: "Calling html`` as a normal function invalidates the security guarantees provided by FAST.",
18
+ [1207 /* onlySetTemplatePolicyOnce */]: "The DOM Policy for an HTML template can only be set once.",
19
+ [1208 /* cannotSetTemplatePolicyAfterCompilation */]: "The DOM Policy cannot be set after a template is compiled.",
48
20
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
49
21
  [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
50
22
  [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
@@ -90,7 +62,48 @@ Object.assign(FAST$1, {
90
62
  },
91
63
  });
92
64
 
93
- // ensure FAST global - duplicated in polyfills.ts and debug.ts
65
+ /**
66
+ * @internal
67
+ */
68
+ const isFunction = (object) => typeof object === "function";
69
+ /**
70
+ * @internal
71
+ */
72
+ const isString = (object) => typeof object === "string";
73
+ /**
74
+ * @internal
75
+ */
76
+ const noop = () => void 0;
77
+
78
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
79
+ (function ensureGlobalThis() {
80
+ if (typeof globalThis !== "undefined") {
81
+ // We're running in a modern environment.
82
+ return;
83
+ }
84
+ // @ts-ignore
85
+ if (typeof global !== "undefined") {
86
+ // We're running in NodeJS
87
+ // @ts-ignore
88
+ global.globalThis = global;
89
+ }
90
+ else if (typeof self !== "undefined") {
91
+ self.globalThis = self;
92
+ }
93
+ else if (typeof window !== "undefined") {
94
+ // We're running in the browser's main thread.
95
+ window.globalThis = window;
96
+ }
97
+ else {
98
+ // Hopefully we never get here...
99
+ // Not all environments allow eval and Function. Use only as a last resort:
100
+ // eslint-disable-next-line no-new-func
101
+ const result = new Function("return this")();
102
+ result.globalThis = result;
103
+ }
104
+ })();
105
+
106
+ // ensure FAST global - duplicated debug.ts
94
107
  const propConfig = {
95
108
  configurable: false,
96
109
  enumerable: false,
@@ -178,15 +191,6 @@ function createMetadataLocator() {
178
191
  };
179
192
  }
180
193
 
181
- /**
182
- * @internal
183
- */
184
- const isFunction = (object) => typeof object === "function";
185
- /**
186
- * @internal
187
- */
188
- const isString = (object) => typeof object === "string";
189
-
190
194
  /**
191
195
  * The default UpdateQueue.
192
196
  * @public
@@ -253,6 +257,456 @@ const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
253
257
  });
254
258
  });
255
259
 
260
+ /**
261
+ * The type of HTML aspect to target.
262
+ * @public
263
+ */
264
+ const DOMAspect = Object.freeze({
265
+ /**
266
+ * Not aspected.
267
+ */
268
+ none: 0,
269
+ /**
270
+ * An attribute.
271
+ */
272
+ attribute: 1,
273
+ /**
274
+ * A boolean attribute.
275
+ */
276
+ booleanAttribute: 2,
277
+ /**
278
+ * A property.
279
+ */
280
+ property: 3,
281
+ /**
282
+ * Content
283
+ */
284
+ content: 4,
285
+ /**
286
+ * A token list.
287
+ */
288
+ tokenList: 5,
289
+ /**
290
+ * An event.
291
+ */
292
+ event: 6,
293
+ });
294
+ const createHTML$1 = html => html;
295
+ const fastTrustedType = globalThis.trustedTypes
296
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML: createHTML$1 })
297
+ : { createHTML: createHTML$1 };
298
+ let defaultPolicy = Object.freeze({
299
+ createHTML(value) {
300
+ return fastTrustedType.createHTML(value);
301
+ },
302
+ protect(tagName, aspect, aspectName, sink) {
303
+ return sink;
304
+ },
305
+ });
306
+ const fastPolicy = defaultPolicy;
307
+ /**
308
+ * Common DOM APIs.
309
+ * @public
310
+ */
311
+ const DOM = Object.freeze({
312
+ /**
313
+ * @deprecated
314
+ * Use Updates.enqueue().
315
+ */
316
+ queueUpdate: Updates.enqueue,
317
+ /**
318
+ * @deprecated
319
+ * Use Updates.next()
320
+ */
321
+ nextUpdate: Updates.next,
322
+ /**
323
+ * @deprecated
324
+ * Use Updates.process()
325
+ */
326
+ processUpdates: Updates.process,
327
+ /**
328
+ * Gets the dom policy used by the templating system.
329
+ */
330
+ get policy() {
331
+ return defaultPolicy;
332
+ },
333
+ /**
334
+ * Sets the dom policy used by the templating system.
335
+ * @param policy - The policy to set.
336
+ * @remarks
337
+ * This API can only be called once, for security reasons. It should be
338
+ * called by the application developer at the start of their program.
339
+ */
340
+ setPolicy(value) {
341
+ if (defaultPolicy !== fastPolicy) {
342
+ throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
343
+ }
344
+ defaultPolicy = value;
345
+ },
346
+ /**
347
+ * Sets an attribute value on an element.
348
+ * @param element - The element to set the attribute value on.
349
+ * @param attributeName - The attribute name to set.
350
+ * @param value - The value of the attribute to set.
351
+ * @remarks
352
+ * If the value is `null` or `undefined`, the attribute is removed, otherwise
353
+ * it is set to the provided value using the standard `setAttribute` API.
354
+ */
355
+ setAttribute(element, attributeName, value) {
356
+ value === null || value === undefined
357
+ ? element.removeAttribute(attributeName)
358
+ : element.setAttribute(attributeName, value);
359
+ },
360
+ /**
361
+ * Sets a boolean attribute value.
362
+ * @param element - The element to set the boolean attribute value on.
363
+ * @param attributeName - The attribute name to set.
364
+ * @param value - The value of the attribute to set.
365
+ * @remarks
366
+ * If the value is true, the attribute is added; otherwise it is removed.
367
+ */
368
+ setBooleanAttribute(element, attributeName, value) {
369
+ value
370
+ ? element.setAttribute(attributeName, "")
371
+ : element.removeAttribute(attributeName);
372
+ },
373
+ });
374
+
375
+ function safeURL(tagName, aspect, aspectName, sink) {
376
+ return (target, name, value, ...rest) => {
377
+ if (isString(value)) {
378
+ value = value.replace("javascript:", "");
379
+ }
380
+ sink(target, name, value, ...rest);
381
+ };
382
+ }
383
+ function block(tagName, aspect, aspectName, sink) {
384
+ throw new Error(`${aspectName} on ${tagName !== null && tagName !== void 0 ? tagName : "text"} is blocked by the current DOMPolicy.`);
385
+ }
386
+ const defaultDOMElementGuards = {
387
+ a: {
388
+ [DOMAspect.attribute]: {
389
+ href: safeURL,
390
+ },
391
+ [DOMAspect.property]: {
392
+ href: safeURL,
393
+ },
394
+ },
395
+ area: {
396
+ [DOMAspect.attribute]: {
397
+ href: safeURL,
398
+ },
399
+ [DOMAspect.property]: {
400
+ href: safeURL,
401
+ },
402
+ },
403
+ button: {
404
+ [DOMAspect.attribute]: {
405
+ formaction: safeURL,
406
+ },
407
+ [DOMAspect.property]: {
408
+ formAction: safeURL,
409
+ },
410
+ },
411
+ embed: {
412
+ [DOMAspect.attribute]: {
413
+ src: block,
414
+ },
415
+ [DOMAspect.property]: {
416
+ src: block,
417
+ },
418
+ },
419
+ form: {
420
+ [DOMAspect.attribute]: {
421
+ action: safeURL,
422
+ },
423
+ [DOMAspect.property]: {
424
+ action: safeURL,
425
+ },
426
+ },
427
+ frame: {
428
+ [DOMAspect.attribute]: {
429
+ src: safeURL,
430
+ },
431
+ [DOMAspect.property]: {
432
+ src: safeURL,
433
+ },
434
+ },
435
+ iframe: {
436
+ [DOMAspect.attribute]: {
437
+ src: safeURL,
438
+ },
439
+ [DOMAspect.property]: {
440
+ src: safeURL,
441
+ srcdoc: block,
442
+ },
443
+ },
444
+ input: {
445
+ [DOMAspect.attribute]: {
446
+ formaction: safeURL,
447
+ },
448
+ [DOMAspect.property]: {
449
+ formAction: safeURL,
450
+ },
451
+ },
452
+ link: {
453
+ [DOMAspect.attribute]: {
454
+ href: block,
455
+ },
456
+ [DOMAspect.property]: {
457
+ href: block,
458
+ },
459
+ },
460
+ object: {
461
+ [DOMAspect.attribute]: {
462
+ codebase: block,
463
+ data: block,
464
+ },
465
+ [DOMAspect.property]: {
466
+ codeBase: block,
467
+ data: block,
468
+ },
469
+ },
470
+ script: {
471
+ [DOMAspect.attribute]: {
472
+ src: block,
473
+ text: block,
474
+ },
475
+ [DOMAspect.property]: {
476
+ src: block,
477
+ text: block,
478
+ innerText: block,
479
+ textContent: block,
480
+ },
481
+ },
482
+ style: {
483
+ [DOMAspect.property]: {
484
+ innerText: block,
485
+ textContent: block,
486
+ },
487
+ },
488
+ };
489
+ const blockedEvents = {
490
+ onabort: block,
491
+ onauxclick: block,
492
+ onbeforeinput: block,
493
+ onbeforematch: block,
494
+ onblur: block,
495
+ oncancel: block,
496
+ oncanplay: block,
497
+ oncanplaythrough: block,
498
+ onchange: block,
499
+ onclick: block,
500
+ onclose: block,
501
+ oncontextlost: block,
502
+ oncontextmenu: block,
503
+ oncontextrestored: block,
504
+ oncopy: block,
505
+ oncuechange: block,
506
+ oncut: block,
507
+ ondblclick: block,
508
+ ondrag: block,
509
+ ondragend: block,
510
+ ondragenter: block,
511
+ ondragleave: block,
512
+ ondragover: block,
513
+ ondragstart: block,
514
+ ondrop: block,
515
+ ondurationchange: block,
516
+ onemptied: block,
517
+ onended: block,
518
+ onerror: block,
519
+ onfocus: block,
520
+ onformdata: block,
521
+ oninput: block,
522
+ oninvalid: block,
523
+ onkeydown: block,
524
+ onkeypress: block,
525
+ onkeyup: block,
526
+ onload: block,
527
+ onloadeddata: block,
528
+ onloadedmetadata: block,
529
+ onloadstart: block,
530
+ onmousedown: block,
531
+ onmouseenter: block,
532
+ onmouseleave: block,
533
+ onmousemove: block,
534
+ onmouseout: block,
535
+ onmouseover: block,
536
+ onmouseup: block,
537
+ onpaste: block,
538
+ onpause: block,
539
+ onplay: block,
540
+ onplaying: block,
541
+ onprogress: block,
542
+ onratechange: block,
543
+ onreset: block,
544
+ onresize: block,
545
+ onscroll: block,
546
+ onsecuritypolicyviolation: block,
547
+ onseeked: block,
548
+ onseeking: block,
549
+ onselect: block,
550
+ onslotchange: block,
551
+ onstalled: block,
552
+ onsubmit: block,
553
+ onsuspend: block,
554
+ ontimeupdate: block,
555
+ ontoggle: block,
556
+ onvolumechange: block,
557
+ onwaiting: block,
558
+ onwebkitanimationend: block,
559
+ onwebkitanimationiteration: block,
560
+ onwebkitanimationstart: block,
561
+ onwebkittransitionend: block,
562
+ onwheel: block,
563
+ };
564
+ const defaultDOMGuards = {
565
+ elements: defaultDOMElementGuards,
566
+ aspects: {
567
+ [DOMAspect.attribute]: Object.assign({}, blockedEvents),
568
+ [DOMAspect.property]: Object.assign({ innerHTML: block }, blockedEvents),
569
+ [DOMAspect.event]: Object.assign({}, blockedEvents),
570
+ },
571
+ };
572
+ function createDomSinkGuards(config, defaults) {
573
+ const result = {};
574
+ for (const name in defaults) {
575
+ const overrideValue = config[name];
576
+ const defaultValue = defaults[name];
577
+ switch (overrideValue) {
578
+ case null:
579
+ // remove the default
580
+ break;
581
+ case undefined:
582
+ // keep the default
583
+ result[name] = defaultValue;
584
+ break;
585
+ default:
586
+ // override the default
587
+ result[name] = overrideValue;
588
+ break;
589
+ }
590
+ }
591
+ // add any new sinks that were not overrides
592
+ for (const name in config) {
593
+ if (!(name in result)) {
594
+ result[name] = config[name];
595
+ }
596
+ }
597
+ return Object.freeze(result);
598
+ }
599
+ function createDOMAspectGuards(config, defaults) {
600
+ const result = {};
601
+ for (const aspect in defaults) {
602
+ const overrideValue = config[aspect];
603
+ const defaultValue = defaults[aspect];
604
+ switch (overrideValue) {
605
+ case null:
606
+ // remove the default
607
+ break;
608
+ case undefined:
609
+ // keep the default
610
+ result[aspect] = createDomSinkGuards(defaultValue, {});
611
+ break;
612
+ default:
613
+ // override the default
614
+ result[aspect] = createDomSinkGuards(overrideValue, defaultValue);
615
+ break;
616
+ }
617
+ }
618
+ // add any new aspect guards that were not overrides
619
+ for (const aspect in config) {
620
+ if (!(aspect in result)) {
621
+ result[aspect] = createDomSinkGuards(config[aspect], {});
622
+ }
623
+ }
624
+ return Object.freeze(result);
625
+ }
626
+ function createElementGuards(config, defaults) {
627
+ const result = {};
628
+ for (const tag in defaults) {
629
+ const overrideValue = config[tag];
630
+ const defaultValue = defaults[tag];
631
+ switch (overrideValue) {
632
+ case null:
633
+ // remove the default
634
+ break;
635
+ case undefined:
636
+ // keep the default
637
+ result[tag] = createDOMAspectGuards(overrideValue, {});
638
+ break;
639
+ default:
640
+ // override the default aspects
641
+ result[tag] = createDOMAspectGuards(overrideValue, defaultValue);
642
+ break;
643
+ }
644
+ }
645
+ // Add any new element guards that were not overrides
646
+ for (const tag in config) {
647
+ if (!(tag in result)) {
648
+ result[tag] = createDOMAspectGuards(config[tag], {});
649
+ }
650
+ }
651
+ return Object.freeze(result);
652
+ }
653
+ function createDOMGuards(config, defaults) {
654
+ return Object.freeze({
655
+ elements: config.elements
656
+ ? createElementGuards(config.elements, defaults.elements)
657
+ : defaults.elements,
658
+ aspects: config.aspects
659
+ ? createDOMAspectGuards(config.aspects, defaults.aspects)
660
+ : defaults.aspects,
661
+ });
662
+ }
663
+ function createTrustedType() {
664
+ const createHTML = html => html;
665
+ return globalThis.trustedTypes
666
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
667
+ : { createHTML };
668
+ }
669
+ function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
670
+ const sinkGuards = aspectGuards[aspect];
671
+ if (sinkGuards) {
672
+ const guard = sinkGuards[aspectName];
673
+ if (guard) {
674
+ return guard(tagName, aspect, aspectName, sink);
675
+ }
676
+ }
677
+ }
678
+ const DOMPolicy = Object.freeze({
679
+ /**
680
+ * Creates a new DOM Policy object.
681
+ * @param options The options to use in creating the policy.
682
+ * @returns The newly created DOMPolicy.
683
+ */
684
+ create(options = {}) {
685
+ var _a, _b;
686
+ const trustedType = (_a = options.trustedType) !== null && _a !== void 0 ? _a : createTrustedType();
687
+ const guards = createDOMGuards((_b = options.guards) !== null && _b !== void 0 ? _b : {}, defaultDOMGuards);
688
+ return Object.freeze({
689
+ createHTML(value) {
690
+ return trustedType.createHTML(value);
691
+ },
692
+ protect(tagName, aspect, aspectName, sink) {
693
+ var _a;
694
+ // Check for element-specific guards.
695
+ const key = (tagName !== null && tagName !== void 0 ? tagName : "").toLowerCase();
696
+ const elementGuards = guards.elements[key];
697
+ if (elementGuards) {
698
+ const guard = tryGuard(elementGuards, tagName, aspect, aspectName, sink);
699
+ if (guard) {
700
+ return guard;
701
+ }
702
+ }
703
+ // Check for guards applicable to all nodes.
704
+ return ((_a = tryGuard(guards.aspects, tagName, aspect, aspectName, sink)) !== null && _a !== void 0 ? _a : sink);
705
+ },
706
+ });
707
+ },
708
+ });
709
+
256
710
  /**
257
711
  * An implementation of {@link Notifier} that efficiently keeps track of
258
712
  * subscribers interested in a specific change notification on an
@@ -495,6 +949,10 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
495
949
  this.propertyName = void 0;
496
950
  this.notifier = void 0;
497
951
  this.next = void 0;
952
+ /**
953
+ * Opts out of JSON stringification.
954
+ */
955
+ this.toJSON = noop;
498
956
  }
499
957
  setMode(isAsync) {
500
958
  this.isAsync = this.needsQueue = isAsync;
@@ -1535,55 +1993,6 @@ css.partial = (strings, ...values) => {
1535
1993
  */
1536
1994
  const cssPartial = css.partial;
1537
1995
 
1538
- /**
1539
- * Common DOM APIs.
1540
- * @public
1541
- */
1542
- const DOM = Object.freeze({
1543
- /**
1544
- * @deprecated
1545
- * Use Updates.enqueue().
1546
- */
1547
- queueUpdate: Updates.enqueue,
1548
- /**
1549
- * @deprecated
1550
- * Use Updates.next()
1551
- */
1552
- nextUpdate: Updates.next,
1553
- /**
1554
- * @deprecated
1555
- * Use Updates.process()
1556
- */
1557
- processUpdates: Updates.process,
1558
- /**
1559
- * Sets an attribute value on an element.
1560
- * @param element - The element to set the attribute value on.
1561
- * @param attributeName - The attribute name to set.
1562
- * @param value - The value of the attribute to set.
1563
- * @remarks
1564
- * If the value is `null` or `undefined`, the attribute is removed, otherwise
1565
- * it is set to the provided value using the standard `setAttribute` API.
1566
- */
1567
- setAttribute(element, attributeName, value) {
1568
- value === null || value === undefined
1569
- ? element.removeAttribute(attributeName)
1570
- : element.setAttribute(attributeName, value);
1571
- },
1572
- /**
1573
- * Sets a boolean attribute value.
1574
- * @param element - The element to set the boolean attribute value on.
1575
- * @param attributeName - The attribute name to set.
1576
- * @param value - The value of the attribute to set.
1577
- * @remarks
1578
- * If the value is true, the attribute is added; otherwise it is removed.
1579
- */
1580
- setBooleanAttribute(element, attributeName, value) {
1581
- value
1582
- ? element.setAttribute(attributeName, "")
1583
- : element.removeAttribute(attributeName);
1584
- },
1585
- });
1586
-
1587
1996
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1588
1997
  const interpolationStart = `${marker}{`;
1589
1998
  const interpolationEnd = `}${marker}`;
@@ -1660,67 +2069,6 @@ const Parser = Object.freeze({
1660
2069
  },
1661
2070
  });
1662
2071
 
1663
- /**
1664
- * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1665
- * control ViewBehaviors.
1666
- * @public
1667
- */
1668
- const ViewBehaviorOrchestrator = Object.freeze({
1669
- /**
1670
- * Creates a ViewBehaviorOrchestrator.
1671
- * @param source - The source to to associate behaviors with.
1672
- * @returns A ViewBehaviorOrchestrator.
1673
- */
1674
- create(source) {
1675
- const behaviors = [];
1676
- const targets = {};
1677
- let unbindables = null;
1678
- let isConnected = false;
1679
- return {
1680
- source,
1681
- context: ExecutionContext.default,
1682
- targets,
1683
- get isBound() {
1684
- return isConnected;
1685
- },
1686
- addBehaviorFactory(factory, target) {
1687
- const nodeId = factory.nodeId || (factory.nodeId = nextId());
1688
- factory.id || (factory.id = nextId());
1689
- this.addTarget(nodeId, target);
1690
- this.addBehavior(factory.createBehavior());
1691
- },
1692
- addTarget(nodeId, target) {
1693
- targets[nodeId] = target;
1694
- },
1695
- addBehavior(behavior) {
1696
- behaviors.push(behavior);
1697
- if (isConnected) {
1698
- behavior.bind(this);
1699
- }
1700
- },
1701
- onUnbind(unbindable) {
1702
- if (unbindables === null) {
1703
- unbindables = [];
1704
- }
1705
- unbindables.push(unbindable);
1706
- },
1707
- connectedCallback(controller) {
1708
- if (!isConnected) {
1709
- isConnected = true;
1710
- behaviors.forEach(x => x.bind(this));
1711
- }
1712
- },
1713
- disconnectedCallback(controller) {
1714
- if (isConnected) {
1715
- isConnected = false;
1716
- if (unbindables !== null) {
1717
- unbindables.forEach(x => x.unbind(this));
1718
- }
1719
- }
1720
- },
1721
- };
1722
- },
1723
- });
1724
2072
  const registry = createTypeRegistry();
1725
2073
  /**
1726
2074
  * Instructs the template engine to apply behavior to a node.
@@ -1748,67 +2096,6 @@ const HTMLDirective = Object.freeze({
1748
2096
  registry.register(options);
1749
2097
  return type;
1750
2098
  },
1751
- });
1752
- /**
1753
- * Decorator: Defines an HTMLDirective.
1754
- * @param options - Provides options that specify the directive's application.
1755
- * @public
1756
- */
1757
- function htmlDirective(options) {
1758
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
1759
- return function (type) {
1760
- HTMLDirective.define(type, options);
1761
- };
1762
- }
1763
- /**
1764
- * Captures a binding expression along with related information and capabilities.
1765
- *
1766
- * @public
1767
- */
1768
- class Binding {
1769
- /**
1770
- * Creates a binding.
1771
- * @param evaluate - Evaluates the binding.
1772
- * @param isVolatile - Indicates whether the binding is volatile.
1773
- */
1774
- constructor(evaluate, isVolatile = false) {
1775
- this.evaluate = evaluate;
1776
- this.isVolatile = isVolatile;
1777
- }
1778
- }
1779
- /**
1780
- * The type of HTML aspect to target.
1781
- * @public
1782
- */
1783
- const Aspect = Object.freeze({
1784
- /**
1785
- * Not aspected.
1786
- */
1787
- none: 0,
1788
- /**
1789
- * An attribute.
1790
- */
1791
- attribute: 1,
1792
- /**
1793
- * A boolean attribute.
1794
- */
1795
- booleanAttribute: 2,
1796
- /**
1797
- * A property.
1798
- */
1799
- property: 3,
1800
- /**
1801
- * Content
1802
- */
1803
- content: 4,
1804
- /**
1805
- * A token list.
1806
- */
1807
- tokenList: 5,
1808
- /**
1809
- * An event.
1810
- */
1811
- event: 6,
1812
2099
  /**
1813
2100
  *
1814
2101
  * @param directive - The directive to assign the aspect to.
@@ -1816,9 +2103,9 @@ const Aspect = Object.freeze({
1816
2103
  * @remarks
1817
2104
  * If a falsy value is provided, then the content aspect will be assigned.
1818
2105
  */
1819
- assign(directive, value) {
2106
+ assignAspect(directive, value) {
1820
2107
  if (!value) {
1821
- directive.aspectType = Aspect.content;
2108
+ directive.aspectType = DOMAspect.content;
1822
2109
  return;
1823
2110
  }
1824
2111
  directive.sourceAspect = value;
@@ -1827,24 +2114,53 @@ const Aspect = Object.freeze({
1827
2114
  directive.targetAspect = value.substring(1);
1828
2115
  directive.aspectType =
1829
2116
  directive.targetAspect === "classList"
1830
- ? Aspect.tokenList
1831
- : Aspect.property;
2117
+ ? DOMAspect.tokenList
2118
+ : DOMAspect.property;
1832
2119
  break;
1833
2120
  case "?":
1834
2121
  directive.targetAspect = value.substring(1);
1835
- directive.aspectType = Aspect.booleanAttribute;
2122
+ directive.aspectType = DOMAspect.booleanAttribute;
1836
2123
  break;
1837
2124
  case "@":
1838
2125
  directive.targetAspect = value.substring(1);
1839
- directive.aspectType = Aspect.event;
2126
+ directive.aspectType = DOMAspect.event;
1840
2127
  break;
1841
2128
  default:
1842
2129
  directive.targetAspect = value;
1843
- directive.aspectType = Aspect.attribute;
2130
+ directive.aspectType = DOMAspect.attribute;
1844
2131
  break;
1845
2132
  }
1846
2133
  },
1847
2134
  });
2135
+ /**
2136
+ * Decorator: Defines an HTMLDirective.
2137
+ * @param options - Provides options that specify the directive's application.
2138
+ * @public
2139
+ */
2140
+ function htmlDirective(options) {
2141
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
2142
+ return function (type) {
2143
+ HTMLDirective.define(type, options);
2144
+ };
2145
+ }
2146
+ /**
2147
+ * Captures a binding expression along with related information and capabilities.
2148
+ *
2149
+ * @public
2150
+ */
2151
+ class Binding {
2152
+ /**
2153
+ * Creates a binding.
2154
+ * @param evaluate - Evaluates the binding.
2155
+ * @param policy - The security policy to associate with this binding.
2156
+ * @param isVolatile - Indicates whether the binding is volatile.
2157
+ */
2158
+ constructor(evaluate, policy, isVolatile = false) {
2159
+ this.evaluate = evaluate;
2160
+ this.policy = policy;
2161
+ this.isVolatile = isVolatile;
2162
+ }
2163
+ }
1848
2164
  /**
1849
2165
  * A base class used for attribute directives that don't need internal state.
1850
2166
  * @public
@@ -1857,9 +2173,10 @@ class StatelessAttachedAttributeDirective {
1857
2173
  constructor(options) {
1858
2174
  this.options = options;
1859
2175
  /**
1860
- * The unique id of the factory.
2176
+ * Opts out of JSON stringification.
2177
+ * @internal
1861
2178
  */
1862
- this.id = nextId();
2179
+ this.toJSON = noop;
1863
2180
  }
1864
2181
  /**
1865
2182
  * Creates a placeholder string based on the directive's index within the template.
@@ -1879,21 +2196,20 @@ class StatelessAttachedAttributeDirective {
1879
2196
  }
1880
2197
  }
1881
2198
 
1882
- const createInnerHTMLBinding = globalThis.TrustedHTML
1883
- ? (binding) => (s, c) => {
1884
- const value = binding(s, c);
1885
- if (value instanceof TrustedHTML) {
1886
- return value;
1887
- }
1888
- throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1889
- }
1890
- : (binding) => binding;
1891
2199
  class OnChangeBinding extends Binding {
1892
2200
  createObserver(_, subscriber) {
1893
2201
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1894
2202
  }
1895
2203
  }
1896
2204
  class OneTimeBinding extends Binding {
2205
+ constructor() {
2206
+ super(...arguments);
2207
+ /**
2208
+ * Opts out of JSON stringification.
2209
+ * @internal
2210
+ */
2211
+ this.toJSON = noop;
2212
+ }
1897
2213
  createObserver() {
1898
2214
  return this;
1899
2215
  }
@@ -1992,8 +2308,14 @@ function updateTokenList(target, aspect, value) {
1992
2308
  }
1993
2309
  }
1994
2310
  }
1995
- const setProperty = (t, a, v) => (t[a] = v);
1996
- const eventTarget = () => void 0;
2311
+ const sinkLookup = {
2312
+ [DOMAspect.attribute]: DOM.setAttribute,
2313
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
2314
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
2315
+ [DOMAspect.content]: updateContent,
2316
+ [DOMAspect.tokenList]: updateTokenList,
2317
+ [DOMAspect.event]: () => void 0,
2318
+ };
1997
2319
  /**
1998
2320
  * A directive that applies bindings.
1999
2321
  * @public
@@ -2006,15 +2328,10 @@ class HTMLBindingDirective {
2006
2328
  constructor(dataBinding) {
2007
2329
  this.dataBinding = dataBinding;
2008
2330
  this.updateTarget = null;
2009
- /**
2010
- * The unique id of the factory.
2011
- */
2012
- this.id = nextId();
2013
2331
  /**
2014
2332
  * The type of aspect to target.
2015
2333
  */
2016
- this.aspectType = Aspect.content;
2017
- this.data = `${this.id}-d`;
2334
+ this.aspectType = DOMAspect.content;
2018
2335
  }
2019
2336
  /**
2020
2337
  * Creates HTML to be used within a template.
@@ -2027,45 +2344,28 @@ class HTMLBindingDirective {
2027
2344
  * Creates a behavior.
2028
2345
  */
2029
2346
  createBehavior() {
2347
+ var _a;
2030
2348
  if (this.updateTarget === null) {
2031
- if (this.targetAspect === "innerHTML") {
2032
- this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2033
- }
2034
- switch (this.aspectType) {
2035
- case 1:
2036
- this.updateTarget = DOM.setAttribute;
2037
- break;
2038
- case 2:
2039
- this.updateTarget = DOM.setBooleanAttribute;
2040
- break;
2041
- case 3:
2042
- this.updateTarget = setProperty;
2043
- break;
2044
- case 4:
2045
- this.updateTarget = updateContent;
2046
- break;
2047
- case 5:
2048
- this.updateTarget = updateTokenList;
2049
- break;
2050
- case 6:
2051
- this.updateTarget = eventTarget;
2052
- break;
2053
- default:
2054
- throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2349
+ const sink = sinkLookup[this.aspectType];
2350
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
2351
+ if (!sink) {
2352
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2055
2353
  }
2354
+ this.data = `${this.id}-d`;
2355
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
2056
2356
  }
2057
2357
  return this;
2058
2358
  }
2059
2359
  /** @internal */
2060
2360
  bind(controller) {
2061
2361
  var _a;
2062
- const target = controller.targets[this.nodeId];
2063
- switch (this.updateTarget) {
2064
- case eventTarget:
2362
+ const target = controller.targets[this.targetNodeId];
2363
+ switch (this.aspectType) {
2364
+ case DOMAspect.event:
2065
2365
  target[this.data] = controller;
2066
2366
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2067
2367
  break;
2068
- case updateContent:
2368
+ case DOMAspect.content:
2069
2369
  controller.onUnbind(this);
2070
2370
  // intentional fall through
2071
2371
  default:
@@ -2078,7 +2378,7 @@ class HTMLBindingDirective {
2078
2378
  }
2079
2379
  /** @internal */
2080
2380
  unbind(controller) {
2081
- const target = controller.targets[this.nodeId];
2381
+ const target = controller.targets[this.targetNodeId];
2082
2382
  const view = target.$fastView;
2083
2383
  if (view !== void 0 && view.isComposed) {
2084
2384
  view.unbind();
@@ -2108,21 +2408,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2108
2408
  /**
2109
2409
  * Creates an standard binding.
2110
2410
  * @param expression - The binding to refresh when changed.
2411
+ * @param policy - The security policy to associate with th binding.
2111
2412
  * @param isVolatile - Indicates whether the binding is volatile or not.
2112
2413
  * @returns A binding configuration.
2113
2414
  * @public
2114
2415
  */
2115
- function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
2116
- return new OnChangeBinding(expression, isVolatile);
2416
+ function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
2417
+ return new OnChangeBinding(expression, policy, isVolatile);
2117
2418
  }
2118
2419
  /**
2119
2420
  * Creates a one time binding
2120
2421
  * @param expression - The binding to refresh when signaled.
2422
+ * @param policy - The security policy to associate with th binding.
2121
2423
  * @returns A binding configuration.
2122
2424
  * @public
2123
2425
  */
2124
- function oneTime(expression) {
2125
- return new OneTimeBinding(expression);
2426
+ function oneTime(expression, policy) {
2427
+ return new OneTimeBinding(expression, policy);
2126
2428
  }
2127
2429
  /**
2128
2430
  * Creates an event listener binding.
@@ -2132,7 +2434,7 @@ function oneTime(expression) {
2132
2434
  * @public
2133
2435
  */
2134
2436
  function listener(expression, options) {
2135
- const config = new OnChangeBinding(expression, false);
2437
+ const config = new OnChangeBinding(expression);
2136
2438
  config.options = options;
2137
2439
  return config;
2138
2440
  }
@@ -2201,6 +2503,11 @@ class HTMLView {
2201
2503
  * The length of the current collection within a repeat context.
2202
2504
  */
2203
2505
  this.length = 0;
2506
+ /**
2507
+ * Opts out of JSON stringification.
2508
+ * @internal
2509
+ */
2510
+ this.toJSON = noop;
2204
2511
  this.firstChild = fragment.firstChild;
2205
2512
  this.lastChild = fragment.lastChild;
2206
2513
  }
@@ -2408,20 +2715,25 @@ const warningHost = new Proxy(document.createElement("div"), {
2408
2715
  },
2409
2716
  });
2410
2717
  class CompilationContext {
2411
- constructor(fragment, directives) {
2718
+ constructor(fragment, directives, policy) {
2412
2719
  this.fragment = fragment;
2413
2720
  this.directives = directives;
2721
+ this.policy = policy;
2414
2722
  this.proto = null;
2415
2723
  this.nodeIds = new Set();
2416
2724
  this.descriptors = {};
2417
2725
  this.factories = [];
2418
2726
  }
2419
- addFactory(factory, parentId, nodeId, targetIndex) {
2727
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
2728
+ var _a, _b;
2420
2729
  if (!this.nodeIds.has(nodeId)) {
2421
2730
  this.nodeIds.add(nodeId);
2422
2731
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
2423
2732
  }
2424
- factory.nodeId = nodeId;
2733
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
2734
+ factory.targetNodeId = nodeId;
2735
+ factory.targetTagName = tagName;
2736
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
2425
2737
  this.factories.push(factory);
2426
2738
  }
2427
2739
  freeze() {
@@ -2474,19 +2786,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2474
2786
  let result = null;
2475
2787
  if (parseResult === null) {
2476
2788
  if (includeBasicValues) {
2477
- result = new HTMLBindingDirective(oneTime(() => attrValue));
2478
- Aspect.assign(result, attr.name);
2789
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
2790
+ HTMLDirective.assignAspect(result, attr.name);
2479
2791
  }
2480
2792
  }
2481
2793
  else {
2482
2794
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2483
- result = Compiler.aggregate(parseResult);
2795
+ result = Compiler.aggregate(parseResult, context.policy);
2484
2796
  }
2485
2797
  if (result !== null) {
2486
2798
  node.removeAttributeNode(attr);
2487
2799
  i--;
2488
2800
  ii--;
2489
- context.addFactory(result, parentId, nodeId, nodeIndex);
2801
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
2490
2802
  }
2491
2803
  }
2492
2804
  }
@@ -2511,8 +2823,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2511
2823
  }
2512
2824
  else {
2513
2825
  currentNode.textContent = " ";
2514
- Aspect.assign(currentPart);
2515
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2826
+ HTMLDirective.assignAspect(currentPart);
2827
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
2516
2828
  }
2517
2829
  lastNode = currentNode;
2518
2830
  }
@@ -2544,7 +2856,7 @@ function compileNode(context, parentId, node, nodeIndex) {
2544
2856
  if (parts !== null) {
2545
2857
  context.addFactory(
2546
2858
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2547
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
2859
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2548
2860
  }
2549
2861
  break;
2550
2862
  }
@@ -2558,45 +2870,28 @@ function isMarker(node, directives) {
2558
2870
  Parser.parse(node.data, directives) !== null);
2559
2871
  }
2560
2872
  const templateTag = "TEMPLATE";
2561
- const policyOptions = { createHTML: html => html };
2562
- let htmlPolicy = globalThis.trustedTypes
2563
- ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
2564
- : policyOptions;
2565
- const fastHTMLPolicy = htmlPolicy;
2566
2873
  /**
2567
2874
  * Common APIs related to compilation.
2568
2875
  * @public
2569
2876
  */
2570
2877
  const Compiler = {
2571
- /**
2572
- * Sets the HTML trusted types policy used by the compiler.
2573
- * @param policy - The policy to set for HTML.
2574
- * @remarks
2575
- * This API can only be called once, for security reasons. It should be
2576
- * called by the application developer at the start of their program.
2577
- */
2578
- setHTMLPolicy(policy) {
2579
- if (htmlPolicy !== fastHTMLPolicy) {
2580
- throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
2581
- }
2582
- htmlPolicy = policy;
2583
- },
2584
2878
  /**
2585
2879
  * Compiles a template and associated directives into a compilation
2586
2880
  * result which can be used to create views.
2587
2881
  * @param html - The html string or template element to compile.
2588
- * @param directives - The directives referenced by the template.
2882
+ * @param factories - The behavior factories referenced by the template.
2883
+ * @param policy - The security policy to compile the html with.
2589
2884
  * @remarks
2590
2885
  * The template that is provided for compilation is altered in-place
2591
2886
  * and cannot be compiled again. If the original template must be preserved,
2592
2887
  * it is recommended that you clone the original and pass the clone to this API.
2593
2888
  * @public
2594
2889
  */
2595
- compile(html, directives) {
2890
+ compile(html, factories, policy = DOM.policy) {
2596
2891
  let template;
2597
2892
  if (isString(html)) {
2598
2893
  template = document.createElement(templateTag);
2599
- template.innerHTML = htmlPolicy.createHTML(html);
2894
+ template.innerHTML = policy.createHTML(html);
2600
2895
  const fec = template.content.firstElementChild;
2601
2896
  if (fec !== null && fec.tagName === templateTag) {
2602
2897
  template = fec;
@@ -2607,18 +2902,18 @@ const Compiler = {
2607
2902
  }
2608
2903
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
2609
2904
  const fragment = document.adoptNode(template.content);
2610
- const context = new CompilationContext(fragment, directives);
2905
+ const context = new CompilationContext(fragment, factories, policy);
2611
2906
  compileAttributes(context, "", template, /* host */ "h", 0, true);
2612
2907
  if (
2613
2908
  // If the first node in a fragment is a marker, that means it's an unstable first node,
2614
2909
  // because something like a when, repeat, etc. could add nodes before the marker.
2615
2910
  // To mitigate this, we insert a stable first node. However, if we insert a node,
2616
2911
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
2617
- isMarker(fragment.firstChild, directives) ||
2912
+ isMarker(fragment.firstChild, factories) ||
2618
2913
  // Or if there is only one node and a directive, it means the template's content
2619
2914
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
2620
2915
  // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
2621
- (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
2916
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
2622
2917
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
2623
2918
  }
2624
2919
  compileChildren(context, fragment, /* root */ "r");
@@ -2637,15 +2932,17 @@ const Compiler = {
2637
2932
  * Aggregates an array of strings and directives into a single directive.
2638
2933
  * @param parts - A heterogeneous array of static strings interspersed with
2639
2934
  * directives.
2935
+ * @param policy - The security policy to use with the aggregated bindings.
2640
2936
  * @returns A single inline directive that aggregates the behavior of all the parts.
2641
2937
  */
2642
- aggregate(parts) {
2938
+ aggregate(parts, policy = DOM.policy) {
2643
2939
  if (parts.length === 1) {
2644
2940
  return parts[0];
2645
2941
  }
2646
2942
  let sourceAspect;
2647
2943
  let binding;
2648
2944
  let isVolatile = false;
2945
+ let bindingPolicy = void 0;
2649
2946
  const partCount = parts.length;
2650
2947
  const finalParts = parts.map((x) => {
2651
2948
  if (isString(x)) {
@@ -2654,6 +2951,7 @@ const Compiler = {
2654
2951
  sourceAspect = x.sourceAspect || sourceAspect;
2655
2952
  binding = x.dataBinding || binding;
2656
2953
  isVolatile = isVolatile || x.dataBinding.isVolatile;
2954
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
2657
2955
  return x.dataBinding.evaluate;
2658
2956
  });
2659
2957
  const expression = (scope, context) => {
@@ -2665,12 +2963,26 @@ const Compiler = {
2665
2963
  };
2666
2964
  binding.evaluate = expression;
2667
2965
  binding.isVolatile = isVolatile;
2966
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
2668
2967
  const directive = new HTMLBindingDirective(binding);
2669
- Aspect.assign(directive, sourceAspect);
2968
+ HTMLDirective.assignAspect(directive, sourceAspect);
2670
2969
  return directive;
2671
2970
  },
2672
2971
  };
2673
2972
 
2973
+ // Much thanks to LitHTML for working this out!
2974
+ const lastAttributeNameRegex =
2975
+ /* eslint-disable-next-line no-control-regex */
2976
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2977
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
2978
+ if (definition.aspected) {
2979
+ const match = lastAttributeNameRegex.exec(prevString);
2980
+ if (match !== null) {
2981
+ HTMLDirective.assignAspect(value, match[2]);
2982
+ }
2983
+ }
2984
+ return value.createHTML(add);
2985
+ }
2674
2986
  /**
2675
2987
  * A template capable of creating HTMLView instances or rendering directly to DOM.
2676
2988
  * @public
@@ -2680,9 +2992,16 @@ class ViewTemplate {
2680
2992
  * Creates an instance of ViewTemplate.
2681
2993
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
2682
2994
  * @param factories - The directives that will be connected to placeholders in the html.
2995
+ * @param policy - The security policy to use when compiling this template.
2683
2996
  */
2684
- constructor(html, factories) {
2997
+ constructor(html, factories = {}, policy) {
2998
+ this.policy = policy;
2685
2999
  this.result = null;
3000
+ /**
3001
+ * Opts out of JSON stringification.
3002
+ * @internal
3003
+ */
3004
+ this.toJSON = noop;
2686
3005
  this.html = html;
2687
3006
  this.factories = factories;
2688
3007
  }
@@ -2692,10 +3011,28 @@ class ViewTemplate {
2692
3011
  */
2693
3012
  create(hostBindingTarget) {
2694
3013
  if (this.result === null) {
2695
- this.result = Compiler.compile(this.html, this.factories);
3014
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
2696
3015
  }
2697
3016
  return this.result.createView(hostBindingTarget);
2698
3017
  }
3018
+ /**
3019
+ * Sets the DOMPolicy for this template.
3020
+ * @param policy - The policy to associated with this template.
3021
+ * @returns The modified template instance.
3022
+ * @remarks
3023
+ * The DOMPolicy can only be set once for a template and cannot be
3024
+ * set after the template is compiled.
3025
+ */
3026
+ withPolicy(policy) {
3027
+ if (this.result) {
3028
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
3029
+ }
3030
+ if (this.policy) {
3031
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
3032
+ }
3033
+ this.policy = policy;
3034
+ return this;
3035
+ }
2699
3036
  /**
2700
3037
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
2701
3038
  * @param source - The data source to bind the template to.
@@ -2709,17 +3046,47 @@ class ViewTemplate {
2709
3046
  view.appendTo(host);
2710
3047
  return view;
2711
3048
  }
2712
- }
2713
- // Much thanks to LitHTML for working this out!
2714
- const lastAttributeNameRegex =
2715
- /* eslint-disable-next-line no-control-regex */
2716
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2717
- function createAspectedHTML(value, prevString, add) {
2718
- const match = lastAttributeNameRegex.exec(prevString);
2719
- if (match !== null) {
2720
- Aspect.assign(value, match[2]);
3049
+ /**
3050
+ * Creates a template based on a set of static strings and dynamic values.
3051
+ * @param strings - The static strings to create the template with.
3052
+ * @param values - The dynamic values to create the template with.
3053
+ * @param policy - The DOMPolicy to associated with the template.
3054
+ * @returns A ViewTemplate.
3055
+ * @remarks
3056
+ * This API should not be used directly under normal circumstances because constructing
3057
+ * a template in this way, if not done properly, can open up the application to XSS
3058
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
3059
+ * and also be sure to manually sanitize all static strings particularly if they can
3060
+ * come from user input.
3061
+ */
3062
+ static create(strings, values, policy) {
3063
+ let html = "";
3064
+ const factories = Object.create(null);
3065
+ const add = (factory) => {
3066
+ var _a;
3067
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
3068
+ factories[id] = factory;
3069
+ return id;
3070
+ };
3071
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
3072
+ const currentString = strings[i];
3073
+ let currentValue = values[i];
3074
+ let definition;
3075
+ html += currentString;
3076
+ if (isFunction(currentValue)) {
3077
+ currentValue = new HTMLBindingDirective(bind(currentValue));
3078
+ }
3079
+ else if (currentValue instanceof Binding) {
3080
+ currentValue = new HTMLBindingDirective(currentValue);
3081
+ }
3082
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
3083
+ const staticValue = currentValue;
3084
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
3085
+ }
3086
+ html += createHTML(currentValue, currentString, add, definition);
3087
+ }
3088
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
2721
3089
  }
2722
- return value.createHTML(add);
2723
3090
  }
2724
3091
  /**
2725
3092
  * Transforms a template literal string into a ViewTemplate.
@@ -2731,49 +3098,10 @@ function createAspectedHTML(value, prevString, add) {
2731
3098
  * @public
2732
3099
  */
2733
3100
  function html(strings, ...values) {
2734
- let html = "";
2735
- const factories = Object.create(null);
2736
- const add = (factory) => {
2737
- var _a;
2738
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
2739
- factories[id] = factory;
2740
- return id;
2741
- };
2742
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
2743
- const currentString = strings[i];
2744
- const currentValue = values[i];
2745
- let definition;
2746
- html += currentString;
2747
- if (isFunction(currentValue)) {
2748
- html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2749
- }
2750
- else if (isString(currentValue)) {
2751
- const match = lastAttributeNameRegex.exec(currentString);
2752
- if (match !== null) {
2753
- const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2754
- Aspect.assign(directive, match[2]);
2755
- html += directive.createHTML(add);
2756
- }
2757
- else {
2758
- html += currentValue;
2759
- }
2760
- }
2761
- else if (currentValue instanceof Binding) {
2762
- html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2763
- }
2764
- else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2765
- html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2766
- }
2767
- else {
2768
- if (definition.aspected) {
2769
- html += createAspectedHTML(currentValue, currentString, add);
2770
- }
2771
- else {
2772
- html += currentValue.createHTML(add);
2773
- }
2774
- }
3101
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
3102
+ return ViewTemplate.create(strings, values);
2775
3103
  }
2776
- return new ViewTemplate(html + strings[strings.length - 1], factories);
3104
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
2777
3105
  }
2778
3106
 
2779
3107
  /**
@@ -2786,7 +3114,7 @@ class RefDirective extends StatelessAttachedAttributeDirective {
2786
3114
  * @param controller - The view controller that manages the lifecycle of this behavior.
2787
3115
  */
2788
3116
  bind(controller) {
2789
- controller.source[this.options] = controller.targets[this.nodeId];
3117
+ controller.source[this.options] = controller.targets[this.targetNodeId];
2790
3118
  }
2791
3119
  }
2792
3120
  HTMLDirective.define(RefDirective);
@@ -2860,7 +3188,7 @@ class RepeatBehavior {
2860
3188
  * @param controller - The view controller that manages the lifecycle of this behavior.
2861
3189
  */
2862
3190
  bind(controller) {
2863
- this.location = controller.targets[this.directive.nodeId];
3191
+ this.location = controller.targets[this.directive.targetNodeId];
2864
3192
  this.controller = controller;
2865
3193
  this.items = this.itemsBindingObserver.bind(controller);
2866
3194
  this.template = this.templateBindingObserver.bind(controller);
@@ -3040,10 +3368,6 @@ class RepeatDirective {
3040
3368
  this.dataBinding = dataBinding;
3041
3369
  this.templateBinding = templateBinding;
3042
3370
  this.options = options;
3043
- /**
3044
- * The unique id of the factory.
3045
- */
3046
- this.id = nextId();
3047
3371
  ArrayObserver.enable();
3048
3372
  }
3049
3373
  /**
@@ -3092,9 +3416,15 @@ const elements = (selector) => selector
3092
3416
  * Internally used by the SlottedDirective and the ChildrenDirective.
3093
3417
  */
3094
3418
  class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3095
- constructor() {
3096
- super(...arguments);
3097
- this.sourceProperty = `${this.id}-s`;
3419
+ /**
3420
+ * The unique id of the factory.
3421
+ */
3422
+ get id() {
3423
+ return this._id;
3424
+ }
3425
+ set id(value) {
3426
+ this._id = value;
3427
+ this._controllerProperty = `${value}-c`;
3098
3428
  }
3099
3429
  /**
3100
3430
  * Bind this behavior to the source.
@@ -3103,8 +3433,8 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3103
3433
  * @param targets - The targets that behaviors in a view can attach to.
3104
3434
  */
3105
3435
  bind(controller) {
3106
- const target = controller.targets[this.nodeId];
3107
- target[this.sourceProperty] = controller.source;
3436
+ const target = controller.targets[this.targetNodeId];
3437
+ target[this._controllerProperty] = controller;
3108
3438
  this.updateTarget(controller.source, this.computeNodes(target));
3109
3439
  this.observe(target);
3110
3440
  controller.onUnbind(this);
@@ -3116,10 +3446,10 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3116
3446
  * @param targets - The targets that behaviors in a view can attach to.
3117
3447
  */
3118
3448
  unbind(controller) {
3119
- const target = controller.targets[this.nodeId];
3449
+ const target = controller.targets[this.targetNodeId];
3120
3450
  this.updateTarget(controller.source, emptyArray);
3121
3451
  this.disconnect(target);
3122
- target[this.sourceProperty] = null;
3452
+ target[this._controllerProperty] = null;
3123
3453
  }
3124
3454
  /**
3125
3455
  * Gets the data source for the target.
@@ -3127,7 +3457,7 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3127
3457
  * @returns The source.
3128
3458
  */
3129
3459
  getSource(target) {
3130
- return target[this.sourceProperty];
3460
+ return target[this._controllerProperty].source;
3131
3461
  }
3132
3462
  /**
3133
3463
  * Updates the source property with the computed nodes.
@@ -3223,9 +3553,13 @@ class ChildrenDirective extends NodeObservationDirective {
3223
3553
  * @param target - The target to observe.
3224
3554
  */
3225
3555
  observe(target) {
3226
- var _a;
3227
- const observer = (_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = new MutationObserver(this.handleEvent));
3228
- observer.target = target;
3556
+ let observer = target[this.observerProperty];
3557
+ if (!observer) {
3558
+ observer = new MutationObserver(this.handleEvent);
3559
+ observer.toJSON = noop;
3560
+ observer.target = target;
3561
+ target[this.observerProperty] = observer;
3562
+ }
3229
3563
  observer.observe(target, this.options);
3230
3564
  }
3231
3565
  /**
@@ -3264,6 +3598,29 @@ function children(propertyOrOptions) {
3264
3598
  return new ChildrenDirective(propertyOrOptions);
3265
3599
  }
3266
3600
 
3601
+ /**
3602
+ * A directive capable of injecting static HTML platform runtime protection.
3603
+ * @public
3604
+ */
3605
+ class DangerousHTMLDirective {
3606
+ constructor(html) {
3607
+ this.html = html;
3608
+ }
3609
+ createHTML() {
3610
+ return this.html;
3611
+ }
3612
+ }
3613
+ HTMLDirective.define(DangerousHTMLDirective);
3614
+ /**
3615
+ * Injects static HTML without platform protection.
3616
+ * @param html - The html to injection.
3617
+ * @returns A DangerousHTMLDirective.
3618
+ * @public
3619
+ */
3620
+ function dangerousHTML(html) {
3621
+ return new DangerousHTMLDirective(html);
3622
+ }
3623
+
3267
3624
  const booleanMode = "boolean";
3268
3625
  const reflectMode = "reflect";
3269
3626
  /**
@@ -3617,6 +3974,11 @@ class ElementController extends PropertyChangeNotifier {
3617
3974
  * If `null` then the element is managing its own rendering.
3618
3975
  */
3619
3976
  this.view = null;
3977
+ /**
3978
+ * Opts out of JSON stringification.
3979
+ * @internal
3980
+ */
3981
+ this.toJSON = noop;
3620
3982
  this.source = element;
3621
3983
  this.definition = definition;
3622
3984
  const shadowOptions = definition.shadowOptions;
@@ -4119,4 +4481,6 @@ function customElement(nameOrDef) {
4119
4481
  };
4120
4482
  }
4121
4483
 
4122
- export { ArrayObserver, Aspect, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewBehaviorOrchestrator, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };
4484
+ DOM.setPolicy(DOMPolicy.create());
4485
+
4486
+ export { ArrayObserver, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, DOMAspect, DangerousHTMLDirective, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, dangerousHTML, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };