@webqit/observer 3.8.2 → 3.8.3
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/README copy.md +500 -0
- package/README.md +918 -284
- package/package.json +1 -1
package/README copy.md
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
# The Observer API
|
|
2
|
+
|
|
3
|
+
<!-- BADGES/ -->
|
|
4
|
+
|
|
5
|
+
<span class="badge-npmversion"><a href="https://npmjs.org/package/@webqit/observer" title="View this project on NPM"><img src="https://img.shields.io/npm/v/@webqit/observer.svg" alt="NPM version" /></a></span> <span class="badge-npmdownloads"><a href="https://npmjs.org/package/@webqit/observer" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/@webqit/observer.svg" alt="NPM downloads" /></a></span>
|
|
6
|
+
|
|
7
|
+
<!-- /BADGES -->
|
|
8
|
+
|
|
9
|
+
**[Motivation](#motivation) • [Overview](#an-overview) • [Documentation](#documentation) • [Polyfill](#the-polyfill) • [Getting Involved](#getting-involved) • [License](#license)**
|
|
10
|
+
|
|
11
|
+
Observe and intercept operations on arbitrary JavaScript objects and arrays using a utility-first, general-purpose reactivity API! This API re-explores the unique design of the [`Object.observe()`](https://web.dev/es7-observe/) API and takes a stab at what could be **a unifying API** over *related but disparate* things like `Object.observe()`, [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) APIs, and the "traps" API (proxy traps)!
|
|
12
|
+
|
|
13
|
+
Observer API is an upcoming proposal!
|
|
14
|
+
|
|
15
|
+
## Motivation
|
|
16
|
+
|
|
17
|
+
Tracking mutations on JavaScript objects has historically relied on "object wrapping" techniques with [ES6 Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), and on "property mangling" techniques with [getters and setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). Besides how the first poses an *object identity* problem and the second, an *interoperability* problem, there is also much inflexibility in the programming model that each enables!
|
|
18
|
+
|
|
19
|
+
This is discussed extensively in [the introductory blog post](https://dev.to/oxharris/re-exploring-reactivity-and-introducing-the-observer-api-and-reflex-functions-4h70)
|
|
20
|
+
|
|
21
|
+
We find a design precedent to object observability in the [`Object.observe()`](https://web.dev/es7-observe/) API, which at one time checked all the boxes and touched the very pain points we have today! The idea with the new **Observer API** is to re-explore that unique design with a more wholistic approach that considers the broader subject of Reactive Programming in JavaScript!
|
|
22
|
+
|
|
23
|
+
## Status
|
|
24
|
+
|
|
25
|
+
+ Working implementation via a polyfill
|
|
26
|
+
+ Integral to the [Quantum JS project](https://github.com/webqit/quantum-js)
|
|
27
|
+
+ Actively developed
|
|
28
|
+
+ Open to contributions
|
|
29
|
+
|
|
30
|
+
## An Overview
|
|
31
|
+
|
|
32
|
+
The Observer API is a set of utility functions - notably, the `Observer.observe()` and `Observer.intercept()` methods - for all things object observability.
|
|
33
|
+
|
|
34
|
+
<details><summary>This is documentation for Observer@2.x</summary>
|
|
35
|
+
|
|
36
|
+
Looking for [`Observer@1.x`](https://github.com/webqit/observer/tree/v1.7.6)?
|
|
37
|
+
|
|
38
|
+
</details>
|
|
39
|
+
|
|
40
|
+
### Method: `Observer.observe()`
|
|
41
|
+
|
|
42
|
+
Observe mutations on arbitrary objects or arrays!
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
// An object
|
|
46
|
+
const obj = {};
|
|
47
|
+
// Mtation observer on an object
|
|
48
|
+
const abortController = Observer.observe( obj, inspect );
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// An array
|
|
53
|
+
const arr = [];
|
|
54
|
+
// Mtation observer on an array
|
|
55
|
+
const abortController = Observer.observe( arr, inspect );
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
└ *Changes are delivered [**synchronously**](https://github.com/webqit/observer/wiki/#timing-and-batching) - as they happen.*
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
// The change handler
|
|
62
|
+
function inspect( mutations ) {
|
|
63
|
+
mutations.forEach( mutation => {
|
|
64
|
+
console.log( mutation.type, mutation.key, mutation.value, mutation.oldValue );
|
|
65
|
+
} );
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**-->** Stop observing at any time by calling `abort()` on the returned *abortController*:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
// Remove listener
|
|
73
|
+
abortController.abort();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
└ And you can provide your own [Abort Signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) instance:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
// Providing an AbortSignal
|
|
80
|
+
const abortController = new AbortController;
|
|
81
|
+
Observer.observe( obj, inspect, { signal: abortController.signal } );
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// Abort at any time
|
|
86
|
+
abortController.abort();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**-->** Where listeners initiate nested observers (child observers), leverage "AbortSignal-cascading" to tie child observers to parent observer's lifecycle:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
// Parent -
|
|
93
|
+
const abortController = Observer.observe( obj, ( mutations, flags ) => {
|
|
94
|
+
|
|
95
|
+
// Child
|
|
96
|
+
Observer.observe( obj, inspect, { signal: flags.signal } ); // <<<---- AbortSignal-cascading
|
|
97
|
+
|
|
98
|
+
// Child
|
|
99
|
+
Observer.observe( obj, inspect, { signal: flags.signal } ); // <<<---- AbortSignal-cascading
|
|
100
|
+
|
|
101
|
+
} );
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
└ *"Child" observers get automatically aborted at parent's "next turn", and at parent's own abortion!*
|
|
105
|
+
|
|
106
|
+
**-->** Use the `options.diff` parameter to ignore mutation events whose current value is same as previous value:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
// Parent -
|
|
110
|
+
const abortController = Observer.observe( obj, mutations => {
|
|
111
|
+
console.log( m.type, m.value, m.oldValue );
|
|
112
|
+
}, { diff: true } );
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
obj.property = 'Same value';
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
obj.property = 'Same value';
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
└ *Observer is called only on the first update!*
|
|
124
|
+
|
|
125
|
+
#### Concept: *Mutation APIs*
|
|
126
|
+
|
|
127
|
+
In addition to making literal operations, you can also programmatically mutate properties of an object using the *[Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect#static_methods)-like* set of operators; each operation will be reported by observers:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
// A single "set" operation on an object
|
|
131
|
+
Observer.set( obj, 'prop0', 'value0' );
|
|
132
|
+
Observer.defineProperty( obj, 'prop1', { get: () => 'value1' } );
|
|
133
|
+
Observer.deleteProperty( obj, 'prop2' );
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
// A single "set" operation on an array
|
|
138
|
+
Observer.set( arr, 0, 'item0' ); // Array [ 'item0' ]
|
|
139
|
+
Observer.deleteProperty( arr, 0 ); // Array [ <1 empty slot> ]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
<details><summary>Polyfill limitations</summary>
|
|
143
|
+
|
|
144
|
+
In the polyfill, object observability doesn't work with literal operations. **Beware non-reactive operations**:
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
// Literal object operators
|
|
148
|
+
delete obj.prop0;
|
|
149
|
+
obj.prop3 = 'value3';
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
// Array methods
|
|
154
|
+
arr.push( 'item3' );
|
|
155
|
+
arr.pop();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
</details>
|
|
159
|
+
|
|
160
|
+
**-->** Enable reactivity on *specific* properties with literal *object accessors* - using the `Observer.accessorize()` method:
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
// Accessorize all current enumerable properties
|
|
164
|
+
Observer.accessorize( obj );
|
|
165
|
+
// Accessorize specific properties (existing or new)
|
|
166
|
+
Observer.accessorize( obj, [ 'prop0', 'prop1', 'prop2' ] );
|
|
167
|
+
|
|
168
|
+
// Make reactive UPDATES
|
|
169
|
+
obj.prop0 = 'value0';
|
|
170
|
+
obj.prop1 = 'value1';
|
|
171
|
+
obj.prop2 = 'value2';
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
// Accessorize all current indexes
|
|
176
|
+
Observer.accessorize( arr );
|
|
177
|
+
// Accessorize specific indexes (existing or new)
|
|
178
|
+
Observer.accessorize( arr, [ 0, 1, 2 ] );
|
|
179
|
+
|
|
180
|
+
// Make reactive UPDATES
|
|
181
|
+
arr[ 0 ] = 'item0';
|
|
182
|
+
arr[ 1 ] = 'item1';
|
|
183
|
+
arr[ 2 ] = 'item2';
|
|
184
|
+
|
|
185
|
+
// Bonus reactivity with array methods that re-index existing items
|
|
186
|
+
arr.unshift( 'new-item0' );
|
|
187
|
+
arr.shift();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
<details><summary>Polyfill limitations</summary>
|
|
191
|
+
|
|
192
|
+
In the polyfill, object observability doesn't work with literal operations. **Beware non-reactive operations**:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
// The delete operator and object properties that haven't been accessorized
|
|
196
|
+
delete obj.prop0;
|
|
197
|
+
obj.prop3 = 'value3';
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
// Array methods that do not re-index existing items
|
|
202
|
+
arr.push( 'item0' );
|
|
203
|
+
arr.pop();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
</details>
|
|
207
|
+
|
|
208
|
+
**-->** Enable reactivity on *arbitray* properties with *Proxies* - using the `Observer.proxy()` method:
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
// Obtain a reactive Proxy for an object
|
|
212
|
+
const $obj = Observer.proxy( obj );
|
|
213
|
+
|
|
214
|
+
// Make reactive operations
|
|
215
|
+
$obj.prop1 = 'value1';
|
|
216
|
+
$obj.prop4 = 'value4';
|
|
217
|
+
$obj.prop8 = 'value8';
|
|
218
|
+
|
|
219
|
+
// With the delete operator
|
|
220
|
+
delete $obj.prop0;
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
// Obtain a reactive Proxy for an array
|
|
225
|
+
const $arr = Observer.proxy( arr );
|
|
226
|
+
|
|
227
|
+
// Make reactive operations
|
|
228
|
+
$arr[ 0 ] = 'item0';
|
|
229
|
+
$arr[ 1 ] = 'item1';
|
|
230
|
+
$arr[ 2 ] = 'item2';
|
|
231
|
+
|
|
232
|
+
// With an instance method
|
|
233
|
+
$arr.push( 'item3' );
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
└ *And no problem if you end up nesting the approaches.*
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
// 'value1'-->obj
|
|
240
|
+
Observer.accessorize( obj, [ 'prop0', 'prop1', 'prop2', ] );
|
|
241
|
+
obj.prop1 = 'value1';
|
|
242
|
+
|
|
243
|
+
// 'value1'-->$obj-->obj
|
|
244
|
+
let $obj = Observer.proxy( obj );
|
|
245
|
+
$obj.prop1 = 'value1';
|
|
246
|
+
|
|
247
|
+
// 'value1'-->set()-->$obj-->obj
|
|
248
|
+
Observer.set( $obj, 'prop1', 'value1' );
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**-->** "Restore" accessorized properties to their normal state by calling the `unaccessorize()` method:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
Observer.unaccessorize( obj, [ 'prop1', 'prop6', 'prop10' ] );
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**-->** "Reproduce" original objects from Proxies obtained via `Observer.proxy()` by calling the `unproxy()` method:
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
obj = Observer.unproxy( $obj );
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### Concept: *Paths*
|
|
264
|
+
|
|
265
|
+
Observe "a property" at a path in an object tree:
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
const obj = {
|
|
269
|
+
level1: {
|
|
270
|
+
level2: 'level2-value',
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
const path = Observer.path( 'level1', 'level2' );
|
|
277
|
+
Observer.observe( obj, path, m => {
|
|
278
|
+
console.log( m.type, m.path, m.value, m.isUpdate );
|
|
279
|
+
} );
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
Observer.set( obj.level1, 'level2', 'level2-new-value' );
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
<details><summary>Console</summary>
|
|
287
|
+
|
|
288
|
+
| type | path | value | isUpdate |
|
|
289
|
+
| ---- | ---- | ----- | -------- |
|
|
290
|
+
| `set` | [ `level1`, `level2`, ] | `level2-new-value` | `true` |
|
|
291
|
+
|
|
292
|
+
</details>
|
|
293
|
+
|
|
294
|
+
└ *And the initial tree structure can be whatever*:
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
// A tree structure that is yet to be built
|
|
298
|
+
const obj = {};
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
```js
|
|
302
|
+
const path = Observer.path( 'level1', 'level2', 'level3', 'level4' );
|
|
303
|
+
Observer.observe( obj, path, m => {
|
|
304
|
+
console.log( m.type, m.path, m.value, m.isUpdate );
|
|
305
|
+
} );
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
└ *Now, any operation that changes what "the value" at the path resolves to - either by tree extension or tree truncation - will fire our listener*:
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
Observer.set( obj, 'level1', { level2: {}, } );
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
<details>
|
|
315
|
+
<summary>Console</summary>
|
|
316
|
+
|
|
317
|
+
| type | path | value | isUpdate |
|
|
318
|
+
| ---- | ---- | ----- | -------- |
|
|
319
|
+
| `set` | [ `level1`, `level2`, `level3`, `level4`, ] | `undefined` | `false` |
|
|
320
|
+
|
|
321
|
+
</details>
|
|
322
|
+
|
|
323
|
+
└ *Meanwhile, this next one completes the tree, and the listener reports a value at its observed path*:
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
Observer.set( obj.level1, 'level2', { level3: { level4: 'level4-value', }, } );
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
<details>
|
|
330
|
+
<summary>Console</summary>
|
|
331
|
+
|
|
332
|
+
| type | path | value | isUpdate |
|
|
333
|
+
| ---- | ---- | ----- | -------- |
|
|
334
|
+
| `set` | [ `level1`, `level2`, `level3`, `level4`, ] | `level4-value` | `false` |
|
|
335
|
+
|
|
336
|
+
</details>
|
|
337
|
+
|
|
338
|
+
**-->** Use the event's `context` property to inspect the parent event if you were to find the exact point at which mutation happened in the path in an audit trail:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
let context = m.context;
|
|
342
|
+
console.log(context);
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
└ *And up again one level until the root event*:
|
|
346
|
+
|
|
347
|
+
```js
|
|
348
|
+
let parentContext = context.context;
|
|
349
|
+
console.log(parentContext);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**-->** Observe trees that are built *asynchronously*! Where a promise is encountered along the path, further access is paused until promise resolves:
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
Observer.set( obj.level1, 'level2', Promise.resolve( { level3: { level4: 'level4-value', }, } ) );
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### Concept: *Batch Mutations*
|
|
359
|
+
|
|
360
|
+
Make multiple mutations at a go, and they'll be correctly delivered as a batch to observers!
|
|
361
|
+
|
|
362
|
+
```js
|
|
363
|
+
// Batch operations on an object
|
|
364
|
+
Observer.set( obj, {
|
|
365
|
+
prop0: 'value0',
|
|
366
|
+
prop1: 'value1',
|
|
367
|
+
prop2: 'value2',
|
|
368
|
+
} );
|
|
369
|
+
Observer.defineProperties( obj, {
|
|
370
|
+
prop0: { value: 'value0' },
|
|
371
|
+
prop1: { value: 'value1' },
|
|
372
|
+
prop2: { get: () => 'value2' },
|
|
373
|
+
} );
|
|
374
|
+
Observer.deleteProperties( obj, [ 'prop0', 'prop1', 'prop2' ] );
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
```js
|
|
378
|
+
// Batch operations on an array
|
|
379
|
+
Observer.set( arr, {
|
|
380
|
+
'0': 'item0',
|
|
381
|
+
'1': 'item1',
|
|
382
|
+
'2': 'item2',
|
|
383
|
+
} );
|
|
384
|
+
Object.proxy( arr ).push( 'item3', 'item4', 'item5', );
|
|
385
|
+
Object.proxy( arr ).unshift( 'new-item0' );
|
|
386
|
+
Object.proxy( arr ).splice( 0 );
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**-->** Use the `Observer.batch()` to batch multiple arbitrary mutations - whether related or not:
|
|
390
|
+
|
|
391
|
+
```js
|
|
392
|
+
Observer.batch( arr, async () => {
|
|
393
|
+
Observer.set( arr, 0, 'item0' ); // Array [ 'item0' ]
|
|
394
|
+
await somePromise();
|
|
395
|
+
Observer.set( arr, 2, 'item2' ); // Array [ 'item0', <1 empty slot>, 'item2' ]
|
|
396
|
+
} );
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
> Method calls on a proxied instance - e.g. `Object.proxy( arr ).splice( 0 )` - also follow this strategy.
|
|
400
|
+
|
|
401
|
+
### Method: `Observer.intercept()`
|
|
402
|
+
|
|
403
|
+
Intercept operations on any object or array before they happen! This helps you extend standard operations on an object - `Observer.set()`, `Observer.deleteProperty()`, etc - using Proxy-like traps.
|
|
404
|
+
|
|
405
|
+
└ *Below, we intercept all "set" operations for an HTTP URL then transform it to an HTTPS URL.*
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
const setTrap = ( operation, previous, next ) => {
|
|
409
|
+
if ( operation.key === 'url' && operation.value.startsWith( 'http:' ) ) {
|
|
410
|
+
operation.value = operation.value.replace( 'http:', 'https:' );
|
|
411
|
+
}
|
|
412
|
+
return next();
|
|
413
|
+
};
|
|
414
|
+
Observer.intercept( obj, 'set', setTrap );
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
└ *Now, only the first of the following will fly as-is.*
|
|
418
|
+
|
|
419
|
+
```js
|
|
420
|
+
// Not transformed
|
|
421
|
+
Observer.set( obj, 'url', 'https://webqit.io' );
|
|
422
|
+
|
|
423
|
+
// Transformed
|
|
424
|
+
Observer.set( obj, 'url', 'http://webqit.io' );
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
└ *And below, we intercept all "get" operations for a certain value to trigger a network fetch behind the scenes.*
|
|
428
|
+
|
|
429
|
+
```js
|
|
430
|
+
const getTrap = ( operation, previous, next ) => {
|
|
431
|
+
if ( operation.key === 'token' ) {
|
|
432
|
+
return next( fetch( tokenUrl ) );
|
|
433
|
+
}
|
|
434
|
+
return next();
|
|
435
|
+
};
|
|
436
|
+
Observer.intercept( obj, 'get', getTrap );
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
└ *And all of that can go into one "traps" object:*
|
|
440
|
+
|
|
441
|
+
```js
|
|
442
|
+
Observer.intercept( obj, {
|
|
443
|
+
get: getTrap,
|
|
444
|
+
set: setTrap,
|
|
445
|
+
deleteProperty: deletePropertyTrap,
|
|
446
|
+
defineProperty: definePropertyTrap,
|
|
447
|
+
ownKeys: ownKeysTrap,
|
|
448
|
+
has: hasTrap,
|
|
449
|
+
// etc
|
|
450
|
+
} );
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Documentation
|
|
454
|
+
|
|
455
|
+
Visit the [docs](https://github.com/webqit/observer/wiki) for full details - including [Reflect API Supersets](https://github.com/webqit/observer/wiki#featuring-reflect-api-supersets), [Timing and Batching](https://github.com/webqit/observer/wiki#timing-and-batching), [API Reference](https://github.com/webqit/observer/wiki#putting-it-all-together), etc.
|
|
456
|
+
|
|
457
|
+
## The Polyfill
|
|
458
|
+
|
|
459
|
+
The Observer API is being developed as something to be used today - via a polyfill. The polyfill features all of what's documented - with limitations in the area of making mutations: you can only make mutations using the [Mutation APIs](#concept-mutation-apis).
|
|
460
|
+
|
|
461
|
+
<details><summary>Load from a CDN</summary>
|
|
462
|
+
|
|
463
|
+
```html
|
|
464
|
+
<script src="https://unpkg.com/@webqit/observer/dist/main.js"></script>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
> `4.4` kB min + gz | `13.9` KB min [↗](https://bundlephobia.com/package/@webqit/observer)
|
|
468
|
+
|
|
469
|
+
```js
|
|
470
|
+
// Obtain the APIs
|
|
471
|
+
const Observer = window.webqit.Observer;
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
</details>
|
|
475
|
+
|
|
476
|
+
<details><summary>Install from NPM</summary>
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
npm i @webqit/observer
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
```js
|
|
483
|
+
// Import
|
|
484
|
+
import Observer from '@webqit/observer';;
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
</details>
|
|
488
|
+
|
|
489
|
+
## Getting Involved
|
|
490
|
+
|
|
491
|
+
All forms of contributions are welcome at this time. For example, implementation details are all up for discussion. And here are specific links:
|
|
492
|
+
|
|
493
|
+
+ [Project](https://github.com/webqit/observer)
|
|
494
|
+
+ [Documentation](https://github.com/webqit/observer/wiki)
|
|
495
|
+
+ [Discusions](https://github.com/webqit/observer/discussions)
|
|
496
|
+
+ [Issues](https://github.com/webqit/observer/issues)
|
|
497
|
+
|
|
498
|
+
## License
|
|
499
|
+
|
|
500
|
+
MIT.
|