@webqit/observer 2.1.5 → 2.1.7

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 (2) hide show
  1. package/README.md +71 -28
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  **[Motivation](#motivation) • [Overview](#an-overview) • [Polyfill](#the-polyfill) • [Design Discussion](#design-discussion) • [Getting Involved](#getting-involved) • [License](#license)**
10
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 extends it with the best of JavaScript's other [reflection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) and interception APIs - *Proxies*, *accessors* - to support any kind of reactive programming model!
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
12
 
13
13
  Observer API is an upcoming proposal!
14
14
 
@@ -20,13 +20,13 @@ Tracking mutations on JavaScript objects has historically relied on "object wrap
20
20
 
21
21
  + **Programming model**: proxy traps and object accessors only lend themselves to being wired to *one* underlying listenining logic in the entire program. Objects are effectively open to multiple interactions on the outside but "locked" to one observer on the inside, enabling just a "many to one" communication model. This does not correctly reflect the most common usecases where the idea is to have any number of listeners per event, to enable a "many to many" model! It takes yet a non-trivial amount of effort to go from the default model to the one desired.
22
22
 
23
- Interestingly, we at one time had an *object observability* primitive that checked all the boxes and touched the very pain points we have today: the [`Object.observe()`](https://web.dev/es7-observe/) API. So, how about an equivalent API that brings all of the good thinking from `Object.observe()` together with the idea of *Proxies*, *accessors*, and JavaScript's other [*reflection* API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) in one design, delivered as one utility for all things *reactivity*? This is the idea with the new **Observer API**!
23
+ 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! This is the idea with the new **Observer API**!
24
24
 
25
- └ [See more in the introductory blog post](https://dev.to/oxharris/reinvestigating-reactivity-22e0-temp-slug-5973064?preview=8afd0f8b156bf0b0b1c08058837fe4986054e52a7450f0a28adbaf07dcb7f5659b724166f553fb98ceab3d080748e86b244684f515d579bcd0f48cbb)<sup>draft</sup>
25
+ └ [See more in the introductory blog post](https://dev.to/oxharris/reinvestigating-reactivity-22e0-temp-slug-5973064?preview=8afd0f8b156bf0b0b1c08058837fe4986054e52a7450f0a28adbaf07dcb7f5659b724166f553fb98ceab3d080748e86b244684f515d579bcd0f48cbb#introducing-the-observer-api)<sup>draft</sup>
26
26
 
27
27
  ## An Overview
28
28
 
29
- The Observer API comes as a set of utility functions.
29
+ The Observer API is a set of utility functions.
30
30
 
31
31
  + [Method: `Observer.observe()`](#method-observerobserve)
32
32
  + [Usage](#usage)
@@ -53,7 +53,12 @@ Observer.observe( obj, callback[, options = {} ]);
53
53
 
54
54
  ```js
55
55
  // Signature 2
56
- Observer.observe( obj, props, callback[, options = {} ]);
56
+ Observer.observe( obj, [ prop, prop, ... ], callback[, options = {} ]);
57
+ ```
58
+
59
+ ```js
60
+ // Signature 3
61
+ Observer.observe( obj, prop, callback[, options = {} ]);
57
62
  ```
58
63
 
59
64
  #### Usage
@@ -74,7 +79,7 @@ const arr = [];
74
79
  const abortController = Observer.observe( arr, handleChanges );
75
80
  ```
76
81
 
77
- *Now changes will be delivered **synchronously** - as they happen. (The *sync* design is discussed shortly.)*
82
+ *Changes are delivered [**synchronously**](https://dev.to/oxharris/reinvestigating-reactivity-22e0-temp-slug-5973064?preview=8afd0f8b156bf0b0b1c08058837fe4986054e52a7450f0a28adbaf07dcb7f5659b724166f553fb98ceab3d080748e86b244684f515d579bcd0f48cbb#timing-and-batching) - as they happen.*
78
83
 
79
84
  ```js
80
85
  // The change handler
@@ -85,21 +90,19 @@ function handleChanges( mutations ) {
85
90
  }
86
91
  ```
87
92
 
88
- **-->** Stop observing at any time by calling `abort()` on the returned *abortController*...
93
+ **-->** Stop observing at any time by calling `abort()` on the returned *abortController*:
89
94
 
90
95
  ```js
91
96
  // Remove listener
92
97
  abortController.abort();
93
98
  ```
94
99
 
95
- ...or you can provide your own [Abort Signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) instance:
100
+ And you can provide your own [Abort Signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) instance:
96
101
 
97
102
  ```js
98
103
  // Providing an AbortSignal
99
104
  const abortController = new AbortController;
100
- Observer.observe( obj, mutations => {
101
- // Handle...
102
- }, { signal: abortController.signal } );
105
+ Observer.observe( obj, handleChanges, { signal: abortController.signal } );
103
106
  ```
104
107
 
105
108
  ```js
@@ -107,6 +110,20 @@ Observer.observe( obj, mutations => {
107
110
  abortController.abort();
108
111
  ```
109
112
 
113
+ **-->** Where listeners initiate nested observers (child observers), leverage "AbortSignal-cascading" to tie child observers to parent observer's lifecycle:
114
+
115
+ ```js
116
+ // Parent -
117
+ const abortController = Observer.observe( obj, ( mutations, flags ) => {
118
+
119
+ // Child
120
+ Observer.observe( obj, handleChanges, { signal: flags.signal } ); // <<<---- AbortSignal-cascading
121
+
122
+ } );
123
+ ```
124
+
125
+ └ *"Child" gets automatically aborted at parent's "next turn", and at parent's own abortion!*
126
+
110
127
  #### Concept: *Mutation APIs*
111
128
 
112
129
  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:
@@ -220,7 +237,7 @@ $arr[ 2 ] = 'item2';
220
237
  $arr.push( 'item3' );
221
238
  ```
222
239
 
223
- *And no problem if you end up nesting the approaches.*
240
+ *And no problem if you end up nesting the approaches.*
224
241
 
225
242
  ```js
226
243
  // 'value1'-->obj
@@ -249,10 +266,9 @@ obj = Observer.unproxy( $obj );
249
266
 
250
267
  #### Concept: *Paths*
251
268
 
252
- Observe "a value at a path" on a given tree:
269
+ Observe "the value" at a path in a given tree:
253
270
 
254
271
  ```js
255
- // A tree structure that satisfies the path above
256
272
  const obj = {
257
273
  level1: {
258
274
  level2: 'level2-value',
@@ -280,7 +296,7 @@ Observer.set( obj.level1, 'level2', 'level2-new-value' );
280
296
 
281
297
  </details>
282
298
 
283
- And well, the initial tree structure can be whatever:
299
+ └ *And the initial tree structure can be whatever*:
284
300
 
285
301
  ```js
286
302
  // A tree structure that is yet to be built
@@ -294,7 +310,7 @@ Observer.observe( obj, path, m => {
294
310
  } );
295
311
  ```
296
312
 
297
- Now, any operation that "modifies" the observed tree - either by extension or truncation - will fire our listener:
313
+ └ *Now, any operation that "modifies" the observed tree - either by extension or truncation - will fire our listener*:
298
314
 
299
315
  ```js
300
316
  Observer.set( obj, 'level1', { level2: {}, } );
@@ -309,7 +325,7 @@ Observer.set( obj, 'level1', { level2: {}, } );
309
325
 
310
326
  </details>
311
327
 
312
- Meanwhile, this next one completes the tree, and the listener reports a value at its observed path:
328
+ └ *Meanwhile, this next one completes the tree, and the listener reports a value at its observed path*:
313
329
 
314
330
  ```js
315
331
  Observer.set( obj.level1, 'level2', { level3: { level4: 'level4-value', }, } );
@@ -324,19 +340,21 @@ Observer.set( obj.level1, 'level2', { level3: { level4: 'level4-value', }, } );
324
340
 
325
341
  </details>
326
342
 
327
- If you were to find the exact point of mutation in the path in an audit trail, you could inspect the event's `context` property, which itself returns the parent event...
343
+ **-->** 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:
328
344
 
329
345
  ```js
330
346
  let context = m.context;
347
+ console.log(context);
331
348
  ```
332
349
 
333
- ...allowing you to go up one more level until the root event:
350
+ *And up again one level until the root event*:
334
351
 
335
352
  ```js
336
353
  let parentContext = context.context;
354
+ console.log(parentContext);
337
355
  ```
338
356
 
339
- Where a promise is encountered along the path, further access is paused until promise resolves:
357
+ **-->** Observe trees that are built *asynchronously*! Where a promise is encountered along the path, further access is paused until promise resolves:
340
358
 
341
359
  ```js
342
360
  Observer.set( obj.level1, 'level2', Promise.resolve( { level3: { level4: 'level4-value', }, } ) );
@@ -344,7 +362,7 @@ Observer.set( obj.level1, 'level2', Promise.resolve( { level3: { level4: 'level4
344
362
 
345
363
  #### Concept: *Batch Mutations*
346
364
 
347
- Make multiple mutations at a go, and they'll be correctly delivered in batch to observers!
365
+ Make multiple mutations at a go, and they'll be correctly delivered as a batch to observers!
348
366
 
349
367
  ```js
350
368
  // Batch operations on an object
@@ -397,7 +415,7 @@ Observer.set( obj, {
397
415
  }, { detail: 'Certain detail' } );
398
416
  ```
399
417
 
400
- *Observers recieve this value on their `mutation.detail` property.*
418
+ *Observers recieve this value on their `mutation.detail` property.*
401
419
 
402
420
  ```js
403
421
  // An observer with detail
@@ -449,7 +467,7 @@ Observer.intercept( obj, traps[, options = {} ]);
449
467
 
450
468
  Extend standard operations on an object - `Observer.set()`, `Observer.deleteProperty()`, etc - with custom traps using the [`Observer.intercept()`](https://webqit.io/tooling/observer/docs/api/reactions/intercept) method!
451
469
 
452
- *Below, we intercept all "set" operations for an HTTP URL then transform it to an HTTPS URL.*
470
+ *Below, we intercept all "set" operations for an HTTP URL then transform it to an HTTPS URL.*
453
471
 
454
472
  ```js
455
473
  const setTrap = ( operation, previous, next ) => {
@@ -461,7 +479,7 @@ const setTrap = ( operation, previous, next ) => {
461
479
  Observer.intercept( obj, 'set', setTrap );
462
480
  ```
463
481
 
464
- *Now, only the first of the following will fly as-is.*
482
+ *Now, only the first of the following will fly as-is.*
465
483
 
466
484
  ```js
467
485
  // Not transformed
@@ -471,7 +489,7 @@ Observer.set( obj, 'url', 'https://webqit.io' );
471
489
  Observer.set( obj, 'url', 'http://webqit.io' );
472
490
  ```
473
491
 
474
- *And below, we intercept all "get" operations for a certain value to trigger a network fetch behind the scenes.*
492
+ *And below, we intercept all "get" operations for a certain value to trigger a network fetch behind the scenes.*
475
493
 
476
494
  ```js
477
495
  const getTrap = ( operation, previous, next ) => {
@@ -483,7 +501,7 @@ const getTrap = ( operation, previous, next ) => {
483
501
  Observer.intercept( obj, 'get', getTrap );
484
502
  ```
485
503
 
486
- *And all of that can go into one "traps" object:*
504
+ *And all of that can go into one "traps" object:*
487
505
 
488
506
  ```js
489
507
  Observer.intercept( obj, {
@@ -525,7 +543,32 @@ const Observer = window.webqit.Observer;
525
543
 
526
544
  ## API Reference
527
545
 
528
- **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!
546
+ <!--
547
+
548
+ | Observer API | Reflect API | Description | Trap |
549
+ | -------------- | ------------ | ----------- | --------------- |
550
+ | `apply()` | ✓ | Invokes a function [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply) | `apply() {}` |
551
+ | `batch()` | `×` | Creates a batching context [↗](https://github.com/webqit/observer#:~:text=use%20the%20observer.batch()%20to%20batch%20multiple%20arbitrary%20mutations%20-%20whether%20related%20or%20not) | `-` |
552
+ | `construct()` | ✓ | Initializes a constructor [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct) | `construct() {}` |
553
+ | `defineProperty()` | ✓ | Defines a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty) | `defineProperty() {}` |
554
+ | `deleteProperty()` | ✓ | Deletes a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty) | `deleteProperty() {}` |
555
+ | `get()` | ✓ | Reads a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get) | `get() {}` |
556
+ | `getOwnPropertyDescriptor()` | ✓ | Obtains property descriptor [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor) | `getOwnPropertyDescriptor() {}` |
557
+ | `getPrototypeOf()` | ✓ | Obtains object prototype [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf) | `getPrototypeOf() {}` |
558
+ | `has()` | ✓ | Checks property existence [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has) | `has() {}` |
559
+ | `intercept()` | `×` | Binds a "traps" object [↗](https://github.com/webqit/observer#method-observerintercept) | `-` |
560
+ | `isExtensible()` | ✓ | Checks object extensibility [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/isExtensible) | `isExtensible() {}` |
561
+ | `observe()` | `×` | Binds a mutation observer [↗](https://github.com/webqit/observer#method-observerobserve) | `-` |
562
+ | `ownKeys()` | ✓ | Obtains object keys [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys) | `ownKeys() {}` |
563
+ | `path()` | `×` | Evaluates a path [↗](#) | `-` |
564
+ | `preventExtensions()` | ✓ | Prevents object extensibility [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions) | `preventExtensions() {}` |
565
+ | `set()` | ✓ | Sets a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set) | `set() {}` |
566
+ | `setPrototypeOf()` | ✓ | Sets object prototype [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf) | `setPrototypeOf() {}` |
567
+ | . | . | . | . |
568
+ | `accessorize()` | `×` | Applies pre-intercepted getters/setters to properties [↗](https://github.com/webqit/observer#:~:text=enable%20reactivity%20on%20specific%20properties%20with%20literal%20object%20accessors%20-%20using%20the%20observer.accessorize()%20method) | `-` |
569
+ | `proxy()` | `×` | Creates a pre-intercepted proxy object [↗](https://github.com/webqit/observer#:~:text=enable%20reactivity%20on%20arbitray%20properties%20with%20proxies%20-%20using%20the%20observer.proxy()%20method) | `-` |
570
+
571
+ -->
529
572
 
530
573
  | Observer API | Reflect API | Trap |
531
574
  | -------------- | ------------ | ----------- |
@@ -552,7 +595,7 @@ const Observer = window.webqit.Observer;
552
595
 
553
596
  ## Design Discussion
554
597
 
555
- *[TODO]*
598
+ [See more in the introductory blog post](https://dev.to/oxharris/reinvestigating-reactivity-22e0-temp-slug-5973064?preview=8afd0f8b156bf0b0b1c08058837fe4986054e52a7450f0a28adbaf07dcb7f5659b724166f553fb98ceab3d080748e86b244684f515d579bcd0f48cbb#introducing-the-observer-api)<sup>draft</sup>
556
599
 
557
600
  ## Getting Involved
558
601
 
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "events"
13
13
  ],
14
14
  "homepage": "https://webqit.io/tooling/observer",
15
- "version": "2.1.5",
15
+ "version": "2.1.7",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",