Socket
Socket
Sign inDemoInstall

@effection/core

Package Overview
Dependencies
Maintainers
1
Versions
83
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@effection/core - npm Package Compare versions

Comparing version 2.0.0-preview.8 to 2.0.0-preview.9

dist/controller/resource-controller.d.ts

13

CHANGELOG.md
# @effection/core
## 2.0.0-preview.9
### Minor Changes
- 110a2cd: Add ignoreError option to prevent a task from propagating its errors to the parent
- e2545b2: Remove delay on starting iterator/generator
- 2b92370: Prevent race condition in promise controller if promise resolves in the same tick as halt signal
- 00562fd: Fix race condition when halting a task while in between yield points
- 110a2cd: When yielded to an asynchronously halting task, wait for the task to be fully halted before proceeding
- 110a2cd: The sub task created by iterators is now linked to the parent task
- 02446ad: Add Resource to create an option for spawning tasks in the background in an operation
- da86a9c: Convert controllers into functions
## 2.0.0-preview.8

@@ -4,0 +17,0 @@

6

dist/controller/controller.d.ts

@@ -1,7 +0,7 @@

import type { Controls, Task } from '../task';
import type { Task } from '../task';
import type { Operation } from '../operation';
export interface Controller<TOut> {
start(task: Task<TOut>): void;
start(): void;
halt(): void;
}
export declare function createController<T>(task: Task<T>, controls: Controls<T>, operation: Operation<T>): Controller<T>;
export declare function createController<T>(task: Task<T>, operation: Operation<T>): Controller<T>;

@@ -1,10 +0,6 @@

import { Controls, Task } from '../task';
import { Task } from '../task';
import { Controller } from './controller';
export declare class FunctionController<T> implements Controller<T> {
controls: Controls<T>;
createController: () => Controller<T>;
delegate?: Controller<T>;
constructor(controls: Controls<T>, createController: () => Controller<T>);
start(task: Task<T>): void;
halt(): void;
}
export declare function createFunctionController<TOut>(task: Task<TOut>, createController: () => Controller<TOut>): {
start: () => void;
halt: () => void;
};
import { Controller } from './controller';
import { OperationIterator } from '../operation';
import { Task, Controls } from '../task';
import { Operation } from '../operation';
import { Trapper } from '../trapper';
declare type Continuation = () => IteratorResult<Operation<unknown>>;
import { Task } from '../task';
declare const claimed: unique symbol;

@@ -11,16 +8,6 @@ interface Claimable {

}
export declare class IteratorController<TOut> implements Controller<TOut>, Trapper {
private controls;
private iterator;
private didHalt;
private didEnter;
private subTask?;
private continuations;
constructor(controls: Controls<TOut>, iterator: OperationIterator<TOut> & Claimable);
start(): Promise<void>;
resume(iter: Continuation): void;
step(iter: Continuation): void;
trap(child: Task): void;
halt(): void;
}
declare type Options = {
resourceTask?: Task;
};
export declare function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options?: Options): Controller<TOut>;
export {};
import { Controller } from './controller';
import { Controls } from '../task';
export declare class PromiseController<TOut> implements Controller<TOut> {
private controls;
private promise;
private haltSignal;
constructor(controls: Controls<TOut>, promise: PromiseLike<TOut>);
start(): void;
halt(): void;
}
import { Task } from '../task';
export declare function createPromiseController<TOut>(task: Task<TOut>, promise: PromiseLike<TOut>): Controller<TOut>;
import { Controller } from './controller';
import { OperationResolution } from '../operation';
import { Controls } from '../task';
export declare class ResolutionController<TOut> implements Controller<TOut> {
private controls;
private resolution;
constructor(controls: Controls<TOut>, resolution: OperationResolution<TOut>);
start(): void;
halt(): void;
}
import { Task } from '../task';
export declare function createResolutionController<TOut>(task: Task<TOut>, resolution: OperationResolution<TOut>): Controller<TOut>;
import { Controller } from './controller';
import { Controls } from '../task';
export declare class SuspendController<TOut> implements Controller<TOut> {
private controls;
constructor(controls: Controls<TOut>);
start(): void;
halt(): void;
}
import { Task } from '../task';
export declare function createSuspendController<TOut>(task: Task<TOut>): Controller<TOut>;

@@ -164,50 +164,51 @@ 'use strict';

}
function isResource(value) {
return typeof value === 'object' && typeof value.init === 'function';
}
var FunctionController = /*#__PURE__*/function () {
function FunctionController(controls, createController) {
this.controls = controls;
this.createController = createController;
}
function createFunctionController(task, createController) {
var delegate;
var controls = getControls(task);
var _proto = FunctionController.prototype;
_proto.start = function start(task) {
function start() {
try {
this.delegate = this.createController();
delegate = createController();
} catch (error) {
this.controls.reject(error);
controls.reject(error);
return;
}
this.delegate.start(task);
};
delegate.start();
}
_proto.halt = function halt() {
if (!this.delegate) {
function halt() {
if (!delegate) {
throw new Error("EFFECTION INTERNAL ERROR halt() called before start()");
}
this.delegate.halt();
delegate.halt();
}
return {
start: start,
halt: halt
};
}
return FunctionController;
}();
function createSuspendController(task) {
var controls = getControls(task);
var SuspendController = /*#__PURE__*/function () {
function SuspendController(controls) {
this.controls = controls;
function start() {// no op
}
var _proto = SuspendController.prototype;
function halt() {
controls.halted();
}
_proto.start = function start() {// no op
return {
start: start,
halt: halt
};
}
_proto.halt = function halt() {
this.controls.halted();
};
return SuspendController;
}();
function Deferred() {

@@ -234,95 +235,49 @@ var resolve = function resolve() {

var HALT = /*#__PURE__*/Symbol("halt");
var PromiseController = /*#__PURE__*/function () {
function PromiseController(controls, promise) {
this.controls = controls;
this.promise = promise; // TODO: to prevent memory leaks of tasks if a promise never resolves, but
// the task is halted, we should retain the task through a weak reference.
function createPromiseController(task, promise) {
var controls = getControls(task);
var haltSignal = Deferred();
this.haltSignal = Deferred();
}
var _proto = PromiseController.prototype;
_proto.start = function start() {
var _this = this;
Promise.race([this.promise, this.haltSignal.promise]).then(function (value) {
if (value === HALT) {
_this.controls.halted();
} else {
_this.controls.resolve(value);
function start() {
Promise.race([promise, haltSignal.promise]).then(function (value) {
if (value !== HALT) {
controls.resolve(value);
}
}, function (error) {
_this.controls.reject(error);
controls.reject(error);
});
};
}
_proto.halt = function halt() {
this.haltSignal.resolve(HALT);
};
return PromiseController;
}();
var HaltError = /*#__PURE__*/function (_Error) {
_inheritsLoose(HaltError, _Error);
function HaltError() {
return _Error.call(this, "halted") || this;
function halt() {
haltSignal.resolve(HALT);
controls.halted();
}
_createClass(HaltError, [{
key: "__isEffectionHaltError",
get: function get() {
return true;
}
}]);
return HaltError;
}( /*#__PURE__*/_wrapNativeSuper(Error)); // eslint-disable-next-line @typescript-eslint/no-explicit-any
function isHaltError(value) {
return !!(value && value.__isEffectionHaltError);
return {
start: start,
halt: halt
};
}
function swallowHalt(error) {
if (!isHaltError(error)) {
throw error;
} else {
return undefined;
}
}
var claimed = /*#__PURE__*/Symbol["for"]('effection/v2/iterator-controller/claimed');
var IteratorController = /*#__PURE__*/function () {
function IteratorController(controls, iterator) {
this.controls = controls;
this.iterator = iterator;
this.didHalt = false;
this.didEnter = false;
this.continuations = [];
} // make this an async function to delay the first iteration until the next event loop tick
function createIteratorController(task, iterator, options) {
if (options === void 0) {
options = {};
}
var didHalt = false;
var didEnter = false;
var subTask;
var controls = getControls(task);
var continuations = [];
var _proto = IteratorController.prototype;
_proto.start = function start() {
try {
var _this2 = this;
if (_this2.iterator[claimed]) {
var error = new Error("An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times");
error.name = 'DoubleEvalError';
_this2.controls.reject(error);
} else {
_this2.iterator[claimed] = true;
_this2.resume(function () {
return _this2.iterator.next();
});
}
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
function start() {
if (iterator[claimed]) {
var error = new Error("An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times");
error.name = 'DoubleEvalError';
controls.reject(error);
} else {
iterator[claimed] = true;
resume(function () {
return iterator.next();
});
}

@@ -334,9 +289,9 @@ } // the purpose of this method is solely to make `step` reentrant, that is we

// siblings to fail.
;
_proto.resume = function resume(iter) {
this.continuations.push(iter); // only enter this loop if we aren't already running it
if (!this.didEnter) {
this.didEnter = true; // acquire lock
function resume(iter) {
continuations.push(iter); // only enter this loop if we aren't already running it
if (!didEnter) {
didEnter = true; // acquire lock
// use while loop since collection can be modified during iteration

@@ -346,11 +301,11 @@

while (continuation = this.continuations.shift()) {
this.step(continuation);
while (continuation = continuations.shift()) {
step(continuation);
}
this.didEnter = false; // release lock
didEnter = false; // release lock
}
};
}
_proto.step = function step(iter) {
function step(iter) {
var next;

@@ -361,3 +316,3 @@

} catch (error) {
this.controls.reject(error);
controls.reject(error);
return;

@@ -367,21 +322,25 @@ }

if (next.done) {
if (this.didHalt) {
this.controls.halted();
if (didHalt) {
controls.halted();
} else {
this.controls.resolve(next.value);
controls.resolve(next.value);
}
} else {
this.subTask = createTask(next.value);
getControls(this.subTask).addTrapper(this);
getControls(this.subTask).start();
subTask = createTask(next.value, {
parent: options.resourceTask || task,
ignoreError: true
});
getControls(task).link(subTask);
getControls(subTask).addTrapper(trap);
getControls(subTask).start();
}
};
}
_proto.trap = function trap(child) {
var _this3 = this;
function trap(child) {
subTask = undefined;
if (child.state === 'completed') {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.resume(function () {
return _this3.iterator.next(getControls(child).result);
resume(function () {
return iterator.next(getControls(child).result);
});

@@ -392,4 +351,4 @@ }

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.resume(function () {
return _this3.iterator["throw"](getControls(child).error);
resume(function () {
return iterator["throw"](getControls(child).error);
});

@@ -399,41 +358,34 @@ }

if (child.state === 'halted') {
this.resume(function () {
return _this3.iterator["throw"](new HaltError());
resume(function () {
return iterator["return"](undefined);
});
}
};
}
_proto.halt = function halt() {
var _this4 = this;
function halt() {
if (!didHalt) {
didHalt = true;
if (!this.didHalt) {
this.didHalt = true;
if (this.subTask) {
getControls(this.subTask).removeTrapper(this);
this.subTask.halt();
if (subTask) {
subTask.halt();
} else {
resume(function () {
return iterator["return"](undefined);
});
}
this.resume(function () {
return _this4.iterator["return"](undefined);
});
}
};
return IteratorController;
}();
var ResolutionController = /*#__PURE__*/function () {
function ResolutionController(controls, resolution) {
this.controls = controls;
this.resolution = resolution;
}
var _proto = ResolutionController.prototype;
return {
start: start,
halt: halt
};
}
_proto.start = function start() {
var controls = this.controls;
function createResolutionController(task, resolution) {
var controls = getControls(task);
function start() {
try {
var atExit = this.resolution.perform(this.controls.resolve, this.controls.reject);
var atExit = resolution.perform(controls.resolve, controls.reject);

@@ -444,26 +396,66 @@ if (atExit) {

} catch (error) {
this.controls.reject(error);
controls.reject(error);
}
}
function halt() {
controls.halted();
}
return {
start: start,
halt: halt
};
}
_proto.halt = function halt() {
this.controls.halted();
function createResourceController(task, resource) {
var controls = getControls(task);
var delegate;
var parent = controls.options.parent;
function start() {
if (!parent) {
throw new Error('cannot spawn resource in task which has no parent');
}
var init;
try {
init = resource.init(parent);
} catch (error) {
controls.reject(error);
return;
}
delegate = createIteratorController(task, init, {
resourceTask: parent
});
delegate.start();
}
function halt() {
delegate.halt();
}
return {
start: start,
halt: halt
};
}
return ResolutionController;
}();
function createController(task, controls, operation) {
function createController(task, operation) {
if (typeof operation === 'function') {
return new FunctionController(controls, function () {
return createController(task, controls, operation(task));
return createFunctionController(task, function () {
return createController(task, operation(task));
});
} else if (!operation) {
return new SuspendController(controls);
return createSuspendController(task);
} else if (isResource(operation)) {
return createResourceController(task, operation);
} else if (isResolution(operation)) {
return new ResolutionController(controls, operation);
return createResolutionController(task, operation);
} else if (isPromise(operation)) {
return new PromiseController(controls, operation);
return createPromiseController(task, operation);
} else if (isGenerator(operation)) {
return new IteratorController(controls, operation);
return createIteratorController(task, operation);
}

@@ -474,2 +466,30 @@

var HaltError = /*#__PURE__*/function (_Error) {
_inheritsLoose(HaltError, _Error);
function HaltError() {
return _Error.call(this, "halted") || this;
}
_createClass(HaltError, [{
key: "__isEffectionHaltError",
get: function get() {
return true;
}
}]);
return HaltError;
}( /*#__PURE__*/_wrapNativeSuper(Error)); // eslint-disable-next-line @typescript-eslint/no-explicit-any
function isHaltError(value) {
return !!(value && value.__isEffectionHaltError);
}
function swallowHalt(error) {
if (!isHaltError(error)) {
throw error;
} else {
return undefined;
}
}
function f(value) {

@@ -582,3 +602,3 @@ return JSON.stringify(value);

trappers.forEach(function (trapper) {
return trapper.trap(task);
return trapper(task);
});

@@ -610,6 +630,4 @@ ensureHandlers.clear();

// The child will always have been removed from the Set when this runs.
_controls.addTrapper({
trap: function trap() {
return haltChildren(force);
}
_controls.addTrapper(function () {
return haltChildren(force);
});

@@ -628,3 +646,3 @@

stateMachine.start();
controller.start(task);
controller.start();
},

@@ -655,3 +673,3 @@ resolve: function resolve(result) {

if (!children.has(child)) {
getControls(child).addTrapper(controls);
getControls(child).addTrapper(controls.trap);
children.add(child);

@@ -663,3 +681,3 @@ emitter.emit('link', child);

if (children.has(child)) {
getControls(child).removeTrapper(controls);
getControls(child).removeTrapper(controls.trap);
children["delete"](child);

@@ -671,3 +689,3 @@ emitter.emit('unlink', child);

if (children.has(child)) {
if (child.state === 'errored' && !options.ignoreChildErrors) {
if (child.state === 'errored' && !getControls(child).options.ignoreError && !options.ignoreChildErrors) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

@@ -707,2 +725,6 @@ controls.reject(getControls(child).error);

spawn: function spawn(operation, options) {
if (options === void 0) {
options = {};
}
if (stateMachine.current !== 'running') {

@@ -712,2 +734,3 @@ throw new Error('cannot spawn a child on a task which is not running');

options.parent = task;
var child = createTask(operation, options);

@@ -744,3 +767,3 @@ controls.link(child);

}, _task[Symbol.toStringTag] = "[Task " + id + "]", _task[CONTROLS] = controls, _task);
controller = createController(task, controls, operation);
controller = createController(task, operation);
return task;

@@ -758,3 +781,3 @@ }

var version = "2.0.0-preview.8";
var version = "2.0.0-preview.9";

@@ -761,0 +784,0 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

@@ -1,2 +0,2 @@

"use strict";var t=require("events");function r(t,r){for(var n=0;n<r.length;n++){var e=r[n];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(t,e.key,e)}}function n(t,n,e){return n&&r(t.prototype,n),e&&r(t,e),t}function e(t){return(e=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function o(t,r){return(o=Object.setPrototypeOf||function(t,r){return t.__proto__=r,t})(t,r)}function i(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function a(t,r,n){return(a=i()?Reflect.construct:function(t,r,n){var e=[null];e.push.apply(e,r);var i=new(Function.bind.apply(t,e));return n&&o(i,n.prototype),i}).apply(null,arguments)}function c(t){var r="function"==typeof Map?new Map:void 0;return(c=function(t){if(null===t||-1===Function.toString.call(t).indexOf("[native code]"))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==r){if(r.has(t))return r.get(t);r.set(t,n)}function n(){return a(t,arguments,e(this).constructor)}return n.prototype=Object.create(t.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),o(n,t)})(t)}function u(t,r){(null==r||r>t.length)&&(r=t.length);for(var n=0,e=new Array(r);n<r;n++)e[n]=t[n];return e}var s=function(){function t(t,r){this.controls=t,this.createController=r}var r=t.prototype;return r.start=function(t){try{this.delegate=this.createController()}catch(t){return void this.controls.reject(t)}this.delegate.start(t)},r.halt=function(){if(!this.delegate)throw new Error("EFFECTION INTERNAL ERROR halt() called before start()");this.delegate.halt()},t}(),l=function(){function t(t){this.controls=t}var r=t.prototype;return r.start=function(){},r.halt=function(){this.controls.halted()},t}();function f(){var t=function(){},r=function(){},n=new Promise((function(n,e){t=n,r=e}));return{resolve:t,reject:r,promise:n}}var h=Symbol("halt"),p=function(){function t(t,r){this.controls=t,this.promise=r,this.haltSignal=f()}var r=t.prototype;return r.start=function(){var t=this;Promise.race([this.promise,this.haltSignal.promise]).then((function(r){r===h?t.controls.halted():t.controls.resolve(r)}),(function(r){t.controls.reject(r)}))},r.halt=function(){this.haltSignal.resolve(h)},t}(),d=function(t){var r,e;function i(){return t.call(this,"halted")||this}return e=t,(r=i).prototype=Object.create(e.prototype),r.prototype.constructor=r,o(r,e),n(i,[{key:"__isEffectionHaltError",get:function(){return!0}}]),i}(c(Error));function v(t){var r;if(!(r=t)||!r.__isEffectionHaltError)throw t}var m=Symbol.for("effection/v2/iterator-controller/claimed"),y=function(){function t(t,r){this.controls=t,this.iterator=r,this.didHalt=!1,this.didEnter=!1,this.continuations=[]}var r=t.prototype;return r.start=function(){try{var t=this;if(t.iterator[m]){var r=new Error("An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times");r.name="DoubleEvalError",t.controls.reject(r)}else t.iterator[m]=!0,t.resume((function(){return t.iterator.next()}));return Promise.resolve()}catch(t){return Promise.reject(t)}},r.resume=function(t){if(this.continuations.push(t),!this.didEnter){var r;for(this.didEnter=!0;r=this.continuations.shift();)this.step(r);this.didEnter=!1}},r.step=function(t){var r;try{r=t()}catch(t){return void this.controls.reject(t)}r.done?this.didHalt?this.controls.halted():this.controls.resolve(r.value):(this.subTask=T(r.value),k(this.subTask).addTrapper(this),k(this.subTask).start())},r.trap=function(t){var r=this;"completed"===t.state&&this.resume((function(){return r.iterator.next(k(t).result)})),"errored"===t.state&&this.resume((function(){return r.iterator.throw(k(t).error)})),"halted"===t.state&&this.resume((function(){return r.iterator.throw(new d)}))},r.halt=function(){var t=this;this.didHalt||(this.didHalt=!0,this.subTask&&(k(this.subTask).removeTrapper(this),this.subTask.halt()),this.resume((function(){return t.iterator.return(void 0)})))},t}(),g=function(){function t(t,r){this.controls=t,this.resolution=r}var r=t.prototype;return r.start=function(){var t=this.controls;try{var r=this.resolution.perform(this.controls.resolve,this.controls.reject);r&&t.ensure(r)}catch(t){this.controls.reject(t)}},r.halt=function(){this.controls.halted()},t}();function b(t){return JSON.stringify(t)}var w=function(){function t(t){this.emitter=t,this.current="pending"}var r=t.prototype;return r.transition=function(t,r){var n=this.current,e=r[n];if(!e){var o=Object.keys(r).map(b).join(", ");throw new Error("INTERNAL ERROR: state transition "+b(t)+" is not valid in current state "+b(n)+", should be one of "+o)}this.current=e,this.emitter.emit("state",{from:n,to:e})},r.start=function(){this.transition("start",{pending:"running"})},r.resolve=function(){this.transition("resolve",{running:"completing",completing:"completing",erroring:"erroring",halting:"halting"})},r.reject=function(){this.transition("reject",{running:"erroring",completing:"erroring",halting:"erroring",erroring:"erroring"})},r.halt=function(){this.transition("halt",{running:"halting",completing:"halting",halting:"halting",erroring:"erroring",halted:"halted",completed:"completed",errored:"errored"})},r.finish=function(){this.transition("finish",{completing:"completed",erroring:"errored",halting:"halted"})},n(t,[{key:"isFinishing",get:function(){return["completing","halting","erroring"].includes(this.current)}}]),t}(),E=0,j=Symbol.for("effection/v2/controls");function T(r,n){var e;void 0===n&&(n={});var o=++E,i=new Set,a=new Set,c=new Set,h=new t.EventEmitter,m=new w(h),b=f();function O(){m.isFinishing&&0===i.size&&(m.finish(),c.forEach((function(t){return t()})),a.forEach((function(t){return t.trap(x)})),c.clear(),a.clear(),"completed"===m.current?b.resolve(R.result):"halted"===m.current?b.reject(new d):"errored"===m.current&&b.reject(R.error))}function S(t){for(var r,n=function(t,r){var n;if("undefined"==typeof Symbol||null==t[Symbol.iterator]){if(Array.isArray(t)||(n=function(t,r){if(t){if("string"==typeof t)return u(t,void 0);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?u(t,void 0):void 0}}(t))){n&&(t=n);var e=0;return function(){return e>=t.length?{done:!0}:{done:!1,value:t[e++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=t[Symbol.iterator]()).next.bind(n)}(Array.from(i).reverse());!(r=n()).done;){var e=r.value,o=k(e);if(t||!o.options.blockParent)return o.addTrapper({trap:function(){return S(t)}}),void e.halt()}}b.promise.catch((function(){}));var P,R={options:n,children:i,start:function(){m.start(),P.start(x)},resolve:function(t){m.resolve(),R.result=t,S(!1),O()},reject:function(t){m.reject(),R.result=void 0,R.error=t,S(!0),O()},ensure:function(t){c.add(t)},halted:function(){m.halt(),S(!0),O()},link:function(t){i.has(t)||(k(t).addTrapper(R),i.add(t),h.emit("link",t))},unlink:function(t){i.has(t)&&(k(t).removeTrapper(R),i.delete(t),h.emit("unlink",t))},trap:function(t){i.has(t)&&("errored"!==t.state||n.ignoreChildErrors||R.reject(k(t).error),R.unlink(t)),O()},addTrapper:function(t){a.add(t)},removeTrapper:function(t){a.delete(t)},on:function(t,r){h.on(t,r)},off:function(t,r){h.off(t,r)}},x=((e={id:o,get state(){return m.current},catchHalt:function(){return b.promise.catch(v)},spawn:function(t,r){if("running"!==m.current)throw new Error("cannot spawn a child on a task which is not running");var n=T(t,r);return R.link(n),k(n).start(),n},halt:function(){try{return P.halt(),Promise.resolve(b.promise.catch((function(){}))).then((function(){}))}catch(t){return Promise.reject(t)}},then:function(){var t;return(t=b.promise).then.apply(t,arguments)},catch:function(){var t;return(t=b.promise).catch.apply(t,arguments)},finally:function(){var t;return(t=b.promise).finally.apply(t,arguments)}})[Symbol.toStringTag]="[Task "+o+"]",e[j]=R,e);return P=function t(r,n,e){if("function"==typeof e)return new s(n,(function(){return t(r,n,e(r))}));if(!e)return new l(n);if((o=e)&&"function"==typeof o.perform)return new g(n,e);if(function(t){return t&&"function"==typeof t.then}(e))return new p(n,e);if(function(t){return t&&"function"==typeof t.next}(e))return new y(n,e);var o;throw new Error("unkown type of operation: "+e)}(x,R,r),x}function k(t){var r=t[j];if(!r)throw new Error("EFFECTION INTERNAL ERROR unable to retrieve controls for task "+t);return r}var O="__effectionV"+"2.0.0-preview.8".split(".")[0];function S(){var t=T(void 0,{ignoreChildErrors:!0});return k(t).start(),t}function P(){return globalThis[O]||(globalThis[O]={root:S()}),globalThis[O]}var R={get root(){return P().root},set root(t){P().root=t},reset:function(){try{return Promise.resolve(R.root.halt()).then((function(){R.root=S()}))}catch(t){return Promise.reject(t)}},halt:function(){try{return Promise.resolve(R.root.halt()).then((function(){}))}catch(t){return Promise.reject(t)}}};exports.Deferred=f,exports.Effection=R,exports.createTask=T,exports.deprecated=function(t,r){return function(){try{throw new Error("trace")}catch(r){var n=r.stack.split("\n").slice(3,4),e=n[0];console.warn(t,"\n"+e)}for(var o=arguments.length,i=new Array(o),a=0;a<o;a++)i[a]=arguments[a];return r.call.apply(r,[this].concat(i))}},exports.getControls=k,exports.run=function(t,r){return R.root.spawn(t,r)},exports.sleep=function(t){return{perform:function(r){var n=setTimeout(r,t);return function(){return clearTimeout(n)}}}};
"use strict";var t=require("events");function r(t,r){for(var n=0;n<r.length;n++){var e=r[n];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(t,e.key,e)}}function n(t,n,e){return n&&r(t.prototype,n),e&&r(t,e),t}function e(t){return(e=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function o(t,r){return(o=Object.setPrototypeOf||function(t,r){return t.__proto__=r,t})(t,r)}function i(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}function u(t,r,n){return(u=i()?Reflect.construct:function(t,r,n){var e=[null];e.push.apply(e,r);var i=new(Function.bind.apply(t,e));return n&&o(i,n.prototype),i}).apply(null,arguments)}function c(t){var r="function"==typeof Map?new Map:void 0;return(c=function(t){if(null===t||-1===Function.toString.call(t).indexOf("[native code]"))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==r){if(r.has(t))return r.get(t);r.set(t,n)}function n(){return u(t,arguments,e(this).constructor)}return n.prototype=Object.create(t.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),o(n,t)})(t)}function a(t,r){(null==r||r>t.length)&&(r=t.length);for(var n=0,e=new Array(r);n<r;n++)e[n]=t[n];return e}function f(){var t=function(){},r=function(){},n=new Promise((function(n,e){t=n,r=e}));return{resolve:t,reject:r,promise:n}}var l=Symbol("halt"),s=Symbol.for("effection/v2/iterator-controller/claimed");function p(t,r,n){void 0===n&&(n={});var e,o=!1,i=!1,u=w(t),c=[];function a(t){if(c.push(t),!i){var r;for(i=!0;r=c.shift();)f(r);i=!1}}function f(r){var i;try{i=r()}catch(t){return void u.reject(t)}i.done?o?u.halted():u.resolve(i.value):(e=b(i.value,{parent:n.resourceTask||t,ignoreError:!0}),w(t).link(e),w(e).addTrapper(l),w(e).start())}function l(t){e=void 0,"completed"===t.state&&a((function(){return r.next(w(t).result)})),"errored"===t.state&&a((function(){return r.throw(w(t).error)})),"halted"===t.state&&a((function(){return r.return(void 0)}))}return{start:function(){if(r[s]){var t=new Error("An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times");t.name="DoubleEvalError",u.reject(t)}else r[s]=!0,a((function(){return r.next()}))},halt:function(){o||(o=!0,e?e.halt():a((function(){return r.return(void 0)})))}}}var h=function(t){var r,e;function i(){return t.call(this,"halted")||this}return e=t,(r=i).prototype=Object.create(e.prototype),r.prototype.constructor=r,o(r,e),n(i,[{key:"__isEffectionHaltError",get:function(){return!0}}]),i}(c(Error));function v(t){var r;if(!(r=t)||!r.__isEffectionHaltError)throw t}function d(t){return JSON.stringify(t)}var y=function(){function t(t){this.emitter=t,this.current="pending"}var r=t.prototype;return r.transition=function(t,r){var n=this.current,e=r[n];if(!e){var o=Object.keys(r).map(d).join(", ");throw new Error("INTERNAL ERROR: state transition "+d(t)+" is not valid in current state "+d(n)+", should be one of "+o)}this.current=e,this.emitter.emit("state",{from:n,to:e})},r.start=function(){this.transition("start",{pending:"running"})},r.resolve=function(){this.transition("resolve",{running:"completing",completing:"completing",erroring:"erroring",halting:"halting"})},r.reject=function(){this.transition("reject",{running:"erroring",completing:"erroring",halting:"erroring",erroring:"erroring"})},r.halt=function(){this.transition("halt",{running:"halting",completing:"halting",halting:"halting",erroring:"erroring",halted:"halted",completed:"completed",errored:"errored"})},r.finish=function(){this.transition("finish",{completing:"completed",erroring:"errored",halting:"halted"})},n(t,[{key:"isFinishing",get:function(){return["completing","halting","erroring"].includes(this.current)}}]),t}(),m=0,g=Symbol.for("effection/v2/controls");function b(r,n){var e;void 0===n&&(n={});var o=++m,i=new Set,u=new Set,c=new Set,s=new t.EventEmitter,d=new y(s),E=f();function j(){d.isFinishing&&0===i.size&&(d.finish(),c.forEach((function(t){return t()})),u.forEach((function(t){return t(R)})),c.clear(),u.clear(),"completed"===d.current?E.resolve(O.result):"halted"===d.current?E.reject(new h):"errored"===d.current&&E.reject(O.error))}function k(t){for(var r,n=function(t,r){var n;if("undefined"==typeof Symbol||null==t[Symbol.iterator]){if(Array.isArray(t)||(n=function(t,r){if(t){if("string"==typeof t)return a(t,void 0);var n=Object.prototype.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(t,void 0):void 0}}(t))){n&&(t=n);var e=0;return function(){return e>=t.length?{done:!0}:{done:!1,value:t[e++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=t[Symbol.iterator]()).next.bind(n)}(Array.from(i).reverse());!(r=n()).done;){var e=r.value,o=w(e);if(t||!o.options.blockParent)return o.addTrapper((function(){return k(t)})),void e.halt()}}E.promise.catch((function(){}));var T,O={options:n,children:i,start:function(){d.start(),T.start()},resolve:function(t){d.resolve(),O.result=t,k(!1),j()},reject:function(t){d.reject(),O.result=void 0,O.error=t,k(!0),j()},ensure:function(t){c.add(t)},halted:function(){d.halt(),k(!0),j()},link:function(t){i.has(t)||(w(t).addTrapper(O.trap),i.add(t),s.emit("link",t))},unlink:function(t){i.has(t)&&(w(t).removeTrapper(O.trap),i.delete(t),s.emit("unlink",t))},trap:function(t){i.has(t)&&("errored"!==t.state||w(t).options.ignoreError||n.ignoreChildErrors||O.reject(w(t).error),O.unlink(t)),j()},addTrapper:function(t){u.add(t)},removeTrapper:function(t){u.delete(t)},on:function(t,r){s.on(t,r)},off:function(t,r){s.off(t,r)}},R=((e={id:o,get state(){return d.current},catchHalt:function(){return E.promise.catch(v)},spawn:function(t,r){if(void 0===r&&(r={}),"running"!==d.current)throw new Error("cannot spawn a child on a task which is not running");r.parent=R;var n=b(t,r);return O.link(n),w(n).start(),n},halt:function(){try{return T.halt(),Promise.resolve(E.promise.catch((function(){}))).then((function(){}))}catch(t){return Promise.reject(t)}},then:function(){var t;return(t=E.promise).then.apply(t,arguments)},catch:function(){var t;return(t=E.promise).catch.apply(t,arguments)},finally:function(){var t;return(t=E.promise).finally.apply(t,arguments)}})[Symbol.toStringTag]="[Task "+o+"]",e[g]=O,e);return T=function t(r,n){if("function"==typeof n)return function(t,r){var n,e=w(t);return{start:function(){try{n=r()}catch(t){return void e.reject(t)}n.start()},halt:function(){if(!n)throw new Error("EFFECTION INTERNAL ERROR halt() called before start()");n.halt()}}}(r,(function(){return t(r,n(r))}));if(!n)return function(t){var r=w(t);return{start:function(){},halt:function(){r.halted()}}}(r);if("object"==typeof(e=n)&&"function"==typeof e.init)return function(t,r){var n,e=w(t),o=e.options.parent;return{start:function(){if(!o)throw new Error("cannot spawn resource in task which has no parent");var i;try{i=r.init(o)}catch(t){return void e.reject(t)}(n=p(t,i,{resourceTask:o})).start()},halt:function(){n.halt()}}}(r,n);if(function(t){return t&&"function"==typeof t.perform}(n))return function(t,r){var n=w(t);return{start:function(){try{var t=r.perform(n.resolve,n.reject);t&&n.ensure(t)}catch(t){n.reject(t)}},halt:function(){n.halted()}}}(r,n);if(function(t){return t&&"function"==typeof t.then}(n))return function(t,r){var n=w(t),e=f();return{start:function(){Promise.race([r,e.promise]).then((function(t){t!==l&&n.resolve(t)}),(function(t){n.reject(t)}))},halt:function(){e.resolve(l),n.halted()}}}(r,n);if(function(t){return t&&"function"==typeof t.next}(n))return p(r,n);var e;throw new Error("unkown type of operation: "+n)}(R,r),R}function w(t){var r=t[g];if(!r)throw new Error("EFFECTION INTERNAL ERROR unable to retrieve controls for task "+t);return r}var E="__effectionV"+"2.0.0-preview.9".split(".")[0];function j(){var t=b(void 0,{ignoreChildErrors:!0});return w(t).start(),t}function k(){return globalThis[E]||(globalThis[E]={root:j()}),globalThis[E]}var T={get root(){return k().root},set root(t){k().root=t},reset:function(){try{return Promise.resolve(T.root.halt()).then((function(){T.root=j()}))}catch(t){return Promise.reject(t)}},halt:function(){try{return Promise.resolve(T.root.halt()).then((function(){}))}catch(t){return Promise.reject(t)}}};exports.Deferred=f,exports.Effection=T,exports.createTask=b,exports.deprecated=function(t,r){return function(){try{throw new Error("trace")}catch(r){var n=r.stack.split("\n").slice(3,4),e=n[0];console.warn(t,"\n"+e)}for(var o=arguments.length,i=new Array(o),u=0;u<o;u++)i[u]=arguments[u];return r.call.apply(r,[this].concat(i))}},exports.getControls=w,exports.run=function(t,r){return T.root.spawn(t,r)},exports.sleep=function(t){return{perform:function(r){var n=setTimeout(r,t);return function(){return clearTimeout(n)}}}};
//# sourceMappingURL=core.cjs.production.min.js.map

@@ -162,50 +162,51 @@ import { EventEmitter } from 'events';

}
function isResource(value) {
return typeof value === 'object' && typeof value.init === 'function';
}
var FunctionController = /*#__PURE__*/function () {
function FunctionController(controls, createController) {
this.controls = controls;
this.createController = createController;
}
function createFunctionController(task, createController) {
var delegate;
var controls = getControls(task);
var _proto = FunctionController.prototype;
_proto.start = function start(task) {
function start() {
try {
this.delegate = this.createController();
delegate = createController();
} catch (error) {
this.controls.reject(error);
controls.reject(error);
return;
}
this.delegate.start(task);
};
delegate.start();
}
_proto.halt = function halt() {
if (!this.delegate) {
function halt() {
if (!delegate) {
throw new Error("EFFECTION INTERNAL ERROR halt() called before start()");
}
this.delegate.halt();
delegate.halt();
}
return {
start: start,
halt: halt
};
}
return FunctionController;
}();
function createSuspendController(task) {
var controls = getControls(task);
var SuspendController = /*#__PURE__*/function () {
function SuspendController(controls) {
this.controls = controls;
function start() {// no op
}
var _proto = SuspendController.prototype;
function halt() {
controls.halted();
}
_proto.start = function start() {// no op
return {
start: start,
halt: halt
};
}
_proto.halt = function halt() {
this.controls.halted();
};
return SuspendController;
}();
function Deferred() {

@@ -232,95 +233,49 @@ var resolve = function resolve() {

var HALT = /*#__PURE__*/Symbol("halt");
var PromiseController = /*#__PURE__*/function () {
function PromiseController(controls, promise) {
this.controls = controls;
this.promise = promise; // TODO: to prevent memory leaks of tasks if a promise never resolves, but
// the task is halted, we should retain the task through a weak reference.
function createPromiseController(task, promise) {
var controls = getControls(task);
var haltSignal = Deferred();
this.haltSignal = Deferred();
}
var _proto = PromiseController.prototype;
_proto.start = function start() {
var _this = this;
Promise.race([this.promise, this.haltSignal.promise]).then(function (value) {
if (value === HALT) {
_this.controls.halted();
} else {
_this.controls.resolve(value);
function start() {
Promise.race([promise, haltSignal.promise]).then(function (value) {
if (value !== HALT) {
controls.resolve(value);
}
}, function (error) {
_this.controls.reject(error);
controls.reject(error);
});
};
}
_proto.halt = function halt() {
this.haltSignal.resolve(HALT);
};
return PromiseController;
}();
var HaltError = /*#__PURE__*/function (_Error) {
_inheritsLoose(HaltError, _Error);
function HaltError() {
return _Error.call(this, "halted") || this;
function halt() {
haltSignal.resolve(HALT);
controls.halted();
}
_createClass(HaltError, [{
key: "__isEffectionHaltError",
get: function get() {
return true;
}
}]);
return HaltError;
}( /*#__PURE__*/_wrapNativeSuper(Error)); // eslint-disable-next-line @typescript-eslint/no-explicit-any
function isHaltError(value) {
return !!(value && value.__isEffectionHaltError);
return {
start: start,
halt: halt
};
}
function swallowHalt(error) {
if (!isHaltError(error)) {
throw error;
} else {
return undefined;
}
}
var claimed = /*#__PURE__*/Symbol["for"]('effection/v2/iterator-controller/claimed');
var IteratorController = /*#__PURE__*/function () {
function IteratorController(controls, iterator) {
this.controls = controls;
this.iterator = iterator;
this.didHalt = false;
this.didEnter = false;
this.continuations = [];
} // make this an async function to delay the first iteration until the next event loop tick
function createIteratorController(task, iterator, options) {
if (options === void 0) {
options = {};
}
var didHalt = false;
var didEnter = false;
var subTask;
var controls = getControls(task);
var continuations = [];
var _proto = IteratorController.prototype;
_proto.start = function start() {
try {
var _this2 = this;
if (_this2.iterator[claimed]) {
var error = new Error("An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times");
error.name = 'DoubleEvalError';
_this2.controls.reject(error);
} else {
_this2.iterator[claimed] = true;
_this2.resume(function () {
return _this2.iterator.next();
});
}
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
function start() {
if (iterator[claimed]) {
var error = new Error("An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times");
error.name = 'DoubleEvalError';
controls.reject(error);
} else {
iterator[claimed] = true;
resume(function () {
return iterator.next();
});
}

@@ -332,9 +287,9 @@ } // the purpose of this method is solely to make `step` reentrant, that is we

// siblings to fail.
;
_proto.resume = function resume(iter) {
this.continuations.push(iter); // only enter this loop if we aren't already running it
if (!this.didEnter) {
this.didEnter = true; // acquire lock
function resume(iter) {
continuations.push(iter); // only enter this loop if we aren't already running it
if (!didEnter) {
didEnter = true; // acquire lock
// use while loop since collection can be modified during iteration

@@ -344,11 +299,11 @@

while (continuation = this.continuations.shift()) {
this.step(continuation);
while (continuation = continuations.shift()) {
step(continuation);
}
this.didEnter = false; // release lock
didEnter = false; // release lock
}
};
}
_proto.step = function step(iter) {
function step(iter) {
var next;

@@ -359,3 +314,3 @@

} catch (error) {
this.controls.reject(error);
controls.reject(error);
return;

@@ -365,21 +320,25 @@ }

if (next.done) {
if (this.didHalt) {
this.controls.halted();
if (didHalt) {
controls.halted();
} else {
this.controls.resolve(next.value);
controls.resolve(next.value);
}
} else {
this.subTask = createTask(next.value);
getControls(this.subTask).addTrapper(this);
getControls(this.subTask).start();
subTask = createTask(next.value, {
parent: options.resourceTask || task,
ignoreError: true
});
getControls(task).link(subTask);
getControls(subTask).addTrapper(trap);
getControls(subTask).start();
}
};
}
_proto.trap = function trap(child) {
var _this3 = this;
function trap(child) {
subTask = undefined;
if (child.state === 'completed') {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.resume(function () {
return _this3.iterator.next(getControls(child).result);
resume(function () {
return iterator.next(getControls(child).result);
});

@@ -390,4 +349,4 @@ }

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.resume(function () {
return _this3.iterator["throw"](getControls(child).error);
resume(function () {
return iterator["throw"](getControls(child).error);
});

@@ -397,41 +356,34 @@ }

if (child.state === 'halted') {
this.resume(function () {
return _this3.iterator["throw"](new HaltError());
resume(function () {
return iterator["return"](undefined);
});
}
};
}
_proto.halt = function halt() {
var _this4 = this;
function halt() {
if (!didHalt) {
didHalt = true;
if (!this.didHalt) {
this.didHalt = true;
if (this.subTask) {
getControls(this.subTask).removeTrapper(this);
this.subTask.halt();
if (subTask) {
subTask.halt();
} else {
resume(function () {
return iterator["return"](undefined);
});
}
this.resume(function () {
return _this4.iterator["return"](undefined);
});
}
};
return IteratorController;
}();
var ResolutionController = /*#__PURE__*/function () {
function ResolutionController(controls, resolution) {
this.controls = controls;
this.resolution = resolution;
}
var _proto = ResolutionController.prototype;
return {
start: start,
halt: halt
};
}
_proto.start = function start() {
var controls = this.controls;
function createResolutionController(task, resolution) {
var controls = getControls(task);
function start() {
try {
var atExit = this.resolution.perform(this.controls.resolve, this.controls.reject);
var atExit = resolution.perform(controls.resolve, controls.reject);

@@ -442,26 +394,66 @@ if (atExit) {

} catch (error) {
this.controls.reject(error);
controls.reject(error);
}
}
function halt() {
controls.halted();
}
return {
start: start,
halt: halt
};
}
_proto.halt = function halt() {
this.controls.halted();
function createResourceController(task, resource) {
var controls = getControls(task);
var delegate;
var parent = controls.options.parent;
function start() {
if (!parent) {
throw new Error('cannot spawn resource in task which has no parent');
}
var init;
try {
init = resource.init(parent);
} catch (error) {
controls.reject(error);
return;
}
delegate = createIteratorController(task, init, {
resourceTask: parent
});
delegate.start();
}
function halt() {
delegate.halt();
}
return {
start: start,
halt: halt
};
}
return ResolutionController;
}();
function createController(task, controls, operation) {
function createController(task, operation) {
if (typeof operation === 'function') {
return new FunctionController(controls, function () {
return createController(task, controls, operation(task));
return createFunctionController(task, function () {
return createController(task, operation(task));
});
} else if (!operation) {
return new SuspendController(controls);
return createSuspendController(task);
} else if (isResource(operation)) {
return createResourceController(task, operation);
} else if (isResolution(operation)) {
return new ResolutionController(controls, operation);
return createResolutionController(task, operation);
} else if (isPromise(operation)) {
return new PromiseController(controls, operation);
return createPromiseController(task, operation);
} else if (isGenerator(operation)) {
return new IteratorController(controls, operation);
return createIteratorController(task, operation);
}

@@ -472,2 +464,30 @@

var HaltError = /*#__PURE__*/function (_Error) {
_inheritsLoose(HaltError, _Error);
function HaltError() {
return _Error.call(this, "halted") || this;
}
_createClass(HaltError, [{
key: "__isEffectionHaltError",
get: function get() {
return true;
}
}]);
return HaltError;
}( /*#__PURE__*/_wrapNativeSuper(Error)); // eslint-disable-next-line @typescript-eslint/no-explicit-any
function isHaltError(value) {
return !!(value && value.__isEffectionHaltError);
}
function swallowHalt(error) {
if (!isHaltError(error)) {
throw error;
} else {
return undefined;
}
}
function f(value) {

@@ -580,3 +600,3 @@ return JSON.stringify(value);

trappers.forEach(function (trapper) {
return trapper.trap(task);
return trapper(task);
});

@@ -608,6 +628,4 @@ ensureHandlers.clear();

// The child will always have been removed from the Set when this runs.
_controls.addTrapper({
trap: function trap() {
return haltChildren(force);
}
_controls.addTrapper(function () {
return haltChildren(force);
});

@@ -626,3 +644,3 @@

stateMachine.start();
controller.start(task);
controller.start();
},

@@ -653,3 +671,3 @@ resolve: function resolve(result) {

if (!children.has(child)) {
getControls(child).addTrapper(controls);
getControls(child).addTrapper(controls.trap);
children.add(child);

@@ -661,3 +679,3 @@ emitter.emit('link', child);

if (children.has(child)) {
getControls(child).removeTrapper(controls);
getControls(child).removeTrapper(controls.trap);
children["delete"](child);

@@ -669,3 +687,3 @@ emitter.emit('unlink', child);

if (children.has(child)) {
if (child.state === 'errored' && !options.ignoreChildErrors) {
if (child.state === 'errored' && !getControls(child).options.ignoreError && !options.ignoreChildErrors) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

@@ -705,2 +723,6 @@ controls.reject(getControls(child).error);

spawn: function spawn(operation, options) {
if (options === void 0) {
options = {};
}
if (stateMachine.current !== 'running') {

@@ -710,2 +732,3 @@ throw new Error('cannot spawn a child on a task which is not running');

options.parent = task;
var child = createTask(operation, options);

@@ -742,3 +765,3 @@ controls.link(child);

}, _task[Symbol.toStringTag] = "[Task " + id + "]", _task[CONTROLS] = controls, _task);
controller = createController(task, controls, operation);
controller = createController(task, operation);
return task;

@@ -756,3 +779,3 @@ }

var version = "2.0.0-preview.8";
var version = "2.0.0-preview.9";

@@ -759,0 +782,0 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

@@ -5,3 +5,3 @@ import { Operation } from './operation';

export { createTask, Task, TaskOptions, Controls, getControls } from './task';
export { Operation } from './operation';
export { Operation, Resource } from './operation';
export { sleep } from './sleep';

@@ -8,0 +8,0 @@ export { Effection } from './effection';

@@ -8,2 +8,5 @@ import { Task } from './task';

export declare type ContinuationFunction<TOut> = (task: Task<TOut>) => Continuation<TOut>;
export declare type Operation<TOut> = Continuation<TOut> | ContinuationFunction<TOut>;
export declare type Operation<TOut> = Continuation<TOut> | ContinuationFunction<TOut> | Resource<TOut>;
export interface Resource<TOut> {
init(scope: Task): OperationIterator<TOut>;
}

@@ -1,4 +0,5 @@

import { OperationResolution } from "./operation";
import { OperationResolution, Resource } from "./operation";
export declare function isPromise(value: any): value is PromiseLike<unknown>;
export declare function isGenerator(value: any): value is Iterator<unknown>;
export declare function isResolution<T>(value: any): value is OperationResolution<T>;
export declare function isResource<TOut>(value: any): value is Resource<TOut>;

@@ -5,4 +5,6 @@ import { Operation } from './operation';

export interface TaskOptions {
parent?: Task;
blockParent?: boolean;
ignoreChildErrors?: boolean;
ignoreError?: boolean;
}

@@ -17,3 +19,3 @@ declare type EnsureHandler = () => void;

}
export interface Controls<TOut = unknown> extends Trapper {
export interface Controls<TOut = unknown> {
options: TaskOptions;

@@ -32,2 +34,3 @@ children: Set<Task>;

removeTrapper(trapper: Trapper): void;
trap: Trapper;
on(name: 'state', listener: (transition: StateTransition) => void): void;

@@ -34,0 +37,0 @@ on(name: 'link', listener: (child: Task) => void): void;

import { Task } from './task';
export interface Trapper {
trap(task: Task): void;
(task: Task): void;
}
{
"name": "@effection/core",
"version": "2.0.0-preview.8",
"version": "2.0.0-preview.9",
"main": "dist/index.js",

@@ -5,0 +5,0 @@ "types": "dist/index.d.ts",

@@ -1,26 +0,29 @@

import type { Controls, Task } from '../task';
import type { Task } from '../task';
import type { Operation } from '../operation';
import { isResolution, isPromise, isGenerator } from '../predicates';
import { FunctionController } from './function-controller';
import { SuspendController } from './suspend-controller';
import { PromiseController } from './promise-controller';
import { IteratorController } from './iterator-controller';
import { ResolutionController } from './resolution-controller';
import { isResource, isResolution, isPromise, isGenerator } from '../predicates';
import { createFunctionController } from './function-controller';
import { createSuspendController } from './suspend-controller';
import { createPromiseController } from './promise-controller';
import { createIteratorController } from './iterator-controller';
import { createResolutionController } from './resolution-controller';
import { createResourceController } from './resource-controller';
export interface Controller<TOut> {
start(task: Task<TOut>): void;
start(): void;
halt(): void;
}
export function createController<T>(task: Task<T>, controls: Controls<T>, operation: Operation<T>): Controller<T> {
export function createController<T>(task: Task<T>, operation: Operation<T>): Controller<T> {
if (typeof(operation) === 'function') {
return new FunctionController(controls, () => createController(task, controls, operation(task)));
return createFunctionController(task, () => createController(task, operation(task)));
} else if(!operation) {
return new SuspendController(controls);
return createSuspendController(task);
} else if (isResource(operation)) {
return createResourceController(task, operation);
} else if (isResolution(operation)) {
return new ResolutionController(controls, operation);
return createResolutionController(task, operation);
} else if(isPromise(operation)) {
return new PromiseController(controls, operation);
return createPromiseController(task, operation);
} else if (isGenerator(operation)) {
return new IteratorController(controls, operation);
return createIteratorController(task, operation);
}

@@ -27,0 +30,0 @@

@@ -1,25 +0,26 @@

import { Controls, Task } from '../task';
import { Task, getControls } from '../task';
import { Controller } from './controller';
export class FunctionController<T> implements Controller<T> {
delegate?: Controller<T>;
export function createFunctionController<TOut>(task: Task<TOut>, createController: () => Controller<TOut>) {
let delegate: Controller<TOut>;
let controls = getControls(task);
constructor(public controls: Controls<T>, public createController: () => Controller<T>) {}
start(task: Task<T>) {
function start() {
try {
this.delegate = this.createController();
delegate = createController();
} catch (error) {
this.controls.reject(error);
controls.reject(error);
return;
}
this.delegate.start(task);
delegate.start();
}
halt() {
if (!this.delegate) {
function halt() {
if (!delegate) {
throw new Error(`EFFECTION INTERNAL ERROR halt() called before start()`);
}
this.delegate.halt();
delegate.halt();
}
return { start, halt };
}
import { Controller } from './controller';
import { OperationIterator } from '../operation';
import { createTask, Task, Controls, getControls } from '../task';
import { HaltError } from '../halt-error';
import { createTask, Task, getControls } from '../task';
import { Operation } from '../operation';
import { Trapper } from '../trapper';

@@ -16,20 +14,22 @@ type Continuation = () => IteratorResult<Operation<unknown>>;

export class IteratorController<TOut> implements Controller<TOut>, Trapper {
private didHalt = false;
private didEnter = false;
private subTask?: Task;
type Options = {
resourceTask?: Task;
}
private continuations: Continuation[] = [];
export function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options: Options = {}): Controller<TOut> {
let didHalt = false;
let didEnter = false;
let subTask: Task | undefined;
let controls = getControls(task);
constructor(private controls: Controls<TOut>, private iterator: OperationIterator<TOut> & Claimable) {}
let continuations: Continuation[] = [];
// make this an async function to delay the first iteration until the next event loop tick
async start() {
if (this.iterator[claimed]) {
function start() {
if (iterator[claimed]) {
let error = new Error(`An operation iterator can only be run once in a single task, but it looks like has been either yielded to, or run multiple times`)
error.name = 'DoubleEvalError';
this.controls.reject(error);
controls.reject(error);
} else {
this.iterator[claimed] = true;
this.resume(() => this.iterator.next());
iterator[claimed] = true;
resume(() => iterator.next());
}

@@ -43,17 +43,17 @@ }

// siblings to fail.
resume(iter: Continuation) {
this.continuations.push(iter);
function resume(iter: Continuation) {
continuations.push(iter);
// only enter this loop if we aren't already running it
if(!this.didEnter) {
this.didEnter = true; // acquire lock
if(!didEnter) {
didEnter = true; // acquire lock
// use while loop since collection can be modified during iteration
let continuation;
while(continuation = this.continuations.shift()) {
this.step(continuation);
while(continuation = continuations.shift()) {
step(continuation);
}
this.didEnter = false; // release lock
didEnter = false; // release lock
}
}
step(iter: Continuation) {
function step(iter: Continuation) {
let next;

@@ -63,42 +63,46 @@ try {

} catch(error) {
this.controls.reject(error);
controls.reject(error);
return;
}
if(next.done) {
if(this.didHalt) {
this.controls.halted();
if(didHalt) {
controls.halted();
} else {
this.controls.resolve(next.value);
controls.resolve(next.value);
}
} else {
this.subTask = createTask(next.value);
getControls(this.subTask).addTrapper(this);
getControls(this.subTask).start();
subTask = createTask(next.value, { parent: options.resourceTask || task, ignoreError: true });
getControls(task).link(subTask);
getControls(subTask).addTrapper(trap);
getControls(subTask).start();
}
}
trap(child: Task) {
function trap(child: Task) {
subTask = undefined;
if(child.state === 'completed') {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.resume(() => this.iterator.next(getControls(child).result!));
resume(() => iterator.next(getControls(child).result!));
}
if(child.state === 'errored') {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.resume(() => this.iterator.throw(getControls(child).error!));
resume(() => iterator.throw(getControls(child).error!));
}
if(child.state === 'halted') {
this.resume(() => this.iterator.throw(new HaltError()));
resume(() => iterator.return(undefined));
}
}
halt() {
if(!this.didHalt) {
this.didHalt = true;
if(this.subTask) {
getControls(this.subTask).removeTrapper(this);
this.subTask.halt();
function halt() {
if(!didHalt) {
didHalt = true;
if(subTask) {
subTask.halt();
} else {
resume(() => iterator.return(undefined));
}
this.resume(() => this.iterator.return(undefined));
}
}
return { start, halt };
}
import { Controller } from './controller';
import { Controls } from '../task';
import { Task, getControls } from '../task';
import { Deferred } from '../deferred';

@@ -7,22 +7,15 @@

export class PromiseController<TOut> implements Controller<TOut> {
// TODO: to prevent memory leaks of tasks if a promise never resolves, but
// the task is halted, we should retain the task through a weak reference.
export function createPromiseController<TOut>(task: Task<TOut>, promise: PromiseLike<TOut>): Controller<TOut> {
let controls = getControls(task);
let haltSignal = Deferred<typeof HALT>();
private haltSignal = Deferred<typeof HALT>();
constructor(private controls: Controls<TOut>, private promise: PromiseLike<TOut>) {
}
start() {
Promise.race([this.promise, this.haltSignal.promise]).then(
function start() {
Promise.race([promise, haltSignal.promise]).then(
(value) => {
if(value === HALT) {
this.controls.halted();
} else {
this.controls.resolve(value);
if(value !== HALT) {
controls.resolve(value);
}
},
(error) => {
this.controls.reject(error);
controls.reject(error);
}

@@ -32,5 +25,8 @@ )

halt() {
this.haltSignal.resolve(HALT);
function halt() {
haltSignal.resolve(HALT);
controls.halted();
}
return { start, halt };
}
import { Controller } from './controller';
import { OperationResolution } from '../operation';
import { Controls } from '../task';
import { Task, getControls } from '../task';
export class ResolutionController<TOut> implements Controller<TOut> {
constructor(private controls: Controls<TOut>, private resolution: OperationResolution<TOut>) {
}
export function createResolutionController<TOut>(task: Task<TOut>, resolution: OperationResolution<TOut>): Controller<TOut> {
let controls = getControls(task);
start() {
let { controls } = this;
function start() {
try {
let atExit = this.resolution.perform(this.controls.resolve, this.controls.reject);
let atExit = resolution.perform(controls.resolve, controls.reject);
if (atExit) {

@@ -18,9 +15,11 @@ controls.ensure(atExit);

} catch(error) {
this.controls.reject(error);
controls.reject(error);
}
}
halt() {
this.controls.halted();
function halt() {
controls.halted();
}
return { start, halt };
}
import { Controller } from './controller';
import { Controls } from '../task';
import { getControls, Task } from '../task';
export class SuspendController<TOut> implements Controller<TOut> {
constructor(private controls: Controls<TOut>) {
}
export function createSuspendController<TOut>(task: Task<TOut>): Controller<TOut> {
let controls = getControls(task);
start() {
function start() {
// no op
}
halt() {
this.controls.halted();
function halt() {
controls.halted();
}
return { start, halt };
}

@@ -7,3 +7,3 @@ import { Operation } from './operation';

export { createTask, Task, TaskOptions, Controls, getControls } from './task';
export { Operation } from './operation';
export { Operation, Resource } from './operation';
export { sleep } from './sleep';

@@ -10,0 +10,0 @@ export { Effection } from './effection';

@@ -14,2 +14,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

export type Operation<TOut> = Continuation<TOut> | ContinuationFunction<TOut>;
export type Operation<TOut> = Continuation<TOut> | ContinuationFunction<TOut> | Resource<TOut>;
export interface Resource<TOut> {
init(scope: Task): OperationIterator<TOut>;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
import { OperationResolution } from "./operation";
import { OperationResolution, Resource } from "./operation";

@@ -16,1 +16,5 @@ export function isPromise(value: any): value is PromiseLike<unknown> {

}
export function isResource<TOut>(value: any): value is Resource<TOut> {
return typeof(value) === 'object' && typeof(value.init) === 'function';
}

@@ -15,4 +15,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

export interface TaskOptions {
parent?: Task;
blockParent?: boolean;
ignoreChildErrors?: boolean;
ignoreError?: boolean;
}

@@ -31,3 +33,3 @@

export interface Controls<TOut = unknown> extends Trapper {
export interface Controls<TOut = unknown> {
options: TaskOptions;

@@ -46,2 +48,3 @@ children: Set<Task>;

removeTrapper(trapper: Trapper): void;
trap: Trapper;
on(name: 'state', listener: (transition: StateTransition) => void): void;

@@ -77,3 +80,3 @@ on(name: 'link', listener: (child: Task) => void): void;

ensureHandlers.forEach((handler) => handler());
trappers.forEach((trapper) => trapper.trap(task as Task));
trappers.forEach((trapper) => trapper(task as Task));

@@ -102,3 +105,3 @@ ensureHandlers.clear();

// The child will always have been removed from the Set when this runs.
controls.addTrapper({ trap: () => haltChildren(force) });
controls.addTrapper(() => haltChildren(force));
child.halt()

@@ -117,3 +120,3 @@ return;

stateMachine.start();
controller.start(task);
controller.start();
},

@@ -148,3 +151,3 @@

if(!children.has(child)) {
getControls(child).addTrapper(controls);
getControls(child).addTrapper(controls.trap);
children.add(child);

@@ -157,3 +160,3 @@ emitter.emit('link', child);

if(children.has(child)) {
getControls(child).removeTrapper(controls);
getControls(child).removeTrapper(controls.trap);
children.delete(child);

@@ -166,3 +169,3 @@ emitter.emit('unlink', child);

if(children.has(child)) {
if(child.state === 'errored' && !options.ignoreChildErrors) {
if(child.state === 'errored' && !getControls(child).options.ignoreError && !options.ignoreChildErrors) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

@@ -199,6 +202,7 @@ controls.reject(getControls(child).error!);

spawn(operation?, options?) {
spawn(operation?, options = {}) {
if(stateMachine.current !== 'running') {
throw new Error('cannot spawn a child on a task which is not running');
}
options.parent = task;
let child = createTask(operation, options);

@@ -224,3 +228,3 @@ controls.link(child as Task);

controller = createController(task, controls, operation);
controller = createController(task, operation);

@@ -227,0 +231,0 @@ return task;

import { Task } from './task';
export interface Trapper {
trap(task: Task): void;
(task: Task): void;
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc