@microsoft/fast-element 2.0.0-beta.17 → 2.0.0-beta.19

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 (59) hide show
  1. package/CHANGELOG.json +36 -0
  2. package/CHANGELOG.md +18 -1
  3. package/dist/dts/dom-policy.d.ts +68 -0
  4. package/dist/dts/dom.d.ts +116 -0
  5. package/dist/dts/index.d.ts +3 -2
  6. package/dist/dts/index.rollup.d.ts +0 -1
  7. package/dist/dts/index.rollup.debug.d.ts +0 -1
  8. package/dist/dts/interfaces.d.ts +24 -31
  9. package/dist/dts/polyfills.d.ts +0 -1
  10. package/dist/dts/templating/binding-signal.d.ts +3 -1
  11. package/dist/dts/templating/binding-two-way.d.ts +3 -1
  12. package/dist/dts/templating/binding.d.ts +16 -5
  13. package/dist/dts/templating/compiler.d.ts +11 -13
  14. package/dist/dts/templating/dangerous-html.d.ts +18 -0
  15. package/dist/dts/templating/html-directive.d.ts +50 -119
  16. package/dist/dts/templating/node-observation.d.ts +11 -1
  17. package/dist/dts/templating/ref.d.ts +4 -0
  18. package/dist/dts/templating/render.d.ts +30 -6
  19. package/dist/dts/templating/repeat.d.ts +1 -5
  20. package/dist/dts/templating/template.d.ts +39 -13
  21. package/dist/dts/templating/view.d.ts +2 -2
  22. package/dist/dts/utilities.d.ts +39 -0
  23. package/dist/esm/components/attributes.js +1 -1
  24. package/dist/esm/components/fast-definitions.js +2 -2
  25. package/dist/esm/debug.js +4 -1
  26. package/dist/esm/dom-policy.js +337 -0
  27. package/dist/esm/dom.js +117 -0
  28. package/dist/esm/index.js +2 -1
  29. package/dist/esm/index.rollup.debug.js +3 -1
  30. package/dist/esm/index.rollup.js +3 -1
  31. package/dist/esm/interfaces.js +45 -0
  32. package/dist/esm/observation/observable.js +3 -3
  33. package/dist/esm/observation/update-queue.js +2 -2
  34. package/dist/esm/platform.js +1 -1
  35. package/dist/esm/polyfills.js +3 -7
  36. package/dist/esm/templating/binding-signal.js +3 -2
  37. package/dist/esm/templating/binding-two-way.js +3 -2
  38. package/dist/esm/templating/binding.js +31 -54
  39. package/dist/esm/templating/compiler.js +31 -38
  40. package/dist/esm/templating/dangerous-html.js +23 -0
  41. package/dist/esm/templating/html-directive.js +38 -135
  42. package/dist/esm/templating/node-observation.js +14 -8
  43. package/dist/esm/templating/ref.js +1 -1
  44. package/dist/esm/templating/render.js +17 -6
  45. package/dist/esm/templating/repeat.js +2 -6
  46. package/dist/esm/templating/template.js +81 -56
  47. package/dist/esm/testing/fixture.js +1 -1
  48. package/dist/esm/utilities.js +68 -0
  49. package/dist/fast-element.api.json +1088 -608
  50. package/dist/fast-element.d.ts +190 -147
  51. package/dist/fast-element.debug.js +756 -388
  52. package/dist/fast-element.debug.min.js +1 -1
  53. package/dist/fast-element.js +727 -362
  54. package/dist/fast-element.min.js +1 -1
  55. package/dist/fast-element.untrimmed.d.ts +190 -147
  56. package/docs/api-report.md +66 -56
  57. package/package.json +5 -1
  58. package/dist/dts/templating/dom.d.ts +0 -41
  59. 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,92 @@ Object.assign(FAST$1, {
90
62
  },
91
63
  });
92
64
 
93
- // ensure FAST global - duplicated in polyfills.ts and debug.ts
65
+ let kernelMode;
66
+ const kernelAttr = "fast-kernel";
67
+ try {
68
+ if (document.currentScript) {
69
+ kernelMode = document.currentScript.getAttribute(kernelAttr);
70
+ }
71
+ else {
72
+ const scripts = document.getElementsByTagName("script");
73
+ const currentScript = scripts[scripts.length - 1];
74
+ kernelMode = currentScript.getAttribute(kernelAttr);
75
+ }
76
+ }
77
+ catch (e) {
78
+ kernelMode = "isolate";
79
+ }
80
+ let KernelServiceId;
81
+ switch (kernelMode) {
82
+ case "share": // share the kernel across major versions
83
+ KernelServiceId = Object.freeze({
84
+ updateQueue: 1,
85
+ observable: 2,
86
+ contextEvent: 3,
87
+ elementRegistry: 4,
88
+ });
89
+ break;
90
+ case "share-v2": // only share the kernel with other v2 instances
91
+ KernelServiceId = Object.freeze({
92
+ updateQueue: 1.2,
93
+ observable: 2.2,
94
+ contextEvent: 3.2,
95
+ elementRegistry: 4.2,
96
+ });
97
+ break;
98
+ default:
99
+ // fully isolate the kernel from all other FAST instances
100
+ const postfix = `-${Math.random().toString(36).substring(2, 8)}`;
101
+ KernelServiceId = Object.freeze({
102
+ updateQueue: `1.2${postfix}`,
103
+ observable: `2.2${postfix}`,
104
+ contextEvent: `3.2${postfix}`,
105
+ elementRegistry: `4.2${postfix}`,
106
+ });
107
+ break;
108
+ }
109
+ /**
110
+ * @internal
111
+ */
112
+ const isFunction = (object) => typeof object === "function";
113
+ /**
114
+ * @internal
115
+ */
116
+ const isString = (object) => typeof object === "string";
117
+ /**
118
+ * @internal
119
+ */
120
+ const noop = () => void 0;
121
+
122
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
123
+ (function ensureGlobalThis() {
124
+ if (typeof globalThis !== "undefined") {
125
+ // We're running in a modern environment.
126
+ return;
127
+ }
128
+ // @ts-ignore
129
+ if (typeof global !== "undefined") {
130
+ // We're running in NodeJS
131
+ // @ts-ignore
132
+ global.globalThis = global;
133
+ }
134
+ else if (typeof self !== "undefined") {
135
+ self.globalThis = self;
136
+ }
137
+ else if (typeof window !== "undefined") {
138
+ // We're running in the browser's main thread.
139
+ window.globalThis = window;
140
+ }
141
+ else {
142
+ // Hopefully we never get here...
143
+ // Not all environments allow eval and Function. Use only as a last resort:
144
+ // eslint-disable-next-line no-new-func
145
+ const result = new Function("return this")();
146
+ result.globalThis = result;
147
+ }
148
+ })();
149
+
150
+ // ensure FAST global - duplicated debug.ts
94
151
  const propConfig = {
95
152
  configurable: false,
96
153
  enumerable: false,
@@ -178,24 +235,11 @@ function createMetadataLocator() {
178
235
  };
179
236
  }
180
237
 
181
- /**
182
- * @internal
183
- */
184
- const isFunction = (object) => typeof object === "function";
185
- /**
186
- * @internal
187
- */
188
- const isString = (object) => typeof object === "string";
189
- /**
190
- * @internal
191
- */
192
- const noop = () => void 0;
193
-
194
238
  /**
195
239
  * The default UpdateQueue.
196
240
  * @public
197
241
  */
198
- const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
242
+ const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
199
243
  const tasks = [];
200
244
  const pendingErrors = [];
201
245
  const rAF = globalThis.requestAnimationFrame;
@@ -257,6 +301,456 @@ const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
257
301
  });
258
302
  });
259
303
 
304
+ /**
305
+ * The type of HTML aspect to target.
306
+ * @public
307
+ */
308
+ const DOMAspect = Object.freeze({
309
+ /**
310
+ * Not aspected.
311
+ */
312
+ none: 0,
313
+ /**
314
+ * An attribute.
315
+ */
316
+ attribute: 1,
317
+ /**
318
+ * A boolean attribute.
319
+ */
320
+ booleanAttribute: 2,
321
+ /**
322
+ * A property.
323
+ */
324
+ property: 3,
325
+ /**
326
+ * Content
327
+ */
328
+ content: 4,
329
+ /**
330
+ * A token list.
331
+ */
332
+ tokenList: 5,
333
+ /**
334
+ * An event.
335
+ */
336
+ event: 6,
337
+ });
338
+ const createHTML$1 = html => html;
339
+ const fastTrustedType = globalThis.trustedTypes
340
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML: createHTML$1 })
341
+ : { createHTML: createHTML$1 };
342
+ let defaultPolicy = Object.freeze({
343
+ createHTML(value) {
344
+ return fastTrustedType.createHTML(value);
345
+ },
346
+ protect(tagName, aspect, aspectName, sink) {
347
+ return sink;
348
+ },
349
+ });
350
+ const fastPolicy = defaultPolicy;
351
+ /**
352
+ * Common DOM APIs.
353
+ * @public
354
+ */
355
+ const DOM = Object.freeze({
356
+ /**
357
+ * @deprecated
358
+ * Use Updates.enqueue().
359
+ */
360
+ queueUpdate: Updates.enqueue,
361
+ /**
362
+ * @deprecated
363
+ * Use Updates.next()
364
+ */
365
+ nextUpdate: Updates.next,
366
+ /**
367
+ * @deprecated
368
+ * Use Updates.process()
369
+ */
370
+ processUpdates: Updates.process,
371
+ /**
372
+ * Gets the dom policy used by the templating system.
373
+ */
374
+ get policy() {
375
+ return defaultPolicy;
376
+ },
377
+ /**
378
+ * Sets the dom policy used by the templating system.
379
+ * @param policy - The policy to set.
380
+ * @remarks
381
+ * This API can only be called once, for security reasons. It should be
382
+ * called by the application developer at the start of their program.
383
+ */
384
+ setPolicy(value) {
385
+ if (defaultPolicy !== fastPolicy) {
386
+ throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
387
+ }
388
+ defaultPolicy = value;
389
+ },
390
+ /**
391
+ * Sets an attribute value on an element.
392
+ * @param element - The element to set the attribute value on.
393
+ * @param attributeName - The attribute name to set.
394
+ * @param value - The value of the attribute to set.
395
+ * @remarks
396
+ * If the value is `null` or `undefined`, the attribute is removed, otherwise
397
+ * it is set to the provided value using the standard `setAttribute` API.
398
+ */
399
+ setAttribute(element, attributeName, value) {
400
+ value === null || value === undefined
401
+ ? element.removeAttribute(attributeName)
402
+ : element.setAttribute(attributeName, value);
403
+ },
404
+ /**
405
+ * Sets a boolean attribute value.
406
+ * @param element - The element to set the boolean attribute value on.
407
+ * @param attributeName - The attribute name to set.
408
+ * @param value - The value of the attribute to set.
409
+ * @remarks
410
+ * If the value is true, the attribute is added; otherwise it is removed.
411
+ */
412
+ setBooleanAttribute(element, attributeName, value) {
413
+ value
414
+ ? element.setAttribute(attributeName, "")
415
+ : element.removeAttribute(attributeName);
416
+ },
417
+ });
418
+
419
+ function safeURL(tagName, aspect, aspectName, sink) {
420
+ return (target, name, value, ...rest) => {
421
+ if (isString(value)) {
422
+ value = value.replace("javascript:", "");
423
+ }
424
+ sink(target, name, value, ...rest);
425
+ };
426
+ }
427
+ function block(tagName, aspect, aspectName, sink) {
428
+ throw new Error(`${aspectName} on ${tagName !== null && tagName !== void 0 ? tagName : "text"} is blocked by the current DOMPolicy.`);
429
+ }
430
+ const defaultDOMElementGuards = {
431
+ a: {
432
+ [DOMAspect.attribute]: {
433
+ href: safeURL,
434
+ },
435
+ [DOMAspect.property]: {
436
+ href: safeURL,
437
+ },
438
+ },
439
+ area: {
440
+ [DOMAspect.attribute]: {
441
+ href: safeURL,
442
+ },
443
+ [DOMAspect.property]: {
444
+ href: safeURL,
445
+ },
446
+ },
447
+ button: {
448
+ [DOMAspect.attribute]: {
449
+ formaction: safeURL,
450
+ },
451
+ [DOMAspect.property]: {
452
+ formAction: safeURL,
453
+ },
454
+ },
455
+ embed: {
456
+ [DOMAspect.attribute]: {
457
+ src: block,
458
+ },
459
+ [DOMAspect.property]: {
460
+ src: block,
461
+ },
462
+ },
463
+ form: {
464
+ [DOMAspect.attribute]: {
465
+ action: safeURL,
466
+ },
467
+ [DOMAspect.property]: {
468
+ action: safeURL,
469
+ },
470
+ },
471
+ frame: {
472
+ [DOMAspect.attribute]: {
473
+ src: safeURL,
474
+ },
475
+ [DOMAspect.property]: {
476
+ src: safeURL,
477
+ },
478
+ },
479
+ iframe: {
480
+ [DOMAspect.attribute]: {
481
+ src: safeURL,
482
+ },
483
+ [DOMAspect.property]: {
484
+ src: safeURL,
485
+ srcdoc: block,
486
+ },
487
+ },
488
+ input: {
489
+ [DOMAspect.attribute]: {
490
+ formaction: safeURL,
491
+ },
492
+ [DOMAspect.property]: {
493
+ formAction: safeURL,
494
+ },
495
+ },
496
+ link: {
497
+ [DOMAspect.attribute]: {
498
+ href: block,
499
+ },
500
+ [DOMAspect.property]: {
501
+ href: block,
502
+ },
503
+ },
504
+ object: {
505
+ [DOMAspect.attribute]: {
506
+ codebase: block,
507
+ data: block,
508
+ },
509
+ [DOMAspect.property]: {
510
+ codeBase: block,
511
+ data: block,
512
+ },
513
+ },
514
+ script: {
515
+ [DOMAspect.attribute]: {
516
+ src: block,
517
+ text: block,
518
+ },
519
+ [DOMAspect.property]: {
520
+ src: block,
521
+ text: block,
522
+ innerText: block,
523
+ textContent: block,
524
+ },
525
+ },
526
+ style: {
527
+ [DOMAspect.property]: {
528
+ innerText: block,
529
+ textContent: block,
530
+ },
531
+ },
532
+ };
533
+ const blockedEvents = {
534
+ onabort: block,
535
+ onauxclick: block,
536
+ onbeforeinput: block,
537
+ onbeforematch: block,
538
+ onblur: block,
539
+ oncancel: block,
540
+ oncanplay: block,
541
+ oncanplaythrough: block,
542
+ onchange: block,
543
+ onclick: block,
544
+ onclose: block,
545
+ oncontextlost: block,
546
+ oncontextmenu: block,
547
+ oncontextrestored: block,
548
+ oncopy: block,
549
+ oncuechange: block,
550
+ oncut: block,
551
+ ondblclick: block,
552
+ ondrag: block,
553
+ ondragend: block,
554
+ ondragenter: block,
555
+ ondragleave: block,
556
+ ondragover: block,
557
+ ondragstart: block,
558
+ ondrop: block,
559
+ ondurationchange: block,
560
+ onemptied: block,
561
+ onended: block,
562
+ onerror: block,
563
+ onfocus: block,
564
+ onformdata: block,
565
+ oninput: block,
566
+ oninvalid: block,
567
+ onkeydown: block,
568
+ onkeypress: block,
569
+ onkeyup: block,
570
+ onload: block,
571
+ onloadeddata: block,
572
+ onloadedmetadata: block,
573
+ onloadstart: block,
574
+ onmousedown: block,
575
+ onmouseenter: block,
576
+ onmouseleave: block,
577
+ onmousemove: block,
578
+ onmouseout: block,
579
+ onmouseover: block,
580
+ onmouseup: block,
581
+ onpaste: block,
582
+ onpause: block,
583
+ onplay: block,
584
+ onplaying: block,
585
+ onprogress: block,
586
+ onratechange: block,
587
+ onreset: block,
588
+ onresize: block,
589
+ onscroll: block,
590
+ onsecuritypolicyviolation: block,
591
+ onseeked: block,
592
+ onseeking: block,
593
+ onselect: block,
594
+ onslotchange: block,
595
+ onstalled: block,
596
+ onsubmit: block,
597
+ onsuspend: block,
598
+ ontimeupdate: block,
599
+ ontoggle: block,
600
+ onvolumechange: block,
601
+ onwaiting: block,
602
+ onwebkitanimationend: block,
603
+ onwebkitanimationiteration: block,
604
+ onwebkitanimationstart: block,
605
+ onwebkittransitionend: block,
606
+ onwheel: block,
607
+ };
608
+ const defaultDOMGuards = {
609
+ elements: defaultDOMElementGuards,
610
+ aspects: {
611
+ [DOMAspect.attribute]: Object.assign({}, blockedEvents),
612
+ [DOMAspect.property]: Object.assign({ innerHTML: block }, blockedEvents),
613
+ [DOMAspect.event]: Object.assign({}, blockedEvents),
614
+ },
615
+ };
616
+ function createDomSinkGuards(config, defaults) {
617
+ const result = {};
618
+ for (const name in defaults) {
619
+ const overrideValue = config[name];
620
+ const defaultValue = defaults[name];
621
+ switch (overrideValue) {
622
+ case null:
623
+ // remove the default
624
+ break;
625
+ case undefined:
626
+ // keep the default
627
+ result[name] = defaultValue;
628
+ break;
629
+ default:
630
+ // override the default
631
+ result[name] = overrideValue;
632
+ break;
633
+ }
634
+ }
635
+ // add any new sinks that were not overrides
636
+ for (const name in config) {
637
+ if (!(name in result)) {
638
+ result[name] = config[name];
639
+ }
640
+ }
641
+ return Object.freeze(result);
642
+ }
643
+ function createDOMAspectGuards(config, defaults) {
644
+ const result = {};
645
+ for (const aspect in defaults) {
646
+ const overrideValue = config[aspect];
647
+ const defaultValue = defaults[aspect];
648
+ switch (overrideValue) {
649
+ case null:
650
+ // remove the default
651
+ break;
652
+ case undefined:
653
+ // keep the default
654
+ result[aspect] = createDomSinkGuards(defaultValue, {});
655
+ break;
656
+ default:
657
+ // override the default
658
+ result[aspect] = createDomSinkGuards(overrideValue, defaultValue);
659
+ break;
660
+ }
661
+ }
662
+ // add any new aspect guards that were not overrides
663
+ for (const aspect in config) {
664
+ if (!(aspect in result)) {
665
+ result[aspect] = createDomSinkGuards(config[aspect], {});
666
+ }
667
+ }
668
+ return Object.freeze(result);
669
+ }
670
+ function createElementGuards(config, defaults) {
671
+ const result = {};
672
+ for (const tag in defaults) {
673
+ const overrideValue = config[tag];
674
+ const defaultValue = defaults[tag];
675
+ switch (overrideValue) {
676
+ case null:
677
+ // remove the default
678
+ break;
679
+ case undefined:
680
+ // keep the default
681
+ result[tag] = createDOMAspectGuards(overrideValue, {});
682
+ break;
683
+ default:
684
+ // override the default aspects
685
+ result[tag] = createDOMAspectGuards(overrideValue, defaultValue);
686
+ break;
687
+ }
688
+ }
689
+ // Add any new element guards that were not overrides
690
+ for (const tag in config) {
691
+ if (!(tag in result)) {
692
+ result[tag] = createDOMAspectGuards(config[tag], {});
693
+ }
694
+ }
695
+ return Object.freeze(result);
696
+ }
697
+ function createDOMGuards(config, defaults) {
698
+ return Object.freeze({
699
+ elements: config.elements
700
+ ? createElementGuards(config.elements, defaults.elements)
701
+ : defaults.elements,
702
+ aspects: config.aspects
703
+ ? createDOMAspectGuards(config.aspects, defaults.aspects)
704
+ : defaults.aspects,
705
+ });
706
+ }
707
+ function createTrustedType() {
708
+ const createHTML = html => html;
709
+ return globalThis.trustedTypes
710
+ ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
711
+ : { createHTML };
712
+ }
713
+ function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
714
+ const sinkGuards = aspectGuards[aspect];
715
+ if (sinkGuards) {
716
+ const guard = sinkGuards[aspectName];
717
+ if (guard) {
718
+ return guard(tagName, aspect, aspectName, sink);
719
+ }
720
+ }
721
+ }
722
+ const DOMPolicy = Object.freeze({
723
+ /**
724
+ * Creates a new DOM Policy object.
725
+ * @param options The options to use in creating the policy.
726
+ * @returns The newly created DOMPolicy.
727
+ */
728
+ create(options = {}) {
729
+ var _a, _b;
730
+ const trustedType = (_a = options.trustedType) !== null && _a !== void 0 ? _a : createTrustedType();
731
+ const guards = createDOMGuards((_b = options.guards) !== null && _b !== void 0 ? _b : {}, defaultDOMGuards);
732
+ return Object.freeze({
733
+ createHTML(value) {
734
+ return trustedType.createHTML(value);
735
+ },
736
+ protect(tagName, aspect, aspectName, sink) {
737
+ var _a;
738
+ // Check for element-specific guards.
739
+ const key = (tagName !== null && tagName !== void 0 ? tagName : "").toLowerCase();
740
+ const elementGuards = guards.elements[key];
741
+ if (elementGuards) {
742
+ const guard = tryGuard(elementGuards, tagName, aspect, aspectName, sink);
743
+ if (guard) {
744
+ return guard;
745
+ }
746
+ }
747
+ // Check for guards applicable to all nodes.
748
+ return ((_a = tryGuard(guards.aspects, tagName, aspect, aspectName, sink)) !== null && _a !== void 0 ? _a : sink);
749
+ },
750
+ });
751
+ },
752
+ });
753
+
260
754
  /**
261
755
  * An implementation of {@link Notifier} that efficiently keeps track of
262
756
  * subscribers interested in a specific change notification on an
@@ -441,7 +935,7 @@ const SourceLifetime = Object.freeze({
441
935
  * Common Observable APIs.
442
936
  * @public
443
937
  */
444
- const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
938
+ const Observable = FAST.getById(KernelServiceId.observable, () => {
445
939
  const queueUpdate = Updates.enqueue;
446
940
  const volatileRegex = /(:|&&|\|\||if)/;
447
941
  const notifierLookup = new WeakMap();
@@ -712,7 +1206,7 @@ function volatile(target, name, descriptor) {
712
1206
  },
713
1207
  });
714
1208
  }
715
- const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
1209
+ const contextEvent = FAST.getById(KernelServiceId.contextEvent, () => {
716
1210
  let current = null;
717
1211
  return {
718
1212
  get() {
@@ -1543,55 +2037,6 @@ css.partial = (strings, ...values) => {
1543
2037
  */
1544
2038
  const cssPartial = css.partial;
1545
2039
 
1546
- /**
1547
- * Common DOM APIs.
1548
- * @public
1549
- */
1550
- const DOM = Object.freeze({
1551
- /**
1552
- * @deprecated
1553
- * Use Updates.enqueue().
1554
- */
1555
- queueUpdate: Updates.enqueue,
1556
- /**
1557
- * @deprecated
1558
- * Use Updates.next()
1559
- */
1560
- nextUpdate: Updates.next,
1561
- /**
1562
- * @deprecated
1563
- * Use Updates.process()
1564
- */
1565
- processUpdates: Updates.process,
1566
- /**
1567
- * Sets an attribute value on an element.
1568
- * @param element - The element to set the attribute value on.
1569
- * @param attributeName - The attribute name to set.
1570
- * @param value - The value of the attribute to set.
1571
- * @remarks
1572
- * If the value is `null` or `undefined`, the attribute is removed, otherwise
1573
- * it is set to the provided value using the standard `setAttribute` API.
1574
- */
1575
- setAttribute(element, attributeName, value) {
1576
- value === null || value === undefined
1577
- ? element.removeAttribute(attributeName)
1578
- : element.setAttribute(attributeName, value);
1579
- },
1580
- /**
1581
- * Sets a boolean attribute value.
1582
- * @param element - The element to set the boolean attribute value on.
1583
- * @param attributeName - The attribute name to set.
1584
- * @param value - The value of the attribute to set.
1585
- * @remarks
1586
- * If the value is true, the attribute is added; otherwise it is removed.
1587
- */
1588
- setBooleanAttribute(element, attributeName, value) {
1589
- value
1590
- ? element.setAttribute(attributeName, "")
1591
- : element.removeAttribute(attributeName);
1592
- },
1593
- });
1594
-
1595
2040
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1596
2041
  const interpolationStart = `${marker}{`;
1597
2042
  const interpolationEnd = `}${marker}`;
@@ -1668,67 +2113,6 @@ const Parser = Object.freeze({
1668
2113
  },
1669
2114
  });
1670
2115
 
1671
- /**
1672
- * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1673
- * control ViewBehaviors.
1674
- * @public
1675
- */
1676
- const ViewBehaviorOrchestrator = Object.freeze({
1677
- /**
1678
- * Creates a ViewBehaviorOrchestrator.
1679
- * @param source - The source to to associate behaviors with.
1680
- * @returns A ViewBehaviorOrchestrator.
1681
- */
1682
- create(source) {
1683
- const behaviors = [];
1684
- const targets = {};
1685
- let unbindables = null;
1686
- let isConnected = false;
1687
- return {
1688
- source,
1689
- context: ExecutionContext.default,
1690
- targets,
1691
- get isBound() {
1692
- return isConnected;
1693
- },
1694
- addBehaviorFactory(factory, target) {
1695
- const nodeId = factory.nodeId || (factory.nodeId = nextId());
1696
- factory.id || (factory.id = nextId());
1697
- this.addTarget(nodeId, target);
1698
- this.addBehavior(factory.createBehavior());
1699
- },
1700
- addTarget(nodeId, target) {
1701
- targets[nodeId] = target;
1702
- },
1703
- addBehavior(behavior) {
1704
- behaviors.push(behavior);
1705
- if (isConnected) {
1706
- behavior.bind(this);
1707
- }
1708
- },
1709
- onUnbind(unbindable) {
1710
- if (unbindables === null) {
1711
- unbindables = [];
1712
- }
1713
- unbindables.push(unbindable);
1714
- },
1715
- connectedCallback(controller) {
1716
- if (!isConnected) {
1717
- isConnected = true;
1718
- behaviors.forEach(x => x.bind(this));
1719
- }
1720
- },
1721
- disconnectedCallback(controller) {
1722
- if (isConnected) {
1723
- isConnected = false;
1724
- if (unbindables !== null) {
1725
- unbindables.forEach(x => x.unbind(this));
1726
- }
1727
- }
1728
- },
1729
- };
1730
- },
1731
- });
1732
2116
  const registry = createTypeRegistry();
1733
2117
  /**
1734
2118
  * Instructs the template engine to apply behavior to a node.
@@ -1756,67 +2140,6 @@ const HTMLDirective = Object.freeze({
1756
2140
  registry.register(options);
1757
2141
  return type;
1758
2142
  },
1759
- });
1760
- /**
1761
- * Decorator: Defines an HTMLDirective.
1762
- * @param options - Provides options that specify the directive's application.
1763
- * @public
1764
- */
1765
- function htmlDirective(options) {
1766
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
1767
- return function (type) {
1768
- HTMLDirective.define(type, options);
1769
- };
1770
- }
1771
- /**
1772
- * Captures a binding expression along with related information and capabilities.
1773
- *
1774
- * @public
1775
- */
1776
- class Binding {
1777
- /**
1778
- * Creates a binding.
1779
- * @param evaluate - Evaluates the binding.
1780
- * @param isVolatile - Indicates whether the binding is volatile.
1781
- */
1782
- constructor(evaluate, isVolatile = false) {
1783
- this.evaluate = evaluate;
1784
- this.isVolatile = isVolatile;
1785
- }
1786
- }
1787
- /**
1788
- * The type of HTML aspect to target.
1789
- * @public
1790
- */
1791
- const Aspect = Object.freeze({
1792
- /**
1793
- * Not aspected.
1794
- */
1795
- none: 0,
1796
- /**
1797
- * An attribute.
1798
- */
1799
- attribute: 1,
1800
- /**
1801
- * A boolean attribute.
1802
- */
1803
- booleanAttribute: 2,
1804
- /**
1805
- * A property.
1806
- */
1807
- property: 3,
1808
- /**
1809
- * Content
1810
- */
1811
- content: 4,
1812
- /**
1813
- * A token list.
1814
- */
1815
- tokenList: 5,
1816
- /**
1817
- * An event.
1818
- */
1819
- event: 6,
1820
2143
  /**
1821
2144
  *
1822
2145
  * @param directive - The directive to assign the aspect to.
@@ -1824,9 +2147,9 @@ const Aspect = Object.freeze({
1824
2147
  * @remarks
1825
2148
  * If a falsy value is provided, then the content aspect will be assigned.
1826
2149
  */
1827
- assign(directive, value) {
2150
+ assignAspect(directive, value) {
1828
2151
  if (!value) {
1829
- directive.aspectType = Aspect.content;
2152
+ directive.aspectType = DOMAspect.content;
1830
2153
  return;
1831
2154
  }
1832
2155
  directive.sourceAspect = value;
@@ -1835,24 +2158,53 @@ const Aspect = Object.freeze({
1835
2158
  directive.targetAspect = value.substring(1);
1836
2159
  directive.aspectType =
1837
2160
  directive.targetAspect === "classList"
1838
- ? Aspect.tokenList
1839
- : Aspect.property;
2161
+ ? DOMAspect.tokenList
2162
+ : DOMAspect.property;
1840
2163
  break;
1841
2164
  case "?":
1842
2165
  directive.targetAspect = value.substring(1);
1843
- directive.aspectType = Aspect.booleanAttribute;
2166
+ directive.aspectType = DOMAspect.booleanAttribute;
1844
2167
  break;
1845
2168
  case "@":
1846
2169
  directive.targetAspect = value.substring(1);
1847
- directive.aspectType = Aspect.event;
2170
+ directive.aspectType = DOMAspect.event;
1848
2171
  break;
1849
2172
  default:
1850
2173
  directive.targetAspect = value;
1851
- directive.aspectType = Aspect.attribute;
2174
+ directive.aspectType = DOMAspect.attribute;
1852
2175
  break;
1853
2176
  }
1854
2177
  },
1855
2178
  });
2179
+ /**
2180
+ * Decorator: Defines an HTMLDirective.
2181
+ * @param options - Provides options that specify the directive's application.
2182
+ * @public
2183
+ */
2184
+ function htmlDirective(options) {
2185
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
2186
+ return function (type) {
2187
+ HTMLDirective.define(type, options);
2188
+ };
2189
+ }
2190
+ /**
2191
+ * Captures a binding expression along with related information and capabilities.
2192
+ *
2193
+ * @public
2194
+ */
2195
+ class Binding {
2196
+ /**
2197
+ * Creates a binding.
2198
+ * @param evaluate - Evaluates the binding.
2199
+ * @param policy - The security policy to associate with this binding.
2200
+ * @param isVolatile - Indicates whether the binding is volatile.
2201
+ */
2202
+ constructor(evaluate, policy, isVolatile = false) {
2203
+ this.evaluate = evaluate;
2204
+ this.policy = policy;
2205
+ this.isVolatile = isVolatile;
2206
+ }
2207
+ }
1856
2208
  /**
1857
2209
  * A base class used for attribute directives that don't need internal state.
1858
2210
  * @public
@@ -1864,10 +2216,6 @@ class StatelessAttachedAttributeDirective {
1864
2216
  */
1865
2217
  constructor(options) {
1866
2218
  this.options = options;
1867
- /**
1868
- * The unique id of the factory.
1869
- */
1870
- this.id = nextId();
1871
2219
  /**
1872
2220
  * Opts out of JSON stringification.
1873
2221
  * @internal
@@ -1892,15 +2240,6 @@ class StatelessAttachedAttributeDirective {
1892
2240
  }
1893
2241
  }
1894
2242
 
1895
- const createInnerHTMLBinding = globalThis.TrustedHTML
1896
- ? (binding) => (s, c) => {
1897
- const value = binding(s, c);
1898
- if (value instanceof TrustedHTML) {
1899
- return value;
1900
- }
1901
- throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1902
- }
1903
- : (binding) => binding;
1904
2243
  class OnChangeBinding extends Binding {
1905
2244
  createObserver(_, subscriber) {
1906
2245
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
@@ -2013,8 +2352,14 @@ function updateTokenList(target, aspect, value) {
2013
2352
  }
2014
2353
  }
2015
2354
  }
2016
- const setProperty = (t, a, v) => (t[a] = v);
2017
- const eventTarget = () => void 0;
2355
+ const sinkLookup = {
2356
+ [DOMAspect.attribute]: DOM.setAttribute,
2357
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
2358
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
2359
+ [DOMAspect.content]: updateContent,
2360
+ [DOMAspect.tokenList]: updateTokenList,
2361
+ [DOMAspect.event]: () => void 0,
2362
+ };
2018
2363
  /**
2019
2364
  * A directive that applies bindings.
2020
2365
  * @public
@@ -2027,15 +2372,10 @@ class HTMLBindingDirective {
2027
2372
  constructor(dataBinding) {
2028
2373
  this.dataBinding = dataBinding;
2029
2374
  this.updateTarget = null;
2030
- /**
2031
- * The unique id of the factory.
2032
- */
2033
- this.id = nextId();
2034
2375
  /**
2035
2376
  * The type of aspect to target.
2036
2377
  */
2037
- this.aspectType = Aspect.content;
2038
- this.data = `${this.id}-d`;
2378
+ this.aspectType = DOMAspect.content;
2039
2379
  }
2040
2380
  /**
2041
2381
  * Creates HTML to be used within a template.
@@ -2048,45 +2388,28 @@ class HTMLBindingDirective {
2048
2388
  * Creates a behavior.
2049
2389
  */
2050
2390
  createBehavior() {
2391
+ var _a;
2051
2392
  if (this.updateTarget === null) {
2052
- if (this.targetAspect === "innerHTML") {
2053
- this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2054
- }
2055
- switch (this.aspectType) {
2056
- case 1:
2057
- this.updateTarget = DOM.setAttribute;
2058
- break;
2059
- case 2:
2060
- this.updateTarget = DOM.setBooleanAttribute;
2061
- break;
2062
- case 3:
2063
- this.updateTarget = setProperty;
2064
- break;
2065
- case 4:
2066
- this.updateTarget = updateContent;
2067
- break;
2068
- case 5:
2069
- this.updateTarget = updateTokenList;
2070
- break;
2071
- case 6:
2072
- this.updateTarget = eventTarget;
2073
- break;
2074
- default:
2075
- throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2393
+ const sink = sinkLookup[this.aspectType];
2394
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
2395
+ if (!sink) {
2396
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2076
2397
  }
2398
+ this.data = `${this.id}-d`;
2399
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
2077
2400
  }
2078
2401
  return this;
2079
2402
  }
2080
2403
  /** @internal */
2081
2404
  bind(controller) {
2082
2405
  var _a;
2083
- const target = controller.targets[this.nodeId];
2084
- switch (this.updateTarget) {
2085
- case eventTarget:
2406
+ const target = controller.targets[this.targetNodeId];
2407
+ switch (this.aspectType) {
2408
+ case DOMAspect.event:
2086
2409
  target[this.data] = controller;
2087
2410
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2088
2411
  break;
2089
- case updateContent:
2412
+ case DOMAspect.content:
2090
2413
  controller.onUnbind(this);
2091
2414
  // intentional fall through
2092
2415
  default:
@@ -2099,7 +2422,7 @@ class HTMLBindingDirective {
2099
2422
  }
2100
2423
  /** @internal */
2101
2424
  unbind(controller) {
2102
- const target = controller.targets[this.nodeId];
2425
+ const target = controller.targets[this.targetNodeId];
2103
2426
  const view = target.$fastView;
2104
2427
  if (view !== void 0 && view.isComposed) {
2105
2428
  view.unbind();
@@ -2129,21 +2452,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2129
2452
  /**
2130
2453
  * Creates an standard binding.
2131
2454
  * @param expression - The binding to refresh when changed.
2455
+ * @param policy - The security policy to associate with th binding.
2132
2456
  * @param isVolatile - Indicates whether the binding is volatile or not.
2133
2457
  * @returns A binding configuration.
2134
2458
  * @public
2135
2459
  */
2136
- function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
2137
- return new OnChangeBinding(expression, isVolatile);
2460
+ function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
2461
+ return new OnChangeBinding(expression, policy, isVolatile);
2138
2462
  }
2139
2463
  /**
2140
2464
  * Creates a one time binding
2141
2465
  * @param expression - The binding to refresh when signaled.
2466
+ * @param policy - The security policy to associate with th binding.
2142
2467
  * @returns A binding configuration.
2143
2468
  * @public
2144
2469
  */
2145
- function oneTime(expression) {
2146
- return new OneTimeBinding(expression);
2470
+ function oneTime(expression, policy) {
2471
+ return new OneTimeBinding(expression, policy);
2147
2472
  }
2148
2473
  /**
2149
2474
  * Creates an event listener binding.
@@ -2153,7 +2478,7 @@ function oneTime(expression) {
2153
2478
  * @public
2154
2479
  */
2155
2480
  function listener(expression, options) {
2156
- const config = new OnChangeBinding(expression, false);
2481
+ const config = new OnChangeBinding(expression);
2157
2482
  config.options = options;
2158
2483
  return config;
2159
2484
  }
@@ -2434,20 +2759,25 @@ const warningHost = new Proxy(document.createElement("div"), {
2434
2759
  },
2435
2760
  });
2436
2761
  class CompilationContext {
2437
- constructor(fragment, directives) {
2762
+ constructor(fragment, directives, policy) {
2438
2763
  this.fragment = fragment;
2439
2764
  this.directives = directives;
2765
+ this.policy = policy;
2440
2766
  this.proto = null;
2441
2767
  this.nodeIds = new Set();
2442
2768
  this.descriptors = {};
2443
2769
  this.factories = [];
2444
2770
  }
2445
- addFactory(factory, parentId, nodeId, targetIndex) {
2771
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
2772
+ var _a, _b;
2446
2773
  if (!this.nodeIds.has(nodeId)) {
2447
2774
  this.nodeIds.add(nodeId);
2448
2775
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
2449
2776
  }
2450
- factory.nodeId = nodeId;
2777
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
2778
+ factory.targetNodeId = nodeId;
2779
+ factory.targetTagName = tagName;
2780
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
2451
2781
  this.factories.push(factory);
2452
2782
  }
2453
2783
  freeze() {
@@ -2500,19 +2830,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2500
2830
  let result = null;
2501
2831
  if (parseResult === null) {
2502
2832
  if (includeBasicValues) {
2503
- result = new HTMLBindingDirective(oneTime(() => attrValue));
2504
- Aspect.assign(result, attr.name);
2833
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
2834
+ HTMLDirective.assignAspect(result, attr.name);
2505
2835
  }
2506
2836
  }
2507
2837
  else {
2508
2838
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2509
- result = Compiler.aggregate(parseResult);
2839
+ result = Compiler.aggregate(parseResult, context.policy);
2510
2840
  }
2511
2841
  if (result !== null) {
2512
2842
  node.removeAttributeNode(attr);
2513
2843
  i--;
2514
2844
  ii--;
2515
- context.addFactory(result, parentId, nodeId, nodeIndex);
2845
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
2516
2846
  }
2517
2847
  }
2518
2848
  }
@@ -2537,8 +2867,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2537
2867
  }
2538
2868
  else {
2539
2869
  currentNode.textContent = " ";
2540
- Aspect.assign(currentPart);
2541
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2870
+ HTMLDirective.assignAspect(currentPart);
2871
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
2542
2872
  }
2543
2873
  lastNode = currentNode;
2544
2874
  }
@@ -2570,7 +2900,7 @@ function compileNode(context, parentId, node, nodeIndex) {
2570
2900
  if (parts !== null) {
2571
2901
  context.addFactory(
2572
2902
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2573
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
2903
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2574
2904
  }
2575
2905
  break;
2576
2906
  }
@@ -2584,45 +2914,28 @@ function isMarker(node, directives) {
2584
2914
  Parser.parse(node.data, directives) !== null);
2585
2915
  }
2586
2916
  const templateTag = "TEMPLATE";
2587
- const policyOptions = { createHTML: html => html };
2588
- let htmlPolicy = globalThis.trustedTypes
2589
- ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
2590
- : policyOptions;
2591
- const fastHTMLPolicy = htmlPolicy;
2592
2917
  /**
2593
2918
  * Common APIs related to compilation.
2594
2919
  * @public
2595
2920
  */
2596
2921
  const Compiler = {
2597
- /**
2598
- * Sets the HTML trusted types policy used by the compiler.
2599
- * @param policy - The policy to set for HTML.
2600
- * @remarks
2601
- * This API can only be called once, for security reasons. It should be
2602
- * called by the application developer at the start of their program.
2603
- */
2604
- setHTMLPolicy(policy) {
2605
- if (htmlPolicy !== fastHTMLPolicy) {
2606
- throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
2607
- }
2608
- htmlPolicy = policy;
2609
- },
2610
2922
  /**
2611
2923
  * Compiles a template and associated directives into a compilation
2612
2924
  * result which can be used to create views.
2613
2925
  * @param html - The html string or template element to compile.
2614
- * @param directives - The directives referenced by the template.
2926
+ * @param factories - The behavior factories referenced by the template.
2927
+ * @param policy - The security policy to compile the html with.
2615
2928
  * @remarks
2616
2929
  * The template that is provided for compilation is altered in-place
2617
2930
  * and cannot be compiled again. If the original template must be preserved,
2618
2931
  * it is recommended that you clone the original and pass the clone to this API.
2619
2932
  * @public
2620
2933
  */
2621
- compile(html, directives) {
2934
+ compile(html, factories, policy = DOM.policy) {
2622
2935
  let template;
2623
2936
  if (isString(html)) {
2624
2937
  template = document.createElement(templateTag);
2625
- template.innerHTML = htmlPolicy.createHTML(html);
2938
+ template.innerHTML = policy.createHTML(html);
2626
2939
  const fec = template.content.firstElementChild;
2627
2940
  if (fec !== null && fec.tagName === templateTag) {
2628
2941
  template = fec;
@@ -2633,18 +2946,18 @@ const Compiler = {
2633
2946
  }
2634
2947
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
2635
2948
  const fragment = document.adoptNode(template.content);
2636
- const context = new CompilationContext(fragment, directives);
2949
+ const context = new CompilationContext(fragment, factories, policy);
2637
2950
  compileAttributes(context, "", template, /* host */ "h", 0, true);
2638
2951
  if (
2639
2952
  // If the first node in a fragment is a marker, that means it's an unstable first node,
2640
2953
  // because something like a when, repeat, etc. could add nodes before the marker.
2641
2954
  // To mitigate this, we insert a stable first node. However, if we insert a node,
2642
2955
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
2643
- isMarker(fragment.firstChild, directives) ||
2956
+ isMarker(fragment.firstChild, factories) ||
2644
2957
  // Or if there is only one node and a directive, it means the template's content
2645
2958
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
2646
2959
  // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
2647
- (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
2960
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
2648
2961
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
2649
2962
  }
2650
2963
  compileChildren(context, fragment, /* root */ "r");
@@ -2663,15 +2976,17 @@ const Compiler = {
2663
2976
  * Aggregates an array of strings and directives into a single directive.
2664
2977
  * @param parts - A heterogeneous array of static strings interspersed with
2665
2978
  * directives.
2979
+ * @param policy - The security policy to use with the aggregated bindings.
2666
2980
  * @returns A single inline directive that aggregates the behavior of all the parts.
2667
2981
  */
2668
- aggregate(parts) {
2982
+ aggregate(parts, policy = DOM.policy) {
2669
2983
  if (parts.length === 1) {
2670
2984
  return parts[0];
2671
2985
  }
2672
2986
  let sourceAspect;
2673
2987
  let binding;
2674
2988
  let isVolatile = false;
2989
+ let bindingPolicy = void 0;
2675
2990
  const partCount = parts.length;
2676
2991
  const finalParts = parts.map((x) => {
2677
2992
  if (isString(x)) {
@@ -2680,6 +2995,7 @@ const Compiler = {
2680
2995
  sourceAspect = x.sourceAspect || sourceAspect;
2681
2996
  binding = x.dataBinding || binding;
2682
2997
  isVolatile = isVolatile || x.dataBinding.isVolatile;
2998
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
2683
2999
  return x.dataBinding.evaluate;
2684
3000
  });
2685
3001
  const expression = (scope, context) => {
@@ -2691,12 +3007,26 @@ const Compiler = {
2691
3007
  };
2692
3008
  binding.evaluate = expression;
2693
3009
  binding.isVolatile = isVolatile;
3010
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
2694
3011
  const directive = new HTMLBindingDirective(binding);
2695
- Aspect.assign(directive, sourceAspect);
3012
+ HTMLDirective.assignAspect(directive, sourceAspect);
2696
3013
  return directive;
2697
3014
  },
2698
3015
  };
2699
3016
 
3017
+ // Much thanks to LitHTML for working this out!
3018
+ const lastAttributeNameRegex =
3019
+ /* eslint-disable-next-line no-control-regex */
3020
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
3021
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
3022
+ if (definition.aspected) {
3023
+ const match = lastAttributeNameRegex.exec(prevString);
3024
+ if (match !== null) {
3025
+ HTMLDirective.assignAspect(value, match[2]);
3026
+ }
3027
+ }
3028
+ return value.createHTML(add);
3029
+ }
2700
3030
  /**
2701
3031
  * A template capable of creating HTMLView instances or rendering directly to DOM.
2702
3032
  * @public
@@ -2706,8 +3036,10 @@ class ViewTemplate {
2706
3036
  * Creates an instance of ViewTemplate.
2707
3037
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
2708
3038
  * @param factories - The directives that will be connected to placeholders in the html.
3039
+ * @param policy - The security policy to use when compiling this template.
2709
3040
  */
2710
- constructor(html, factories) {
3041
+ constructor(html, factories = {}, policy) {
3042
+ this.policy = policy;
2711
3043
  this.result = null;
2712
3044
  /**
2713
3045
  * Opts out of JSON stringification.
@@ -2723,10 +3055,28 @@ class ViewTemplate {
2723
3055
  */
2724
3056
  create(hostBindingTarget) {
2725
3057
  if (this.result === null) {
2726
- this.result = Compiler.compile(this.html, this.factories);
3058
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
2727
3059
  }
2728
3060
  return this.result.createView(hostBindingTarget);
2729
3061
  }
3062
+ /**
3063
+ * Sets the DOMPolicy for this template.
3064
+ * @param policy - The policy to associated with this template.
3065
+ * @returns The modified template instance.
3066
+ * @remarks
3067
+ * The DOMPolicy can only be set once for a template and cannot be
3068
+ * set after the template is compiled.
3069
+ */
3070
+ withPolicy(policy) {
3071
+ if (this.result) {
3072
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
3073
+ }
3074
+ if (this.policy) {
3075
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
3076
+ }
3077
+ this.policy = policy;
3078
+ return this;
3079
+ }
2730
3080
  /**
2731
3081
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
2732
3082
  * @param source - The data source to bind the template to.
@@ -2740,17 +3090,47 @@ class ViewTemplate {
2740
3090
  view.appendTo(host);
2741
3091
  return view;
2742
3092
  }
2743
- }
2744
- // Much thanks to LitHTML for working this out!
2745
- const lastAttributeNameRegex =
2746
- /* eslint-disable-next-line no-control-regex */
2747
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2748
- function createAspectedHTML(value, prevString, add) {
2749
- const match = lastAttributeNameRegex.exec(prevString);
2750
- if (match !== null) {
2751
- Aspect.assign(value, match[2]);
3093
+ /**
3094
+ * Creates a template based on a set of static strings and dynamic values.
3095
+ * @param strings - The static strings to create the template with.
3096
+ * @param values - The dynamic values to create the template with.
3097
+ * @param policy - The DOMPolicy to associated with the template.
3098
+ * @returns A ViewTemplate.
3099
+ * @remarks
3100
+ * This API should not be used directly under normal circumstances because constructing
3101
+ * a template in this way, if not done properly, can open up the application to XSS
3102
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
3103
+ * and also be sure to manually sanitize all static strings particularly if they can
3104
+ * come from user input.
3105
+ */
3106
+ static create(strings, values, policy) {
3107
+ let html = "";
3108
+ const factories = Object.create(null);
3109
+ const add = (factory) => {
3110
+ var _a;
3111
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
3112
+ factories[id] = factory;
3113
+ return id;
3114
+ };
3115
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
3116
+ const currentString = strings[i];
3117
+ let currentValue = values[i];
3118
+ let definition;
3119
+ html += currentString;
3120
+ if (isFunction(currentValue)) {
3121
+ currentValue = new HTMLBindingDirective(bind(currentValue));
3122
+ }
3123
+ else if (currentValue instanceof Binding) {
3124
+ currentValue = new HTMLBindingDirective(currentValue);
3125
+ }
3126
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
3127
+ const staticValue = currentValue;
3128
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
3129
+ }
3130
+ html += createHTML(currentValue, currentString, add, definition);
3131
+ }
3132
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
2752
3133
  }
2753
- return value.createHTML(add);
2754
3134
  }
2755
3135
  /**
2756
3136
  * Transforms a template literal string into a ViewTemplate.
@@ -2762,49 +3142,10 @@ function createAspectedHTML(value, prevString, add) {
2762
3142
  * @public
2763
3143
  */
2764
3144
  function html(strings, ...values) {
2765
- let html = "";
2766
- const factories = Object.create(null);
2767
- const add = (factory) => {
2768
- var _a;
2769
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
2770
- factories[id] = factory;
2771
- return id;
2772
- };
2773
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
2774
- const currentString = strings[i];
2775
- const currentValue = values[i];
2776
- let definition;
2777
- html += currentString;
2778
- if (isFunction(currentValue)) {
2779
- html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2780
- }
2781
- else if (isString(currentValue)) {
2782
- const match = lastAttributeNameRegex.exec(currentString);
2783
- if (match !== null) {
2784
- const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2785
- Aspect.assign(directive, match[2]);
2786
- html += directive.createHTML(add);
2787
- }
2788
- else {
2789
- html += currentValue;
2790
- }
2791
- }
2792
- else if (currentValue instanceof Binding) {
2793
- html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2794
- }
2795
- else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2796
- html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2797
- }
2798
- else {
2799
- if (definition.aspected) {
2800
- html += createAspectedHTML(currentValue, currentString, add);
2801
- }
2802
- else {
2803
- html += currentValue.createHTML(add);
2804
- }
2805
- }
3145
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
3146
+ return ViewTemplate.create(strings, values);
2806
3147
  }
2807
- return new ViewTemplate(html + strings[strings.length - 1], factories);
3148
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
2808
3149
  }
2809
3150
 
2810
3151
  /**
@@ -2817,7 +3158,7 @@ class RefDirective extends StatelessAttachedAttributeDirective {
2817
3158
  * @param controller - The view controller that manages the lifecycle of this behavior.
2818
3159
  */
2819
3160
  bind(controller) {
2820
- controller.source[this.options] = controller.targets[this.nodeId];
3161
+ controller.source[this.options] = controller.targets[this.targetNodeId];
2821
3162
  }
2822
3163
  }
2823
3164
  HTMLDirective.define(RefDirective);
@@ -2891,7 +3232,7 @@ class RepeatBehavior {
2891
3232
  * @param controller - The view controller that manages the lifecycle of this behavior.
2892
3233
  */
2893
3234
  bind(controller) {
2894
- this.location = controller.targets[this.directive.nodeId];
3235
+ this.location = controller.targets[this.directive.targetNodeId];
2895
3236
  this.controller = controller;
2896
3237
  this.items = this.itemsBindingObserver.bind(controller);
2897
3238
  this.template = this.templateBindingObserver.bind(controller);
@@ -3071,10 +3412,6 @@ class RepeatDirective {
3071
3412
  this.dataBinding = dataBinding;
3072
3413
  this.templateBinding = templateBinding;
3073
3414
  this.options = options;
3074
- /**
3075
- * The unique id of the factory.
3076
- */
3077
- this.id = nextId();
3078
3415
  ArrayObserver.enable();
3079
3416
  }
3080
3417
  /**
@@ -3123,9 +3460,15 @@ const elements = (selector) => selector
3123
3460
  * Internally used by the SlottedDirective and the ChildrenDirective.
3124
3461
  */
3125
3462
  class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3126
- constructor() {
3127
- super(...arguments);
3128
- this.controllerProperty = `${this.id}-c`;
3463
+ /**
3464
+ * The unique id of the factory.
3465
+ */
3466
+ get id() {
3467
+ return this._id;
3468
+ }
3469
+ set id(value) {
3470
+ this._id = value;
3471
+ this._controllerProperty = `${value}-c`;
3129
3472
  }
3130
3473
  /**
3131
3474
  * Bind this behavior to the source.
@@ -3134,8 +3477,8 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3134
3477
  * @param targets - The targets that behaviors in a view can attach to.
3135
3478
  */
3136
3479
  bind(controller) {
3137
- const target = controller.targets[this.nodeId];
3138
- target[this.controllerProperty] = controller;
3480
+ const target = controller.targets[this.targetNodeId];
3481
+ target[this._controllerProperty] = controller;
3139
3482
  this.updateTarget(controller.source, this.computeNodes(target));
3140
3483
  this.observe(target);
3141
3484
  controller.onUnbind(this);
@@ -3147,10 +3490,10 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3147
3490
  * @param targets - The targets that behaviors in a view can attach to.
3148
3491
  */
3149
3492
  unbind(controller) {
3150
- const target = controller.targets[this.nodeId];
3493
+ const target = controller.targets[this.targetNodeId];
3151
3494
  this.updateTarget(controller.source, emptyArray);
3152
3495
  this.disconnect(target);
3153
- target[this.controllerProperty] = null;
3496
+ target[this._controllerProperty] = null;
3154
3497
  }
3155
3498
  /**
3156
3499
  * Gets the data source for the target.
@@ -3158,7 +3501,7 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3158
3501
  * @returns The source.
3159
3502
  */
3160
3503
  getSource(target) {
3161
- return target[this.controllerProperty].source;
3504
+ return target[this._controllerProperty].source;
3162
3505
  }
3163
3506
  /**
3164
3507
  * Updates the source property with the computed nodes.
@@ -3299,6 +3642,29 @@ function children(propertyOrOptions) {
3299
3642
  return new ChildrenDirective(propertyOrOptions);
3300
3643
  }
3301
3644
 
3645
+ /**
3646
+ * A directive capable of injecting static HTML platform runtime protection.
3647
+ * @public
3648
+ */
3649
+ class DangerousHTMLDirective {
3650
+ constructor(html) {
3651
+ this.html = html;
3652
+ }
3653
+ createHTML() {
3654
+ return this.html;
3655
+ }
3656
+ }
3657
+ HTMLDirective.define(DangerousHTMLDirective);
3658
+ /**
3659
+ * Injects static HTML without platform protection.
3660
+ * @param html - The html to injection.
3661
+ * @returns A DangerousHTMLDirective.
3662
+ * @public
3663
+ */
3664
+ function dangerousHTML(html) {
3665
+ return new DangerousHTMLDirective(html);
3666
+ }
3667
+
3302
3668
  const booleanMode = "boolean";
3303
3669
  const reflectMode = "reflect";
3304
3670
  /**
@@ -3497,7 +3863,7 @@ function attr(configOrTarget, prop) {
3497
3863
  const defaultShadowOptions = { mode: "open" };
3498
3864
  const defaultElementOptions = {};
3499
3865
  const fastElementBaseTypes = new Set();
3500
- const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
3866
+ const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
3501
3867
  /**
3502
3868
  * Defines metadata for a FASTElement.
3503
3869
  * @public
@@ -4159,4 +4525,6 @@ function customElement(nameOrDef) {
4159
4525
  };
4160
4526
  }
4161
4527
 
4162
- 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 };
4528
+ DOM.setPolicy(DOMPolicy.create());
4529
+
4530
+ 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 };