UNPKG

doddle

Version:

Tiny yet feature-packed (async) iteration toolkit.

189 lines 6.28 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Doddle = exports.ownerInstance = void 0; exports.doddle = doddle; exports.lazyOperator = lazyOperator; exports.pull = pull; const error_js_1 = require("../errors/error.js"); const utils_js_1 = require("../utils.js"); exports.ownerInstance = Symbol("ownerInstance"); /** * A TypeScript-first doddle evaluation primitive. An object that will only evaluate its initializer * function when the {@link pull} method is called. * * The initializer can return another {@link Doddle}, which will be chained like a promise. * * @category Use */ class Doddle { /** @ignore */ constructor(initializer) { this._info = { syncness: 0 /* Syncness.Untouched */, stage: 0 /* Stage.Untouched */ }; this._init = initializer; for (const name of ["map", "do", "zip", "catch", "pull"]) { const bound = this[name].bind(this); bound[exports.ownerInstance] = this; this[name] = bound; } (0, error_js_1.loadCheckers)(this); } /** @internal */ get [Symbol.toStringTag]() { return "Doddle"; } /** Returns metadata about the current state of the Doddle. */ get info() { const { stage, syncness } = this._info; const syncnessWord = ["untouched", "sync", "async"][syncness]; const syncnessPart = syncness === 0 /* Syncness.Untouched */ ? [] : [syncnessWord]; const stageWord = ["untouched", "executing", "done", "threw"][stage]; const stagePart = stage === 2 /* Stage.Done */ ? this._cacheName : `<${stageWord}>`; return { isReady: stage >= 2 /* Stage.Done */, desc: ["doddle", ...syncnessPart, stagePart].join(" "), stage: stageWord, syncness: syncnessWord }; } catch(handler) { (0, error_js_1.chk)(this.catch).handler(handler); return doddle(() => { try { const pulled = this.pull(); if ((0, utils_js_1.isThenable)(pulled)) { return pulled.then(undefined, handler); } return pulled; } catch (e) { return handler(e); } }); } do(action) { (0, error_js_1.chk)(this.do).action(action); return this.map((x) => { const result = action(x); return doddle(() => { return result; }).map(() => x); }); } map(projection) { const _projection = (0, error_js_1.chk)(this.map).projection(projection); return doddle(() => { const pulled = this.pull(); if ((0, utils_js_1.isThenable)(pulled)) { return pulled.then(_projection); } return _projection(pulled); }); } /** * Returns a memoized function, which acts like this Doddle while hiding its type. * * @returns A memoized function that pulls `this` and returns its result. */ memoize() { return this.pull; } /** * Evaluates this {@link Doddle} instance, flattening any nested {@link Doddle} or {@link Promise} * types and yielding its value. * * @returns The yielded value. * @throws The error thrown during initialization, if any. */ pull() { const info = this._info; if (info.stage === 1 /* Stage.Executing */) { if (info.syncness === 2 /* Syncness.Async */) { return this._cached; } else { throw new error_js_1.DoddleError(`Tried to call 'Doddle.pull' recursively in a sync context, which would not terminate.`); } } if (info.stage === 2 /* Stage.Done */) { return this._cached; } info.stage = 1 /* Stage.Executing */; let resource; try { const result = this._init(); resource = (0, utils_js_1.isDoddle)(result) ? result.pull() : result; } finally { if (!resource) { info.stage = 3 /* Stage.Threw */; } } // No need to keep holding a reference to the constructor. this._init = null; if ((0, utils_js_1.isThenable)(resource)) { info.syncness = 2 /* Syncness.Async */; resource = resource.then(value => { if ((0, utils_js_1.isDoddle)(value)) { value = value.pull(); } info.stage = 2 /* Stage.Done */; this._cacheName = (0, utils_js_1.getClassName)(value); return value; }); } else { info.syncness = 1 /* Syncness.Sync */; info.stage = 2 /* Stage.Done */; this._cacheName = (0, utils_js_1.getClassName)(resource); } this._cached = resource; return resource; } /** Returns a short description of the Doddle value and its state. */ toString() { return this.info.desc; } zip(...others) { return doddle(() => { const values = [this, ...others].map(x => x.pull()); if (values.some(utils_js_1.isThenable)) { return Promise.all(values); } return values; }); } } exports.Doddle = Doddle; function doddle(initializer) { if (!(0, utils_js_1.isFunction)(initializer)) { throw new Error(`Initializer must be a function, but got ${(0, utils_js_1.getValueDesc)(initializer)}`); } if (exports.ownerInstance in initializer) { return initializer[exports.ownerInstance]; } return new Doddle(initializer); } /** * Doddle utility functions. * * @category Create */ (function (doddle) { doddle.is = utils_js_1.isDoddle; })(doddle || (exports.doddle = doddle = {})); /** @internal */ function lazyOperator(operand, func) { const lz = doddle(() => func.call(operand, operand)); Object.assign(lz, { operator: func.name, operand }); return lz; } function pull(input) { return doddle(() => input).pull(); } //# sourceMappingURL=index.js.map