@webqit/observer 2.1.5 → 2.1.6

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 -27
  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 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,7 +266,7 @@ 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
272
  // A tree structure that satisfies the path above
@@ -280,7 +297,7 @@ Observer.set( obj.level1, 'level2', 'level2-new-value' );
280
297
 
281
298
  </details>
282
299
 
283
- And well, the initial tree structure can be whatever:
300
+ └ *And the initial tree structure can be whatever*:
284
301
 
285
302
  ```js
286
303
  // A tree structure that is yet to be built
@@ -294,7 +311,7 @@ Observer.observe( obj, path, m => {
294
311
  } );
295
312
  ```
296
313
 
297
- Now, any operation that "modifies" the observed tree - either by extension or truncation - will fire our listener:
314
+ └ *Now, any operation that "modifies" the observed tree - either by extension or truncation - will fire our listener*:
298
315
 
299
316
  ```js
300
317
  Observer.set( obj, 'level1', { level2: {}, } );
@@ -309,7 +326,7 @@ Observer.set( obj, 'level1', { level2: {}, } );
309
326
 
310
327
  </details>
311
328
 
312
- Meanwhile, this next one completes the tree, and the listener reports a value at its observed path:
329
+ └ *Meanwhile, this next one completes the tree, and the listener reports a value at its observed path*:
313
330
 
314
331
  ```js
315
332
  Observer.set( obj.level1, 'level2', { level3: { level4: 'level4-value', }, } );
@@ -324,19 +341,21 @@ Observer.set( obj.level1, 'level2', { level3: { level4: 'level4-value', }, } );
324
341
 
325
342
  </details>
326
343
 
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...
344
+ **-->** 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
345
 
329
346
  ```js
330
347
  let context = m.context;
348
+ console.log(context);
331
349
  ```
332
350
 
333
- ...allowing you to go up one more level until the root event:
351
+ *And up again one level until the root event*:
334
352
 
335
353
  ```js
336
354
  let parentContext = context.context;
355
+ console.log(parentContext);
337
356
  ```
338
357
 
339
- Where a promise is encountered along the path, further access is paused until promise resolves:
358
+ **-->** Observe trees that are built *asynchronously*! Where a promise is encountered along the path, further access is paused until promise resolves:
340
359
 
341
360
  ```js
342
361
  Observer.set( obj.level1, 'level2', Promise.resolve( { level3: { level4: 'level4-value', }, } ) );
@@ -344,7 +363,7 @@ Observer.set( obj.level1, 'level2', Promise.resolve( { level3: { level4: 'level4
344
363
 
345
364
  #### Concept: *Batch Mutations*
346
365
 
347
- Make multiple mutations at a go, and they'll be correctly delivered in batch to observers!
366
+ Make multiple mutations at a go, and they'll be correctly delivered as a batch to observers!
348
367
 
349
368
  ```js
350
369
  // Batch operations on an object
@@ -397,7 +416,7 @@ Observer.set( obj, {
397
416
  }, { detail: 'Certain detail' } );
398
417
  ```
399
418
 
400
- *Observers recieve this value on their `mutation.detail` property.*
419
+ *Observers recieve this value on their `mutation.detail` property.*
401
420
 
402
421
  ```js
403
422
  // An observer with detail
@@ -449,7 +468,7 @@ Observer.intercept( obj, traps[, options = {} ]);
449
468
 
450
469
  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
470
 
452
- *Below, we intercept all "set" operations for an HTTP URL then transform it to an HTTPS URL.*
471
+ *Below, we intercept all "set" operations for an HTTP URL then transform it to an HTTPS URL.*
453
472
 
454
473
  ```js
455
474
  const setTrap = ( operation, previous, next ) => {
@@ -461,7 +480,7 @@ const setTrap = ( operation, previous, next ) => {
461
480
  Observer.intercept( obj, 'set', setTrap );
462
481
  ```
463
482
 
464
- *Now, only the first of the following will fly as-is.*
483
+ *Now, only the first of the following will fly as-is.*
465
484
 
466
485
  ```js
467
486
  // Not transformed
@@ -471,7 +490,7 @@ Observer.set( obj, 'url', 'https://webqit.io' );
471
490
  Observer.set( obj, 'url', 'http://webqit.io' );
472
491
  ```
473
492
 
474
- *And below, we intercept all "get" operations for a certain value to trigger a network fetch behind the scenes.*
493
+ *And below, we intercept all "get" operations for a certain value to trigger a network fetch behind the scenes.*
475
494
 
476
495
  ```js
477
496
  const getTrap = ( operation, previous, next ) => {
@@ -483,7 +502,7 @@ const getTrap = ( operation, previous, next ) => {
483
502
  Observer.intercept( obj, 'get', getTrap );
484
503
  ```
485
504
 
486
- *And all of that can go into one "traps" object:*
505
+ *And all of that can go into one "traps" object:*
487
506
 
488
507
  ```js
489
508
  Observer.intercept( obj, {
@@ -525,7 +544,32 @@ const Observer = window.webqit.Observer;
525
544
 
526
545
  ## API Reference
527
546
 
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!
547
+ <!--
548
+
549
+ | Observer API | Reflect API | Description | Trap |
550
+ | -------------- | ------------ | ----------- | --------------- |
551
+ | `apply()` | ✓ | Invokes a function [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply) | `apply() {}` |
552
+ | `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) | `-` |
553
+ | `construct()` | ✓ | Initializes a constructor [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct) | `construct() {}` |
554
+ | `defineProperty()` | ✓ | Defines a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty) | `defineProperty() {}` |
555
+ | `deleteProperty()` | ✓ | Deletes a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty) | `deleteProperty() {}` |
556
+ | `get()` | ✓ | Reads a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get) | `get() {}` |
557
+ | `getOwnPropertyDescriptor()` | ✓ | Obtains property descriptor [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor) | `getOwnPropertyDescriptor() {}` |
558
+ | `getPrototypeOf()` | ✓ | Obtains object prototype [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf) | `getPrototypeOf() {}` |
559
+ | `has()` | ✓ | Checks property existence [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has) | `has() {}` |
560
+ | `intercept()` | `×` | Binds a "traps" object [↗](https://github.com/webqit/observer#method-observerintercept) | `-` |
561
+ | `isExtensible()` | ✓ | Checks object extensibility [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/isExtensible) | `isExtensible() {}` |
562
+ | `observe()` | `×` | Binds a mutation observer [↗](https://github.com/webqit/observer#method-observerobserve) | `-` |
563
+ | `ownKeys()` | ✓ | Obtains object keys [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys) | `ownKeys() {}` |
564
+ | `path()` | `×` | Evaluates a path [↗](#) | `-` |
565
+ | `preventExtensions()` | ✓ | Prevents object extensibility [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions) | `preventExtensions() {}` |
566
+ | `set()` | ✓ | Sets a property [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set) | `set() {}` |
567
+ | `setPrototypeOf()` | ✓ | Sets object prototype [↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf) | `setPrototypeOf() {}` |
568
+ | . | . | . | . |
569
+ | `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) | `-` |
570
+ | `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) | `-` |
571
+
572
+ -->
529
573
 
530
574
  | Observer API | Reflect API | Trap |
531
575
  | -------------- | ------------ | ----------- |
@@ -552,7 +596,7 @@ const Observer = window.webqit.Observer;
552
596
 
553
597
  ## Design Discussion
554
598
 
555
- *[TODO]*
599
+ [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
600
 
557
601
  ## Getting Involved
558
602
 
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.6",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",