ig-types 6.14.0 → 6.14.1

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 (3) hide show
  1. package/Promise.js +112 -80
  2. package/README.md +51 -0
  3. package/package.json +1 -1
package/Promise.js CHANGED
@@ -39,7 +39,7 @@ var object = require('ig-object')
39
39
  // Like Promise.all(..) but adds ability to iterate through results
40
40
  // via generators .map(..)/.reduce(..) and friends...
41
41
  //
42
- // NOTE: it would be nice to support throwing STOP from the iterable
42
+ // NOTE: it would be nice to support throwing STOP (XXX) from the iterable
43
43
  // promise but...
44
44
  // - this is more complicated than simply using .smap(..) instead
45
45
  // of .map(..) because the list can contain async promises...
@@ -49,11 +49,27 @@ var object = require('ig-object')
49
49
  // - another issue here is that the stop would happen in order of
50
50
  // execution and not order of elements...
51
51
  // XXX though stopping within a single level might be useful...
52
- //
53
-
52
+ // NOTE: the following can not be implemented here:
53
+ // .splice(..) - can't both modify and return
54
+ // a result...
55
+ // .pop() / .shift() - can't modify the promise, use
56
+ // .first() / .last() instead.
57
+ // [Symbol.iterator]() - needs to be sync and we can't
58
+ // know the number of elements to
59
+ // return promises before the whole
60
+ // iterable promise is resolved.
54
61
  // NOTE: we are not using async/await here as we need to control the
55
62
  // type of promise returned in cases where we know we are returning
56
63
  // an array...
64
+ // NOTE: there is no point in implementing a 1:1 version of this that
65
+ // would not support element expansion/contraction as it would only
66
+ // simplify a couple of methods that are 1:1 (like .map(..) and
67
+ // .some(..)) while methods like .filter(..) will throw everything
68
+ // back to the complex IterablePromise...
69
+ //
70
+ // XXX how do we handle errors/rejections???
71
+ //
72
+
57
73
  // XXX should these be exported???
58
74
  var iterPromiseProxy =
59
75
  //module.iterPromiseProxy =
@@ -71,6 +87,9 @@ function(name){
71
87
  var IterablePromise =
72
88
  module.IterablePromise =
73
89
  object.Constructor('IterablePromise', Promise, {
90
+ // packed array...
91
+ //
92
+ // Holds promise state.
74
93
  //
75
94
  // Format:
76
95
  // [
@@ -87,16 +106,20 @@ object.Constructor('IterablePromise', Promise, {
87
106
  //
88
107
  // NOTE: in general iterable promises are implicitly immutable, so
89
108
  // it is not recomended to ever edit this inplace...
90
- __list: null,
109
+ // NOTE: we are not isolation any internals to enable users to
110
+ // responsibly extend the code.
111
+ __packed: null,
91
112
 
92
- // low-level .__list handlers/helpers...
113
+ // low-level .__packed handlers/helpers...
93
114
  //
94
115
  // NOTE: these can be useful for debugging and extending...
95
- __pack: function(list, handler){
116
+ //
117
+ // pack and oprionally transform/handle an array (sync)...
118
+ __pack: function(list, handler=undefined){
96
119
  var that = this
97
120
  // handle iterable promise list...
98
121
  if(list instanceof IterablePromise){
99
- return this.__handle(list.__list, handler) }
122
+ return this.__handle(list.__packed, handler) }
100
123
  // handle promise list...
101
124
  if(list instanceof Promise){
102
125
  return list.then(function(list){
@@ -120,11 +143,12 @@ object.Constructor('IterablePromise', Promise, {
120
143
  : !handle ?
121
144
  elem
122
145
  : handler(elem) }) },
123
- __handle: function(list, handler){
146
+ // transform/handle packed array (sync)...
147
+ __handle: function(list, handler=undefined){
124
148
  var that = this
125
149
  if(typeof(list) == 'function'){
126
150
  handler = list
127
- list = this.__list }
151
+ list = this.__packed }
128
152
  if(!handler){
129
153
  return list }
130
154
  // handle promise list...
@@ -144,49 +168,28 @@ object.Constructor('IterablePromise', Promise, {
144
168
  return elem.flat() })
145
169
  : [handler(elem)] })
146
170
  .flat() },
147
- __unpack: function(list){
171
+ // unpack array (async)...
172
+ __unpack: async function(list){
148
173
  list = list
149
- ?? this.__list
174
+ ?? this.__packed
150
175
  // handle promise list...
151
- if(list instanceof Promise){
152
- var that = this
153
- return list.then(function(list){
154
- return that.__unpack(list) }) }
155
- // do the work...
156
- return Promise.all(list)
157
- .then(function(list){
158
- return list.flat() }) },
176
+ return list instanceof Promise ?
177
+ this.__unpack(await list)
178
+ // do the work...
179
+ : (await Promise.all(list))
180
+ .flat() },
159
181
 
160
182
 
161
183
  // iterator methods...
162
184
  //
163
185
  // These will return a new IterablePromise instance...
164
186
  //
165
- // When called from a resolved promise these will return a new
166
- // resolved promise with updated values...
167
- //
168
- // When called from a rejected promise these will return a rejected
169
- // with the same reason promise...
170
- //
171
- //
172
187
  // NOTE: these are different to Array's equivalents in that the handler
173
188
  // is called not in the order of the elements but rather in order
174
189
  // of promise resolution...
175
190
  // NOTE: index of items is unknowable because items can expand and
176
- // contract depending on handlrs (e.g. .filter(..) can remove
191
+ // contract depending on handlers (e.g. .filter(..) can remove
177
192
  // items)...
178
- // This the following can not be implemented here:
179
- // .slice(..)
180
- // .splice(..)
181
- // .values() / .keys()
182
- // .at(..)
183
- // [Symbol.iterator]() - needs to be sync...
184
- // The followng methods are questionable:
185
- // .indexOf(..)
186
- // .includes(..)
187
- // .some(..) / .every(..)
188
- // .sort(..)
189
- // XXX update this note...
190
193
  map: function(func){
191
194
  return this.constructor(this,
192
195
  function(e){
@@ -208,11 +211,11 @@ object.Constructor('IterablePromise', Promise, {
208
211
  : _filter(e) }) },
209
212
  // NOTE: this does not return an iterable promise as we can't know
210
213
  // what the user reduces to...
211
- // XXX we could look at the initial state though...
212
214
  // NOTE: the items can be handled out of order because the nested
213
215
  // promises can resolve in any order...
214
216
  // NOTE: since order of execution can not be guaranteed there is no
215
- // point in implementing .reduceRight(..)
217
+ // point in implementing .reduceRight(..) in the same way
218
+ // (see below)...
216
219
  reduce: function(func, res){
217
220
  return this.constructor(this,
218
221
  function(e){
@@ -232,7 +235,7 @@ object.Constructor('IterablePromise', Promise, {
232
235
  e
233
236
  : [e] }) },
234
237
  reverse: function(){
235
- var lst = this.__list
238
+ var lst = this.__packed
236
239
  return this.constructor(
237
240
  lst instanceof Promise ?
238
241
  lst.then(function(elems){
@@ -277,21 +280,24 @@ object.Constructor('IterablePromise', Promise, {
277
280
  return this.constructor([elem])
278
281
  .concat(this) },
279
282
 
280
- // XXX can we do these?
281
- // .pop()
282
- // .shift()
283
- // .splice(..)
284
- // ...would be nice if these could stop everything that's not
285
- // needed to execute -- likely not possible (XXX)
286
- // ...these need to somehow both return an element and affect
287
- // the iterator (or return a new one) -- we can trivially do either
288
- // one action within the spec but not both...
289
-
290
- // NOTE: this can avoid waiting for the whole promise to resolve only
291
- // for indexes 0 and -1, everything else is non-deterministic and
292
- // we'll have to wait for the whole thing to resolve.
283
+
284
+ // XXX thses need to stop once an element is found so we cant simply
285
+ // use .map(..) or .reduce(..)...
286
+ // XXX .find(func) / .findIndex(func)
287
+
288
+
289
+ // proxy methods...
290
+ //
291
+ // These require the whole promise to resolve to trigger.
292
+ //
293
+ // An exception to this would be .at(0)/.first() and .at(-1)/.last()
294
+ // that can get the target element if it's accessible.
295
+ //
296
+ // NOTE: methods that are guaranteed to return an array will return
297
+ // an iterable promise (created with iterPromiseProxy(..))...
298
+ //
293
299
  at: async function(i){
294
- var list = this.__list
300
+ var list = this.__packed
295
301
  return ((i != 0 && i != -1)
296
302
  || list instanceof Promise
297
303
  || list.at(i) instanceof Promise) ?
@@ -308,24 +314,35 @@ object.Constructor('IterablePromise', Promise, {
308
314
  last: function(){
309
315
  return this.at(-1) },
310
316
 
317
+ // NOTE: unlike .reduce(..) this needs the parent fully resolved
318
+ // to be able to iterate from the end.
319
+ // XXX ???
320
+ reduceRight: promiseProxy('reduceRight'),
321
+
311
322
  // NOTE: there is no way we can do a sync generator returning
312
- // promises for values because any promise in .__list makes the
323
+ // promises for values because any promise in .__packed makes the
313
324
  // value count/index non-deterministic...
314
325
  sort: iterPromiseProxy('sort'),
326
+ // XXX we could have a special-case here for .slice()/slice(0, -1)
327
+ // and possibly othets, should we???
315
328
  slice: iterPromiseProxy('slice'),
329
+
316
330
  entries: iterPromiseProxy('entries'),
317
331
  keys: iterPromiseProxy('keys'),
318
332
  // XXX we could possibly make this better via .map(..)
319
333
  values: iterPromiseProxy('values'),
320
334
 
321
335
  indexOf: promiseProxy('indexOf'),
336
+ lastIndexOf: promiseProxy('lastIndexOf'),
322
337
  includes: promiseProxy('includes'),
323
338
 
324
339
  every: promiseProxy('every'),
325
- // XXX this can be lazy...
340
+ // XXX this can be lazy... (???)
326
341
  some: promiseProxy('some'),
327
342
 
328
343
 
344
+ // promise api...
345
+ //
329
346
  // Overload .then(..), .catch(..) and .finally(..) to return a plain
330
347
  // Promise instnace...
331
348
  //
@@ -351,6 +368,13 @@ object.Constructor('IterablePromise', Promise, {
351
368
  : p },
352
369
 
353
370
 
371
+ // this is defined globally as Promise.prototype.iter(.,)
372
+ //
373
+ // for details see: PromiseMixin(..) below...
374
+ //iter: function(handler=undefined){ ... },
375
+
376
+
377
+ // constructor...
354
378
  //
355
379
  // Promise.iter([ .. ])
356
380
  // -> iterable-promise
@@ -374,7 +398,7 @@ object.Constructor('IterablePromise', Promise, {
374
398
  //
375
399
  // Special cases useful for extending this constructor...
376
400
  //
377
- // Set raw .__list without any pre-processing...
401
+ // Set raw .__packed without any pre-processing...
378
402
  // Promise.iter([ .. ], 'raw')
379
403
  // -> iterable-promise
380
404
  //
@@ -382,12 +406,6 @@ object.Constructor('IterablePromise', Promise, {
382
406
  // Promise.iter(false)
383
407
  // -> iterable-promise
384
408
  //
385
- //
386
- // XXX iterator/generator as input:
387
- // - do we unwind here or externally?
388
- // ...feels like with the generator external unwinding is
389
- // needed...
390
- // XXX how do we handle errors/rejections???
391
409
  __new__: function(_, list, handler){
392
410
  // instance...
393
411
  var promise
@@ -408,9 +426,9 @@ object.Constructor('IterablePromise', Promise, {
408
426
  // handle/pack input data...
409
427
  if(handler != 'raw'){
410
428
  list = list instanceof IterablePromise ?
411
- this.__handle(list.__list, handler)
429
+ this.__handle(list.__packed, handler)
412
430
  : this.__pack(list, handler) }
413
- Object.defineProperty(obj, '__list', {
431
+ Object.defineProperty(obj, '__packed', {
414
432
  value: list,
415
433
  enumerable: false,
416
434
  })
@@ -473,13 +491,14 @@ object.Constructor('InteractivePromise', Promise, {
473
491
  // register a handler...
474
492
  } else {
475
493
  var h = obj == null ?
476
- // NOTE: we need to get the handlers from .__message_handlers
477
- // unless we are not fully defined yet, then use the
478
- // bootstrap container (handlers)...
479
- // ...since we can call onmessage(..) while the promise
480
- // is still defined there is no way to .send(..) until it
481
- // returns a promise object, this races here are highly
482
- // unlikely...
494
+ // NOTE: we need to get the handlers from
495
+ // .__message_handlers unless we are not
496
+ // fully defined yet, then use the bootstrap
497
+ // container (handlers)...
498
+ // ...since we can call onmessage(..) while
499
+ // the promise is still defined there is no
500
+ // way to .send(..) until it returns a promise
501
+ // object, this races here are highly unlikely...
483
502
  handlers
484
503
  : (obj.__message_handlers =
485
504
  obj.__message_handlers ?? [])
@@ -566,10 +585,13 @@ object.Constructor('CooperativePromise', Promise, {
566
585
  var ProxyPromise =
567
586
  module.ProxyPromise =
568
587
  object.Constructor('ProxyPromise', Promise, {
569
- __new__: function(context, constructor){
570
- var proto = 'prototype' in constructor ?
571
- constructor.prototype
572
- : constructor
588
+
589
+ then: IterablePromise.prototype.then,
590
+
591
+ __new__: function(context, other, nooverride=false){
592
+ var proto = 'prototype' in other ?
593
+ other.prototype
594
+ : other
573
595
  var obj = Reflect.construct(
574
596
  ProxyPromise.__proto__,
575
597
  [function(resolve, reject){
@@ -580,6 +602,9 @@ object.Constructor('ProxyPromise', Promise, {
580
602
  // NOTE: we are not using object.deepKeys(..) here as we need
581
603
  // the key origin not to trigger property getters...
582
604
  var seen = new Set()
605
+ nooverride = nooverride instanceof Array ?
606
+ new Set(nooverride)
607
+ : nooverride
583
608
  while(proto != null){
584
609
  Object.entries(Object.getOwnPropertyDescriptors(proto))
585
610
  .forEach(function([key, value]){
@@ -594,6 +619,13 @@ object.Constructor('ProxyPromise', Promise, {
594
619
  && Object.prototype.run === value.value)
595
620
  && !value.enumerable){
596
621
  return }
622
+ // do not override existing methods...
623
+ if(nooverride === true ?
624
+ key in obj
625
+ : nooverride instanceof Set ?
626
+ nooverride.has(key)
627
+ : nooverride){
628
+ return }
597
629
  // proxy...
598
630
  obj[key] = promiseProxy(key) })
599
631
  proto = proto.__proto__ }
@@ -619,7 +651,7 @@ var PromiseProtoMixin =
619
651
  module.PromiseProtoMixin =
620
652
  object.Mixin('PromiseProtoMixin', 'soft', {
621
653
  as: ProxyPromise,
622
- iter: function(handler){
654
+ iter: function(handler=undefined){
623
655
  return IterablePromise(this, handler) },
624
656
  })
625
657
 
@@ -629,4 +661,4 @@ PromiseProtoMixin(Promise.prototype)
629
661
 
630
662
 
631
663
  /**********************************************************************
632
- * vim:set ts=4 sw=4 : */ return module })
664
+ * vim:set ts=4 sw=4 nowrap : */ return module })
package/README.md CHANGED
@@ -77,6 +77,9 @@ Library of JavaScript type extensions, types and utilities.
77
77
  - [`<promise-iter>.reverse()`](#promise-iterreverse)
78
78
  - [`<promise-iter>.concat(..)`](#promise-iterconcat)
79
79
  - [`<promise-iter>.push(..)` / `<promise-iter>.unshift(..)`](#promise-iterpush--promise-iterunshift)
80
+ - [`<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`](#promise-iterat--promise-iterfirst--promise-iterlast)
81
+ - [Array proxy methods returning `<promise-iter>`](#array-proxy-methods-returning-promise-iter)
82
+ - [Array proxy methods returning a `<promise>`](#array-proxy-methods-returning-a-promise)
80
83
  - [`<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally)
81
84
  - [Advanced handler](#advanced-handler)
82
85
  - [Promise proxies](#promise-proxies)
@@ -1663,6 +1666,54 @@ and [`<array>.unshift(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScr
1663
1666
  see them for more info.
1664
1667
 
1665
1668
 
1669
+ #### `<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`
1670
+
1671
+ Proxies to the appropriate array methods with a special-case: when getting elements
1672
+ at positions `0` or `-1` (i.e. `.first()` / `.last()`) these _can_ resolve before the
1673
+ parent `<promise-iter>`.
1674
+
1675
+ XXX
1676
+
1677
+
1678
+ #### Array proxy methods returning `<promise-iter>`
1679
+
1680
+ - `<promise-iter>.sort(..)`
1681
+ - `<promise-iter>.slice(..)`
1682
+ - `<promise-iter>.entries()` / `<promise-iter>.keys()` / `<promise-iter>.values()`
1683
+
1684
+ These methods are proxies to the appropriate array methods.
1685
+
1686
+ ```bnf
1687
+ <promise-iter>.<method>(..)
1688
+ -> <promise-iter>
1689
+ ```
1690
+
1691
+ These methods need the parent `<promise-iter>` to resolve before resolving themselves.
1692
+
1693
+ XXX links...
1694
+
1695
+
1696
+ #### Array proxy methods returning a `<promise>`
1697
+
1698
+ - `<promise-iter>.indexOf(..)`
1699
+ - `<promise-iter>.includes(..)`
1700
+ - `<promise-iter>.every(..)` / `<promise-iter>.some(..)`
1701
+
1702
+ These methods are proxies to the appropriate array methods.
1703
+
1704
+ ```bnf
1705
+ <promise-iter>.<method>(..)
1706
+ -> <promise>
1707
+ ```
1708
+
1709
+ These methods need the parent `<promise-iter>` to resolve before resolving themselves.
1710
+
1711
+ Since the equivalent array methods do not return iterables these will return a basic
1712
+ (non-iterable) `<promise>`.
1713
+
1714
+ XXX links...
1715
+
1716
+
1666
1717
  #### `<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`
1667
1718
 
1668
1719
  An extension to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.14.0",
3
+ "version": "6.14.1",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {