@microsoft/fast-element 2.0.0-beta.2 → 2.0.0-beta.20

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 (113) hide show
  1. package/CHANGELOG.json +488 -0
  2. package/CHANGELOG.md +180 -1
  3. package/dist/dts/components/attributes.d.ts +15 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +74 -28
  5. package/dist/dts/components/fast-definitions.d.ts +41 -9
  6. package/dist/dts/components/fast-element.d.ts +14 -26
  7. package/dist/dts/components/hydration.d.ts +14 -0
  8. package/dist/{esm/observation/behavior.js → dts/components/install-hydration.d.ts} +0 -0
  9. package/dist/dts/context.d.ts +1 -1
  10. package/dist/dts/di/di.d.ts +894 -0
  11. package/dist/dts/dom-policy.d.ts +68 -0
  12. package/dist/dts/dom.d.ts +100 -0
  13. package/dist/dts/index.d.ts +5 -4
  14. package/dist/dts/index.rollup.d.ts +0 -1
  15. package/dist/dts/index.rollup.debug.d.ts +0 -1
  16. package/dist/dts/interfaces.d.ts +60 -79
  17. package/dist/dts/observation/observable.d.ts +99 -54
  18. package/dist/dts/pending-task.d.ts +20 -0
  19. package/dist/dts/platform.d.ts +7 -0
  20. package/dist/dts/polyfills.d.ts +0 -8
  21. package/dist/dts/state/exports.d.ts +3 -0
  22. package/dist/dts/state/reactive.d.ts +8 -0
  23. package/dist/dts/state/state.d.ts +141 -0
  24. package/dist/dts/state/visitor.d.ts +6 -0
  25. package/dist/dts/state/watch.d.ts +10 -0
  26. package/dist/dts/styles/css-directive.d.ts +2 -2
  27. package/dist/dts/styles/css.d.ts +0 -5
  28. package/dist/dts/styles/element-styles.d.ts +10 -17
  29. package/dist/dts/styles/host.d.ts +68 -0
  30. package/dist/dts/styles/style-strategy.d.ts +42 -0
  31. package/dist/dts/templating/binding-signal.d.ts +12 -27
  32. package/dist/dts/templating/binding-two-way.d.ts +22 -37
  33. package/dist/dts/templating/binding.d.ts +76 -208
  34. package/dist/dts/templating/children.d.ts +1 -1
  35. package/dist/dts/templating/compiler.d.ts +11 -13
  36. package/dist/dts/templating/html-directive.d.ts +91 -97
  37. package/dist/dts/templating/node-observation.d.ts +15 -6
  38. package/dist/dts/templating/ref.d.ts +7 -11
  39. package/dist/dts/templating/render.d.ts +296 -0
  40. package/dist/dts/templating/repeat.d.ts +23 -34
  41. package/dist/dts/templating/slotted.d.ts +1 -1
  42. package/dist/dts/templating/template.d.ts +92 -14
  43. package/dist/dts/templating/view.d.ts +81 -11
  44. package/dist/dts/templating/when.d.ts +3 -3
  45. package/dist/dts/testing/exports.d.ts +3 -0
  46. package/dist/dts/testing/fakes.d.ts +14 -0
  47. package/dist/dts/testing/fixture.d.ts +84 -0
  48. package/dist/dts/testing/timeout.d.ts +7 -0
  49. package/dist/dts/utilities.d.ts +53 -18
  50. package/dist/esm/components/attributes.js +28 -5
  51. package/dist/esm/components/{controller.js → element-controller.js} +239 -137
  52. package/dist/esm/components/fast-definitions.js +38 -30
  53. package/dist/esm/components/fast-element.js +27 -16
  54. package/dist/esm/components/hydration.js +35 -0
  55. package/dist/esm/components/install-hydration.js +2 -0
  56. package/dist/esm/context.js +5 -1
  57. package/dist/esm/debug.js +40 -5
  58. package/dist/esm/di/di.js +1430 -0
  59. package/dist/esm/dom-policy.js +337 -0
  60. package/dist/esm/dom.js +101 -0
  61. package/dist/esm/index.js +4 -2
  62. package/dist/esm/index.rollup.debug.js +3 -1
  63. package/dist/esm/index.rollup.js +3 -1
  64. package/dist/esm/interfaces.js +52 -0
  65. package/dist/esm/observation/arrays.js +303 -2
  66. package/dist/esm/observation/observable.js +88 -142
  67. package/dist/esm/observation/update-queue.js +2 -2
  68. package/dist/esm/pending-task.js +16 -0
  69. package/dist/esm/platform.js +27 -2
  70. package/dist/esm/polyfills.js +3 -61
  71. package/dist/esm/state/exports.js +3 -0
  72. package/dist/esm/state/reactive.js +34 -0
  73. package/dist/esm/state/state.js +148 -0
  74. package/dist/esm/state/visitor.js +28 -0
  75. package/dist/esm/state/watch.js +36 -0
  76. package/dist/esm/styles/css.js +4 -9
  77. package/dist/esm/styles/element-styles.js +14 -33
  78. package/dist/esm/styles/host.js +1 -0
  79. package/dist/esm/styles/style-strategy.js +1 -0
  80. package/dist/esm/templating/binding-signal.js +67 -62
  81. package/dist/esm/templating/binding-two-way.js +72 -39
  82. package/dist/esm/templating/binding.js +142 -286
  83. package/dist/esm/templating/children.js +8 -4
  84. package/dist/esm/templating/compiler.js +59 -43
  85. package/dist/esm/templating/html-directive.js +56 -75
  86. package/dist/esm/templating/node-observation.js +20 -13
  87. package/dist/esm/templating/ref.js +4 -12
  88. package/dist/esm/templating/render.js +402 -0
  89. package/dist/esm/templating/repeat.js +88 -75
  90. package/dist/esm/templating/template.js +132 -60
  91. package/dist/esm/templating/view.js +113 -29
  92. package/dist/esm/templating/when.js +5 -4
  93. package/dist/esm/testing/exports.js +3 -0
  94. package/dist/esm/testing/fakes.js +107 -0
  95. package/dist/esm/testing/fixture.js +86 -0
  96. package/dist/esm/testing/timeout.js +24 -0
  97. package/dist/esm/utilities.js +95 -95
  98. package/dist/fast-element.api.json +9487 -8326
  99. package/dist/fast-element.d.ts +847 -644
  100. package/dist/fast-element.debug.js +1993 -1166
  101. package/dist/fast-element.debug.min.js +1 -1
  102. package/dist/fast-element.js +1903 -1111
  103. package/dist/fast-element.min.js +1 -1
  104. package/dist/fast-element.untrimmed.d.ts +911 -701
  105. package/docs/api-report.md +329 -252
  106. package/package.json +38 -16
  107. package/dist/dts/hooks.d.ts +0 -20
  108. package/dist/dts/observation/behavior.d.ts +0 -19
  109. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  110. package/dist/dts/templating/dom.d.ts +0 -41
  111. package/dist/esm/hooks.js +0 -32
  112. package/dist/esm/observation/splice-strategies.js +0 -400
  113. package/dist/esm/templating/dom.js +0 -49
@@ -67,10 +67,311 @@ export const SpliceStrategySupport = Object.freeze({
67
67
  const reset = new Splice(0, emptyArray, 0);
68
68
  reset.reset = true;
69
69
  const resetSplices = [reset];
70
+ // Note: This function is *based* on the computation of the Levenshtein
71
+ // "edit" distance. The one change is that "updates" are treated as two
72
+ // edits - not one. With Array splices, an update is really a delete
73
+ // followed by an add. By retaining this, we optimize for "keeping" the
74
+ // maximum array items in the original array. For example:
75
+ //
76
+ // 'xxxx123' to '123yyyy'
77
+ //
78
+ // With 1-edit updates, the shortest path would be just to update all seven
79
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
80
+ // leaves the substring '123' intact.
81
+ function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
82
+ // "Deletion" columns
83
+ const rowCount = oldEnd - oldStart + 1;
84
+ const columnCount = currentEnd - currentStart + 1;
85
+ const distances = new Array(rowCount);
86
+ let north;
87
+ let west;
88
+ // "Addition" rows. Initialize null column.
89
+ for (let i = 0; i < rowCount; ++i) {
90
+ distances[i] = new Array(columnCount);
91
+ distances[i][0] = i;
92
+ }
93
+ // Initialize null row
94
+ for (let j = 0; j < columnCount; ++j) {
95
+ distances[0][j] = j;
96
+ }
97
+ for (let i = 1; i < rowCount; ++i) {
98
+ for (let j = 1; j < columnCount; ++j) {
99
+ if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
100
+ distances[i][j] = distances[i - 1][j - 1];
101
+ }
102
+ else {
103
+ north = distances[i - 1][j] + 1;
104
+ west = distances[i][j - 1] + 1;
105
+ distances[i][j] = north < west ? north : west;
106
+ }
107
+ }
108
+ }
109
+ return distances;
110
+ }
111
+ // This starts at the final weight, and walks "backward" by finding
112
+ // the minimum previous weight recursively until the origin of the weight
113
+ // matrix.
114
+ function spliceOperationsFromEditDistances(distances) {
115
+ let i = distances.length - 1;
116
+ let j = distances[0].length - 1;
117
+ let current = distances[i][j];
118
+ const edits = [];
119
+ while (i > 0 || j > 0) {
120
+ if (i === 0) {
121
+ edits.push(2 /* Edit.add */);
122
+ j--;
123
+ continue;
124
+ }
125
+ if (j === 0) {
126
+ edits.push(3 /* Edit.delete */);
127
+ i--;
128
+ continue;
129
+ }
130
+ const northWest = distances[i - 1][j - 1];
131
+ const west = distances[i - 1][j];
132
+ const north = distances[i][j - 1];
133
+ let min;
134
+ if (west < north) {
135
+ min = west < northWest ? west : northWest;
136
+ }
137
+ else {
138
+ min = north < northWest ? north : northWest;
139
+ }
140
+ if (min === northWest) {
141
+ if (northWest === current) {
142
+ edits.push(0 /* Edit.leave */);
143
+ }
144
+ else {
145
+ edits.push(1 /* Edit.update */);
146
+ current = northWest;
147
+ }
148
+ i--;
149
+ j--;
150
+ }
151
+ else if (min === west) {
152
+ edits.push(3 /* Edit.delete */);
153
+ i--;
154
+ current = west;
155
+ }
156
+ else {
157
+ edits.push(2 /* Edit.add */);
158
+ j--;
159
+ current = north;
160
+ }
161
+ }
162
+ return edits.reverse();
163
+ }
164
+ function sharedPrefix(current, old, searchLength) {
165
+ for (let i = 0; i < searchLength; ++i) {
166
+ if (current[i] !== old[i]) {
167
+ return i;
168
+ }
169
+ }
170
+ return searchLength;
171
+ }
172
+ function sharedSuffix(current, old, searchLength) {
173
+ let index1 = current.length;
174
+ let index2 = old.length;
175
+ let count = 0;
176
+ while (count < searchLength && current[--index1] === old[--index2]) {
177
+ count++;
178
+ }
179
+ return count;
180
+ }
181
+ function intersect(start1, end1, start2, end2) {
182
+ // Disjoint
183
+ if (end1 < start2 || end2 < start1) {
184
+ return -1;
185
+ }
186
+ // Adjacent
187
+ if (end1 === start2 || end2 === start1) {
188
+ return 0;
189
+ }
190
+ // Non-zero intersect, span1 first
191
+ if (start1 < start2) {
192
+ if (end1 < end2) {
193
+ return end1 - start2; // Overlap
194
+ }
195
+ return end2 - start2; // Contained
196
+ }
197
+ // Non-zero intersect, span2 first
198
+ if (end2 < end1) {
199
+ return end2 - start1; // Overlap
200
+ }
201
+ return end1 - start1; // Contained
202
+ }
203
+ /**
204
+ * @remarks
205
+ * Lacking individual splice mutation information, the minimal set of
206
+ * splices can be synthesized given the previous state and final state of an
207
+ * array. The basic approach is to calculate the edit distance matrix and
208
+ * choose the shortest path through it.
209
+ *
210
+ * Complexity: O(l * p)
211
+ * l: The length of the current array
212
+ * p: The length of the old array
213
+ */
214
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
215
+ let prefixCount = 0;
216
+ let suffixCount = 0;
217
+ const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
218
+ if (currentStart === 0 && oldStart === 0) {
219
+ prefixCount = sharedPrefix(current, old, minLength);
220
+ }
221
+ if (currentEnd === current.length && oldEnd === old.length) {
222
+ suffixCount = sharedSuffix(current, old, minLength - prefixCount);
223
+ }
224
+ currentStart += prefixCount;
225
+ oldStart += prefixCount;
226
+ currentEnd -= suffixCount;
227
+ oldEnd -= suffixCount;
228
+ if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
229
+ return emptyArray;
230
+ }
231
+ if (currentStart === currentEnd) {
232
+ const splice = new Splice(currentStart, [], 0);
233
+ while (oldStart < oldEnd) {
234
+ splice.removed.push(old[oldStart++]);
235
+ }
236
+ return [splice];
237
+ }
238
+ else if (oldStart === oldEnd) {
239
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
240
+ }
241
+ const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
242
+ const splices = [];
243
+ let splice = void 0;
244
+ let index = currentStart;
245
+ let oldIndex = oldStart;
246
+ for (let i = 0; i < ops.length; ++i) {
247
+ switch (ops[i]) {
248
+ case 0 /* Edit.leave */:
249
+ if (splice !== void 0) {
250
+ splices.push(splice);
251
+ splice = void 0;
252
+ }
253
+ index++;
254
+ oldIndex++;
255
+ break;
256
+ case 1 /* Edit.update */:
257
+ if (splice === void 0) {
258
+ splice = new Splice(index, [], 0);
259
+ }
260
+ splice.addedCount++;
261
+ index++;
262
+ splice.removed.push(old[oldIndex]);
263
+ oldIndex++;
264
+ break;
265
+ case 2 /* Edit.add */:
266
+ if (splice === void 0) {
267
+ splice = new Splice(index, [], 0);
268
+ }
269
+ splice.addedCount++;
270
+ index++;
271
+ break;
272
+ case 3 /* Edit.delete */:
273
+ if (splice === void 0) {
274
+ splice = new Splice(index, [], 0);
275
+ }
276
+ splice.removed.push(old[oldIndex]);
277
+ oldIndex++;
278
+ break;
279
+ // no default
280
+ }
281
+ }
282
+ if (splice !== void 0) {
283
+ splices.push(splice);
284
+ }
285
+ return splices;
286
+ }
287
+ function merge(splice, splices) {
288
+ let inserted = false;
289
+ let insertionOffset = 0;
290
+ for (let i = 0; i < splices.length; i++) {
291
+ const current = splices[i];
292
+ current.index += insertionOffset;
293
+ if (inserted) {
294
+ continue;
295
+ }
296
+ const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
297
+ if (intersectCount >= 0) {
298
+ // Merge the two splices
299
+ splices.splice(i, 1);
300
+ i--;
301
+ insertionOffset -= current.addedCount - current.removed.length;
302
+ splice.addedCount += current.addedCount - intersectCount;
303
+ const deleteCount = splice.removed.length + current.removed.length - intersectCount;
304
+ if (!splice.addedCount && !deleteCount) {
305
+ // merged splice is a noop. discard.
306
+ inserted = true;
307
+ }
308
+ else {
309
+ let currentRemoved = current.removed;
310
+ if (splice.index < current.index) {
311
+ // some prefix of splice.removed is prepended to current.removed.
312
+ const prepend = splice.removed.slice(0, current.index - splice.index);
313
+ prepend.push(...currentRemoved);
314
+ currentRemoved = prepend;
315
+ }
316
+ if (splice.index + splice.removed.length >
317
+ current.index + current.addedCount) {
318
+ // some suffix of splice.removed is appended to current.removed.
319
+ const append = splice.removed.slice(current.index + current.addedCount - splice.index);
320
+ currentRemoved.push(...append);
321
+ }
322
+ splice.removed = currentRemoved;
323
+ if (current.index < splice.index) {
324
+ splice.index = current.index;
325
+ }
326
+ }
327
+ }
328
+ else if (splice.index < current.index) {
329
+ // Insert splice here.
330
+ inserted = true;
331
+ splices.splice(i, 0, splice);
332
+ i++;
333
+ const offset = splice.addedCount - splice.removed.length;
334
+ current.index += offset;
335
+ insertionOffset += offset;
336
+ }
337
+ }
338
+ if (!inserted) {
339
+ splices.push(splice);
340
+ }
341
+ }
342
+ function project(array, changes) {
343
+ let splices = [];
344
+ const initialSplices = [];
345
+ for (let i = 0, ii = changes.length; i < ii; i++) {
346
+ merge(changes[i], initialSplices);
347
+ }
348
+ for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
349
+ const splice = initialSplices[i];
350
+ if (splice.addedCount === 1 && splice.removed.length === 1) {
351
+ if (splice.removed[0] !== array[splice.index]) {
352
+ splices.push(splice);
353
+ }
354
+ continue;
355
+ }
356
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
357
+ }
358
+ return splices;
359
+ }
360
+ /**
361
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
362
+ * splices needed to represent the change from the old array to the new array.
363
+ * @public
364
+ */
70
365
  let defaultSpliceStrategy = Object.freeze({
71
- support: SpliceStrategySupport.splice,
366
+ support: SpliceStrategySupport.optimized,
72
367
  normalize(previous, current, changes) {
73
- return previous === void 0 ? changes !== null && changes !== void 0 ? changes : emptyArray : resetSplices;
368
+ if (previous === void 0) {
369
+ if (changes === void 0) {
370
+ return emptyArray;
371
+ }
372
+ return changes.length > 1 ? project(current, changes) : changes;
373
+ }
374
+ return resetSplices;
74
375
  },
75
376
  pop(array, observer, pop, args) {
76
377
  const notEmpty = array.length > 0;
@@ -1,16 +1,30 @@
1
- import { isFunction, isString, } from "../interfaces.js";
2
- import { FAST } from "../platform.js";
1
+ import { isFunction, isString, KernelServiceId, noop, } from "../interfaces.js";
2
+ import { createMetadataLocator, FAST } from "../platform.js";
3
3
  import { Updates } from "./update-queue.js";
4
4
  import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js";
5
+ /**
6
+ * Describes how the source's lifetime relates to its controller's lifetime.
7
+ * @public
8
+ */
9
+ export const SourceLifetime = Object.freeze({
10
+ /**
11
+ * The source to controller lifetime relationship is unknown.
12
+ */
13
+ unknown: void 0,
14
+ /**
15
+ * The source and controller lifetimes are coupled to one another.
16
+ * They can/will be GC'd together.
17
+ */
18
+ coupled: 1,
19
+ });
5
20
  /**
6
21
  * Common Observable APIs.
7
22
  * @public
8
23
  */
9
- export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
24
+ export const Observable = FAST.getById(KernelServiceId.observable, () => {
10
25
  const queueUpdate = Updates.enqueue;
11
26
  const volatileRegex = /(:|&&|\|\||if)/;
12
27
  const notifierLookup = new WeakMap();
13
- const accessorLookup = new WeakMap();
14
28
  let watcher = void 0;
15
29
  let createArrayObserver = (array) => {
16
30
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -25,19 +39,7 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
25
39
  }
26
40
  return found;
27
41
  }
28
- function getAccessors(target) {
29
- let accessors = accessorLookup.get(target);
30
- if (accessors === void 0) {
31
- let currentTarget = Reflect.getPrototypeOf(target);
32
- while (accessors === void 0 && currentTarget !== null) {
33
- accessors = accessorLookup.get(currentTarget);
34
- currentTarget = Reflect.getPrototypeOf(currentTarget);
35
- }
36
- accessors = accessors === void 0 ? [] : accessors.slice(0);
37
- accessorLookup.set(target, accessors);
38
- }
39
- return accessors;
40
- }
42
+ const getAccessors = createMetadataLocator();
41
43
  class DefaultObservableAccessor {
42
44
  constructor(name) {
43
45
  this.name = name;
@@ -63,10 +65,10 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
63
65
  }
64
66
  }
65
67
  }
66
- class BindingObserverImplementation extends SubscriberSet {
67
- constructor(binding, initialSubscriber, isVolatileBinding = false) {
68
- super(binding, initialSubscriber);
69
- this.binding = binding;
68
+ class ExpressionNotifierImplementation extends SubscriberSet {
69
+ constructor(expression, initialSubscriber, isVolatileBinding = false) {
70
+ super(expression, initialSubscriber);
71
+ this.expression = expression;
70
72
  this.isVolatileBinding = isVolatileBinding;
71
73
  this.needsRefresh = true;
72
74
  this.needsQueue = true;
@@ -77,10 +79,30 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
77
79
  this.propertyName = void 0;
78
80
  this.notifier = void 0;
79
81
  this.next = void 0;
82
+ /**
83
+ * Opts out of JSON stringification.
84
+ */
85
+ this.toJSON = noop;
80
86
  }
81
87
  setMode(isAsync) {
82
88
  this.isAsync = this.needsQueue = isAsync;
83
89
  }
90
+ bind(controller) {
91
+ this.controller = controller;
92
+ const value = this.observe(controller.source, controller.context);
93
+ if (!controller.isBound && this.requiresUnbind(controller)) {
94
+ controller.onUnbind(this);
95
+ }
96
+ return value;
97
+ }
98
+ requiresUnbind(controller) {
99
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
100
+ this.first !== this.last ||
101
+ this.first.propertySource !== controller.source);
102
+ }
103
+ unbind(controller) {
104
+ this.dispose();
105
+ }
84
106
  observe(source, context) {
85
107
  if (this.needsRefresh && this.last !== null) {
86
108
  this.dispose();
@@ -88,10 +110,19 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
88
110
  const previousWatcher = watcher;
89
111
  watcher = this.needsRefresh ? this : void 0;
90
112
  this.needsRefresh = this.isVolatileBinding;
91
- const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
92
- watcher = previousWatcher;
113
+ let result;
114
+ try {
115
+ result = this.expression(source, context);
116
+ }
117
+ finally {
118
+ watcher = previousWatcher;
119
+ }
93
120
  return result;
94
121
  }
122
+ // backwards compat with v1 kernel
123
+ disconnect() {
124
+ this.dispose();
125
+ }
95
126
  dispose() {
96
127
  if (this.last !== null) {
97
128
  let current = this.first;
@@ -218,22 +249,22 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
218
249
  */
219
250
  getAccessors,
220
251
  /**
221
- * Creates a {@link BindingObserver} that can watch the
222
- * provided {@link Binding} for changes.
223
- * @param binding - The binding to observe.
252
+ * Creates a {@link ExpressionNotifier} that can watch the
253
+ * provided {@link Expression} for changes.
254
+ * @param expression - The binding to observe.
224
255
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
225
256
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
226
257
  */
227
- binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
228
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
258
+ binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
259
+ return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
229
260
  },
230
261
  /**
231
262
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
232
263
  * on every evaluation of the value.
233
- * @param binding - The binding to inspect.
264
+ * @param expression - The binding to inspect.
234
265
  */
235
- isVolatileBinding(binding) {
236
- return volatileRegex.test(binding.toString());
266
+ isVolatileBinding(expression) {
267
+ return volatileRegex.test(expression.toString());
237
268
  },
238
269
  });
239
270
  });
@@ -261,7 +292,7 @@ export function volatile(target, name, descriptor) {
261
292
  },
262
293
  });
263
294
  }
264
- const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
295
+ const contextEvent = FAST.getById(KernelServiceId.contextEvent, () => {
265
296
  let current = null;
266
297
  return {
267
298
  get() {
@@ -276,120 +307,35 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
276
307
  * Provides additional contextual information available to behaviors and expressions.
277
308
  * @public
278
309
  */
279
- export class ExecutionContext {
280
- constructor(parentSource = null, parentContext = null) {
281
- /**
282
- * The index of the current item within a repeat context.
283
- */
284
- this.index = 0;
285
- /**
286
- * The length of the current collection within a repeat context.
287
- */
288
- this.length = 0;
289
- this.parent = parentSource;
290
- this.parentContext = parentContext;
291
- }
292
- /**
293
- * The current event within an event handler.
294
- */
295
- get event() {
296
- return contextEvent.get();
297
- }
298
- /**
299
- * Indicates whether the current item within a repeat context
300
- * has an even index.
301
- */
302
- get isEven() {
303
- return this.index % 2 === 0;
304
- }
305
- /**
306
- * Indicates whether the current item within a repeat context
307
- * has an odd index.
308
- */
309
- get isOdd() {
310
- return this.index % 2 !== 0;
311
- }
310
+ export const ExecutionContext = Object.freeze({
312
311
  /**
313
- * Indicates whether the current item within a repeat context
314
- * is the first item in the collection.
312
+ * A default execution context.
315
313
  */
316
- get isFirst() {
317
- return this.index === 0;
318
- }
319
- /**
320
- * Indicates whether the current item within a repeat context
321
- * is somewhere in the middle of the collection.
322
- */
323
- get isInMiddle() {
324
- return !this.isFirst && !this.isLast;
325
- }
326
- /**
327
- * Indicates whether the current item within a repeat context
328
- * is the last item in the collection.
329
- */
330
- get isLast() {
331
- return this.index === this.length - 1;
332
- }
333
- /**
334
- * Returns the typed event detail of a custom event.
335
- */
336
- eventDetail() {
337
- return this.event.detail;
338
- }
339
- /**
340
- * Returns the typed event target of the event.
341
- */
342
- eventTarget() {
343
- return this.event.target;
344
- }
345
- /**
346
- * Updates the position/size on a context associated with a list item.
347
- * @param index - The new index of the item.
348
- * @param length - The new length of the list.
349
- */
350
- updatePosition(index, length) {
351
- this.index = index;
352
- this.length = length;
353
- }
354
- /**
355
- * Creates a new execution context descendent from the current context.
356
- * @param source - The source for the context if different than the parent.
357
- * @returns A child execution context.
358
- */
359
- createChildContext(parentSource) {
360
- return new ExecutionContext(parentSource, this);
361
- }
314
+ default: {
315
+ index: 0,
316
+ length: 0,
317
+ get event() {
318
+ return ExecutionContext.getEvent();
319
+ },
320
+ eventDetail() {
321
+ return this.event.detail;
322
+ },
323
+ eventTarget() {
324
+ return this.event.target;
325
+ },
326
+ },
362
327
  /**
363
- * Creates a new execution context descent suitable for use in list rendering.
364
- * @param item - The list item to serve as the source.
365
- * @param index - The index of the item in the list.
366
- * @param length - The length of the list.
328
+ * Gets the current event.
329
+ * @returns An event object.
367
330
  */
368
- createItemContext(index, length) {
369
- const childContext = Object.create(this);
370
- childContext.index = index;
371
- childContext.length = length;
372
- return childContext;
373
- }
331
+ getEvent() {
332
+ return contextEvent.get();
333
+ },
374
334
  /**
375
- * Sets the event for the current execution context.
376
- * @param event - The event to set.
377
- * @internal
335
+ * Sets the current event.
336
+ * @param event - An event object.
378
337
  */
379
- static setEvent(event) {
338
+ setEvent(event) {
380
339
  contextEvent.set(event);
381
- }
382
- /**
383
- * Creates a new root execution context.
384
- * @returns A new execution context.
385
- */
386
- static create() {
387
- return new ExecutionContext();
388
- }
389
- }
390
- /**
391
- * The default execution context.
392
- */
393
- ExecutionContext.default = new ExecutionContext();
394
- Observable.defineProperty(ExecutionContext.prototype, "index");
395
- Observable.defineProperty(ExecutionContext.prototype, "length");
340
+ },
341
+ });
@@ -1,10 +1,10 @@
1
- import "../interfaces.js";
1
+ import { KernelServiceId } from "../interfaces.js";
2
2
  import { FAST } from "../platform.js";
3
3
  /**
4
4
  * The default UpdateQueue.
5
5
  * @public
6
6
  */
7
- export const Updates = FAST.getById(1 /* KernelServiceId.updateQueue */, () => {
7
+ export const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
8
8
  const tasks = [];
9
9
  const pendingErrors = [];
10
10
  const rAF = globalThis.requestAnimationFrame;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A concrete implementation of {@link PendingTask}
3
+ * @beta
4
+ */
5
+ export class PendingTaskEvent extends Event {
6
+ constructor(complete) {
7
+ super(PendingTaskEvent.type, { bubbles: true, composed: true });
8
+ this.complete = complete;
9
+ }
10
+ static isPendingTask(value) {
11
+ var _a;
12
+ return (value.type === PendingTaskEvent.type &&
13
+ typeof ((_a = value.complete) === null || _a === void 0 ? void 0 : _a.then) === "function");
14
+ }
15
+ }
16
+ PendingTaskEvent.type = "pending-task";