@microsoft/fast-element 2.0.0-beta.17 → 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 (56) hide show
  1. package/CHANGELOG.json +21 -0
  2. package/CHANGELOG.md +10 -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 +15 -24
  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/debug.js +4 -1
  25. package/dist/esm/dom-policy.js +337 -0
  26. package/dist/esm/dom.js +117 -0
  27. package/dist/esm/index.js +2 -1
  28. package/dist/esm/index.rollup.debug.js +3 -1
  29. package/dist/esm/index.rollup.js +3 -1
  30. package/dist/esm/platform.js +1 -1
  31. package/dist/esm/polyfills.js +3 -7
  32. package/dist/esm/templating/binding-signal.js +3 -2
  33. package/dist/esm/templating/binding-two-way.js +3 -2
  34. package/dist/esm/templating/binding.js +31 -54
  35. package/dist/esm/templating/compiler.js +31 -38
  36. package/dist/esm/templating/dangerous-html.js +23 -0
  37. package/dist/esm/templating/html-directive.js +38 -135
  38. package/dist/esm/templating/node-observation.js +14 -8
  39. package/dist/esm/templating/ref.js +1 -1
  40. package/dist/esm/templating/render.js +17 -6
  41. package/dist/esm/templating/repeat.js +2 -6
  42. package/dist/esm/templating/template.js +81 -56
  43. package/dist/esm/testing/fixture.js +1 -1
  44. package/dist/esm/utilities.js +68 -0
  45. package/dist/fast-element.api.json +1088 -608
  46. package/dist/fast-element.d.ts +190 -147
  47. package/dist/fast-element.debug.js +708 -384
  48. package/dist/fast-element.debug.min.js +1 -1
  49. package/dist/fast-element.js +679 -358
  50. package/dist/fast-element.min.js +1 -1
  51. package/dist/fast-element.untrimmed.d.ts +190 -147
  52. package/docs/api-report.md +66 -56
  53. package/package.json +5 -1
  54. package/yarn-error.log +177 -0
  55. package/dist/dts/templating/dom.d.ts +0 -41
  56. package/dist/esm/templating/dom.js +0 -49
@@ -1,18 +1,9 @@
1
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);
@@ -125,8 +116,14 @@ function updateTokenList(target, aspect, value) {
125
116
  }
126
117
  }
127
118
  }
128
- const setProperty = (t, a, v) => (t[a] = v);
129
- 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
+ };
130
127
  /**
131
128
  * A directive that applies bindings.
132
129
  * @public
@@ -139,15 +136,10 @@ export class HTMLBindingDirective {
139
136
  constructor(dataBinding) {
140
137
  this.dataBinding = dataBinding;
141
138
  this.updateTarget = null;
142
- /**
143
- * The unique id of the factory.
144
- */
145
- this.id = nextId();
146
139
  /**
147
140
  * The type of aspect to target.
148
141
  */
149
- this.aspectType = Aspect.content;
150
- this.data = `${this.id}-d`;
142
+ this.aspectType = DOMAspect.content;
151
143
  }
152
144
  /**
153
145
  * Creates HTML to be used within a template.
@@ -160,45 +152,28 @@ export class HTMLBindingDirective {
160
152
  * Creates a behavior.
161
153
  */
162
154
  createBehavior() {
155
+ var _a;
163
156
  if (this.updateTarget === null) {
164
- if (this.targetAspect === "innerHTML") {
165
- this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
166
- }
167
- switch (this.aspectType) {
168
- case 1:
169
- this.updateTarget = DOM.setAttribute;
170
- break;
171
- case 2:
172
- this.updateTarget = DOM.setBooleanAttribute;
173
- break;
174
- case 3:
175
- this.updateTarget = setProperty;
176
- break;
177
- case 4:
178
- this.updateTarget = updateContent;
179
- break;
180
- case 5:
181
- this.updateTarget = updateTokenList;
182
- break;
183
- case 6:
184
- this.updateTarget = eventTarget;
185
- break;
186
- default:
187
- 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 */);
188
161
  }
162
+ this.data = `${this.id}-d`;
163
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
189
164
  }
190
165
  return this;
191
166
  }
192
167
  /** @internal */
193
168
  bind(controller) {
194
169
  var _a;
195
- const target = controller.targets[this.nodeId];
196
- switch (this.updateTarget) {
197
- case eventTarget:
170
+ const target = controller.targets[this.targetNodeId];
171
+ switch (this.aspectType) {
172
+ case DOMAspect.event:
198
173
  target[this.data] = controller;
199
174
  target.addEventListener(this.targetAspect, this, this.dataBinding.options);
200
175
  break;
201
- case updateContent:
176
+ case DOMAspect.content:
202
177
  controller.onUnbind(this);
203
178
  // intentional fall through
204
179
  default:
@@ -211,7 +186,7 @@ export class HTMLBindingDirective {
211
186
  }
212
187
  /** @internal */
213
188
  unbind(controller) {
214
- const target = controller.targets[this.nodeId];
189
+ const target = controller.targets[this.targetNodeId];
215
190
  const view = target.$fastView;
216
191
  if (view !== void 0 && view.isComposed) {
217
192
  view.unbind();
@@ -241,21 +216,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
241
216
  /**
242
217
  * Creates an standard binding.
243
218
  * @param expression - The binding to refresh when changed.
219
+ * @param policy - The security policy to associate with th binding.
244
220
  * @param isVolatile - Indicates whether the binding is volatile or not.
245
221
  * @returns A binding configuration.
246
222
  * @public
247
223
  */
248
- export function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
249
- return new OnChangeBinding(expression, isVolatile);
224
+ export function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
225
+ return new OnChangeBinding(expression, policy, isVolatile);
250
226
  }
251
227
  /**
252
228
  * Creates a one time binding
253
229
  * @param expression - The binding to refresh when signaled.
230
+ * @param policy - The security policy to associate with th binding.
254
231
  * @returns A binding configuration.
255
232
  * @public
256
233
  */
257
- export function oneTime(expression) {
258
- return new OneTimeBinding(expression);
234
+ export function oneTime(expression, policy) {
235
+ return new OneTimeBinding(expression, policy);
259
236
  }
260
237
  /**
261
238
  * Creates an event listener binding.
@@ -265,7 +242,7 @@ export function oneTime(expression) {
265
242
  * @public
266
243
  */
267
244
  export function listener(expression, options) {
268
- const config = new OnChangeBinding(expression, false);
245
+ const config = new OnChangeBinding(expression);
269
246
  config.options = options;
270
247
  return config;
271
248
  }
@@ -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,68 +1,7 @@
1
+ import { DOMAspect } from "../dom.js";
1
2
  import { noop } from "../interfaces.js";
2
- import { ExecutionContext, } from "../observation/observable.js";
3
3
  import { createTypeRegistry } from "../platform.js";
4
- import { Markup, nextId } from "./markup.js";
5
- /**
6
- * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
7
- * control ViewBehaviors.
8
- * @public
9
- */
10
- export const ViewBehaviorOrchestrator = Object.freeze({
11
- /**
12
- * Creates a ViewBehaviorOrchestrator.
13
- * @param source - The source to to associate behaviors with.
14
- * @returns A ViewBehaviorOrchestrator.
15
- */
16
- create(source) {
17
- const behaviors = [];
18
- const targets = {};
19
- let unbindables = null;
20
- let isConnected = false;
21
- return {
22
- source,
23
- context: ExecutionContext.default,
24
- targets,
25
- get isBound() {
26
- return isConnected;
27
- },
28
- addBehaviorFactory(factory, target) {
29
- const nodeId = factory.nodeId || (factory.nodeId = nextId());
30
- factory.id || (factory.id = nextId());
31
- this.addTarget(nodeId, target);
32
- this.addBehavior(factory.createBehavior());
33
- },
34
- addTarget(nodeId, target) {
35
- targets[nodeId] = target;
36
- },
37
- addBehavior(behavior) {
38
- behaviors.push(behavior);
39
- if (isConnected) {
40
- behavior.bind(this);
41
- }
42
- },
43
- onUnbind(unbindable) {
44
- if (unbindables === null) {
45
- unbindables = [];
46
- }
47
- unbindables.push(unbindable);
48
- },
49
- connectedCallback(controller) {
50
- if (!isConnected) {
51
- isConnected = true;
52
- behaviors.forEach(x => x.bind(this));
53
- }
54
- },
55
- disconnectedCallback(controller) {
56
- if (isConnected) {
57
- isConnected = false;
58
- if (unbindables !== null) {
59
- unbindables.forEach(x => x.unbind(this));
60
- }
61
- }
62
- },
63
- };
64
- },
65
- });
4
+ import { Markup } from "./markup.js";
66
5
  const registry = createTypeRegistry();
67
6
  /**
68
7
  * Instructs the template engine to apply behavior to a node.
@@ -90,67 +29,6 @@ export const HTMLDirective = Object.freeze({
90
29
  registry.register(options);
91
30
  return type;
92
31
  },
93
- });
94
- /**
95
- * Decorator: Defines an HTMLDirective.
96
- * @param options - Provides options that specify the directive's application.
97
- * @public
98
- */
99
- export function htmlDirective(options) {
100
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
101
- return function (type) {
102
- HTMLDirective.define(type, options);
103
- };
104
- }
105
- /**
106
- * Captures a binding expression along with related information and capabilities.
107
- *
108
- * @public
109
- */
110
- export class Binding {
111
- /**
112
- * Creates a binding.
113
- * @param evaluate - Evaluates the binding.
114
- * @param isVolatile - Indicates whether the binding is volatile.
115
- */
116
- constructor(evaluate, isVolatile = false) {
117
- this.evaluate = evaluate;
118
- this.isVolatile = isVolatile;
119
- }
120
- }
121
- /**
122
- * The type of HTML aspect to target.
123
- * @public
124
- */
125
- export const Aspect = Object.freeze({
126
- /**
127
- * Not aspected.
128
- */
129
- none: 0,
130
- /**
131
- * An attribute.
132
- */
133
- attribute: 1,
134
- /**
135
- * A boolean attribute.
136
- */
137
- booleanAttribute: 2,
138
- /**
139
- * A property.
140
- */
141
- property: 3,
142
- /**
143
- * Content
144
- */
145
- content: 4,
146
- /**
147
- * A token list.
148
- */
149
- tokenList: 5,
150
- /**
151
- * An event.
152
- */
153
- event: 6,
154
32
  /**
155
33
  *
156
34
  * @param directive - The directive to assign the aspect to.
@@ -158,9 +36,9 @@ export const Aspect = Object.freeze({
158
36
  * @remarks
159
37
  * If a falsy value is provided, then the content aspect will be assigned.
160
38
  */
161
- assign(directive, value) {
39
+ assignAspect(directive, value) {
162
40
  if (!value) {
163
- directive.aspectType = Aspect.content;
41
+ directive.aspectType = DOMAspect.content;
164
42
  return;
165
43
  }
166
44
  directive.sourceAspect = value;
@@ -169,24 +47,53 @@ export const Aspect = Object.freeze({
169
47
  directive.targetAspect = value.substring(1);
170
48
  directive.aspectType =
171
49
  directive.targetAspect === "classList"
172
- ? Aspect.tokenList
173
- : Aspect.property;
50
+ ? DOMAspect.tokenList
51
+ : DOMAspect.property;
174
52
  break;
175
53
  case "?":
176
54
  directive.targetAspect = value.substring(1);
177
- directive.aspectType = Aspect.booleanAttribute;
55
+ directive.aspectType = DOMAspect.booleanAttribute;
178
56
  break;
179
57
  case "@":
180
58
  directive.targetAspect = value.substring(1);
181
- directive.aspectType = Aspect.event;
59
+ directive.aspectType = DOMAspect.event;
182
60
  break;
183
61
  default:
184
62
  directive.targetAspect = value;
185
- directive.aspectType = Aspect.attribute;
63
+ directive.aspectType = DOMAspect.attribute;
186
64
  break;
187
65
  }
188
66
  },
189
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
+ }
190
97
  /**
191
98
  * A base class used for attribute directives that don't need internal state.
192
99
  * @public
@@ -198,10 +105,6 @@ export class StatelessAttachedAttributeDirective {
198
105
  */
199
106
  constructor(options) {
200
107
  this.options = options;
201
- /**
202
- * The unique id of the factory.
203
- */
204
- this.id = nextId();
205
108
  /**
206
109
  * Opts out of JSON stringification.
207
110
  * @internal
@@ -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.controllerProperty = `${this.id}-c`;
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.controllerProperty] = controller;
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.controllerProperty] = 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.controllerProperty].source;
60
+ return target[this._controllerProperty].source;
55
61
  }
56
62
  /**
57
63
  * Updates the source property with the computed nodes.
@@ -9,7 +9,7 @@ export class RefDirective extends StatelessAttachedAttributeDirective {
9
9
  * @param controller - The view controller that manages the lifecycle of this behavior.
10
10
  */
11
11
  bind(controller) {
12
- controller.source[this.options] = controller.targets[this.nodeId];
12
+ controller.source[this.options] = controller.targets[this.targetNodeId];
13
13
  }
14
14
  }
15
15
  HTMLDirective.define(RefDirective);