ig-types 6.25.4 → 6.26.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.
package/Number.js ADDED
@@ -0,0 +1,103 @@
1
+ /**********************************************************************
2
+ *
3
+ *
4
+ **********************************************/ /* c8 ignore next 2 */
5
+ ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
6
+ (function(require){ var module={} // make module AMD/node compatible...
7
+ /*********************************************************************/
8
+
9
+ var object = require('ig-object')
10
+
11
+
12
+ //---------------------------------------------------------------------
13
+
14
+ var NumberMixin =
15
+ module.NumberMixin =
16
+ object.Mixin('NumberMixin', 'soft', {
17
+ toAlpha: function(n, alpha='abcdefghijklmnopqrstuvwxyz'){
18
+ var res = ''
19
+ var base = alpha.length
20
+ do{
21
+ res = alpha[n % base] + res
22
+ n = Math.floor(n/base) - 1
23
+ }while(n >= 0)
24
+ return res },
25
+ fromAlpha: function(str, alpha='abcdefghijklmnopqrstuvwxyz'){
26
+ var res = 0
27
+ var base = alpha.length
28
+ var i = 0
29
+ for(var c of [...str].reverse()){
30
+ var val = alpha.indexOf(c) + 1
31
+ res += val * (base ** i++) }
32
+ return res-1 },
33
+
34
+ toRoman: function(n){
35
+ var index = {
36
+ M: 1000,
37
+ CM: 900,
38
+ D: 500,
39
+ CD: 400,
40
+ C: 100,
41
+ XC: 90,
42
+ L: 50,
43
+ XL: 40,
44
+ X: 10,
45
+ IX: 9,
46
+ V: 5,
47
+ IV: 4,
48
+ I: 1,
49
+ }
50
+ var res = ''
51
+ for(var R in index){
52
+ while(n >= index[R]){
53
+ res += R
54
+ n -= index[R] } }
55
+ return res },
56
+ fromRoman: function(str){
57
+ var index = {
58
+ M: 1000,
59
+ CM: 900,
60
+ D: 500,
61
+ CD: 400,
62
+ C: 100,
63
+ XC: 90,
64
+ L: 50,
65
+ XL: 40,
66
+ X: 10,
67
+ IX: 9,
68
+ V: 5,
69
+ IV: 4,
70
+ I: 1,
71
+ }
72
+ var n = 0
73
+ str = str.toUpperCase()
74
+ while(str != ''){
75
+ if(str.slice(0, 2) in index){
76
+ n += index[str.slice(0, 2)]
77
+ str = str.slice(2)
78
+ } else if(str[0] in index){
79
+ n += index[str[0]]
80
+ str = str.slice(1)
81
+ } else {
82
+ throw new Error('fromRoman(..): Unknown sequence: '+ str) } }
83
+ return n },
84
+ })
85
+
86
+ NumberMixin(Number)
87
+
88
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89
+ var NumberProtoMixin =
90
+ module.NumberProtoMixin =
91
+ object.Mixin('NumberProtoMixin', 'soft', {
92
+ toAlpha: function(alpha='abcdefghijklmnopqrstuvwxyz'){
93
+ return this.constructor.toAlpha(this, alpha) },
94
+ toRoman: function(n){
95
+ return this.constructor.toRoman(this) },
96
+ })
97
+
98
+ NumberProtoMixin(Number.prototype)
99
+
100
+
101
+
102
+ /**********************************************************************
103
+ * vim:set ts=4 sw=4 nowrap : */ return module })
package/main.js CHANGED
@@ -10,6 +10,7 @@
10
10
  var object = require('ig-object')
11
11
 
12
12
  // Extend built-in types...
13
+ require('./Number')
13
14
  require('./Object')
14
15
  require('./Array')
15
16
  require('./Set')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.25.4",
3
+ "version": "6.26.0",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {
package/Promise.js.new DELETED
@@ -1,1127 +0,0 @@
1
- /**********************************************************************
2
- *
3
- * This defines the following extensions to Promise:
4
- *
5
- * Promise.iter(seq)
6
- * <promise>.iter()
7
- * Iterable promise object.
8
- * Similar to Promise.all(..) but adds basic iterator API.
9
- *
10
- * Promise.interactive(handler)
11
- * Interactive promise object.
12
- * This adds a basic message passing API to the promise.
13
- *
14
- * Promise.cooperative()
15
- * Cooperative promise object.
16
- * Exposes the API to resolve/reject the promise object
17
- * externally.
18
- *
19
- * <promise>.as(obj)
20
- * Promise proxy.
21
- * Proxies the methods available from obj to promise value.
22
- *
23
- *
24
- *
25
- *
26
- **********************************************/ /* c8 ignore next 2 */
27
- ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
28
- (function(require){ var module={} // make module AMD/node compatible...
29
- /*********************************************************************/
30
-
31
- var object = require('ig-object')
32
-
33
- var generator = require('./generator')
34
-
35
-
36
-
37
- //---------------------------------------------------------------------
38
- // Iterable promise...
39
- //
40
- // Like Promise.all(..) but adds ability to iterate through results
41
- // via generators .map(..)/.reduce(..) and friends...
42
- //
43
- // NOTE: the following can not be implemented here:
44
- // .splice(..) - can't both modify and return
45
- // a result...
46
- // .pop() / .shift() - can't modify the promise, use
47
- // .first() / .last() instead.
48
- // [Symbol.iterator]() - needs to be sync and we can't
49
- // know the number of elements to
50
- // return promises before the whole
51
- // iterable promise is resolved.
52
- // NOTE: we are not using async/await here as we need to control the
53
- // type of promise returned in cases where we know we are returning
54
- // an array...
55
- // NOTE: there is no point in implementing a 1:1 version of this that
56
- // would not support element expansion/contraction as it would only
57
- // simplify a couple of methods that are 1:1 (like .map(..) and
58
- // .some(..)) while methods like .filter(..) will throw everything
59
- // back to the complex IterablePromise...
60
- //
61
- // XXX how do we handle errors/rejections???
62
- // ...mostly the current state is OK, but need more testing...
63
- // XXX add support for async generators...
64
- //
65
-
66
- var iterPromiseProxy =
67
- module.iterPromiseProxy =
68
- function(name){
69
- return function(...args){
70
- return this.constructor(
71
- this.then(function(lst){
72
- return lst[name](...args) })) } }
73
-
74
- // XXX ASYNC should this be async or simply return a SyncPromise/Promise???
75
- var promiseProxy =
76
- module.promiseProxy =
77
- function(name){
78
- return async function(...args){
79
- return (await this)[name](...args) } }
80
-
81
-
82
- var IterablePromise =
83
- module.IterablePromise =
84
- object.Constructor('IterablePromise', Promise, {
85
- get STOP(){
86
- return Array.STOP },
87
-
88
- }, {
89
- // packed array...
90
- //
91
- // Holds promise state.
92
- //
93
- // Format:
94
- // [
95
- // <non-array-value>,
96
- // [ <value> ],
97
- // <promise>,
98
- // ...
99
- // ]
100
- //
101
- // This format has several useful features:
102
- // - concatenating packed lists results in a packed list
103
- // - adding an iterable promise (as-is) into a packed list results
104
- // in a packed list
105
- //
106
- // NOTE: in general iterable promises are implicitly immutable, so
107
- // it is not recomended to ever edit this in-place...
108
- // NOTE: we are not isolating or "protecting" any internals to
109
- // enable users to responsibly extend the code.
110
- __packed: null,
111
-
112
- // low-level .__packed handlers/helpers...
113
- //
114
- // NOTE: these can be useful for debugging and extending...
115
- //
116
- // pack and oprionally transform/handle an array (sync)...
117
- //
118
- // NOTE: if 'types/Array' is imported this will support throwing STOP
119
- // from the handler.
120
- // Due to the async nature of promises though the way stops are
121
- // handled may be unpredictable -- the handlers can be run out
122
- // of order, as the nested promises resolve and thus throwing
123
- // stop will stop the handlers not yet run and not the next
124
- // handlers in sequence.
125
- // XXX EXPEREMENTAL: STOP...
126
- // XXX ITER can we unwind (sync/async) generators one by one???
127
- __pack: function(list, handler=undefined, onerror=undefined){
128
- var that = this
129
- // handle iterator...
130
- // XXX ITER do we unwind the iterator here or wait to unwind later???
131
- if(typeof(list) == 'object'
132
- && Symbol.iterator in list){
133
- if(!onerror){
134
- list = [...list]
135
- // handle errors in input generator...
136
- // NOTE: this does not offer the most control because semantically
137
- // we bust behave in the same manner as <generator>.iter(..)
138
- } else {
139
- var res = []
140
- try{
141
- for(var e of list){
142
- res.push(e) }
143
- }catch(err){
144
- var r = onerror(err)
145
- r !== undefined
146
- && res.push(r) }
147
- list = res } }
148
- // handle iterable promise...
149
- if(list instanceof IterablePromise){
150
- return this.__handle(list.__packed, handler, onerror) }
151
- // handle promise / async-iterator...
152
- // XXX ITER do we unwind the iterator here or wait to unwind later???
153
- if(typeof(list) == 'object'
154
- && Symbol.asyncIterator in list){
155
- return list
156
- .iter(handler, onerror)
157
- .then(function(list){
158
- return that.__pack(list) }) }
159
- if(list instanceof Promise){
160
- return list
161
- .then(function(list){
162
- return that.__pack(list, handler, onerror) }) }
163
- // do the work...
164
- // NOTE: packing and handling are mixed here because it's faster
165
- // to do them both on a single list traverse...
166
- handler = handler
167
- ?? function(elem){
168
- return [elem] }
169
- return that.__handle([list].flat(), handler, onerror) },
170
- // transform/handle packed array (sync, but can return promises in the list)...
171
- __handle: function(list, handler=undefined, onerror=undefined){
172
- var that = this
173
- if(typeof(list) == 'function'){
174
- handler = list
175
- list = this.__packed }
176
- if(!handler){
177
- return list }
178
- // handle promise list...
179
- if(list instanceof Promise){
180
- return list.then(function(list){
181
- return that.__handle(list, handler, onerror) }) }
182
- // do the work...
183
- // NOTE: since each section of the packed .__array is the same
184
- // structure as the input we'll use .__pack(..) to handle
185
- // them, this also keeps all the handling code in one place.
186
- var stop = false
187
- var map = 'map'
188
- var pack = function(){
189
- return [list].flat()
190
- [map](function(elem){
191
- // XXX EXPEREMENTAL...
192
- return elem instanceof IterablePromise ?
193
- (elem.isSync() ?
194
- handler(elem.sync())
195
- // XXX need to handle this but keep it IterablePromise...
196
- : elem.iterthen(handler))
197
- : (elem instanceof SyncPromise
198
- && !(elem.sync() instanceof Promise)) ?
199
- handler(elem.sync())
200
- : elem && elem.then ?
201
- (handleSTOP ?
202
- // stoppable -- need to handle stop async...
203
- elem
204
- .then(function(res){
205
- return !stop ?
206
- handler(res)
207
- : [] })
208
- // NOTE: we are using .catch(..) here
209
- // instead of directly passing the
210
- // error handler to be able to catch
211
- // the STOP from the handler...
212
- .catch(handleSTOP)
213
- // non-stoppable...
214
- : elem.then(handler))
215
- : elem instanceof Array ?
216
- handler(elem)
217
- : handler(elem) }) }
218
- // pack (stoppable)...
219
- if(!!this.constructor.STOP){
220
- map = 'smap'
221
- var handleSTOP = function(err){
222
- // handle stop...
223
- stop = err
224
- if(err === that.constructor.STOP
225
- || err instanceof that.constructor.STOP){
226
- return 'value' in err ?
227
- err.value
228
- : [] }
229
- throw err }
230
- try{
231
- return pack()
232
- }catch(err){
233
- return handleSTOP(err) } }
234
- // pack (non-stoppable)...
235
- return pack() },
236
- // XXX this should return IterablePromise if .__packed is partially sync (???)
237
- // unpack array (sync/async)...
238
- __unpack: function(list){
239
- list = list
240
- ?? this.__packed
241
- // handle promise list...
242
- if(list instanceof IterablePromise){
243
- return list.__unpack() }
244
- if(list instanceof Promise){
245
- return list
246
- .then(this.__unpack.bind(this)) }
247
- var res = []
248
- for(var e of list){
249
- if(e instanceof IterablePromise){
250
- e = e.__unpack() }
251
- if(e instanceof SyncPromise){
252
- e = e.sync() }
253
- // give up on a sync solution...
254
- if(e instanceof Promise){
255
- // XXX can we return an IterablePromise???
256
- // XXX this will cause infinite recursion....
257
- //return Promise.iter(list).flat() }
258
- // XXX is there a more elegant way to do this???
259
- return Promise.all(list)
260
- .then(function(list){
261
- return list.flat() })
262
- .iter() }
263
- //return Promise.all(list)
264
- // .then(function(list){
265
- // return list.flat() }) }
266
- res.push(e) }
267
- return res.flat() },
268
-
269
- [Symbol.asyncIterator]: async function*(){
270
- var list = this.__packed
271
- if(list instanceof Promise){
272
- yield* await this.__unpack(list)
273
- return }
274
- for await(var elem of list){
275
- yield* elem instanceof Array ?
276
- elem
277
- : [elem] } },
278
-
279
- // iterator methods...
280
- //
281
- // These will return a new IterablePromise instance...
282
- //
283
- // NOTE: these are different to Array's equivalents in that the handler
284
- // is called not in the order of the elements but rather in order
285
- // of promise resolution...
286
- // NOTE: index of items is unknowable because items can expand and
287
- // contract depending on handlers (e.g. .filter(..) can remove
288
- // items)...
289
- map: function(func){
290
- return this.constructor(this,
291
- function(e){
292
- var res = func(e)
293
- return res instanceof Promise ?
294
- res.then(function(e){
295
- return [e] })
296
- : [res] }) },
297
- filter: function(func){
298
- return this.constructor(this,
299
- function(e){
300
- var res = func(e)
301
- var _filter = function(elem){
302
- return res ?
303
- [elem]
304
- : [] }
305
- return res instanceof Promise ?
306
- res.then(_filter)
307
- : _filter(e) }) },
308
- // NOTE: this does not return an iterable promise as we can't know
309
- // what the user reduces to...
310
- // NOTE: the items can be handled out of order because the nested
311
- // promises can resolve in any order...
312
- // NOTE: since order of execution can not be guaranteed there is no
313
- // point in implementing .reduceRight(..) in the same way
314
- // (see below)...
315
- reduce: function(func, res){
316
- return this.constructor(this,
317
- function(e){
318
- res = func(res, e)
319
- return [] })
320
- .then(function(){
321
- return res }) },
322
-
323
- // XXX BETWEEN...
324
- between: function(func){
325
- var i = 0
326
- var j = 0
327
- var prev
328
- return this.constructor(this,
329
- function(e){
330
- return i++ > 0 ?
331
- [
332
- typeof(func) == 'function' ?
333
- func.call(this, [prev, e], i, i + j++)
334
- : func,
335
- e,
336
- ]
337
- : [e] }) },
338
-
339
- // XXX .chain(..) -- see generator.chain(..)
340
-
341
- flat: function(depth=1){
342
- return this.constructor(this,
343
- function(e){
344
- return (depth > 1
345
- && e != null
346
- && e.flat) ?
347
- e.flat(depth-1)
348
- : depth != 0 ?
349
- e
350
- : [e] }) },
351
- reverse: function(){
352
- var lst = this.__packed
353
- return this.constructor(
354
- lst instanceof Promise ?
355
- lst.then(function(elems){
356
- return elems instanceof Array ?
357
- elems.slice()
358
- .reverse()
359
- : elems })
360
- : lst
361
- .map(function(elems){
362
- return elems instanceof Array ?
363
- elems.slice()
364
- .reverse()
365
- : elems instanceof Promise ?
366
- elems.then(function(elems){
367
- return elems.reverse() })
368
- : elems })
369
- .reverse(),
370
- 'raw') },
371
-
372
- // NOTE: the following methods can create an unresolved promise from
373
- // a resolved promise...
374
- concat: function(other){
375
- var that = this
376
- var cur = this.__pack(this)
377
- var other = this.__pack(other)
378
- return this.constructor(
379
- // NOTE: we need to keep things as exposed as possible, this
380
- // is why we're not blanketing all the cases with
381
- // Promise.all(..)...
382
- (cur instanceof Promise
383
- && other instanceof Promise) ?
384
- [cur, other]
385
- : cur instanceof Promise ?
386
- [cur, ...other]
387
- : other instanceof Promise ?
388
- [...cur, other]
389
- : [...cur, ...other],
390
- 'raw') },
391
- push: function(elem){
392
- return this.concat([elem]) },
393
- unshift: function(elem){
394
- return this.constructor([elem])
395
- .concat(this) },
396
-
397
- // proxy methods...
398
- //
399
- // These require the whole promise to resolve to trigger.
400
- //
401
- // An exception to this would be .at(0)/.first() and .at(-1)/.last()
402
- // that can get the target element if it's accessible.
403
- //
404
- // NOTE: methods that are guaranteed to return an array will return
405
- // an iterable promise (created with iterPromiseProxy(..))...
406
- //
407
- // XXX ASYNC should this be async or simply return a SyncPromise/Promise???
408
- at: async function(i){
409
- var list = this.__packed
410
- return ((i != 0 && i != -1)
411
- || list instanceof Promise
412
- // XXX not sure if this is correct...
413
- || list.at(i) instanceof Promise) ?
414
- (await this).at(i)
415
- // NOTE: we can only reason about first/last explicit elements,
416
- // anything else is non-deterministic...
417
- : list.at(i) instanceof Promise ?
418
- [await list.at(i)].flat().at(i)
419
- : list.at(i) instanceof Array ?
420
- list.at(i).at(i)
421
- : list.at(i) },
422
- first: function(){
423
- return this.at(0) },
424
- last: function(){
425
- return this.at(-1) },
426
-
427
- // NOTE: unlike .reduce(..) this needs the parent fully resolved
428
- // to be able to iterate from the end.
429
- // XXX is it faster to do .reverse().reduce(..) ???
430
- reduceRight: promiseProxy('reduceRight'),
431
-
432
- // NOTE: there is no way we can do a sync generator returning
433
- // promises for values because any promise in .__packed makes
434
- // the value count/index non-deterministic...
435
- sort: iterPromiseProxy('sort'),
436
- slice: iterPromiseProxy('slice'),
437
-
438
- entries: iterPromiseProxy('entries'),
439
- keys: iterPromiseProxy('keys'),
440
- values: iterPromiseProxy('values'),
441
-
442
- indexOf: promiseProxy('indexOf'),
443
- lastIndexOf: promiseProxy('lastIndexOf'),
444
- includes: promiseProxy('includes'),
445
-
446
- //
447
- // .find(<func>)
448
- // .find(<func>, 'value')
449
- // -> <promise>(<value>)
450
- //
451
- // .find(<func>, 'result')
452
- // -> <promise>(<result>)
453
- //
454
- // .find(<func>, 'bool')
455
- // -> <promise>(<bool>)
456
- //
457
- // NOTE: this is slightly different to Array's .find(..) in that it
458
- // accepts the result value enabling returning both the value
459
- // itself ('value', default), the test function's result
460
- // ('result') or true/false ('bool') -- this is added to be
461
- // able to distinguish between the undefined as a stored value
462
- // and undefined as a "nothing found" result.
463
- // NOTE: I do not get how essentially identical methods .some(..)
464
- // and .find(..) got added to JS's Array...
465
- // the only benefit is that .some(..) handles undefined values
466
- // stored in the array better...
467
- // NOTE: this will return the result as soon as it's available but
468
- // it will not stop the created but unresolved at the time
469
- // promises from executing, this is both good and bad:
470
- // + it will not break other clients waiting for promises
471
- // to resolve...
472
- // - if no clients are available this can lead to wasted
473
- // CPU time...
474
- //
475
- // XXX ASYNC should this be async or simply return a SyncPromise/Promise???
476
- find: async function(func, result='value'){
477
- var that = this
478
- // NOTE: not using pure await here as this is simpler to actually
479
- // control the moment the resulting promise resolves without
480
- // the need for juggling state...
481
- return new Promise(function(resolve, reject){
482
- var resolved = false
483
- that.map(function(elem){
484
- var res = func(elem)
485
- if(res){
486
- resolved = true
487
- resolve(
488
- result == 'bool' ?
489
- true
490
- : result == 'result' ?
491
- res
492
- : elem)
493
- // XXX EXPEREMENTAL: STOP...
494
- // NOTE: we do not need to throw STOP here
495
- // but it can prevent some overhead...
496
- if(that.constructor.STOP){
497
- throw that.constructor.STOP } } })
498
- .then(function(){
499
- resolved
500
- || resolve(
501
- result == 'bool' ?
502
- false
503
- : undefined) }) }) },
504
- findIndex: promiseProxy('findIndex'),
505
-
506
- // NOTE: this is just a special-case of .find(..)
507
- // XXX ASYNC should this be async or simply return a SyncPromise/Promise???
508
- some: async function(func){
509
- return this.find(func, 'bool') },
510
- every: promiseProxy('every'),
511
-
512
-
513
- // XXX ASYNC should this be async or simply return a SyncPromise/Promise???
514
- join: async function(){
515
- return [...(await this)]
516
- .join(...arguments) },
517
-
518
-
519
- // this is defined globally as Promise.prototype.iter(..)
520
- //
521
- // for details see: PromiseMixin(..) below...
522
- //iter: function(handler=undefined){ ... },
523
-
524
-
525
- // promise api...
526
- //
527
- // Overload .then(..), .catch(..) and .finally(..) to return a plain
528
- // Promise instnace...
529
- //
530
- // NOTE: .catch(..) and .finally(..) are implemented through .then(..)
531
- // so we do not need to overload those...
532
- // NOTE: this is slightly different from .then(..) in that it can be
533
- // called without arguments and return a promise wrapper. This can
534
- // be useful to hide special promise functionality...
535
- //
536
- // NOTE: this is internally linked to the actual (via: ..then.call(this, ..))
537
- // state and will be resolved in .__new__(..) below.
538
- then: function(onfulfilled, onrejected){
539
- var p = new Promise(
540
- function(resolve, reject){
541
- Promise.prototype.then.call(this,
542
- // NOTE: resolve(..) / reject(..) return undefined so
543
- // we can't pass them directly here...
544
- function(res){
545
- resolve(res)
546
- return res },
547
- function(res){
548
- reject(res)
549
- return res }) }.bind(this))
550
- return arguments.length > 0 ?
551
- p.then(...arguments)
552
- : p },
553
- // XXX EXPEREMENTAL revise...
554
- // Like .then(..) but returns an IterablePromise instance...
555
- iterthen: function(onfulfilled, onrejected){
556
- if(this.isSync()){
557
- var res = onfulfilled ?
558
- this.constructor(onfulfilled(this.__unpack()))
559
- : this.constructor(this.__unpack())
560
- onrejected
561
- && res.catch(onrejected)
562
- return res }
563
- // XXX we need to feed the output of onfulfilled to the value of
564
- // res, but to this without wrapping the whole thing in a
565
- // promise (possible???)...
566
- return arguments.length > 0 ?
567
- this.constructor(this.then(...arguments))
568
- : this.constructor(this.__packed, 'raw') },
569
-
570
- // XXX EXPEREMENTAL
571
- isSync: function(){
572
- return !(this.__packed instanceof Promise
573
- || this.__packed
574
- .filter(function(e){
575
- return e instanceof IterablePromise ?
576
- !e.isSync()
577
- : e instanceof Promise
578
- && !(e instanceof SyncPromise) })
579
- .length > 0) },
580
- sync: function(error=false){
581
- try{
582
- var res = this.__unpack()
583
- }catch(err){
584
- if(error == false){
585
- return }
586
- if(typeof(error) == 'function'){
587
- return error(err) }
588
- throw err }
589
- return error !== false
590
- && res instanceof Promise ?
591
- // XXX should this be an IterablePromise???
592
- res.catch(error)
593
- : res },
594
-
595
-
596
- // constructor...
597
- //
598
- // Promise.iter([ .. ])
599
- // -> iterable-promise
600
- //
601
- // Promise.iter([ .. ], handler)
602
- // -> iterable-promise
603
- //
604
- //
605
- // handler(e)
606
- // -> [value, ..]
607
- // -> []
608
- // -> <promise>
609
- //
610
- //
611
- // NOTE: element index is unknowable until the full list is expanded
612
- // as handler(..)'s return value can expand to any number of
613
- // items...
614
- // XXX we can make the index a promise, then if the client needs
615
- // the value they can wait for it...
616
- // ...this may be quite an overhead...
617
- //
618
- //
619
- // Special cases useful for extending this constructor...
620
- //
621
- // Set raw .__packed without any pre-processing...
622
- // Promise.iter([ .. ], 'raw')
623
- // -> iterable-promise
624
- //
625
- // Create a rejected iterator...
626
- // Promise.iter(false)
627
- // -> iterable-promise
628
- //
629
- //
630
- // NOTE: if 'types/Array' is imported this will support throwing STOP,
631
- // for more info see notes for .__pack(..)
632
- // XXX EXPEREMENTAL: STOP...
633
- __new__: function(_, list, handler=undefined, onerror=undefined){
634
- // instance...
635
- var promise
636
- var obj = Reflect.construct(
637
- IterablePromise.__proto__,
638
- [function(resolve, reject){
639
- // NOTE: this is here for Promise compatibility...
640
- if(typeof(list) == 'function'){
641
- return list.call(this, ...arguments) }
642
- // initial reject...
643
- if(list === false){
644
- return reject() }
645
- promise = {resolve, reject} }],
646
- IterablePromise)
647
-
648
- // populate new instance...
649
- if(promise){
650
- // handle/pack input data...
651
- if(handler != 'raw'){
652
- list = list instanceof IterablePromise ?
653
- obj.__handle(list.__packed, handler, onerror)
654
- : obj.__pack(list, handler, onerror) }
655
- Object.defineProperty(obj, '__packed', {
656
- value: list,
657
- enumerable: false,
658
- // NOTE: this is needed for self-resolve...
659
- writable: true,
660
- })
661
- // handle promise state...
662
- try{
663
- var res = obj.__unpack(list)
664
- }catch(err){
665
- promise.reject(err) }
666
- res instanceof Promise ?
667
- res
668
- .then(function(list){
669
- promise.resolve(list) })
670
- .catch(promise.reject)
671
- : promise.resolve(res)
672
- // XXX EXPEREMENTAL
673
- // XXX do we handle errors here???
674
- // self-resolve state...
675
- list instanceof Promise ?
676
- list.then(function(list){
677
- obj.__packed = list })
678
- : list.forEach(function(elem, i){
679
- elem instanceof Promise
680
- && elem.then(function(elem){
681
- lst = obj.__packed.slice()
682
- lst[i] = elem
683
- obj.__packed = lst }) }) }
684
- return obj },
685
- })
686
-
687
-
688
-
689
- //---------------------------------------------------------------------
690
- // XXX EXPEREMENTAL/HACK...
691
-
692
- // XXX we are getting a list with promises triggered outside, we can't
693
- // control order of execution of this but we can control in what
694
- // order the handler is called...
695
- // XXX this potentially shows a bug with IterablePromise:
696
- // Promise.seqiter(
697
- // [1, Promise.resolve(2), 3, Promise.resolve(4)],
698
- // e => (console.log('---', e), e))
699
- // ...this will not resolve/replace the 4's promise...
700
- // for context:
701
- // await Promise.resolve(Promise.resolve(Promise.resolve(123)))
702
- // -> 123
703
- // ...is this just a nesting issue here???
704
- // Q: what IterablePromise does/should do if a promise resolves to
705
- // a promise multiple times...
706
- // XXX not sure if this is a viable strategy....
707
- var IterableSequentialPromise =
708
- module.IterableSequentialPromise =
709
- object.Constructor('IterableSequentialPromise', IterablePromise, {
710
- __new__: function(_, list, handler=undefined, onerror=undefined){
711
- var [_, list, ...rest] = arguments
712
- var res = list
713
- // format the list...
714
- if(list instanceof Array
715
- || list instanceof Promise){
716
-
717
- var pre_process = function(list, start=0){
718
- if(list instanceof Promise){
719
- return list.then(pre_process) }
720
- res = []
721
- for(let [i, e] of list.entries().slice(start)){
722
- if(e instanceof Promise){
723
- res.push(e.then(function(e){
724
- return [e,
725
- // XXX need to flatten this...
726
- ...pre_process(list, i+1)] }))
727
- break }
728
- res.push(e) }
729
- return res }
730
-
731
- res = pre_process(list)
732
-
733
- var obj = IterablePromise.prototype.__new__.call(this, _, res, 'raw')
734
-
735
- return obj
736
- }
737
- // XXX use .parentCall(..)...
738
- return IterablePromise.prototype.__new__.call(this, _, res, ...rest) },
739
- })
740
-
741
-
742
-
743
- //---------------------------------------------------------------------
744
- // Interactive promise...
745
- //
746
- // Adds ability to send messages to the running promise.
747
- //
748
-
749
- var InteractivePromise =
750
- module.InteractivePromise =
751
- object.Constructor('InteractivePromise', Promise, {
752
- // XXX do we need a way to remove handlers???
753
- __message_handlers: null,
754
-
755
- send: function(...args){
756
- var that = this
757
- ;(this.__message_handlers || [])
758
- .forEach(function(h){ h.call(that, ...args) })
759
- return this },
760
-
761
- then: IterablePromise.prototype.then,
762
-
763
- //
764
- // Promise.interactive(handler)
765
- // -> interacive-promise
766
- //
767
- // handler(resolve, reject, onmessage)
768
- //
769
- // onmessage(func)
770
- //
771
- //
772
- __new__: function(_, handler){
773
- var handlers = []
774
-
775
- var onmessage = function(func){
776
- // remove all handlers...
777
- if(func === false){
778
- var h = (obj == null ?
779
- handlers
780
- : (obj.__message_handlers || []))
781
- h.splice(0, handlers.length)
782
- // remove a specific handler...
783
- } else if(arguments[1] === false){
784
- var h = (obj == null ?
785
- handlers
786
- : (obj.__message_handlers || []))
787
- h.splice(h.indexOf(func), 1)
788
- // register a handler...
789
- } else {
790
- var h = obj == null ?
791
- // NOTE: we need to get the handlers from
792
- // .__message_handlers unless we are not
793
- // fully defined yet, then use the bootstrap
794
- // container (handlers)...
795
- // ...since we can call onmessage(..) while
796
- // the promise is still defined there is no
797
- // way to .send(..) until it returns a promise
798
- // object, this races here are highly unlikely...
799
- handlers
800
- : (obj.__message_handlers =
801
- obj.__message_handlers ?? [])
802
- handlers.push(func) } }
803
-
804
- var obj = Reflect.construct(
805
- InteractivePromise.__proto__,
806
- !handler ?
807
- []
808
- : [function(resolve, reject){
809
- return handler(resolve, reject, onmessage) }],
810
- InteractivePromise)
811
- Object.defineProperty(obj, '__message_handlers', {
812
- value: handlers,
813
- enumerable: false,
814
- // XXX should this be .configurable???
815
- configurable: true,
816
- })
817
- return obj },
818
- })
819
-
820
-
821
-
822
- //---------------------------------------------------------------------
823
- // Cooperative promise...
824
- //
825
- // A promise that can be resolved/rejected externally.
826
- //
827
- // NOTE: normally this has no internal resolver logic...
828
- //
829
-
830
- var CooperativePromise =
831
- module.CooperativePromise =
832
- object.Constructor('CooperativePromise', Promise, {
833
- __handlers: null,
834
-
835
- get isSet(){
836
- return this.__handlers === false },
837
-
838
- set: function(value, resolve=true){
839
- // can't set twice...
840
- if(this.isSet){
841
- throw new Error('.set(..): can not set twice') }
842
- // bind to promise...
843
- if(value && value.then && value.catch){
844
- value.then(handlers.resolve)
845
- value.catch(handlers.reject)
846
- // resolve with value...
847
- } else {
848
- resolve ?
849
- this.__handlers.resolve(value)
850
- : this.__handlers.reject(value) }
851
- // cleanup and prevent setting twice...
852
- this.__handlers = false
853
- return this },
854
-
855
- then: IterablePromise.prototype.then,
856
-
857
- __new__: function(){
858
- var handlers
859
- var resolver = arguments[1]
860
-
861
- var obj = Reflect.construct(
862
- CooperativePromise.__proto__,
863
- [function(resolve, reject){
864
- handlers = {resolve, reject}
865
- // NOTE: this is here to support builtin .then(..)
866
- resolver
867
- && resolver(resolve, reject) }],
868
- CooperativePromise)
869
-
870
- Object.defineProperty(obj, '__handlers', {
871
- value: handlers,
872
- enumerable: false,
873
- writable: true,
874
- })
875
- return obj },
876
- })
877
-
878
-
879
-
880
- //---------------------------------------------------------------------
881
-
882
- // XXX EXPEREMENTAL...
883
- var ProxyPromise =
884
- module.ProxyPromise =
885
- object.Constructor('ProxyPromise', Promise, {
886
-
887
- then: IterablePromise.prototype.then,
888
-
889
- __new__: function(context, other, nooverride=false){
890
- var proto = 'prototype' in other ?
891
- other.prototype
892
- : other
893
- var obj = Reflect.construct(
894
- ProxyPromise.__proto__,
895
- [function(resolve, reject){
896
- context.then(resolve)
897
- context.catch(reject) }],
898
- ProxyPromise)
899
- // populate...
900
- // NOTE: we are not using object.deepKeys(..) here as we need
901
- // the key origin not to trigger property getters...
902
- var seen = new Set()
903
- nooverride = nooverride instanceof Array ?
904
- new Set(nooverride)
905
- : nooverride
906
- while(proto != null){
907
- Object.entries(Object.getOwnPropertyDescriptors(proto))
908
- .forEach(function([key, value]){
909
- // skip overloaded keys...
910
- if(seen.has(key)){
911
- return }
912
- // skip non-functions...
913
- if(typeof(value.value) != 'function'){
914
- return }
915
- // skip non-enumerable except for Object.prototype.run(..)...
916
- if(!(key == 'run'
917
- && Object.prototype.run === value.value)
918
- && !value.enumerable){
919
- return }
920
- // do not override existing methods...
921
- if(nooverride === true ?
922
- key in obj
923
- : nooverride instanceof Set ?
924
- nooverride.has(key)
925
- : nooverride){
926
- return }
927
- // proxy...
928
- obj[key] = promiseProxy(key) })
929
- proto = proto.__proto__ }
930
- return obj },
931
- })
932
-
933
-
934
-
935
- //---------------------------------------------------------------------
936
-
937
- // XXX should this support async generators???
938
- var syncAllProxy =
939
- function(name){
940
- return function(lst){
941
- var sync = true
942
- for(var e of lst){
943
- if(e instanceof Promise
944
- && !(e instanceof SyncPromise)){
945
- sync = false
946
- break } }
947
- return sync ?
948
- this.resolve(lst)
949
- : Promise[name](lst) } }
950
-
951
- // XXX REVISE/TEST...
952
- // XXX should this support async generators???
953
- var syncAnyProxy =
954
- function(name){
955
- return function(lst){
956
- for(var e of lst){
957
- if(e instanceof SyncPromise
958
- && !('error' in e)){
959
- return e }
960
- if(!(e instanceof Promise)){
961
- return this.resolve(e) } }
962
- return Promise[name](list) } }
963
-
964
-
965
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
966
-
967
- // XXX EXPEREMENTAL...
968
- // XXX DOCS...
969
- // XXX like promise but if a value can be generated sync then this will
970
- // run in sync otherwise it will fall back to being a promise...
971
- // XXX should we throw errors in sync mode??? ...option???
972
- var SyncPromise =
973
- module.SyncPromise =
974
- object.Constructor('SyncPromise', Promise, {
975
- // NOTE: we need to overload these as the builtin versions sneak-in
976
- // async-ness before we can catch it in .__new__(..)
977
- resolve: function(value){
978
- return new this(function(resolve){ resolve(value) }) },
979
- reject: function(error){
980
- return new this(function(_, reject){ reject(error) }) },
981
- // NOTE: these essentially add a special case where the inputs are
982
- // either not promises or SyncPromise instances...
983
- all: syncAllProxy('all'),
984
- allSettled: syncAllProxy('allSettled'),
985
- any: syncAnyProxy('any'),
986
- race: syncAnyProxy('race'),
987
- },{
988
- //value: undefined,
989
- //error: undefined,
990
-
991
- // NOTE: if this is called it means that .__new__(..) returned in sync
992
- // mode and thus set .value and possibly .error, soe there is no
993
- // need to check for .value...
994
- then: function(resolve, reject){
995
- return this.hasOwnProperty('error') ?
996
- this.constructor.reject(
997
- reject ?
998
- reject(this.error)
999
- : this.error)
1000
- : resolve ?
1001
- this.constructor.resolve(
1002
- resolve(this.value))
1003
- // XXX should we return a copy???
1004
- : this },
1005
- sync: function(error='throw'){
1006
- if(error !== false
1007
- && 'error' in this){
1008
- if(typeof(error) != 'function'){
1009
- throw this.error }
1010
- return error(this.error) }
1011
- return this.value },
1012
-
1013
- // NOTE: if func calls resolve(..) with a promise then this will return
1014
- // that promise...
1015
- __new__: function(context, func){
1016
- var value
1017
- var resolve = function(res){
1018
- return (value = res) }
1019
- var rejected
1020
- var error
1021
- var reject = function(err){
1022
- rejected = true
1023
- return (error = err) }
1024
- // call...
1025
- try{
1026
- func(resolve, reject)
1027
- }catch(err){
1028
- reject(err) }
1029
- // async...
1030
- if(!error
1031
- && value instanceof Promise){
1032
- return object.ASIS(value) }
1033
- // sync...
1034
- var obj = Promise.resolve(value)
1035
- obj.value = value
1036
- rejected
1037
- && (obj.error = error)
1038
- return obj },
1039
- })
1040
-
1041
-
1042
-
1043
- //---------------------------------------------------------------------
1044
-
1045
- var PromiseMixin =
1046
- module.PromiseMixin =
1047
- object.Mixin('PromiseMixin', 'soft', {
1048
- iter: IterablePromise,
1049
- // XXX
1050
- seqiter: IterableSequentialPromise,
1051
-
1052
- interactive: InteractivePromise,
1053
- cooperative: CooperativePromise,
1054
-
1055
- sync: SyncPromise,
1056
-
1057
- // XXX should this be implemented via SyncPromise???
1058
- // XXX not sure if we need to expand async generators...
1059
- // (update README if this changes)
1060
- awaitOrRun: function(data, func, error){
1061
- data = [...arguments]
1062
- func = data.pop()
1063
- if(typeof(data.at(-1)) == 'function'){
1064
- error = func
1065
- func = data.pop() }
1066
- error = error ?
1067
- [error]
1068
- : []
1069
- // check if we need to await...
1070
- return data.reduce(function(res, e){
1071
- return res
1072
- || e instanceof Promise }, false) ?
1073
- // NOTE: we will not reach this on empty data...
1074
- (data.length > 1 ?
1075
- Promise.all(data)
1076
- .then(
1077
- function(res){
1078
- return func(...res) },
1079
- ...error)
1080
- : data[0].then(func, ...error))
1081
- // XXX not sure if we need to expand async generators...
1082
- // ...since it has .then(..) it can be treated as a promise...
1083
- // XXX should we just check for .then(..) ???
1084
- // XXX update README if this changes...
1085
- : (data.length == 1
1086
- && Symbol.asyncIterator in data[0]
1087
- && 'then' in data[0]) ?
1088
- data[0].then(func, ...error)
1089
- : error.length > 0 ?
1090
- function(){
1091
- try{
1092
- return func(...data)
1093
- }catch(err){
1094
- return error[0](err) } }()
1095
- : func(...data) },
1096
- })
1097
-
1098
- PromiseMixin(Promise)
1099
-
1100
-
1101
- var PromiseProtoMixin =
1102
- module.PromiseProtoMixin =
1103
- object.Mixin('PromiseProtoMixin', 'soft', {
1104
- as: ProxyPromise,
1105
-
1106
- iter: function(handler=undefined, onerror=undefined){
1107
- return IterablePromise(this, handler, onerror) },
1108
- // XXX
1109
- seqiter: function(handler=undefined, onerror=undefined){
1110
- return IterableSequentialPromise(this, handler, onerror) },
1111
-
1112
- // unify the general promise API with other promise types...
1113
- // XXX should this be here???
1114
- // XXX error if given must return the result... (???)
1115
- sync: function(error){
1116
- typeof(error) == 'function'
1117
- && this.catch(error)
1118
- return this },
1119
- })
1120
-
1121
- PromiseProtoMixin(Promise.prototype)
1122
-
1123
-
1124
-
1125
-
1126
- /**********************************************************************
1127
- * vim:set ts=4 sw=4 nowrap : */ return module })