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-beta.11 to 2.0.0-beta.12

11

CHANGELOG.md
# @effection/core
## \[2.0.0-beta.12]
- Add @effection/fetch as a dependency and reexport it
- [5ab5d06](https://github.com/thefrontside/effection/commit/5ab5d0691af75f3583de97402b5aac12325e2918) Reexport @effection/fetch from effection package on 2021-08-26
- Share internal run loop among task, task future and task controller. Prevents race conditions which cause internal errors.
- [222d511](https://github.com/thefrontside/effection/commit/222d5116c388c5b597cc3ec5e0fb64b4d22b273a) Share event loop among controller, task and future on 2021-09-01
- Introduce task scope as an alternative to resources for being able to access the outer scope of an operation
- [3ed11bd](https://github.com/thefrontside/effection/commit/3ed11bd4f5d980cd130ea894a63acb57450c5aac) Make resource task accessible through init task on 2021-08-27
- Add `toString()` method to task for nicely formatted rendering of task structure
- [9a63928](https://github.com/thefrontside/effection/commit/9a6392836704ad527d6da5195f5736462d69bef8) Add toString output for tasks on 2021-08-31
## \[2.0.0-beta.11]

@@ -4,0 +15,0 @@

6

dist-cjs/controller/controller.d.ts
import type { Task } from '../task';
import type { RunLoop } from '../run-loop';
import type { Operation } from '../operation';

@@ -7,2 +8,3 @@ import { Future } from '../future';

operation: Operation<TOut>;
resourceTask?: Task;
start(): void;

@@ -13,6 +15,6 @@ halt(): void;

export declare type Options = {
resourceScope?: Task;
runLoop: RunLoop;
onYieldingToChange?: (task: Task | undefined) => void;
};
export declare function createController<T>(task: Task<T>, operation: Operation<T>, options?: Options): Controller<T>;
export declare function createController<T>(task: Task<T>, operation: Operation<T>, options: Options): Controller<T>;
//# sourceMappingURL=controller.d.ts.map

@@ -12,3 +12,3 @@ "use strict";

const resource_controller_1 = require("./resource-controller");
function createController(task, operation, options = {}) {
function createController(task, operation, options) {
if (typeof (operation) === 'function') {

@@ -15,0 +15,0 @@ return function_controller_1.createFunctionController(task, operation, () => createController(task, operation(task), options));

import { Task } from '../task';
import { Controller } from './controller';
import { Future } from '../future';
import { Operation, OperationFunction } from '../operation';
interface FunctionController<TOut> {
readonly type: string;
readonly operation: Operation<TOut>;
future: Future<TOut>;
start: () => void;
halt: () => void;
}
export declare function createFunctionController<TOut>(task: Task<TOut>, fn: OperationFunction<TOut>, createController: () => Controller<TOut>): FunctionController<TOut>;
export {};
import { OperationFunction } from '../operation';
export declare function createFunctionController<TOut>(task: Task<TOut>, fn: OperationFunction<TOut>, createController: () => Controller<TOut>): Controller<TOut>;
//# sourceMappingURL=function-controller.d.ts.map

@@ -42,2 +42,5 @@ "use strict";

},
get resourceTask() {
return delegate === null || delegate === void 0 ? void 0 : delegate.resourceTask;
},
future,

@@ -44,0 +47,0 @@ start,

@@ -8,4 +8,4 @@ import { Controller, Options } from './controller';

}
export declare function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options?: Options): Controller<TOut>;
export declare function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options: Options): Controller<TOut>;
export {};
//# sourceMappingURL=iterator-controller.d.ts.map

@@ -6,9 +6,7 @@ "use strict";

const future_1 = require("../future");
const run_loop_1 = require("../run-loop");
const claimed = Symbol.for('effection/v2/iterator-controller/claimed');
function createIteratorController(task, iterator, options = {}) {
function createIteratorController(task, iterator, options) {
let didHalt = false;
let yieldingTo;
let { produce, future } = future_1.createFuture();
let runLoop = run_loop_1.createRunLoop();
function start() {

@@ -26,3 +24,3 @@ if (iterator[claimed]) {

function resume(iter) {
runLoop.run(() => {
options.runLoop.run(() => {
let next;

@@ -45,3 +43,3 @@ try {

else {
yieldingTo = task_1.createTask(next.value, { resourceScope: options.resourceScope || task, ignoreError: true });
yieldingTo = task_1.createTask(next.value, { scope: task.options.yieldScope || task, ignoreError: true });
yieldingTo.consume(trap);

@@ -48,0 +46,0 @@ yieldingTo.start();

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createResourceController = void 0;
const iterator_controller_1 = require("./iterator-controller");
const future_1 = require("../future");
function createResourceController(task, resource) {
let delegate;
let { resourceScope } = task.options;
let resourceTask;
let initTask;
let { scope } = task.options;
let { produce, future } = future_1.createFuture();
function start() {
if (!resourceScope) {
var _a;
if (!scope) {
throw new Error('cannot spawn resource in task which has no resource scope');
}
let init;
try {
init = resource.init(resourceScope, task);
}
catch (error) {
produce({ state: 'errored', error });
return;
}
delegate = iterator_controller_1.createIteratorController(task, init, { resourceScope });
delegate.future.consume((value) => {
produce(value);
let name = resource.name || ((_a = resource.labels) === null || _a === void 0 ? void 0 : _a.name) || 'resource';
let labels = resource.labels || {};
resourceTask = scope.run(undefined, { type: 'resource', labels: { ...labels, name } });
initTask = resourceTask.run((task) => resource.init(resourceTask, task), {
yieldScope: resourceTask,
labels: { name: 'init' }
});
delegate.start();
initTask.consume(produce);
}
function halt() {
delegate.halt();
initTask === null || initTask === void 0 ? void 0 : initTask.halt();
}
return { start, halt, future, type: 'resource', operation: resource };
return {
type: 'resource constructor',
start,
halt,
future,
get resourceTask() {
return resourceTask;
},
operation: resource
};
}
exports.createResourceController = createResourceController;
//# sourceMappingURL=resource-controller.js.map

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

import { RunLoop } from './run-loop';
export declare type State = 'pending' | 'errored' | 'completed' | 'halted';

@@ -27,2 +28,3 @@ export declare type Value<T> = {

export declare function createFuture<T>(): NewFuture<T>;
export declare function createFutureOnRunLoop<T>(runLoop: RunLoop): NewFuture<T>;
//# sourceMappingURL=future.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFuture = void 0;
exports.createFutureOnRunLoop = exports.createFuture = void 0;
const halt_error_1 = require("./halt-error");
const run_loop_1 = require("./run-loop");
function createFuture() {
let runLoop = run_loop_1.createRunLoop();
return createFutureOnRunLoop(run_loop_1.createRunLoop('future'));
}
exports.createFuture = createFuture;
function createFutureOnRunLoop(runLoop) {
let consumers = [];

@@ -66,3 +69,3 @@ let result;

}
exports.createFuture = createFuture;
exports.createFutureOnRunLoop = createFutureOnRunLoop;
//# sourceMappingURL=future.js.map

@@ -1,3 +0,3 @@

import { Operation, Resource } from '../operation';
export declare function ensure<T>(fn: () => Operation<T> | void): Resource<undefined>;
import { Operation } from '../operation';
export declare function ensure<T>(fn: () => Operation<T> | void): Operation<undefined>;
//# sourceMappingURL=ensure.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensure = void 0;
const spawn_1 = require("./spawn");
const future_1 = require("../future");
function ensure(fn) {
return {
name: 'ensure',
*init() {
yield spawn_1.spawn(function* () {
try {
yield;
return function ensure(task) {
let { scope } = task.options;
if (!scope) {
throw new Error('cannot run `ensure` on a task without scope');
}
scope.run(function* ensureHandler() {
try {
yield;
}
finally {
let result = fn();
if (result) {
yield result;
}
finally {
let result = fn();
if (result) {
yield result;
}
}
}, { labels: { name: 'ensureHandler' } });
return undefined;
}
}
});
let { future, produce } = future_1.createFuture();
produce({ state: 'completed', value: undefined });
return future;
};

@@ -23,0 +26,0 @@ }

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

import { Resource } from '../operation';
import { Operation } from '../operation';
import { Labels } from '../labels';
export declare function label(labels: Labels): Resource<void>;
export declare function label(labels: Labels): Operation<void>;
//# sourceMappingURL=label.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.label = void 0;
const future_1 = require("../future");
const labels_1 = require("../labels");
function label(labels) {
return {
name: 'label',
*init(scope) {
scope.setLabels(labels);
return labels_1.withLabels((task) => {
let { scope } = task.options;
if (!scope) {
throw new Error('cannot run `label` on a task without scope');
}
};
scope.setLabels(labels);
let { future, produce } = future_1.createFuture();
produce({ state: 'completed', value: undefined });
return future;
}, { name: 'label' });
}
exports.label = label;
//# sourceMappingURL=label.js.map

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

import { Operation, Resource } from '../operation';
import { Operation, OperationFunction } from '../operation';
import type { Task, TaskOptions } from '../task';
interface Spawn<T> extends Resource<Task<T>> {
interface Spawn<T> extends OperationFunction<Task<T>> {
within(scope: Task): Operation<Task<T>>;

@@ -5,0 +5,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.spawn = void 0;
const future_1 = require("../future");
function spawn(operation, options) {
function* init(scope) {
return scope.run(operation, options);
function spawn(task) {
let { scope } = task.options;
if (!scope) {
throw new Error('cannot run `spawn` on a task without scope');
}
let result = scope.run(operation, options);
let { future, produce } = future_1.createFuture();
produce({ state: 'completed', value: result });
return future;
}

@@ -11,5 +19,5 @@ function within(scope) {

}
return { init, within, name: 'spawn' };
return Object.assign(spawn, { within });
}
exports.spawn = spawn;
//# sourceMappingURL=spawn.js.map

@@ -5,3 +5,3 @@ export declare type Runnable = () => void;

}
export declare function createRunLoop(): RunLoop;
export declare function createRunLoop(name?: string): RunLoop;
//# sourceMappingURL=run-loop.d.ts.map

@@ -8,3 +8,3 @@ "use strict";

// of synchronous mutex
function createRunLoop() {
function createRunLoop(name) {
let didEnter = false;

@@ -24,3 +24,3 @@ let runnables = [];

catch (e) {
console.error("Caught error in run loop:");
console.error(`Caught error in run loop \`${name}\`:`);
console.error(e);

@@ -27,0 +27,0 @@ }

@@ -13,2 +13,3 @@ /// <reference types="node" />

private transition;
get isFinalized(): boolean;
start(): void;

@@ -15,0 +16,0 @@ completing(): void;

@@ -20,2 +20,5 @@ "use strict";

}
get isFinalized() {
return this.current === 'errored' || this.current === 'completed' || this.current === 'halted';
}
start() {

@@ -22,0 +25,0 @@ this.transition('start', {

@@ -18,3 +18,5 @@ /// <reference types="node" />

export interface TaskOptions {
readonly resourceScope?: Task;
readonly type?: string;
readonly scope?: Task;
readonly yieldScope?: Task;
readonly blockParent?: boolean;

@@ -34,2 +36,3 @@ readonly ignoreChildErrors?: boolean;

readonly yieldingTo: Task | undefined;
readonly resourceTask: Task | undefined;
catchHalt(): Promise<TOut | undefined>;

@@ -42,2 +45,3 @@ setLabels(labels: Labels): void;

toJSON(): TaskTree;
toString(): string;
on: EventEmitter['on'];

@@ -44,0 +48,0 @@ off: EventEmitter['off'];

@@ -19,5 +19,5 @@ "use strict";

let stateMachine = new state_machine_1.StateMachine(emitter);
let { produce, future } = future_1.createFuture();
let result;
let runLoop = run_loop_1.createRunLoop();
let runLoop = run_loop_1.createRunLoop(`task ${id}`);
let { produce, future } = future_1.createFutureOnRunLoop(runLoop);
let controller;

@@ -40,5 +40,6 @@ let labels = { ...operation === null || operation === void 0 ? void 0 : operation.labels, ...options.labels };

get state() { return stateMachine.current; },
get type() { return controller.type; },
get type() { return options.type || controller.type; },
get children() { return Array.from(children); },
get yieldingTo() { return yieldingTo; },
get resourceTask() { return controller.resourceTask; },
catchHalt() {

@@ -55,3 +56,3 @@ return future.catch(halt_error_1.swallowHalt);

}
let child = createTask(operation, { resourceScope: task, ...options });
let child = createTask(operation, { scope: task, ...options });
link(child);

@@ -70,9 +71,11 @@ child.start();

start() {
if (stateMachine.current === 'pending') {
stateMachine.start();
controller.start();
}
runLoop.run(() => {
if (stateMachine.current === 'pending') {
stateMachine.start();
controller.start();
}
});
},
async halt() {
if (stateMachine.current === 'running' || stateMachine.current === 'completing') {
if (stateMachine.current === 'running') {
stateMachine.halting();

@@ -90,3 +93,3 @@ result = { state: 'halted' };

id: id,
type: controller.type,
type: task.type,
labels: labels,

@@ -98,2 +101,10 @@ state: stateMachine.current,

},
toString() {
let formattedLabels = Object.entries(labels).filter(([key]) => key !== 'name' && key !== 'expand').map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(' ');
return [
[labels.name || 'task', formattedLabels, `[${task.type} ${id}]`].filter(Boolean).join(' '),
yieldingTo && yieldingTo.toString().split('\n').map(l => '┃ ' + l).join('\n').replace(/^┃ /, `┣ yield `),
...Array.from(children).map((c) => c.toString().split('\n').map(l => '┃ ' + l).join('\n').replace(/^┃/, '┣'))
].filter(Boolean).join('\n');
},
on: (...args) => emitter.on(...args),

@@ -108,2 +119,3 @@ off: (...args) => emitter.off(...args),

controller = controller_1.createController(task, operation, {
runLoop,
onYieldingToChange(value) {

@@ -115,2 +127,4 @@ yieldingTo = value;

controller.future.consume((value) => {
if (stateMachine.isFinalized)
return;
if (value.state === 'completed') {

@@ -128,2 +142,7 @@ stateMachine.completing();

}
else if (value.state === 'halted' && stateMachine.current !== 'erroring') {
stateMachine.halting();
result = { state: 'halted' };
shutdown(true);
}
finalize();

@@ -134,12 +153,16 @@ });

child.consume((value) => {
if (value.state === 'errored' && !child.options.ignoreError && !options.ignoreChildErrors) {
stateMachine.erroring();
result = { state: 'errored', error: error_1.addTrace(value.error, task) };
shutdown(true);
}
if (children.has(child)) {
children.delete(child);
emitter.emit('unlink', child);
}
finalize();
runLoop.run(() => {
if (stateMachine.isFinalized)
return;
if (value.state === 'errored' && !child.options.ignoreError && !options.ignoreChildErrors) {
stateMachine.erroring();
result = { state: 'errored', error: error_1.addTrace(value.error, task) };
shutdown(true);
}
if (children.has(child)) {
children.delete(child);
emitter.emit('unlink', child);
}
finalize();
});
});

@@ -169,12 +192,10 @@ children.add(child);

function finalize() {
runLoop.run(() => {
if (Array.from(children).length !== 0)
return;
if (controller.future.state === 'pending')
return;
if (future.state !== 'pending')
return;
stateMachine.finish();
produce(result);
});
if (Array.from(children).length !== 0)
return;
if (controller.future.state === 'pending')
return;
if (future.state !== 'pending')
return;
stateMachine.finish();
produce(result);
}

@@ -181,0 +202,0 @@ return task;

import type { Task } from '../task';
import type { RunLoop } from '../run-loop';
import type { Operation } from '../operation';

@@ -7,2 +8,3 @@ import { Future } from '../future';

operation: Operation<TOut>;
resourceTask?: Task;
start(): void;

@@ -13,6 +15,6 @@ halt(): void;

export declare type Options = {
resourceScope?: Task;
runLoop: RunLoop;
onYieldingToChange?: (task: Task | undefined) => void;
};
export declare function createController<T>(task: Task<T>, operation: Operation<T>, options?: Options): Controller<T>;
export declare function createController<T>(task: Task<T>, operation: Operation<T>, options: Options): Controller<T>;
//# sourceMappingURL=controller.d.ts.map

@@ -9,3 +9,3 @@ import { isResource, isResolution, isFuture, isPromise, isGenerator } from '../predicates';

import { createResourceController } from './resource-controller';
export function createController(task, operation, options = {}) {
export function createController(task, operation, options) {
if (typeof (operation) === 'function') {

@@ -12,0 +12,0 @@ return createFunctionController(task, operation, () => createController(task, operation(task), options));

import { Task } from '../task';
import { Controller } from './controller';
import { Future } from '../future';
import { Operation, OperationFunction } from '../operation';
interface FunctionController<TOut> {
readonly type: string;
readonly operation: Operation<TOut>;
future: Future<TOut>;
start: () => void;
halt: () => void;
}
export declare function createFunctionController<TOut>(task: Task<TOut>, fn: OperationFunction<TOut>, createController: () => Controller<TOut>): FunctionController<TOut>;
export {};
import { OperationFunction } from '../operation';
export declare function createFunctionController<TOut>(task: Task<TOut>, fn: OperationFunction<TOut>, createController: () => Controller<TOut>): Controller<TOut>;
//# sourceMappingURL=function-controller.d.ts.map

@@ -39,2 +39,5 @@ import { createFuture } from '../future';

},
get resourceTask() {
return delegate === null || delegate === void 0 ? void 0 : delegate.resourceTask;
},
future,

@@ -41,0 +44,0 @@ start,

@@ -8,4 +8,4 @@ import { Controller, Options } from './controller';

}
export declare function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options?: Options): Controller<TOut>;
export declare function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options: Options): Controller<TOut>;
export {};
//# sourceMappingURL=iterator-controller.d.ts.map
import { createTask } from '../task';
import { createFuture } from '../future';
import { createRunLoop } from '../run-loop';
const claimed = Symbol.for('effection/v2/iterator-controller/claimed');
export function createIteratorController(task, iterator, options = {}) {
export function createIteratorController(task, iterator, options) {
let didHalt = false;
let yieldingTo;
let { produce, future } = createFuture();
let runLoop = createRunLoop();
function start() {

@@ -22,3 +20,3 @@ if (iterator[claimed]) {

function resume(iter) {
runLoop.run(() => {
options.runLoop.run(() => {
let next;

@@ -41,3 +39,3 @@ try {

else {
yieldingTo = createTask(next.value, { resourceScope: options.resourceScope || task, ignoreError: true });
yieldingTo = createTask(next.value, { scope: task.options.yieldScope || task, ignoreError: true });
yieldingTo.consume(trap);

@@ -44,0 +42,0 @@ yieldingTo.start();

@@ -1,30 +0,35 @@

import { createIteratorController } from './iterator-controller';
import { createFuture } from '../future';
export function createResourceController(task, resource) {
let delegate;
let { resourceScope } = task.options;
let resourceTask;
let initTask;
let { scope } = task.options;
let { produce, future } = createFuture();
function start() {
if (!resourceScope) {
var _a;
if (!scope) {
throw new Error('cannot spawn resource in task which has no resource scope');
}
let init;
try {
init = resource.init(resourceScope, task);
}
catch (error) {
produce({ state: 'errored', error });
return;
}
delegate = createIteratorController(task, init, { resourceScope });
delegate.future.consume((value) => {
produce(value);
let name = resource.name || ((_a = resource.labels) === null || _a === void 0 ? void 0 : _a.name) || 'resource';
let labels = resource.labels || {};
resourceTask = scope.run(undefined, { type: 'resource', labels: { ...labels, name } });
initTask = resourceTask.run((task) => resource.init(resourceTask, task), {
yieldScope: resourceTask,
labels: { name: 'init' }
});
delegate.start();
initTask.consume(produce);
}
function halt() {
delegate.halt();
initTask === null || initTask === void 0 ? void 0 : initTask.halt();
}
return { start, halt, future, type: 'resource', operation: resource };
return {
type: 'resource constructor',
start,
halt,
future,
get resourceTask() {
return resourceTask;
},
operation: resource
};
}
//# sourceMappingURL=resource-controller.js.map

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

import { RunLoop } from './run-loop';
export declare type State = 'pending' | 'errored' | 'completed' | 'halted';

@@ -27,2 +28,3 @@ export declare type Value<T> = {

export declare function createFuture<T>(): NewFuture<T>;
export declare function createFutureOnRunLoop<T>(runLoop: RunLoop): NewFuture<T>;
//# sourceMappingURL=future.d.ts.map
import { HaltError } from './halt-error';
import { createRunLoop } from './run-loop';
export function createFuture() {
let runLoop = createRunLoop();
return createFutureOnRunLoop(createRunLoop('future'));
}
export function createFutureOnRunLoop(runLoop) {
let consumers = [];

@@ -6,0 +8,0 @@ let result;

@@ -1,3 +0,3 @@

import { Operation, Resource } from '../operation';
export declare function ensure<T>(fn: () => Operation<T> | void): Resource<undefined>;
import { Operation } from '../operation';
export declare function ensure<T>(fn: () => Operation<T> | void): Operation<undefined>;
//# sourceMappingURL=ensure.d.ts.map

@@ -1,21 +0,24 @@

import { spawn } from './spawn';
import { createFuture } from '../future';
export function ensure(fn) {
return {
name: 'ensure',
*init() {
yield spawn(function* () {
try {
yield;
return function ensure(task) {
let { scope } = task.options;
if (!scope) {
throw new Error('cannot run `ensure` on a task without scope');
}
scope.run(function* ensureHandler() {
try {
yield;
}
finally {
let result = fn();
if (result) {
yield result;
}
finally {
let result = fn();
if (result) {
yield result;
}
}
}, { labels: { name: 'ensureHandler' } });
return undefined;
}
}
});
let { future, produce } = createFuture();
produce({ state: 'completed', value: undefined });
return future;
};
}
//# sourceMappingURL=ensure.js.map

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

import { Resource } from '../operation';
import { Operation } from '../operation';
import { Labels } from '../labels';
export declare function label(labels: Labels): Resource<void>;
export declare function label(labels: Labels): Operation<void>;
//# sourceMappingURL=label.d.ts.map

@@ -0,9 +1,15 @@

import { createFuture } from '../future';
import { withLabels } from '../labels';
export function label(labels) {
return {
name: 'label',
*init(scope) {
scope.setLabels(labels);
return withLabels((task) => {
let { scope } = task.options;
if (!scope) {
throw new Error('cannot run `label` on a task without scope');
}
};
scope.setLabels(labels);
let { future, produce } = createFuture();
produce({ state: 'completed', value: undefined });
return future;
}, { name: 'label' });
}
//# sourceMappingURL=label.js.map

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

import { Operation, Resource } from '../operation';
import { Operation, OperationFunction } from '../operation';
import type { Task, TaskOptions } from '../task';
interface Spawn<T> extends Resource<Task<T>> {
interface Spawn<T> extends OperationFunction<Task<T>> {
within(scope: Task): Operation<Task<T>>;

@@ -5,0 +5,0 @@ }

@@ -0,4 +1,12 @@

import { createFuture } from '../future';
export function spawn(operation, options) {
function* init(scope) {
return scope.run(operation, options);
function spawn(task) {
let { scope } = task.options;
if (!scope) {
throw new Error('cannot run `spawn` on a task without scope');
}
let result = scope.run(operation, options);
let { future, produce } = createFuture();
produce({ state: 'completed', value: result });
return future;
}

@@ -8,4 +16,4 @@ function within(scope) {

}
return { init, within, name: 'spawn' };
return Object.assign(spawn, { within });
}
//# sourceMappingURL=spawn.js.map

@@ -5,3 +5,3 @@ export declare type Runnable = () => void;

}
export declare function createRunLoop(): RunLoop;
export declare function createRunLoop(name?: string): RunLoop;
//# sourceMappingURL=run-loop.d.ts.map

@@ -5,3 +5,3 @@ // A run loop protects against reentrant code, where synchronous callbacks end

// of synchronous mutex
export function createRunLoop() {
export function createRunLoop(name) {
let didEnter = false;

@@ -21,3 +21,3 @@ let runnables = [];

catch (e) {
console.error("Caught error in run loop:");
console.error(`Caught error in run loop \`${name}\`:`);
console.error(e);

@@ -24,0 +24,0 @@ }

@@ -13,2 +13,3 @@ /// <reference types="node" />

private transition;
get isFinalized(): boolean;
start(): void;

@@ -15,0 +16,0 @@ completing(): void;

@@ -17,2 +17,5 @@ function f(value) { return JSON.stringify(value); }

}
get isFinalized() {
return this.current === 'errored' || this.current === 'completed' || this.current === 'halted';
}
start() {

@@ -19,0 +22,0 @@ this.transition('start', {

@@ -18,3 +18,5 @@ /// <reference types="node" />

export interface TaskOptions {
readonly resourceScope?: Task;
readonly type?: string;
readonly scope?: Task;
readonly yieldScope?: Task;
readonly blockParent?: boolean;

@@ -34,2 +36,3 @@ readonly ignoreChildErrors?: boolean;

readonly yieldingTo: Task | undefined;
readonly resourceTask: Task | undefined;
catchHalt(): Promise<TOut | undefined>;

@@ -42,2 +45,3 @@ setLabels(labels: Labels): void;

toJSON(): TaskTree;
toString(): string;
on: EventEmitter['on'];

@@ -44,0 +48,0 @@ off: EventEmitter['off'];

@@ -7,3 +7,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

import { addTrace } from './error';
import { createFuture } from './future';
import { createFutureOnRunLoop } from './future';
import { createRunLoop } from './run-loop';

@@ -17,5 +17,5 @@ let COUNTER = 0;

let stateMachine = new StateMachine(emitter);
let { produce, future } = createFuture();
let result;
let runLoop = createRunLoop();
let runLoop = createRunLoop(`task ${id}`);
let { produce, future } = createFutureOnRunLoop(runLoop);
let controller;

@@ -38,5 +38,6 @@ let labels = { ...operation === null || operation === void 0 ? void 0 : operation.labels, ...options.labels };

get state() { return stateMachine.current; },
get type() { return controller.type; },
get type() { return options.type || controller.type; },
get children() { return Array.from(children); },
get yieldingTo() { return yieldingTo; },
get resourceTask() { return controller.resourceTask; },
catchHalt() {

@@ -53,3 +54,3 @@ return future.catch(swallowHalt);

}
let child = createTask(operation, { resourceScope: task, ...options });
let child = createTask(operation, { scope: task, ...options });
link(child);

@@ -68,9 +69,11 @@ child.start();

start() {
if (stateMachine.current === 'pending') {
stateMachine.start();
controller.start();
}
runLoop.run(() => {
if (stateMachine.current === 'pending') {
stateMachine.start();
controller.start();
}
});
},
async halt() {
if (stateMachine.current === 'running' || stateMachine.current === 'completing') {
if (stateMachine.current === 'running') {
stateMachine.halting();

@@ -88,3 +91,3 @@ result = { state: 'halted' };

id: id,
type: controller.type,
type: task.type,
labels: labels,

@@ -96,2 +99,10 @@ state: stateMachine.current,

},
toString() {
let formattedLabels = Object.entries(labels).filter(([key]) => key !== 'name' && key !== 'expand').map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(' ');
return [
[labels.name || 'task', formattedLabels, `[${task.type} ${id}]`].filter(Boolean).join(' '),
yieldingTo && yieldingTo.toString().split('\n').map(l => '┃ ' + l).join('\n').replace(/^┃ /, `┣ yield `),
...Array.from(children).map((c) => c.toString().split('\n').map(l => '┃ ' + l).join('\n').replace(/^┃/, '┣'))
].filter(Boolean).join('\n');
},
on: (...args) => emitter.on(...args),

@@ -106,2 +117,3 @@ off: (...args) => emitter.off(...args),

controller = createController(task, operation, {
runLoop,
onYieldingToChange(value) {

@@ -113,2 +125,4 @@ yieldingTo = value;

controller.future.consume((value) => {
if (stateMachine.isFinalized)
return;
if (value.state === 'completed') {

@@ -126,2 +140,7 @@ stateMachine.completing();

}
else if (value.state === 'halted' && stateMachine.current !== 'erroring') {
stateMachine.halting();
result = { state: 'halted' };
shutdown(true);
}
finalize();

@@ -132,12 +151,16 @@ });

child.consume((value) => {
if (value.state === 'errored' && !child.options.ignoreError && !options.ignoreChildErrors) {
stateMachine.erroring();
result = { state: 'errored', error: addTrace(value.error, task) };
shutdown(true);
}
if (children.has(child)) {
children.delete(child);
emitter.emit('unlink', child);
}
finalize();
runLoop.run(() => {
if (stateMachine.isFinalized)
return;
if (value.state === 'errored' && !child.options.ignoreError && !options.ignoreChildErrors) {
stateMachine.erroring();
result = { state: 'errored', error: addTrace(value.error, task) };
shutdown(true);
}
if (children.has(child)) {
children.delete(child);
emitter.emit('unlink', child);
}
finalize();
});
});

@@ -167,12 +190,10 @@ children.add(child);

function finalize() {
runLoop.run(() => {
if (Array.from(children).length !== 0)
return;
if (controller.future.state === 'pending')
return;
if (future.state !== 'pending')
return;
stateMachine.finish();
produce(result);
});
if (Array.from(children).length !== 0)
return;
if (controller.future.state === 'pending')
return;
if (future.state !== 'pending')
return;
stateMachine.finish();
produce(result);
}

@@ -179,0 +200,0 @@ return task;

{
"name": "@effection/core",
"version": "2.0.0-beta.11",
"version": "2.0.0-beta.12",
"main": "dist-cjs/index.js",

@@ -5,0 +5,0 @@ "module": "dist-esm/index.js",

import type { Task } from '../task';
import type { RunLoop } from '../run-loop';
import type { Operation } from '../operation';

@@ -16,2 +17,3 @@ import { isResource, isResolution, isFuture, isPromise, isGenerator } from '../predicates';

operation: Operation<TOut>;
resourceTask?: Task;
start(): void;

@@ -23,7 +25,7 @@ halt(): void;

export type Options = {
resourceScope?: Task;
runLoop: RunLoop;
onYieldingToChange?: (task: Task | undefined) => void;
}
export function createController<T>(task: Task<T>, operation: Operation<T>, options: Options = {}): Controller<T> {
export function createController<T>(task: Task<T>, operation: Operation<T>, options: Options): Controller<T> {
if (typeof(operation) === 'function') {

@@ -30,0 +32,0 @@ return createFunctionController(task, operation, () => createController(task, operation(task), options));

import { Task } from '../task';
import { Controller } from './controller';
import { createFuture, Future } from '../future';
import { Operation, OperationFunction } from '../operation';
import { createFuture } from '../future';
import { OperationFunction } from '../operation';
interface FunctionController<TOut> {
readonly type: string;
readonly operation: Operation<TOut>;
future: Future<TOut>;
start: () => void;
halt: () => void;
}
export function createFunctionController<TOut>(task: Task<TOut>, fn: OperationFunction<TOut>, createController: () => Controller<TOut>): FunctionController<TOut> {
export function createFunctionController<TOut>(task: Task<TOut>, fn: OperationFunction<TOut>, createController: () => Controller<TOut>): Controller<TOut> {
let delegate: Controller<TOut>;

@@ -51,2 +43,5 @@ let { produce, future } = createFuture<TOut>();

},
get resourceTask() {
return delegate?.resourceTask;
},
future,

@@ -53,0 +48,0 @@ start,

@@ -6,3 +6,2 @@ import { Controller, Options } from './controller';

import { createFuture, Value } from '../future';
import { createRunLoop } from '../run-loop';

@@ -17,3 +16,3 @@ const claimed = Symbol.for('effection/v2/iterator-controller/claimed');

export function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options: Options = {}): Controller<TOut> {
export function createIteratorController<TOut>(task: Task<TOut>, iterator: OperationIterator<TOut> & Claimable, options: Options): Controller<TOut> {
let didHalt = false;

@@ -23,3 +22,2 @@ let yieldingTo: Task | undefined;

let { produce, future } = createFuture<TOut>();
let runLoop = createRunLoop();

@@ -38,3 +36,3 @@ function start() {

function resume(iter: NextFn) {
runLoop.run(() => {
options.runLoop.run(() => {
let next;

@@ -54,3 +52,3 @@ try {

} else {
yieldingTo = createTask(next.value, { resourceScope: options.resourceScope || task, ignoreError: true });
yieldingTo = createTask(next.value, { scope: task.options.yieldScope || task, ignoreError: true });
yieldingTo.consume(trap);

@@ -57,0 +55,0 @@ yieldingTo.start();

import { Controller } from './controller';
import { createIteratorController } from './iterator-controller';
import { Resource } from '../operation';

@@ -8,29 +7,38 @@ import { Task } from '../task';

export function createResourceController<TOut>(task: Task<TOut>, resource: Resource<TOut>): Controller<TOut> {
let delegate: Controller<TOut>;
let { resourceScope } = task.options;
let resourceTask: Task;
let initTask: Task<TOut>;
let { scope } = task.options;
let { produce, future } = createFuture<TOut>();
function start() {
if(!resourceScope) {
if(!scope) {
throw new Error('cannot spawn resource in task which has no resource scope');
}
let init;
try {
init = resource.init(resourceScope, task);
} catch(error) {
produce({ state: 'errored', error });
return;
}
delegate = createIteratorController(task, init, { resourceScope });
delegate.future.consume((value) => {
produce(value);
let name = resource.name || resource.labels?.name || 'resource';
let labels = resource.labels || {};
resourceTask = scope.run(undefined, { type: 'resource', labels: { ...labels, name } });
initTask = resourceTask.run((task) => resource.init(resourceTask, task), {
yieldScope: resourceTask,
labels: { name: 'init' }
});
delegate.start();
initTask.consume(produce);
}
function halt() {
delegate.halt();
initTask?.halt();
}
return { start, halt, future, type: 'resource', operation: resource };
return {
type: 'resource constructor',
start,
halt,
future,
get resourceTask() {
return resourceTask;
},
operation: resource
};
}
import { HaltError } from './halt-error';
import { createRunLoop } from './run-loop';
import { createRunLoop, RunLoop } from './run-loop';

@@ -30,4 +30,8 @@ export type State = 'pending' | 'errored' | 'completed' | 'halted';

export function createFuture<T>(): NewFuture<T> {
let runLoop = createRunLoop();
return createFutureOnRunLoop(createRunLoop('future'));
}
export function createFutureOnRunLoop<T>(runLoop: RunLoop): NewFuture<T> {
let consumers: Consumer<T>[] = [];

@@ -34,0 +38,0 @@ let result: Value<T>;

@@ -1,22 +0,25 @@

import { Operation, Resource } from '../operation';
import { spawn } from './spawn';
import { Operation } from '../operation';
import { createFuture } from '../future';
export function ensure<T>(fn: () => Operation<T> | void): Operation<undefined> {
return function ensure(task) {
let { scope } = task.options;
if(!scope) {
throw new Error('cannot run `ensure` on a task without scope');
}
scope.run(function* ensureHandler() {
try {
yield;
} finally {
let result = fn();
if(result) {
yield result;
}
}
});
export function ensure<T>(fn: () => Operation<T> | void): Resource<undefined> {
return {
name: 'ensure',
*init() {
yield spawn(function*() {
try {
yield;
} finally {
let result = fn();
if(result) {
yield result;
}
}
}, { labels: { name: 'ensureHandler' } });
return undefined;
}
let { future, produce } = createFuture<undefined>();
produce({ state: 'completed', value: undefined });
return future;
};
}

@@ -1,11 +0,17 @@

import { Resource } from '../operation';
import { Operation } from '../operation';
import { Labels } from '../labels';
import { createFuture } from '../future';
import { withLabels } from '../labels';
export function label(labels: Labels): Resource<void> {
return {
name: 'label',
*init(scope) {
scope.setLabels(labels);
export function label(labels: Labels): Operation<void> {
return withLabels((task) => {
let { scope } = task.options;
if(!scope) {
throw new Error('cannot run `label` on a task without scope');
}
};
scope.setLabels(labels);
let { future, produce } = createFuture<undefined>();
produce({ state: 'completed', value: undefined });
return future;
}, { name: 'label' });
}

@@ -1,5 +0,6 @@

import { Operation, Resource } from '../operation';
import { Operation, OperationFunction } from '../operation';
import { createFuture } from '../future';
import type { Task, TaskOptions } from '../task';
interface Spawn<T> extends Resource<Task<T>> {
interface Spawn<T> extends OperationFunction<Task<T>> {
within(scope: Task): Operation<Task<T>>;

@@ -9,4 +10,11 @@ }

export function spawn<T>(operation?: Operation<T>, options?: TaskOptions): Spawn<T> {
function* init(scope: Task) {
return scope.run(operation, options);
function spawn(task: Task) {
let { scope } = task.options;
if(!scope) {
throw new Error('cannot run `spawn` on a task without scope');
}
let result = scope.run(operation, options);
let { future, produce } = createFuture<Task<T>>();
produce({ state: 'completed', value: result });
return future;
}

@@ -18,3 +26,3 @@

return { init, within, name: 'spawn' };
return Object.assign(spawn, { within });
}

@@ -11,3 +11,3 @@ export type Runnable = () => void;

// of synchronous mutex
export function createRunLoop(): RunLoop {
export function createRunLoop(name?: string): RunLoop {
let didEnter = false;

@@ -27,3 +27,3 @@ let runnables: Runnable[] = [];

} catch(e) {
console.error("Caught error in run loop:");
console.error(`Caught error in run loop \`${name}\`:`);
console.error(e);

@@ -30,0 +30,0 @@ }

@@ -30,2 +30,6 @@ import { EventEmitter } from 'events';

get isFinalized(): boolean {
return this.current === 'errored' || this.current === 'completed' || this.current === 'halted';
}
start(): void {

@@ -32,0 +36,0 @@ this.transition('start', {

@@ -9,3 +9,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

import { addTrace } from './error';
import { createFuture, Future, FutureLike, Value } from './future';
import { createFutureOnRunLoop, Future, FutureLike, Value } from './future';
import { createRunLoop } from './run-loop';

@@ -28,3 +28,5 @@

export interface TaskOptions {
readonly resourceScope?: Task;
readonly type?: string;
readonly scope?: Task;
readonly yieldScope?: Task;
readonly blockParent?: boolean;

@@ -45,2 +47,3 @@ readonly ignoreChildErrors?: boolean;

readonly yieldingTo: Task | undefined;
readonly resourceTask: Task | undefined;
catchHalt(): Promise<TOut | undefined>;

@@ -53,2 +56,3 @@ setLabels(labels: Labels): void;

toJSON(): TaskTree;
toString(): string;
on: EventEmitter['on'];

@@ -68,5 +72,5 @@ off: EventEmitter['off'];

let { produce, future } = createFuture<TOut>();
let result: Value<TOut>;
let runLoop = createRunLoop();
let runLoop = createRunLoop(`task ${id}`);
let { produce, future } = createFutureOnRunLoop<TOut>(runLoop);

@@ -78,3 +82,2 @@ let controller: Controller<TOut>;

if (!labels.name) {

@@ -99,3 +102,3 @@ if (operation?.name) {

get type() { return controller.type },
get type() { return options.type || controller.type },

@@ -106,2 +109,4 @@ get children() { return Array.from(children) },

get resourceTask() { return controller.resourceTask },
catchHalt() {

@@ -120,3 +125,3 @@ return future.catch(swallowHalt);

}
let child = createTask(operation, { resourceScope: task, ...options });
let child = createTask(operation, { scope: task, ...options });
link(child as Task);

@@ -137,10 +142,12 @@ child.start();

start() {
if(stateMachine.current === 'pending') {
stateMachine.start();
controller.start();
}
runLoop.run(() => {
if(stateMachine.current === 'pending') {
stateMachine.start();
controller.start();
}
});
},
async halt() {
if(stateMachine.current === 'running' || stateMachine.current === 'completing') {
if(stateMachine.current === 'running') {
stateMachine.halting();

@@ -159,3 +166,3 @@ result = { state: 'halted' };

id: id,
type: controller.type,
type: task.type,
labels: labels,

@@ -168,2 +175,11 @@ state: stateMachine.current,

toString() {
let formattedLabels = Object.entries(labels).filter(([key]) => key !== 'name' && key !== 'expand').map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(' ');
return [
[labels.name || 'task', formattedLabels, `[${task.type} ${id}]`].filter(Boolean).join(' '),
yieldingTo && yieldingTo.toString().split('\n').map(l => '┃ ' + l).join('\n').replace(/^┃ /, `┣ yield `),
...Array.from(children).map((c) => c.toString().split('\n').map(l => '┃ ' + l).join('\n').replace(/^┃/, '┣'),)
].filter(Boolean).join('\n');
},
on: (...args) => emitter.on(...args),

@@ -179,2 +195,3 @@ off: (...args) => emitter.off(...args),

controller = createController(task, operation, {
runLoop,
onYieldingToChange(value) {

@@ -187,2 +204,3 @@ yieldingTo = value;

controller.future.consume((value) => {
if(stateMachine.isFinalized) return;
if(value.state === 'completed') {

@@ -198,2 +216,6 @@ stateMachine.completing();

shutdown(true);
} else if(value.state === 'halted' && stateMachine.current !== 'erroring') {
stateMachine.halting();
result = { state: 'halted' };
shutdown(true);
}

@@ -206,13 +228,16 @@ finalize();

child.consume((value) => {
if(value.state === 'errored' && !child.options.ignoreError && !options.ignoreChildErrors) {
stateMachine.erroring();
result = { state: 'errored', error: addTrace(value.error, task) };
runLoop.run(() => {
if(stateMachine.isFinalized) return;
if(value.state === 'errored' && !child.options.ignoreError && !options.ignoreChildErrors) {
stateMachine.erroring();
result = { state: 'errored', error: addTrace(value.error, task) };
shutdown(true);
}
if(children.has(child)) {
children.delete(child);
emitter.emit('unlink', child);
}
finalize();
shutdown(true);
}
if(children.has(child)) {
children.delete(child);
emitter.emit('unlink', child);
}
finalize();
});
});

@@ -245,10 +270,8 @@ children.add(child);

function finalize() {
runLoop.run(() => {
if(Array.from(children).length !== 0) return;
if(controller.future.state === 'pending') return;
if(future.state !== 'pending') return;
if(Array.from(children).length !== 0) return;
if(controller.future.state === 'pending') return;
if(future.state !== 'pending') return;
stateMachine.finish();
produce(result);
});
stateMachine.finish();
produce(result);
}

@@ -255,0 +278,0 @@

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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