ig-types 6.13.7 → 6.15.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.
Files changed (4) hide show
  1. package/Array.js +19 -13
  2. package/Promise.js +312 -96
  3. package/README.md +134 -0
  4. package/package.json +1 -1
package/Array.js CHANGED
@@ -139,7 +139,8 @@ var makeChunkIter = function(iter, wrapper){
139
139
  try {
140
140
  // handle iteration...
141
141
  res.push(
142
- val = (chunk = chunks.shift())[iter](_wrapper, ...rest))
142
+ val = (chunk = chunks.shift())
143
+ [iter](_wrapper, ...rest))
143
144
  // handle chunk...
144
145
  postChunk
145
146
  && postChunk.call(that,
@@ -319,9 +320,12 @@ object.Mixin('ArrayProtoMixin', 'soft', {
319
320
  // NOTE: order is preserved...
320
321
  unique: function(normalize){
321
322
  return normalize ?
322
- [...new Map(this.map(function(e){ return [normalize(e), e] })).values()]
323
- // NOTE: we are calling .compact() here to avoid creating undefined
324
- // items from empty slots in sparse arrays...
323
+ [...new Map(this
324
+ .map(function(e){
325
+ return [normalize(e), e] }))
326
+ .values()]
327
+ // NOTE: we are calling .compact() here to avoid creating
328
+ // undefined items from empty slots in sparse arrays...
325
329
  : [...new Set(this.compact())] },
326
330
  tailUnique: function(normalize){
327
331
  return this
@@ -336,8 +340,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
336
340
  // self to other (internal) while match compares two entities
337
341
  // externally.
338
342
  // XXX not sure if we need the destinction in name, will have to
339
- // come back to this when refactoring diff.js -- all three have
340
- // to be similar...
343
+ // come back to this when refactoring diff.js -- all three
344
+ // have to be similar...
341
345
  cmp: function(other){
342
346
  if(this === other){
343
347
  return true }
@@ -353,7 +357,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
353
357
  // NOTE: this will ignore order and repeating elments...
354
358
  setCmp: function(other){
355
359
  return this === other
356
- || (new Set([...this, ...other])).length == (new Set(this)).length },
360
+ || (new Set([...this, ...other])).length
361
+ == (new Set(this)).length },
357
362
 
358
363
  // Sort as the other array...
359
364
  //
@@ -369,12 +374,13 @@ object.Mixin('ArrayProtoMixin', 'soft', {
369
374
  // This will sort the intersecting items in the head keeping the rest
370
375
  // of the items in the same relative order...
371
376
  //
372
- // NOTE: if an item is in the array multiple times only the first index
373
- // is used...
377
+ // NOTE: if an item is in the array multiple times only the first
378
+ // index is used...
374
379
  //
375
380
  // XXX should this extend/patch .sort(..)???
376
- // ...currently do not see a clean way to do this without extending
377
- // and replacing Array or directly re-wrapping .sort(..)...
381
+ // ...currently do not see a clean way to do this without
382
+ // extending and replacing Array or directly re-wrapping
383
+ // .sort(..)...
378
384
  sortAs: function(other, place='head'){
379
385
  place = place == 'tail' ? -1 : 1
380
386
  // NOTE: the memory overhead here is better than the time overhead
@@ -441,8 +447,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
441
447
 
442
448
  // Convert an array to a map...
443
449
  //
444
- // This is similar to Array.prototype.toKeys(..) but does not restrict
445
- // value type to string.
450
+ // This is similar to Array.prototype.toKeys(..) but does not
451
+ // restrict value type to string.
446
452
  //
447
453
  // Format:
448
454
  // Map([
package/Promise.js CHANGED
@@ -1,11 +1,9 @@
1
1
  /**********************************************************************
2
- *
3
- *
4
- *
5
2
  *
6
3
  * This defines the following extensions to Promise:
7
4
  *
8
5
  * Promise.iter(seq)
6
+ * <promise>.iter()
9
7
  * Iterable promise object.
10
8
  * Similar to Promise.all(..) but adds basic iterator/generator
11
9
  * API and will resolve the items as they are ready (resolved).
@@ -19,6 +17,11 @@
19
17
  * Exposes the API to resolve/reject the promise object
20
18
  * externally.
21
19
  *
20
+ * <promise>.as(obj)
21
+ * Promise proxy.
22
+ * Proxies the methods available from obj to promise value.
23
+ *
24
+ *
22
25
  *
23
26
  *
24
27
  **********************************************/ /* c8 ignore next 2 */
@@ -28,18 +31,6 @@
28
31
 
29
32
  var object = require('ig-object')
30
33
 
31
- // XXX required for STOP...
32
- //var generator = require('./generator')
33
-
34
-
35
- /*********************************************************************/
36
-
37
- /* XXX not used yet...
38
- // NOTE: this is used in a similar fashion to Python's StopIteration...
39
- var STOP =
40
- module.STOP =
41
- object.STOP
42
- //*/
43
34
 
44
35
 
45
36
  //---------------------------------------------------------------------
@@ -48,13 +39,62 @@ module.STOP =
48
39
  // Like Promise.all(..) but adds ability to iterate through results
49
40
  // via generators .map(..)/.reduce(..) and friends...
50
41
  //
42
+ // NOTE: it would be nice to support throwing STOP from the iterable
43
+ // promise but...
44
+ // - this is more complicated than simply using .smap(..) instead
45
+ // of .map(..) because the list can contain async promises...
46
+ // ...would need to wrap each .then(..) call in try-catch and
47
+ // manually handle the stop...
48
+ // ...then there's a question of derivative iterators etc.
49
+ // - another issue here is that the stop would happen in order of
50
+ // execution and not order of elements...
51
+ // XXX EXPEREMENTAL: STOP...
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.
61
+ // NOTE: we are not using async/await here as we need to control the
62
+ // type of promise returned in cases where we know we are returning
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
+
73
+ // XXX should these be exported???
74
+ var iterPromiseProxy =
75
+ //module.iterPromiseProxy =
76
+ function(name){
77
+ return function(...args){
78
+ return this.constructor(
79
+ this.then(function(lst){
80
+ return lst[name](...args) })) } }
81
+ var promiseProxy =
82
+ //module.promiseProxy =
83
+ function(name){
84
+ return async function(...args){
85
+ return (await this)[name](...args) } }
51
86
 
52
87
  var IterablePromise =
53
88
  module.IterablePromise =
54
89
  object.Constructor('IterablePromise', Promise, {
55
- // XXX
56
- //STOP: object.STOP,
90
+ // XXX EXPEREMENTAL: STOP...
91
+ get STOP(){
92
+ return Array.STOP },
57
93
 
94
+ }, {
95
+ // packed array...
96
+ //
97
+ // Holds promise state.
58
98
  //
59
99
  // Format:
60
100
  // [
@@ -64,16 +104,36 @@ object.Constructor('IterablePromise', Promise, {
64
104
  // ...
65
105
  // ]
66
106
  //
67
- __list: null,
107
+ // This format has several useful features:
108
+ // - concatenating packed lists results in a packed list
109
+ // - adding an iterable promise (as-is) into a packed list results
110
+ // in a packed list
111
+ //
112
+ // NOTE: in general iterable promises are implicitly immutable, so
113
+ // it is not recomended to ever edit this in-place...
114
+ // NOTE: we are not isolating or "protecting" any internals to
115
+ // enable users to responsibly extend the code.
116
+ __packed: null,
68
117
 
69
- // low-level .__list handlers/helpers...
118
+ // low-level .__packed handlers/helpers...
70
119
  //
71
120
  // NOTE: these can be useful for debugging and extending...
72
- __pack: function(list, handler){
121
+ //
122
+ // pack and oprionally transform/handle an array (sync)...
123
+ //
124
+ // NOTE: if 'types/Array' is imported this will support throwing STOP
125
+ // from the handler.
126
+ // Due to the async nature of promises though the way stops are
127
+ // handled may be unpredictable -- the handlers can be run out
128
+ // of order, as the nested promises resolve and thus throwing
129
+ // stop will stop the handlers not yet run and not the next
130
+ // handlers in sequence.
131
+ // XXX EXPEREMENTAL: STOP...
132
+ __pack: function(list, handler=undefined){
73
133
  var that = this
74
134
  // handle iterable promise list...
75
135
  if(list instanceof IterablePromise){
76
- return this.__handle(list.__list, handler) }
136
+ return this.__handle(list.__packed, handler) }
77
137
  // handle promise list...
78
138
  if(list instanceof Promise){
79
139
  return list.then(function(list){
@@ -85,6 +145,57 @@ object.Constructor('IterablePromise', Promise, {
85
145
  handler = handler
86
146
  ?? function(elem){
87
147
  return [elem] }
148
+
149
+ //* XXX EXPEREMENTAL: STOP...
150
+ var stoppable = false
151
+ var stop = false
152
+ var map = 'map'
153
+ var pack = function(){
154
+ return [list].flat()
155
+ [map](function(elem){
156
+ return elem && elem.then ?
157
+ (stoppable ?
158
+ // stoppable -- need to handle stop async...
159
+ elem
160
+ .then(function(res){
161
+ return !stop ?
162
+ handler(res)
163
+ : [] })
164
+ // NOTE: we are using .catch(..) here
165
+ // instead of directly passing the
166
+ // error handler to be able to catch
167
+ // the STOP from the handler...
168
+ .catch(handleSTOP)
169
+ // non-stoppable...
170
+ : elem.then(handler))
171
+ : elem instanceof Array ?
172
+ handler(elem)
173
+ // NOTE: we keep things that do not need protecting
174
+ // from .flat() as-is...
175
+ : !handle ?
176
+ elem
177
+ : handler(elem) }) }
178
+
179
+ // pack (stoppable)...
180
+ if(!!this.constructor.STOP){
181
+ stoppable = true
182
+ map = 'smap'
183
+ var handleSTOP = function(err){
184
+ stop = err
185
+ if(err === that.constructor.STOP
186
+ || err instanceof that.constructor.STOP){
187
+ return 'value' in err ?
188
+ err.value
189
+ : [] }
190
+ throw err }
191
+ try{
192
+ return pack()
193
+ }catch(err){
194
+ return handleSTOP(err) } }
195
+
196
+ // pack (non-stoppable)...
197
+ return pack() },
198
+ /*/
88
199
  return [list].flat()
89
200
  .map(function(elem){
90
201
  return elem && elem.then ?
@@ -97,11 +208,13 @@ object.Constructor('IterablePromise', Promise, {
97
208
  : !handle ?
98
209
  elem
99
210
  : handler(elem) }) },
100
- __handle: function(list, handler){
211
+ //*/
212
+ // transform/handle packed array (sync)...
213
+ __handle: function(list, handler=undefined){
101
214
  var that = this
102
215
  if(typeof(list) == 'function'){
103
216
  handler = list
104
- list = this.__list }
217
+ list = this.__packed }
105
218
  if(!handler){
106
219
  return list }
107
220
  // handle promise list...
@@ -112,7 +225,14 @@ object.Constructor('IterablePromise', Promise, {
112
225
  // NOTE: since each section of the packed .__array is the same
113
226
  // structure as the input we'll use .__pack(..) to handle
114
227
  // them, this also keeps all the handling code in one place.
228
+ //* XXX EXPEREMENTAL: STOP...
229
+ var map = !!this.constructor.STOP ?
230
+ 'smap'
231
+ : 'map'
232
+ return list[map](function(elem){
233
+ /*/
115
234
  return list.map(function(elem){
235
+ //*/
116
236
  return elem instanceof Array ?
117
237
  that.__pack(elem, handler)
118
238
  : elem instanceof Promise ?
@@ -121,48 +241,28 @@ object.Constructor('IterablePromise', Promise, {
121
241
  return elem.flat() })
122
242
  : [handler(elem)] })
123
243
  .flat() },
124
- __unpack: function(list){
244
+ // unpack array (async)...
245
+ __unpack: async function(list){
125
246
  list = list
126
- ?? this.__list
247
+ ?? this.__packed
127
248
  // handle promise list...
128
- if(list instanceof Promise){
129
- var that = this
130
- return list.then(function(list){
131
- return that.__unpack(list) }) }
132
- // do the work...
133
- return Promise.all(list)
134
- .then(function(list){
135
- return list.flat() }) },
249
+ return list instanceof Promise ?
250
+ this.__unpack(await list)
251
+ // do the work...
252
+ : (await Promise.all(list))
253
+ .flat() },
136
254
 
137
255
 
138
256
  // iterator methods...
139
257
  //
140
258
  // These will return a new IterablePromise instance...
141
259
  //
142
- // When called from a resolved promise these will return a new
143
- // resolved promise with updated values...
144
- //
145
- // When called from a rejected promise these will return a rejected
146
- // with the same reason promise...
147
- //
148
- //
149
260
  // NOTE: these are different to Array's equivalents in that the handler
150
261
  // is called not in the order of the elements but rather in order
151
262
  // of promise resolution...
152
263
  // NOTE: index of items is unknowable because items can expand and
153
- // contract depending on handlrs (e.g. .filter(..) can remove
264
+ // contract depending on handlers (e.g. .filter(..) can remove
154
265
  // items)...
155
- // This the following can not be implemented here:
156
- // .slice(..)
157
- // .splice(..)
158
- // .values() / .keys()
159
- // .at(..)
160
- // [Symbol.iterator]() - needs to be sync...
161
- // The followng methods are questionable:
162
- // .indexOf(..)
163
- // .includes(..)
164
- // .some(..) / .every(..)
165
- // .sort(..)
166
266
  map: function(func){
167
267
  return this.constructor(this,
168
268
  function(e){
@@ -184,11 +284,11 @@ object.Constructor('IterablePromise', Promise, {
184
284
  : _filter(e) }) },
185
285
  // NOTE: this does not return an iterable promise as we can't know
186
286
  // what the user reduces to...
187
- // XXX we could look at the initial state though...
188
287
  // NOTE: the items can be handled out of order because the nested
189
288
  // promises can resolve in any order...
190
289
  // NOTE: since order of execution can not be guaranteed there is no
191
- // point in implementing .reduceRight(..)
290
+ // point in implementing .reduceRight(..) in the same way
291
+ // (see below)...
192
292
  reduce: function(func, res){
193
293
  return this.constructor(this,
194
294
  function(e){
@@ -196,6 +296,7 @@ object.Constructor('IterablePromise', Promise, {
196
296
  return [] })
197
297
  .then(function(){
198
298
  return res }) },
299
+
199
300
  flat: function(depth=1){
200
301
  return this.constructor(this,
201
302
  function(e){
@@ -207,7 +308,7 @@ object.Constructor('IterablePromise', Promise, {
207
308
  e
208
309
  : [e] }) },
209
310
  reverse: function(){
210
- var lst = this.__list
311
+ var lst = this.__packed
211
312
  return this.constructor(
212
313
  lst instanceof Promise ?
213
314
  lst.then(function(elems){
@@ -252,15 +353,120 @@ object.Constructor('IterablePromise', Promise, {
252
353
  return this.constructor([elem])
253
354
  .concat(this) },
254
355
 
255
- // XXX do we need these?
256
- // .pop()
257
- // .shift()
258
- // .first() / .last()
259
- // .at(..)
260
- // ...would be nice if these could stop everything that's not
261
- // needed to execute...
356
+ // proxy methods...
357
+ //
358
+ // These require the whole promise to resolve to trigger.
359
+ //
360
+ // An exception to this would be .at(0)/.first() and .at(-1)/.last()
361
+ // that can get the target element if it's accessible.
362
+ //
363
+ // NOTE: methods that are guaranteed to return an array will return
364
+ // an iterable promise (created with iterPromiseProxy(..))...
365
+ //
366
+ at: async function(i){
367
+ var list = this.__packed
368
+ return ((i != 0 && i != -1)
369
+ || list instanceof Promise
370
+ || list.at(i) instanceof Promise) ?
371
+ (await this).at(i)
372
+ // NOTE: we can only reason about first/last explicit elements,
373
+ // anything else is non-deterministic...
374
+ : list.at(i) instanceof Promise ?
375
+ [await list.at(i)].flat().at(i)
376
+ : list.at(i) instanceof Array ?
377
+ list.at(i).at(i)
378
+ : list.at(i) },
379
+ first: function(){
380
+ return this.at(0) },
381
+ last: function(){
382
+ return this.at(-1) },
262
383
 
384
+ // NOTE: unlike .reduce(..) this needs the parent fully resolved
385
+ // to be able to iterate from the end.
386
+ // XXX is it faster to do .reverse().reduce(..) ???
387
+ // XXX ???
388
+ reduceRight: promiseProxy('reduceRight'),
389
+
390
+ // NOTE: there is no way we can do a sync generator returning
391
+ // promises for values because any promise in .__packed makes
392
+ // the value count/index non-deterministic...
393
+ sort: iterPromiseProxy('sort'),
394
+ slice: iterPromiseProxy('slice'),
395
+
396
+ entries: iterPromiseProxy('entries'),
397
+ keys: iterPromiseProxy('keys'),
398
+ values: iterPromiseProxy('values'),
399
+
400
+ indexOf: promiseProxy('indexOf'),
401
+ lastIndexOf: promiseProxy('lastIndexOf'),
402
+ includes: promiseProxy('includes'),
263
403
 
404
+ //
405
+ // .find(<func>)
406
+ // .find(<func>, 'value')
407
+ // -> <promise>(<value>)
408
+ //
409
+ // .find(<func>, 'result')
410
+ // -> <promise>(<result>)
411
+ //
412
+ // .find(<func>, 'bool')
413
+ // -> <promise>(<bool>)
414
+ //
415
+ // NOTE: this is slightly different to Array's .find(..) in that it
416
+ // accepts the result value enabling returning both the value
417
+ // itself ('value', default), the test function's result
418
+ // ('result') or true/false ('bool') -- this is added to be
419
+ // able to distinguish between the undefined as a stored value
420
+ // and undefined as a "nothing found" result.
421
+ // NOTE: I do not get how essentially identical methods .some(..)
422
+ // and .find(..) got added to JS's Array...
423
+ // the only benefit is that .some(..) handles undefined values
424
+ // stored in the array better...
425
+ // NOTE: this will return the result as soon as it's available but
426
+ // it will not stop the created but unresolved at the time
427
+ // promises from executing, this is both good and bad:
428
+ // + it will not break other clients waiting for promises
429
+ // to resolve...
430
+ // - if no clients are available this can lead to wasted
431
+ // CPU time...
432
+ find: async function(func, result='value'){
433
+ var that = this
434
+ // NOTE: not using pure await here as this is simpler to actually
435
+ // control the moment the resulting promise resolves without
436
+ // the need for juggling state...
437
+ return new Promise(function(resolve, reject){
438
+ var resolved = false
439
+ that.map(function(elem){
440
+ var res = func(elem)
441
+ if(res){
442
+ resolved = true
443
+ resolve(
444
+ result == 'bool' ?
445
+ true
446
+ : result == 'result' ?
447
+ res
448
+ : elem)
449
+ // XXX EXPEREMENTAL: STOP...
450
+ // NOTE: we do not need to throw STOP here
451
+ // but it can prevent some overhead...
452
+ if(that.constructor.STOP){
453
+ throw that.constructor.STOP } } })
454
+ .then(function(){
455
+ resolved
456
+ || resolve(
457
+ result == 'bool' ?
458
+ false
459
+ : undefined) }) }) },
460
+ findIndex: promiseProxy('findIndex'),
461
+
462
+ // NOTE: this is just a special-case of .find(..)
463
+ some: async function(func){
464
+ return this.find(func, 'bool') },
465
+ every: promiseProxy('every'),
466
+
467
+
468
+ // promise api...
469
+ //
264
470
  // Overload .then(..), .catch(..) and .finally(..) to return a plain
265
471
  // Promise instnace...
266
472
  //
@@ -286,6 +492,13 @@ object.Constructor('IterablePromise', Promise, {
286
492
  : p },
287
493
 
288
494
 
495
+ // this is defined globally as Promise.prototype.iter(.,)
496
+ //
497
+ // for details see: PromiseMixin(..) below...
498
+ //iter: function(handler=undefined){ ... },
499
+
500
+
501
+ // constructor...
289
502
  //
290
503
  // Promise.iter([ .. ])
291
504
  // -> iterable-promise
@@ -295,8 +508,9 @@ object.Constructor('IterablePromise', Promise, {
295
508
  //
296
509
  //
297
510
  // handler(e)
298
- // -> [value]
511
+ // -> [value, ..]
299
512
  // -> []
513
+ // -> <promise>
300
514
  //
301
515
  //
302
516
  // NOTE: element index is unknowable until the full list is expanded
@@ -308,7 +522,7 @@ object.Constructor('IterablePromise', Promise, {
308
522
  //
309
523
  // Special cases useful for extending this constructor...
310
524
  //
311
- // Set raw .__list without any pre-processing...
525
+ // Set raw .__packed without any pre-processing...
312
526
  // Promise.iter([ .. ], 'raw')
313
527
  // -> iterable-promise
314
528
  //
@@ -317,18 +531,9 @@ object.Constructor('IterablePromise', Promise, {
317
531
  // -> iterable-promise
318
532
  //
319
533
  //
320
- // XXX iterator/generator as input:
321
- // - do we unwind here or externally?
322
- // ...feels like with the generator external unwinding is
323
- // needed...
324
- // XXX would be nice to support throwing STOP...
325
- // - this is more complicated than simply suing .smap(..) instead
326
- // of .map(..) because the list can contain async promises...
327
- // ...would need to wrap each .then(..) call in try-catch and
328
- // manually handle the stop...
329
- // - another issue here is that the stop would happen in order of
330
- // execution and not order of elements...
331
- // XXX how do we handle errors???
534
+ // NOTE: if 'types/Array' is imported this will support throwing STOP,
535
+ // for more info see notes for .__pack(..)
536
+ // XXX EXPEREMENTAL: STOP...
332
537
  __new__: function(_, list, handler){
333
538
  // instance...
334
539
  var promise
@@ -344,14 +549,14 @@ object.Constructor('IterablePromise', Promise, {
344
549
  promise = {resolve, reject} }],
345
550
  IterablePromise)
346
551
 
347
- // new instance...
552
+ // populate new instance...
348
553
  if(promise){
349
554
  // handle/pack input data...
350
555
  if(handler != 'raw'){
351
556
  list = list instanceof IterablePromise ?
352
- this.__handle(list.__list, handler)
557
+ this.__handle(list.__packed, handler)
353
558
  : this.__pack(list, handler) }
354
- Object.defineProperty(obj, '__list', {
559
+ Object.defineProperty(obj, '__packed', {
355
560
  value: list,
356
561
  enumerable: false,
357
562
  })
@@ -414,13 +619,14 @@ object.Constructor('InteractivePromise', Promise, {
414
619
  // register a handler...
415
620
  } else {
416
621
  var h = obj == null ?
417
- // NOTE: we need to get the handlers from .__message_handlers
418
- // unless we are not fully defined yet, then use the
419
- // bootstrap container (handlers)...
420
- // ...since we can call onmessage(..) while the promise
421
- // is still defined there is no way to .send(..) until it
422
- // returns a promise object, this races here are highly
423
- // unlikely...
622
+ // NOTE: we need to get the handlers from
623
+ // .__message_handlers unless we are not
624
+ // fully defined yet, then use the bootstrap
625
+ // container (handlers)...
626
+ // ...since we can call onmessage(..) while
627
+ // the promise is still defined there is no
628
+ // way to .send(..) until it returns a promise
629
+ // object, this races here are highly unlikely...
424
630
  handlers
425
631
  : (obj.__message_handlers =
426
632
  obj.__message_handlers ?? [])
@@ -507,10 +713,13 @@ object.Constructor('CooperativePromise', Promise, {
507
713
  var ProxyPromise =
508
714
  module.ProxyPromise =
509
715
  object.Constructor('ProxyPromise', Promise, {
510
- __new__: function(context, constructor){
511
- var proto = 'prototype' in constructor ?
512
- constructor.prototype
513
- : constructor
716
+
717
+ then: IterablePromise.prototype.then,
718
+
719
+ __new__: function(context, other, nooverride=false){
720
+ var proto = 'prototype' in other ?
721
+ other.prototype
722
+ : other
514
723
  var obj = Reflect.construct(
515
724
  ProxyPromise.__proto__,
516
725
  [function(resolve, reject){
@@ -521,6 +730,9 @@ object.Constructor('ProxyPromise', Promise, {
521
730
  // NOTE: we are not using object.deepKeys(..) here as we need
522
731
  // the key origin not to trigger property getters...
523
732
  var seen = new Set()
733
+ nooverride = nooverride instanceof Array ?
734
+ new Set(nooverride)
735
+ : nooverride
524
736
  while(proto != null){
525
737
  Object.entries(Object.getOwnPropertyDescriptors(proto))
526
738
  .forEach(function([key, value]){
@@ -535,11 +747,15 @@ object.Constructor('ProxyPromise', Promise, {
535
747
  && Object.prototype.run === value.value)
536
748
  && !value.enumerable){
537
749
  return }
750
+ // do not override existing methods...
751
+ if(nooverride === true ?
752
+ key in obj
753
+ : nooverride instanceof Set ?
754
+ nooverride.has(key)
755
+ : nooverride){
756
+ return }
538
757
  // proxy...
539
- obj[key] = function(...args){
540
- // XXX should we also .catch(..) here???
541
- return context.then(function(res){
542
- return res[key](...args) }) } })
758
+ obj[key] = promiseProxy(key) })
543
759
  proto = proto.__proto__ }
544
760
  return obj },
545
761
  })
@@ -563,7 +779,7 @@ var PromiseProtoMixin =
563
779
  module.PromiseProtoMixin =
564
780
  object.Mixin('PromiseProtoMixin', 'soft', {
565
781
  as: ProxyPromise,
566
- iter: function(handler){
782
+ iter: function(handler=undefined){
567
783
  return IterablePromise(this, handler) },
568
784
  })
569
785
 
@@ -573,4 +789,4 @@ PromiseProtoMixin(Promise.prototype)
573
789
 
574
790
 
575
791
  /**********************************************************************
576
- * vim:set ts=4 sw=4 : */ return module })
792
+ * vim:set ts=4 sw=4 nowrap : */ return module })
package/README.md CHANGED
@@ -77,8 +77,14 @@ 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
+ - [`<promise-iter>.some(..)` / `<promise-iter>.find(..)`](#promise-itersome--promise-iterfind)
82
+ - [Array proxy methods returning `<promise-iter>`](#array-proxy-methods-returning-promise-iter)
83
+ - [Array proxy methods returning a `<promise>`](#array-proxy-methods-returning-a-promise)
80
84
  - [`<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally)
85
+ - [`promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`](#promiseiterablepromisestop--promiseiterablepromisestop)
81
86
  - [Advanced handler](#advanced-handler)
87
+ - [Stopping the iteration](#stopping-the-iteration)
82
88
  - [Promise proxies](#promise-proxies)
83
89
  - [`<promise>.as(..)`](#promiseas)
84
90
  - [`<promise-proxy>.<method>(..)`](#promise-proxymethod)
@@ -1508,6 +1514,17 @@ promise, even if the original is resolved.
1508
1514
  If all values are resolved the `<promise-iter>` will resolve on the next
1509
1515
  execution frame.
1510
1516
 
1517
+ There are two types of iterator methods here, both are transparent but different
1518
+ in how they process values:
1519
+ - *Parallel methods*
1520
+ These handle elements as soon as they are available even if the parent promise
1521
+ is not yet resolved.
1522
+ <!-- XXX list -->
1523
+ - *Proxies*
1524
+ These methods simply wait for the main promise to resolve and then call the
1525
+ appropriate method on the result.
1526
+ <!-- XXX list + mark definitions as "(proxy)" -->
1527
+
1511
1528
  <!--
1512
1529
  XXX should we support generators as input?
1513
1530
  ...not sure about the control flow direction here, on one hand the generator
@@ -1652,6 +1669,95 @@ and [`<array>.unshift(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScr
1652
1669
  see them for more info.
1653
1670
 
1654
1671
 
1672
+ #### `<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`
1673
+
1674
+ Proxies to the appropriate array methods with a special-case: when getting elements
1675
+ at positions `0` or `-1` (i.e. `.first()` / `.last()`) these _can_ resolve before the
1676
+ parent `<promise-iter>`.
1677
+
1678
+ XXX
1679
+
1680
+ #### `<promise-iter>.some(..)` / `<promise-iter>.find(..)`
1681
+
1682
+ ```bnf
1683
+ <promise-iter>.some(<func>)
1684
+ -> <promise>
1685
+
1686
+ <promise-iter>.find(<func>)
1687
+ -> <promise>
1688
+ ```
1689
+
1690
+ The main difference between `.some(..)` and `.find(..)` is in that the `<promise>`
1691
+ returned from the former will resolve to either `true` or `false`, and in the later
1692
+ to the found value or `undefined`.
1693
+
1694
+ `.find(..)` supports an additional argument that controls what returned `<promise>`
1695
+ is resolved to...
1696
+
1697
+ ```bnf
1698
+ <promise-iter>.find(<func>)
1699
+ <promise-iter>.find(<func>, 'value')
1700
+ -> <promise>
1701
+
1702
+ <promise-iter>.find(<func>, 'bool')
1703
+ -> <promise>
1704
+
1705
+ <promise-iter>.find(<func>, 'result')
1706
+ -> <promise>
1707
+ ```
1708
+
1709
+ - `value` (default)
1710
+ resolve to the stored value if found and `undefined` otherwise.
1711
+ - `bool`
1712
+ resolve to `true` if the value is found and `false` otherwise, this is how
1713
+ `.some(..)` is impelemnted.
1714
+ - `result`
1715
+ resolve to the return value of the test `<func>`.
1716
+
1717
+ These are similar to [`<array>.some(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
1718
+ and [`<array>.find(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
1719
+ see them for more info.
1720
+
1721
+ #### Array proxy methods returning `<promise-iter>`
1722
+
1723
+ - `<promise-iter>.sort(..)`
1724
+ - `<promise-iter>.slice(..)`
1725
+ - `<promise-iter>.entries()` / `<promise-iter>.keys()` / `<promise-iter>.values()`
1726
+
1727
+ These methods are proxies to the appropriate array methods.
1728
+
1729
+ ```bnf
1730
+ <promise-iter>.<method>(..)
1731
+ -> <promise-iter>
1732
+ ```
1733
+
1734
+ These methods need the parent `<promise-iter>` to resolve before resolving themselves.
1735
+
1736
+ XXX links...
1737
+
1738
+
1739
+ #### Array proxy methods returning a `<promise>`
1740
+
1741
+ - `<promise-iter>.indexOf(..)`
1742
+ - `<promise-iter>.includes(..)`
1743
+ - `<promise-iter>.every(..)`
1744
+ - `<promise-iter>.findIndex(..)`
1745
+
1746
+ These methods are proxies to the appropriate array methods.
1747
+
1748
+ ```bnf
1749
+ <promise-iter>.<method>(..)
1750
+ -> <promise>
1751
+ ```
1752
+
1753
+ These methods need the parent `<promise-iter>` to resolve before resolving themselves.
1754
+
1755
+ Since the equivalent array methods do not return iterables these will return a basic
1756
+ (non-iterable) `<promise>`.
1757
+
1758
+ XXX links...
1759
+
1760
+
1655
1761
  #### `<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`
1656
1762
 
1657
1763
  An extension to
@@ -1665,6 +1771,16 @@ this adds the ability to pass no arguments
1665
1771
  This will return a generic promise wrapper passing through the results as-is. This
1666
1772
  can be useful to hide the extended promise API from further code.
1667
1773
 
1774
+ #### `promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`
1775
+
1776
+ A special object that when thrown from a function/promise handler will stop
1777
+ further iteration.
1778
+
1779
+ This is `undefined` until the `ig-types/Array` module is loaded.
1780
+
1781
+ For more info see: [Stopping the iteration](#stopping-the-iteration) below, and
1782
+ [the 'Array' STOP section](#arraystop--arraystop)
1783
+
1668
1784
 
1669
1785
  #### Advanced handler
1670
1786
 
@@ -1716,6 +1832,24 @@ var p = Promise.iter(
1716
1832
  console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
1717
1833
  ```
1718
1834
 
1835
+ #### Stopping the iteration
1836
+
1837
+ Like the [`Array`](#arraystop--arraystop) module, this support throwing `STOP` to
1838
+ stop iteration. As we uses [`.smap(..)`](#arraysmap--arraysfilter--arraysreduce--arraysforeach)
1839
+ stopping support is supported if `ig-types/Array` module is loaded.
1840
+
1841
+ ```javascript
1842
+ require('ig-types/Array')
1843
+ ```
1844
+
1845
+ This is also different semantically, as promise iteration can happen out of order,
1846
+ stopping affects the order of processing and not order of the input array with one exception: promises already created can not be stopped in `JavaScript`.
1847
+
1848
+ Any handler function passed to a `<promise-iter>` method can `throw` a STOP.
1849
+
1850
+ For more details see: [the 'Array' STOP section](#arraystop--arraystop)
1851
+
1852
+
1719
1853
 
1720
1854
  ### Promise proxies
1721
1855
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.13.7",
3
+ "version": "6.15.0",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {