diff --git a/Makefile b/Makefile index 7bac6d6..1d8f71c 100755 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ LOCAL_MODULES := \ EXT_MODULES := \ $(wildcard node_modules/pouchdb/dist/*) \ $(wildcard node_modules/jszip/dist/*) \ + $(wildcard node_modules/idb-keyval/dist/*.js) \ $(wildcard node_modules/showdown/dist/*) POUCH_DB := \ diff --git a/lib/types/Promise.js b/lib/types/Promise.js index 215f51b..7614003 100755 --- a/lib/types/Promise.js +++ b/lib/types/Promise.js @@ -249,6 +249,15 @@ object.Constructor('IterablePromise', Promise, { : (await Promise.all(list)) .flat() }, + [Symbol.asyncIterator]: async function*(){ + var list = this.__packed + if(list instanceof Promise){ + yield this.__unpack(await list) + return } + for await(var elem of list){ + yield* elem instanceof Array ? + elem + : [elem] } }, // iterator methods... // diff --git a/lib/types/Set.js b/lib/types/Set.js index 55d0c0e..68d148d 100755 --- a/lib/types/Set.js +++ b/lib/types/Set.js @@ -8,11 +8,29 @@ /*********************************************************************/ var object = require('ig-object') +var stoppable = require('ig-stoppable') /*********************************************************************/ +// Wrap .map(..) / .filter(..) / .reduce(..) / .. to support STOP... +// +// NOTE: internally these are implemented as for-of loops (./generator.js) +var stoppableSet = function(iter){ + return function(func){ + return new Set([...this][iter](...arguments)) } } +var stoppableValue = function(iter, no_return=false){ + return function(func){ + var res = [...this][iter](...arguments) + return no_return ? + undefined + : res } } + + + +//--------------------------------------------------------------------- + var SetProtoMixin = module.SetProtoMixin = object.Mixin('SetMixin', 'soft', { @@ -110,6 +128,12 @@ object.Mixin('SetMixin', 'soft', { this.sort(order) return removed }, + + filter: stoppableSet('filter'), + map: stoppableSet('map'), + reduce: stoppableValue('reduce'), + reduceRight: stoppableValue('reduceRight'), + forEach: stoppableValue('map', true), }) diff --git a/lib/types/generator.js b/lib/types/generator.js index 23cd3ab..5cf3084 100755 --- a/lib/types/generator.js +++ b/lib/types/generator.js @@ -189,11 +189,11 @@ var makeGenerator = function(name, pre){ ].join('\n ') }, }) } } // XXX do a better doc... -var makePromise = function(name){ +var makeProxy = function(name){ return function(...args){ var that = this return function(){ - return that(...arguments)[name](func) } } } + return that(...arguments)[name](...args) } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -267,9 +267,12 @@ object.Mixin('GeneratorMixin', 'soft', { // promises... // - then: makePromise('then'), - catch: makePromise('catch'), - finally: makePromise('finally'), + // NOTE: .then(..) and friends are intentionally not defined here to + // prevent control deadlocks when awaiting for a generator that + // expects manual unwinding e.g.: + // g = function*(){} + // await g // will hang waiting for g to resolve + unwind: makeProxy('unwind'), // combinators... // @@ -450,7 +453,8 @@ object.Mixin('GeneratorProtoMixin', 'soft', { // promises... // - then: function(onresolve, onreject){ + // NOTE: this will unwind the generator... + unwind: function(onresolve, onreject){ var that = this var p = new Promise( function(resolve){ @@ -459,10 +463,12 @@ object.Mixin('GeneratorProtoMixin', 'soft', { p.then(...arguments) : p return p }, + then: function(...args){ + return this.unwind(...args) }, catch: function(func){ - return this.then().catch(func) }, + return this.unwind().catch(...arguments) }, finally: function(func){ - return this.then().finally(func) }, + return this.unwind.finally(...arguments) }, // combinators... // @@ -531,22 +537,23 @@ object.Mixin('AsyncGeneratorProtoMixin', 'soft', { // // NOTE: this will unwind the generator... // XXX create an iterator promise??? - // XXX should we unwind??? - then: function(resolve, reject){ + unwind: function(onresolve, onreject){ var that = this - var p = new Promise(async function(_resolve, _reject){ + var p = new Promise(async function(resolve){ var res = [] for await(var elem of that){ res.push(elem) } - _resolve(res) }) - p = (resolve || reject) ? + resolve(res) }) + p = (onresolve || onreject) ? p.then(...arguments) : p return p }, + then: function(...args){ + return this.unwind(...args) }, catch: function(func){ - return this.then().catch(func) }, - finally: function(){ - return this.then().finally(func) }, + return this.unwind().catch(...arguments) }, + finally: function(func){ + return this.unwind.finally(...arguments) }, // XXX might be a good idea to use this approach above... iter: stoppable(async function*(handler=undefined){ diff --git a/package.json b/package.json index e326082..d1e1473 100755 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@toast-ui/editor": "^3.2.0", "glob": "*", + "idb-keyval": "^6.2.0", "ig-actions": "*", "ig-features": "*", "ig-object": "*", diff --git a/pwiki2.js b/pwiki2.js index ad87b4a..e75aaeb 100755 --- a/pwiki2.js +++ b/pwiki2.js @@ -12,11 +12,14 @@ * - images * * +* XXX test: can we store the file handler with permissions in a ServiceWorker?? +* XXX store: add an indexedDB backend -- save on serialization... +* - idb-keyval +* - native??? * XXX might be a good idea to wrap the wysiwig editor into a separate template * and use it in the main edit template to make it user-selectable... * XXX generalize html/dom api... * ...see refresh() in pwiki2.html -* XXX npx http-server ... -- for testing file access... * XXX test pouchdb latency at scale in browser... * XXX BUG: for some reason deleting and refreshing takes ~2x as long as * refreshing...