@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,24 +1,23 @@
1
- import { isFunction } from "../interfaces.js";
1
+ import { isFunction, noop } from "../interfaces.js";
2
2
  import { ExecutionContext, Observable, } from "../observation/observable.js";
3
3
  import { FAST } from "../platform.js";
4
- import { DOM } from "./dom.js";
5
- import { Aspect, Binding, HTMLDirective, } from "./html-directive.js";
6
- import { Markup, nextId } from "./markup.js";
7
- const createInnerHTMLBinding = globalThis.TrustedHTML
8
- ? (binding) => (s, c) => {
9
- const value = binding(s, c);
10
- if (value instanceof TrustedHTML) {
11
- return value;
12
- }
13
- throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
14
- }
15
- : (binding) => binding;
4
+ import { DOM, DOMAspect } from "../dom.js";
5
+ import { Binding, HTMLDirective, } from "./html-directive.js";
6
+ import { Markup } from "./markup.js";
16
7
  class OnChangeBinding extends Binding {
17
8
  createObserver(_, subscriber) {
18
9
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
19
10
  }
20
11
  }
21
12
  class OneTimeBinding extends Binding {
13
+ constructor() {
14
+ super(...arguments);
15
+ /**
16
+ * Opts out of JSON stringification.
17
+ * @internal
18
+ */
19
+ this.toJSON = noop;
20
+ }
22
21
  createObserver() {
23
22
  return this;
24
23
  }
@@ -117,8 +116,14 @@ function updateTokenList(target, aspect, value) {
117
116
  }
118
117
  }
119
118
  }
120
- const setProperty = (t, a, v) => (t[a] = v);
121
- const eventTarget = () => void 0;
119
+ const sinkLookup = {
120
+ [DOMAspect.attribute]: DOM.setAttribute,
121
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
122
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
123
+ [DOMAspect.content]: updateContent,
124
+ [DOMAspect.tokenList]: updateTokenList,
125
+ [DOMAspect.event]: () => void 0,
126
+ };
122
127
  /**
123
128
  * A directive that applies bindings.
124
129
  * @public
@@ -131,15 +136,10 @@ export class HTMLBindingDirective {
131
136
  constructor(dataBinding) {
132
137
  this.dataBinding = dataBinding;
133
138
  this.updateTarget = null;
134
- /**
135
- * The unique id of the factory.
136
- */
137
- this.id = nextId();
138
139
  /**
139
140
  * The type of aspect to target.
140
141
  */
141
- this.aspectType = Aspect.content;
142
- this.data = `${this.id}-d`;
142
+ this.aspectType = DOMAspect.content;
143
143
  }
144
144
  /**
145
145
  * Creates HTML to be used within a template.
@@ -152,45 +152,28 @@ export class HTMLBindingDirective {
152
152
  * Creates a behavior.
153
153
  */
154
154
  createBehavior() {
155
+ var _a;
155
156
  if (this.updateTarget === null) {
156
- if (this.targetAspect === "innerHTML") {
157
- this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
158
- }
159
- switch (this.aspectType) {
160
- case 1:
161
- this.updateTarget = DOM.setAttribute;
162
- break;
163
- case 2:
164
- this.updateTarget = DOM.setBooleanAttribute;
165
- break;
166
- case 3:
167
- this.updateTarget = setProperty;
168
- break;
169
- case 4:
170
- this.updateTarget = updateContent;
171
- break;
172
- case 5:
173
- this.updateTarget = updateTokenList;
174
- break;
175
- case 6:
176
- this.updateTarget = eventTarget;
177
- break;
178
- default:
179
- throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
157
+ const sink = sinkLookup[this.aspectType];
158
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
159
+ if (!sink) {
160
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
180
161
  }
162
+ this.data = `${this.id}-d`;
163
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
181
164
  }
182
165
  return this;
183
166
  }
184
167
  /** @internal */
185
168
  bind(controller) {
186
169
  var _a;
187
- const target = controller.targets[this.nodeId];
188
- switch (this.updateTarget) {
189
- case eventTarget:
170
+ const target = controller.targets[this.targetNodeId];
171
+ switch (this.aspectType) {
172
+ case DOMAspect.event:
190
173
  target[this.data] = controller;
191
174
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
192
175
  break;
193
- case updateContent:
176
+ case DOMAspect.content:
194
177
  controller.onUnbind(this);
195
178
  // intentional fall through
196
179
  default:
@@ -203,7 +186,7 @@ export class HTMLBindingDirective {
203
186
  }
204
187
  /** @internal */
205
188
  unbind(controller) {
206
- const target = controller.targets[this.nodeId];
189
+ const target = controller.targets[this.targetNodeId];
207
190
  const view = target.$fastView;
208
191
  if (view !== void 0 && view.isComposed) {
209
192
  view.unbind();
@@ -233,21 +216,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
233
216
  /**
234
217
  * Creates an standard binding.
235
218
  * @param expression - The binding to refresh when changed.
219
+ * @param policy - The security policy to associate with th binding.
236
220
  * @param isVolatile - Indicates whether the binding is volatile or not.
237
221
  * @returns A binding configuration.
238
222
  * @public
239
223
  */
240
- export function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
241
- return new OnChangeBinding(expression, isVolatile);
224
+ export function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
225
+ return new OnChangeBinding(expression, policy, isVolatile);
242
226
  }
243
227
  /**
244
228
  * Creates a one time binding
245
229
  * @param expression - The binding to refresh when signaled.
230
+ * @param policy - The security policy to associate with th binding.
246
231
  * @returns A binding configuration.
247
232
  * @public
248
233
  */
249
- export function oneTime(expression) {
250
- return new OneTimeBinding(expression);
234
+ export function oneTime(expression, policy) {
235
+ return new OneTimeBinding(expression, policy);
251
236
  }
252
237
  /**
253
238
  * Creates an event listener binding.
@@ -257,7 +242,7 @@ export function oneTime(expression) {
257
242
  * @public
258
243
  */
259
244
  export function listener(expression, options) {
260
- const config = new OnChangeBinding(expression, false);
245
+ const config = new OnChangeBinding(expression);
261
246
  config.options = options;
262
247
  return config;
263
248
  }
@@ -1,4 +1,4 @@
1
- import { isString } from "../interfaces.js";
1
+ import { isString, noop } from "../interfaces.js";
2
2
  import { HTMLDirective } from "./html-directive.js";
3
3
  import { NodeObservationDirective } from "./node-observation.js";
4
4
  /**
@@ -24,9 +24,13 @@ export class ChildrenDirective extends NodeObservationDirective {
24
24
  * @param target - The target to observe.
25
25
  */
26
26
  observe(target) {
27
- var _a;
28
- const observer = (_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = new MutationObserver(this.handleEvent));
29
- observer.target = target;
27
+ let observer = target[this.observerProperty];
28
+ if (!observer) {
29
+ observer = new MutationObserver(this.handleEvent);
30
+ observer.toJSON = noop;
31
+ observer.target = target;
32
+ target[this.observerProperty] = observer;
33
+ }
30
34
  observer.observe(target, this.options);
31
35
  }
32
36
  /**
@@ -1,8 +1,9 @@
1
1
  import { isFunction, isString } from "../interfaces.js";
2
2
  import { FAST } from "../platform.js";
3
- import { Parser } from "./markup.js";
3
+ import { DOM } from "../dom.js";
4
+ import { nextId, Parser } from "./markup.js";
4
5
  import { HTMLBindingDirective, oneTime } from "./binding.js";
5
- import { Aspect } from "./html-directive.js";
6
+ import { HTMLDirective, } from "./html-directive.js";
6
7
  import { HTMLView } from "./view.js";
7
8
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
8
9
  const descriptorCache = {};
@@ -28,20 +29,25 @@ const warningHost = new Proxy(document.createElement("div"), {
28
29
  },
29
30
  });
30
31
  class CompilationContext {
31
- constructor(fragment, directives) {
32
+ constructor(fragment, directives, policy) {
32
33
  this.fragment = fragment;
33
34
  this.directives = directives;
35
+ this.policy = policy;
34
36
  this.proto = null;
35
37
  this.nodeIds = new Set();
36
38
  this.descriptors = {};
37
39
  this.factories = [];
38
40
  }
39
- addFactory(factory, parentId, nodeId, targetIndex) {
41
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
42
+ var _a, _b;
40
43
  if (!this.nodeIds.has(nodeId)) {
41
44
  this.nodeIds.add(nodeId);
42
45
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
43
46
  }
44
- factory.nodeId = nodeId;
47
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
48
+ factory.targetNodeId = nodeId;
49
+ factory.targetTagName = tagName;
50
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
45
51
  this.factories.push(factory);
46
52
  }
47
53
  freeze() {
@@ -94,19 +100,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
94
100
  let result = null;
95
101
  if (parseResult === null) {
96
102
  if (includeBasicValues) {
97
- result = new HTMLBindingDirective(oneTime(() => attrValue));
98
- Aspect.assign(result, attr.name);
103
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
104
+ HTMLDirective.assignAspect(result, attr.name);
99
105
  }
100
106
  }
101
107
  else {
102
108
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
103
- result = Compiler.aggregate(parseResult);
109
+ result = Compiler.aggregate(parseResult, context.policy);
104
110
  }
105
111
  if (result !== null) {
106
112
  node.removeAttributeNode(attr);
107
113
  i--;
108
114
  ii--;
109
- context.addFactory(result, parentId, nodeId, nodeIndex);
115
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
110
116
  }
111
117
  }
112
118
  }
@@ -131,8 +137,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
131
137
  }
132
138
  else {
133
139
  currentNode.textContent = " ";
134
- Aspect.assign(currentPart);
135
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
140
+ HTMLDirective.assignAspect(currentPart);
141
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
136
142
  }
137
143
  lastNode = currentNode;
138
144
  }
@@ -164,7 +170,7 @@ function compileNode(context, parentId, node, nodeIndex) {
164
170
  if (parts !== null) {
165
171
  context.addFactory(
166
172
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
167
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
173
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
168
174
  }
169
175
  break;
170
176
  }
@@ -178,45 +184,28 @@ function isMarker(node, directives) {
178
184
  Parser.parse(node.data, directives) !== null);
179
185
  }
180
186
  const templateTag = "TEMPLATE";
181
- const policyOptions = { createHTML: html => html };
182
- let htmlPolicy = globalThis.trustedTypes
183
- ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
184
- : policyOptions;
185
- const fastHTMLPolicy = htmlPolicy;
186
187
  /**
187
188
  * Common APIs related to compilation.
188
189
  * @public
189
190
  */
190
191
  export const Compiler = {
191
- /**
192
- * Sets the HTML trusted types policy used by the compiler.
193
- * @param policy - The policy to set for HTML.
194
- * @remarks
195
- * This API can only be called once, for security reasons. It should be
196
- * called by the application developer at the start of their program.
197
- */
198
- setHTMLPolicy(policy) {
199
- if (htmlPolicy !== fastHTMLPolicy) {
200
- throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
201
- }
202
- htmlPolicy = policy;
203
- },
204
192
  /**
205
193
  * Compiles a template and associated directives into a compilation
206
194
  * result which can be used to create views.
207
195
  * @param html - The html string or template element to compile.
208
- * @param directives - The directives referenced by the template.
196
+ * @param factories - The behavior factories referenced by the template.
197
+ * @param policy - The security policy to compile the html with.
209
198
  * @remarks
210
199
  * The template that is provided for compilation is altered in-place
211
200
  * and cannot be compiled again. If the original template must be preserved,
212
201
  * it is recommended that you clone the original and pass the clone to this API.
213
202
  * @public
214
203
  */
215
- compile(html, directives) {
204
+ compile(html, factories, policy = DOM.policy) {
216
205
  let template;
217
206
  if (isString(html)) {
218
207
  template = document.createElement(templateTag);
219
- template.innerHTML = htmlPolicy.createHTML(html);
208
+ template.innerHTML = policy.createHTML(html);
220
209
  const fec = template.content.firstElementChild;
221
210
  if (fec !== null && fec.tagName === templateTag) {
222
211
  template = fec;
@@ -227,18 +216,18 @@ export const Compiler = {
227
216
  }
228
217
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
229
218
  const fragment = document.adoptNode(template.content);
230
- const context = new CompilationContext(fragment, directives);
219
+ const context = new CompilationContext(fragment, factories, policy);
231
220
  compileAttributes(context, "", template, /* host */ "h", 0, true);
232
221
  if (
233
222
  // If the first node in a fragment is a marker, that means it's an unstable first node,
234
223
  // because something like a when, repeat, etc. could add nodes before the marker.
235
224
  // To mitigate this, we insert a stable first node. However, if we insert a node,
236
225
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
237
- isMarker(fragment.firstChild, directives) ||
226
+ isMarker(fragment.firstChild, factories) ||
238
227
  // Or if there is only one node and a directive, it means the template's content
239
228
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
240
229
  // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
241
- (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
230
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
242
231
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
243
232
  }
244
233
  compileChildren(context, fragment, /* root */ "r");
@@ -257,15 +246,17 @@ export const Compiler = {
257
246
  * Aggregates an array of strings and directives into a single directive.
258
247
  * @param parts - A heterogeneous array of static strings interspersed with
259
248
  * directives.
249
+ * @param policy - The security policy to use with the aggregated bindings.
260
250
  * @returns A single inline directive that aggregates the behavior of all the parts.
261
251
  */
262
- aggregate(parts) {
252
+ aggregate(parts, policy = DOM.policy) {
263
253
  if (parts.length === 1) {
264
254
  return parts[0];
265
255
  }
266
256
  let sourceAspect;
267
257
  let binding;
268
258
  let isVolatile = false;
259
+ let bindingPolicy = void 0;
269
260
  const partCount = parts.length;
270
261
  const finalParts = parts.map((x) => {
271
262
  if (isString(x)) {
@@ -274,6 +265,7 @@ export const Compiler = {
274
265
  sourceAspect = x.sourceAspect || sourceAspect;
275
266
  binding = x.dataBinding || binding;
276
267
  isVolatile = isVolatile || x.dataBinding.isVolatile;
268
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
277
269
  return x.dataBinding.evaluate;
278
270
  });
279
271
  const expression = (scope, context) => {
@@ -285,8 +277,9 @@ export const Compiler = {
285
277
  };
286
278
  binding.evaluate = expression;
287
279
  binding.isVolatile = isVolatile;
280
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
288
281
  const directive = new HTMLBindingDirective(binding);
289
- Aspect.assign(directive, sourceAspect);
282
+ HTMLDirective.assignAspect(directive, sourceAspect);
290
283
  return directive;
291
284
  },
292
285
  };
@@ -0,0 +1,23 @@
1
+ import { HTMLDirective } from "./html-directive.js";
2
+ /**
3
+ * A directive capable of injecting static HTML platform runtime protection.
4
+ * @public
5
+ */
6
+ export class DangerousHTMLDirective {
7
+ constructor(html) {
8
+ this.html = html;
9
+ }
10
+ createHTML() {
11
+ return this.html;
12
+ }
13
+ }
14
+ HTMLDirective.define(DangerousHTMLDirective);
15
+ /**
16
+ * Injects static HTML without platform protection.
17
+ * @param html - The html to injection.
18
+ * @returns A DangerousHTMLDirective.
19
+ * @public
20
+ */
21
+ export function dangerousHTML(html) {
22
+ return new DangerousHTMLDirective(html);
23
+ }
@@ -1,67 +1,7 @@
1
- import { ExecutionContext, } from "../observation/observable.js";
1
+ import { DOMAspect } from "../dom.js";
2
+ import { noop } from "../interfaces.js";
2
3
  import { createTypeRegistry } from "../platform.js";
3
- import { Markup, nextId } from "./markup.js";
4
- /**
5
- * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
6
- * control ViewBehaviors.
7
- * @public
8
- */
9
- export const ViewBehaviorOrchestrator = Object.freeze({
10
- /**
11
- * Creates a ViewBehaviorOrchestrator.
12
- * @param source - The source to to associate behaviors with.
13
- * @returns A ViewBehaviorOrchestrator.
14
- */
15
- create(source) {
16
- const behaviors = [];
17
- const targets = {};
18
- let unbindables = null;
19
- let isConnected = false;
20
- return {
21
- source,
22
- context: ExecutionContext.default,
23
- targets,
24
- get isBound() {
25
- return isConnected;
26
- },
27
- addBehaviorFactory(factory, target) {
28
- const nodeId = factory.nodeId || (factory.nodeId = nextId());
29
- factory.id || (factory.id = nextId());
30
- this.addTarget(nodeId, target);
31
- this.addBehavior(factory.createBehavior());
32
- },
33
- addTarget(nodeId, target) {
34
- targets[nodeId] = target;
35
- },
36
- addBehavior(behavior) {
37
- behaviors.push(behavior);
38
- if (isConnected) {
39
- behavior.bind(this);
40
- }
41
- },
42
- onUnbind(unbindable) {
43
- if (unbindables === null) {
44
- unbindables = [];
45
- }
46
- unbindables.push(unbindable);
47
- },
48
- connectedCallback(controller) {
49
- if (!isConnected) {
50
- isConnected = true;
51
- behaviors.forEach(x => x.bind(this));
52
- }
53
- },
54
- disconnectedCallback(controller) {
55
- if (isConnected) {
56
- isConnected = false;
57
- if (unbindables !== null) {
58
- unbindables.forEach(x => x.unbind(this));
59
- }
60
- }
61
- },
62
- };
63
- },
64
- });
4
+ import { Markup } from "./markup.js";
65
5
  const registry = createTypeRegistry();
66
6
  /**
67
7
  * Instructs the template engine to apply behavior to a node.
@@ -89,67 +29,6 @@ export const HTMLDirective = Object.freeze({
89
29
  registry.register(options);
90
30
  return type;
91
31
  },
92
- });
93
- /**
94
- * Decorator: Defines an HTMLDirective.
95
- * @param options - Provides options that specify the directive's application.
96
- * @public
97
- */
98
- export function htmlDirective(options) {
99
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
100
- return function (type) {
101
- HTMLDirective.define(type, options);
102
- };
103
- }
104
- /**
105
- * Captures a binding expression along with related information and capabilities.
106
- *
107
- * @public
108
- */
109
- export class Binding {
110
- /**
111
- * Creates a binding.
112
- * @param evaluate - Evaluates the binding.
113
- * @param isVolatile - Indicates whether the binding is volatile.
114
- */
115
- constructor(evaluate, isVolatile = false) {
116
- this.evaluate = evaluate;
117
- this.isVolatile = isVolatile;
118
- }
119
- }
120
- /**
121
- * The type of HTML aspect to target.
122
- * @public
123
- */
124
- export const Aspect = Object.freeze({
125
- /**
126
- * Not aspected.
127
- */
128
- none: 0,
129
- /**
130
- * An attribute.
131
- */
132
- attribute: 1,
133
- /**
134
- * A boolean attribute.
135
- */
136
- booleanAttribute: 2,
137
- /**
138
- * A property.
139
- */
140
- property: 3,
141
- /**
142
- * Content
143
- */
144
- content: 4,
145
- /**
146
- * A token list.
147
- */
148
- tokenList: 5,
149
- /**
150
- * An event.
151
- */
152
- event: 6,
153
32
  /**
154
33
  *
155
34
  * @param directive - The directive to assign the aspect to.
@@ -157,9 +36,9 @@ export const Aspect = Object.freeze({
157
36
  * @remarks
158
37
  * If a falsy value is provided, then the content aspect will be assigned.
159
38
  */
160
- assign(directive, value) {
39
+ assignAspect(directive, value) {
161
40
  if (!value) {
162
- directive.aspectType = Aspect.content;
41
+ directive.aspectType = DOMAspect.content;
163
42
  return;
164
43
  }
165
44
  directive.sourceAspect = value;
@@ -168,24 +47,53 @@ export const Aspect = Object.freeze({
168
47
  directive.targetAspect = value.substring(1);
169
48
  directive.aspectType =
170
49
  directive.targetAspect === "classList"
171
- ? Aspect.tokenList
172
- : Aspect.property;
50
+ ? DOMAspect.tokenList
51
+ : DOMAspect.property;
173
52
  break;
174
53
  case "?":
175
54
  directive.targetAspect = value.substring(1);
176
- directive.aspectType = Aspect.booleanAttribute;
55
+ directive.aspectType = DOMAspect.booleanAttribute;
177
56
  break;
178
57
  case "@":
179
58
  directive.targetAspect = value.substring(1);
180
- directive.aspectType = Aspect.event;
59
+ directive.aspectType = DOMAspect.event;
181
60
  break;
182
61
  default:
183
62
  directive.targetAspect = value;
184
- directive.aspectType = Aspect.attribute;
63
+ directive.aspectType = DOMAspect.attribute;
185
64
  break;
186
65
  }
187
66
  },
188
67
  });
68
+ /**
69
+ * Decorator: Defines an HTMLDirective.
70
+ * @param options - Provides options that specify the directive's application.
71
+ * @public
72
+ */
73
+ export function htmlDirective(options) {
74
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
75
+ return function (type) {
76
+ HTMLDirective.define(type, options);
77
+ };
78
+ }
79
+ /**
80
+ * Captures a binding expression along with related information and capabilities.
81
+ *
82
+ * @public
83
+ */
84
+ export class Binding {
85
+ /**
86
+ * Creates a binding.
87
+ * @param evaluate - Evaluates the binding.
88
+ * @param policy - The security policy to associate with this binding.
89
+ * @param isVolatile - Indicates whether the binding is volatile.
90
+ */
91
+ constructor(evaluate, policy, isVolatile = false) {
92
+ this.evaluate = evaluate;
93
+ this.policy = policy;
94
+ this.isVolatile = isVolatile;
95
+ }
96
+ }
189
97
  /**
190
98
  * A base class used for attribute directives that don't need internal state.
191
99
  * @public
@@ -198,9 +106,10 @@ export class StatelessAttachedAttributeDirective {
198
106
  constructor(options) {
199
107
  this.options = options;
200
108
  /**
201
- * The unique id of the factory.
109
+ * Opts out of JSON stringification.
110
+ * @internal
202
111
  */
203
- this.id = nextId();
112
+ this.toJSON = noop;
204
113
  }
205
114
  /**
206
115
  * Creates a placeholder string based on the directive's index within the template.
@@ -16,9 +16,15 @@ export const elements = (selector) => selector
16
16
  * Internally used by the SlottedDirective and the ChildrenDirective.
17
17
  */
18
18
  export class NodeObservationDirective extends StatelessAttachedAttributeDirective {
19
- constructor() {
20
- super(...arguments);
21
- this.sourceProperty = `${this.id}-s`;
19
+ /**
20
+ * The unique id of the factory.
21
+ */
22
+ get id() {
23
+ return this._id;
24
+ }
25
+ set id(value) {
26
+ this._id = value;
27
+ this._controllerProperty = `${value}-c`;
22
28
  }
23
29
  /**
24
30
  * Bind this behavior to the source.
@@ -27,8 +33,8 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
27
33
  * @param targets - The targets that behaviors in a view can attach to.
28
34
  */
29
35
  bind(controller) {
30
- const target = controller.targets[this.nodeId];
31
- target[this.sourceProperty] = controller.source;
36
+ const target = controller.targets[this.targetNodeId];
37
+ target[this._controllerProperty] = controller;
32
38
  this.updateTarget(controller.source, this.computeNodes(target));
33
39
  this.observe(target);
34
40
  controller.onUnbind(this);
@@ -40,10 +46,10 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
40
46
  * @param targets - The targets that behaviors in a view can attach to.
41
47
  */
42
48
  unbind(controller) {
43
- const target = controller.targets[this.nodeId];
49
+ const target = controller.targets[this.targetNodeId];
44
50
  this.updateTarget(controller.source, emptyArray);
45
51
  this.disconnect(target);
46
- target[this.sourceProperty] = null;
52
+ target[this._controllerProperty] = null;
47
53
  }
48
54
  /**
49
55
  * Gets the data source for the target.
@@ -51,7 +57,7 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
51
57
  * @returns The source.
52
58
  */
53
59
  getSource(target) {
54
- return target[this.sourceProperty];
60
+ return target[this._controllerProperty].source;
55
61
  }
56
62
  /**
57
63
  * Updates the source property with the computed nodes.