@microsoft/fast-element 1.7.0 → 1.8.0

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.
package/CHANGELOG.json CHANGED
@@ -2,7 +2,52 @@
2
2
  "name": "@microsoft/fast-element",
3
3
  "entries": [
4
4
  {
5
- "date": "Sun, 23 Jan 2022 07:11:35 GMT",
5
+ "date": "Tue, 08 Mar 2022 07:10:44 GMT",
6
+ "tag": "@microsoft/fast-element_v1.8.0",
7
+ "version": "1.8.0",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "comment": "feat: enable multiple fast-element instances in browser at once",
12
+ "author": "roeisenb@microsoft.com",
13
+ "commit": "0e506c6c67a8a7d75e4ef9cfbd000f9da810dc14",
14
+ "package": "@microsoft/fast-element"
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Fri, 25 Feb 2022 17:09:32 GMT",
21
+ "tag": "@microsoft/fast-element_v1.7.2",
22
+ "version": "1.7.2",
23
+ "comments": {
24
+ "patch": [
25
+ {
26
+ "comment": "fix: defend against for/in use on arrays",
27
+ "author": "roeisenb@microsoft.com",
28
+ "commit": "76a9c866277cd13a92a9ca6b3518eae2fb625d79",
29
+ "package": "@microsoft/fast-element"
30
+ }
31
+ ]
32
+ }
33
+ },
34
+ {
35
+ "date": "Thu, 24 Feb 2022 22:21:55 GMT",
36
+ "tag": "@microsoft/fast-element_v1.7.1",
37
+ "version": "1.7.1",
38
+ "comments": {
39
+ "patch": [
40
+ {
41
+ "comment": "fix: prevent duplicative array observation patch",
42
+ "author": "roeisenb@microsoft.com",
43
+ "commit": "9dfb9bb20afa48320eef2c6157e26ec2b10cfd3e",
44
+ "package": "@microsoft/fast-element"
45
+ }
46
+ ]
47
+ }
48
+ },
49
+ {
50
+ "date": "Sun, 23 Jan 2022 07:13:56 GMT",
6
51
  "tag": "@microsoft/fast-element_v1.7.0",
7
52
  "version": "1.7.0",
8
53
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,36 @@
1
1
  # Change Log - @microsoft/fast-element
2
2
 
3
- This log was last generated on Sun, 23 Jan 2022 07:11:35 GMT and should not be manually modified.
3
+ This log was last generated on Tue, 08 Mar 2022 07:10:44 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 1.8.0
8
+
9
+ Tue, 08 Mar 2022 07:10:44 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - feat: enable multiple fast-element instances in browser at once (roeisenb@microsoft.com)
14
+
15
+ ## 1.7.2
16
+
17
+ Fri, 25 Feb 2022 17:09:32 GMT
18
+
19
+ ### Patches
20
+
21
+ - fix: defend against for/in use on arrays (roeisenb@microsoft.com)
22
+
23
+ ## 1.7.1
24
+
25
+ Thu, 24 Feb 2022 22:21:55 GMT
26
+
27
+ ### Patches
28
+
29
+ - fix: prevent duplicative array observation patch (roeisenb@microsoft.com)
30
+
7
31
  ## 1.7.0
8
32
 
9
- Sun, 23 Jan 2022 07:11:35 GMT
33
+ Sun, 23 Jan 2022 07:13:56 GMT
10
34
 
11
35
  ### Minor changes
12
36
 
@@ -44,7 +44,7 @@ export declare class FASTElementDefinition<TType extends Function = Function> {
44
44
  /**
45
45
  * Indicates if this element has been defined in at least one registry.
46
46
  */
47
- readonly isDefined: boolean;
47
+ get isDefined(): boolean;
48
48
  /**
49
49
  * The name of the custom element.
50
50
  */
@@ -93,5 +93,5 @@ export declare class FASTElementDefinition<TType extends Function = Function> {
93
93
  * Gets the element definition associated with the specified type.
94
94
  * @param type - The custom element type to retrieve the definition for.
95
95
  */
96
- static forType<TType extends Function>(type: TType): FASTElementDefinition | undefined;
96
+ static readonly forType: <TType_1 extends Function>(key: TType_1) => FASTElementDefinition<Function> | undefined;
97
97
  }
package/dist/dts/dom.d.ts CHANGED
@@ -67,7 +67,7 @@ export declare const DOM: Readonly<{
67
67
  * Schedules DOM update work in the next async batch.
68
68
  * @param callable - The callable function or object to queue.
69
69
  */
70
- queueUpdate(callable: Callable): void;
70
+ queueUpdate: (callable: Callable) => void;
71
71
  /**
72
72
  * Immediately processes all work previously scheduled
73
73
  * through queueUpdate.
@@ -75,7 +75,7 @@ export declare const DOM: Readonly<{
75
75
  * This also forces nextUpdate promises
76
76
  * to resolve.
77
77
  */
78
- processUpdates(): void;
78
+ processUpdates: () => void;
79
79
  /**
80
80
  * Resolves with the next DOM update.
81
81
  */
@@ -20,6 +20,48 @@ export interface Accessor {
20
20
  */
21
21
  setValue(source: any, value: any): void;
22
22
  }
23
+ /**
24
+ * The signature of an arrow function capable of being evaluated
25
+ * as part of a template binding update.
26
+ * @public
27
+ */
28
+ export declare type Binding<TSource = any, TReturn = any, TParent = any> = (source: TSource, context: ExecutionContext<TParent>) => TReturn;
29
+ /**
30
+ * A record of observable property access.
31
+ * @public
32
+ */
33
+ export interface ObservationRecord {
34
+ /**
35
+ * The source object with an observable property that was accessed.
36
+ */
37
+ propertySource: any;
38
+ /**
39
+ * The name of the observable property on {@link ObservationRecord.propertySource} that was accessed.
40
+ */
41
+ propertyName: string;
42
+ }
43
+ /**
44
+ * Enables evaluation of and subscription to a binding.
45
+ * @public
46
+ */
47
+ export interface BindingObserver<TSource = any, TReturn = any, TParent = any> extends Notifier {
48
+ /**
49
+ * Begins observing the binding for the source and returns the current value.
50
+ * @param source - The source that the binding is based on.
51
+ * @param context - The execution context to execute the binding within.
52
+ * @returns The value of the binding.
53
+ */
54
+ observe(source: TSource, context: ExecutionContext<TParent>): TReturn;
55
+ /**
56
+ * Unsubscribe from all dependent observables of the binding.
57
+ */
58
+ disconnect(): void;
59
+ /**
60
+ * Gets {@link ObservationRecord|ObservationRecords} that the {@link BindingObserver}
61
+ * is observing.
62
+ */
63
+ records(): IterableIterator<ObservationRecord>;
64
+ }
23
65
  /**
24
66
  * Common Observable APIs.
25
67
  * @public
@@ -34,7 +76,7 @@ export declare const Observable: Readonly<{
34
76
  * Gets a notifier for an object or Array.
35
77
  * @param source - The object or Array to get the notifier for.
36
78
  */
37
- getNotifier(source: any): Notifier;
79
+ getNotifier: (source: any) => Notifier;
38
80
  /**
39
81
  * Records a property change for a source object.
40
82
  * @param source - The object to record the change against.
@@ -64,7 +106,7 @@ export declare const Observable: Readonly<{
64
106
  * including its prototype chain.
65
107
  * @param target - The target object to search for accessor on.
66
108
  */
67
- getAccessors(target: {}): Accessor[];
109
+ getAccessors: (target: {}) => Accessor[];
68
110
  /**
69
111
  * Creates a {@link BindingObserver} that can watch the
70
112
  * provided {@link Binding} for changes.
@@ -95,11 +137,6 @@ export declare function observable(target: {}, nameOrAccessor: string | Accessor
95
137
  * @public
96
138
  */
97
139
  export declare function volatile(target: {}, name: string | Accessor, descriptor: PropertyDescriptor): PropertyDescriptor;
98
- /**
99
- * @param event - The event to set as current for the context.
100
- * @internal
101
- */
102
- export declare function setCurrentEvent(event: Event | null): void;
103
140
  /**
104
141
  * Provides additional contextual information available to behaviors and expressions.
105
142
  * @public
@@ -150,51 +187,15 @@ export declare class ExecutionContext<TParent = any, TGrandparent = any> {
150
187
  * is the last item in the collection.
151
188
  */
152
189
  get isLast(): boolean;
153
- }
154
- /**
155
- * The default execution context used in binding expressions.
156
- * @public
157
- */
158
- export declare const defaultExecutionContext: ExecutionContext<any, any>;
159
- /**
160
- * The signature of an arrow function capable of being evaluated
161
- * as part of a template binding update.
162
- * @public
163
- */
164
- export declare type Binding<TSource = any, TReturn = any, TParent = any> = (source: TSource, context: ExecutionContext<TParent>) => TReturn;
165
- /**
166
- * A record of observable property access.
167
- * @public
168
- */
169
- export interface ObservationRecord {
170
190
  /**
171
- * The source object with an observable property that was accessed.
172
- */
173
- propertySource: any;
174
- /**
175
- * The name of the observable property on {@link ObservationRecord.propertySource} that was accessed.
191
+ * Sets the event for the current execution context.
192
+ * @param event - The event to set.
193
+ * @internal
176
194
  */
177
- propertyName: string;
195
+ static setEvent(event: Event | null): void;
178
196
  }
179
197
  /**
180
- * Enables evaluation of and subscription to a binding.
198
+ * The default execution context used in binding expressions.
181
199
  * @public
182
200
  */
183
- export interface BindingObserver<TSource = any, TReturn = any, TParent = any> extends Notifier {
184
- /**
185
- * Begins observing the binding for the source and returns the current value.
186
- * @param source - The source that the binding is based on.
187
- * @param context - The execution context to execute the binding within.
188
- * @returns The value of the binding.
189
- */
190
- observe(source: TSource, context: ExecutionContext): TReturn;
191
- /**
192
- * Unsubscribe from all dependent observables of the binding.
193
- */
194
- disconnect(): void;
195
- /**
196
- * Gets {@link ObservationRecord|ObservationRecords} that the {@link BindingObserver}
197
- * is observing.
198
- */
199
- records(): IterableIterator<ObservationRecord>;
200
- }
201
+ export declare const defaultExecutionContext: ExecutionContext<any, any>;
@@ -21,6 +21,23 @@ export declare type TrustedTypes = {
21
21
  */
22
22
  createPolicy(name: string, rules: TrustedTypesPolicy): TrustedTypesPolicy;
23
23
  };
24
+ /**
25
+ * The FAST global.
26
+ * @internal
27
+ */
28
+ export interface FASTGlobal {
29
+ /**
30
+ * The list of loaded versions.
31
+ */
32
+ readonly versions: string[];
33
+ /**
34
+ * Gets a kernel value.
35
+ * @param id - The id to get the value for.
36
+ * @param initialize - Creates the initial value for the id if not already existing.
37
+ */
38
+ getById<T>(id: string | number): T | null;
39
+ getById<T>(id: string | number, initialize: () => T): T;
40
+ }
24
41
  /**
25
42
  * The platform global type.
26
43
  * @public
@@ -30,6 +47,11 @@ export declare type Global = typeof globalThis & {
30
47
  * Enables working with trusted types.
31
48
  */
32
49
  trustedTypes: TrustedTypes;
50
+ /**
51
+ * The FAST global.
52
+ * @internal
53
+ */
54
+ readonly FAST: FASTGlobal;
33
55
  };
34
56
  /**
35
57
  * A reference to globalThis, with support
@@ -37,6 +59,21 @@ export declare type Global = typeof globalThis & {
37
59
  * @public
38
60
  */
39
61
  export declare const $global: Global;
62
+ /**
63
+ * The FAST global.
64
+ * @internal
65
+ */
66
+ export declare const FAST: FASTGlobal;
67
+ /**
68
+ * Core services shared across FAST instances.
69
+ * @internal
70
+ */
71
+ export declare const enum KernelServiceId {
72
+ updateQueue = 1,
73
+ observable = 2,
74
+ contextEvent = 3,
75
+ elementRegistry = 4
76
+ }
40
77
  /**
41
78
  * A readonly, empty array.
42
79
  * @remarks
@@ -1,9 +1,24 @@
1
+ import { FAST } from "../platform";
1
2
  import { Observable } from "../observation/observable";
2
3
  import { ElementStyles } from "../styles/element-styles";
3
4
  import { AttributeDefinition } from "./attributes";
4
5
  const defaultShadowOptions = { mode: "open" };
5
6
  const defaultElementOptions = {};
6
- const fastDefinitions = new Map();
7
+ const fastRegistry = FAST.getById(4 /* elementRegistry */, () => {
8
+ const typeToDefinition = new Map();
9
+ return Object.freeze({
10
+ register(definition) {
11
+ if (typeToDefinition.has(definition.type)) {
12
+ return false;
13
+ }
14
+ typeToDefinition.set(definition.type, definition);
15
+ return true;
16
+ },
17
+ getByType(key) {
18
+ return typeToDefinition.get(key);
19
+ },
20
+ });
21
+ });
7
22
  /**
8
23
  * Defines metadata for a FASTElement.
9
24
  * @public
@@ -55,13 +70,19 @@ export class FASTElementDefinition {
55
70
  ? nameOrConfig.styles
56
71
  : ElementStyles.create([nameOrConfig.styles]);
57
72
  }
73
+ /**
74
+ * Indicates if this element has been defined in at least one registry.
75
+ */
76
+ get isDefined() {
77
+ return !!fastRegistry.getByType(this.type);
78
+ }
58
79
  /**
59
80
  * Defines a custom element based on this definition.
60
81
  * @param registry - The element registry to define the element in.
61
82
  */
62
83
  define(registry = customElements) {
63
84
  const type = this.type;
64
- if (!this.isDefined) {
85
+ if (fastRegistry.register(this)) {
65
86
  const attributes = this.attributes;
66
87
  const proto = type.prototype;
67
88
  for (let i = 0, ii = attributes.length; i < ii; ++i) {
@@ -71,19 +92,15 @@ export class FASTElementDefinition {
71
92
  value: this.observedAttributes,
72
93
  enumerable: true,
73
94
  });
74
- fastDefinitions.set(type, this);
75
- this.isDefined = true;
76
95
  }
77
96
  if (!registry.get(this.name)) {
78
97
  registry.define(this.name, type, this.elementOptions);
79
98
  }
80
99
  return this;
81
100
  }
82
- /**
83
- * Gets the element definition associated with the specified type.
84
- * @param type - The custom element type to retrieve the definition for.
85
- */
86
- static forType(type) {
87
- return fastDefinitions.get(type);
88
- }
89
101
  }
102
+ /**
103
+ * Gets the element definition associated with the specified type.
104
+ * @param type - The custom element type to retrieve the definition for.
105
+ */
106
+ FASTElementDefinition.forType = fastRegistry.getByType;
package/dist/esm/dom.js CHANGED
@@ -1,27 +1,61 @@
1
1
  import { $global } from "./platform";
2
- const updateQueue = [];
2
+ const updateQueue = $global.FAST.getById(1 /* updateQueue */, () => {
3
+ const tasks = [];
4
+ const pendingErrors = [];
5
+ function throwFirstError() {
6
+ if (pendingErrors.length) {
7
+ throw pendingErrors.shift();
8
+ }
9
+ }
10
+ function tryRunTask(task) {
11
+ try {
12
+ task.call();
13
+ }
14
+ catch (error) {
15
+ pendingErrors.push(error);
16
+ setTimeout(throwFirstError, 0);
17
+ }
18
+ }
19
+ function process() {
20
+ const capacity = 1024;
21
+ let index = 0;
22
+ while (index < tasks.length) {
23
+ tryRunTask(tasks[index]);
24
+ index++;
25
+ // Prevent leaking memory for long chains of recursive calls to `DOM.queueUpdate`.
26
+ // If we call `DOM.queueUpdate` within a task scheduled by `DOM.queueUpdate`, the queue will
27
+ // grow, but to avoid an O(n) walk for every task we execute, we don't
28
+ // shift tasks off the queue after they have been executed.
29
+ // Instead, we periodically shift 1024 tasks off the queue.
30
+ if (index > capacity) {
31
+ // Manually shift all values starting at the index back to the
32
+ // beginning of the queue.
33
+ for (let scan = 0, newLength = tasks.length - index; scan < newLength; scan++) {
34
+ tasks[scan] = tasks[scan + index];
35
+ }
36
+ tasks.length -= index;
37
+ index = 0;
38
+ }
39
+ }
40
+ tasks.length = 0;
41
+ }
42
+ function enqueue(callable) {
43
+ if (tasks.length < 1) {
44
+ $global.requestAnimationFrame(process);
45
+ }
46
+ tasks.push(callable);
47
+ }
48
+ return Object.freeze({
49
+ enqueue,
50
+ process,
51
+ });
52
+ });
3
53
  /* eslint-disable */
4
54
  const fastHTMLPolicy = $global.trustedTypes.createPolicy("fast-html", {
5
55
  createHTML: html => html,
6
56
  });
7
57
  /* eslint-enable */
8
58
  let htmlPolicy = fastHTMLPolicy;
9
- // We use a queue so we can ensure errors are thrown in order.
10
- const pendingErrors = [];
11
- function throwFirstError() {
12
- if (pendingErrors.length) {
13
- throw pendingErrors.shift();
14
- }
15
- }
16
- function tryRunTask(task) {
17
- try {
18
- task.call();
19
- }
20
- catch (error) {
21
- pendingErrors.push(error);
22
- setTimeout(throwFirstError, 0);
23
- }
24
- }
25
59
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
26
60
  /** @internal */
27
61
  export const _interpolationStart = `${marker}{`;
@@ -108,12 +142,7 @@ export const DOM = Object.freeze({
108
142
  * Schedules DOM update work in the next async batch.
109
143
  * @param callable - The callable function or object to queue.
110
144
  */
111
- queueUpdate(callable) {
112
- if (updateQueue.length < 1) {
113
- window.requestAnimationFrame(DOM.processUpdates);
114
- }
115
- updateQueue.push(callable);
116
- },
145
+ queueUpdate: updateQueue.enqueue,
117
146
  /**
118
147
  * Immediately processes all work previously scheduled
119
148
  * through queueUpdate.
@@ -121,36 +150,12 @@ export const DOM = Object.freeze({
121
150
  * This also forces nextUpdate promises
122
151
  * to resolve.
123
152
  */
124
- processUpdates() {
125
- const capacity = 1024;
126
- let index = 0;
127
- while (index < updateQueue.length) {
128
- tryRunTask(updateQueue[index]);
129
- index++;
130
- // Prevent leaking memory for long chains of recursive calls to `DOM.queueUpdate`.
131
- // If we call `DOM.queueUpdate` within a task scheduled by `DOM.queueUpdate`, the queue will
132
- // grow, but to avoid an O(n) walk for every task we execute, we don't
133
- // shift tasks off the queue after they have been executed.
134
- // Instead, we periodically shift 1024 tasks off the queue.
135
- if (index > capacity) {
136
- // Manually shift all values starting at the index back to the
137
- // beginning of the queue.
138
- for (let scan = 0, newLength = updateQueue.length - index; scan < newLength; scan++) {
139
- updateQueue[scan] = updateQueue[scan + index];
140
- }
141
- updateQueue.length -= index;
142
- index = 0;
143
- }
144
- }
145
- updateQueue.length = 0;
146
- },
153
+ processUpdates: updateQueue.process,
147
154
  /**
148
155
  * Resolves with the next DOM update.
149
156
  */
150
157
  nextUpdate() {
151
- return new Promise((resolve) => {
152
- DOM.queueUpdate(resolve);
153
- });
158
+ return new Promise(updateQueue.enqueue);
154
159
  },
155
160
  /**
156
161
  * Sets an attribute value on an element.
@@ -26,7 +26,10 @@ class ArrayObserver extends SubscriberSet {
26
26
  this.splices = void 0;
27
27
  this.needsQueue = true;
28
28
  this.call = this.flush;
29
- source.$fastController = this;
29
+ Reflect.defineProperty(source, "$fastController", {
30
+ value: this,
31
+ enumerable: false,
32
+ });
30
33
  }
31
34
  addSplice(splice) {
32
35
  if (this.splices === void 0) {
@@ -80,15 +83,24 @@ export function enableArrayObservation() {
80
83
  Observable.setArrayObserverFactory((collection) => {
81
84
  return new ArrayObserver(collection);
82
85
  });
83
- const arrayProto = Array.prototype;
84
- const pop = arrayProto.pop;
85
- const push = arrayProto.push;
86
- const reverse = arrayProto.reverse;
87
- const shift = arrayProto.shift;
88
- const sort = arrayProto.sort;
89
- const splice = arrayProto.splice;
90
- const unshift = arrayProto.unshift;
91
- arrayProto.pop = function () {
86
+ const proto = Array.prototype;
87
+ // Don't patch Array if it has already been patched
88
+ // by another copy of fast-element.
89
+ if (proto.$fastPatch) {
90
+ return;
91
+ }
92
+ Reflect.defineProperty(proto, "$fastPatch", {
93
+ value: 1,
94
+ enumerable: false,
95
+ });
96
+ const pop = proto.pop;
97
+ const push = proto.push;
98
+ const reverse = proto.reverse;
99
+ const shift = proto.shift;
100
+ const sort = proto.sort;
101
+ const splice = proto.splice;
102
+ const unshift = proto.unshift;
103
+ proto.pop = function () {
92
104
  const notEmpty = this.length > 0;
93
105
  const methodCallResult = pop.apply(this, arguments);
94
106
  const o = this.$fastController;
@@ -97,7 +109,7 @@ export function enableArrayObservation() {
97
109
  }
98
110
  return methodCallResult;
99
111
  };
100
- arrayProto.push = function () {
112
+ proto.push = function () {
101
113
  const methodCallResult = push.apply(this, arguments);
102
114
  const o = this.$fastController;
103
115
  if (o !== void 0) {
@@ -105,7 +117,7 @@ export function enableArrayObservation() {
105
117
  }
106
118
  return methodCallResult;
107
119
  };
108
- arrayProto.reverse = function () {
120
+ proto.reverse = function () {
109
121
  let oldArray;
110
122
  const o = this.$fastController;
111
123
  if (o !== void 0) {
@@ -118,7 +130,7 @@ export function enableArrayObservation() {
118
130
  }
119
131
  return methodCallResult;
120
132
  };
121
- arrayProto.shift = function () {
133
+ proto.shift = function () {
122
134
  const notEmpty = this.length > 0;
123
135
  const methodCallResult = shift.apply(this, arguments);
124
136
  const o = this.$fastController;
@@ -127,7 +139,7 @@ export function enableArrayObservation() {
127
139
  }
128
140
  return methodCallResult;
129
141
  };
130
- arrayProto.sort = function () {
142
+ proto.sort = function () {
131
143
  let oldArray;
132
144
  const o = this.$fastController;
133
145
  if (o !== void 0) {
@@ -140,7 +152,7 @@ export function enableArrayObservation() {
140
152
  }
141
153
  return methodCallResult;
142
154
  };
143
- arrayProto.splice = function () {
155
+ proto.splice = function () {
144
156
  const methodCallResult = splice.apply(this, arguments);
145
157
  const o = this.$fastController;
146
158
  if (o !== void 0) {
@@ -148,7 +160,7 @@ export function enableArrayObservation() {
148
160
  }
149
161
  return methodCallResult;
150
162
  };
151
- arrayProto.unshift = function () {
163
+ proto.unshift = function () {
152
164
  const methodCallResult = unshift.apply(this, arguments);
153
165
  const o = this.$fastController;
154
166
  if (o !== void 0) {