veffect
Version:
powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha
498 lines • 16.6 kB
JavaScript
import * as Cause from "../../Cause.js";
import * as Context from "../../Context.js";
import * as Effect from "../../Effect.js";
import * as Either from "../../Either.js";
import * as Equal from "../../Equal.js";
import * as Exit from "../../Exit.js";
import * as FiberRef from "../../FiberRef.js";
import { constVoid, dual, pipe } from "../../Function.js";
import * as Hash from "../../Hash.js";
import { pipeArguments } from "../../Pipeable.js";
import { hasProperty } from "../../Predicate.js";
import { StreamTypeId } from "../../Stream.js";
import { ChannelTypeId } from "../core-stream.js";
import { withFiberRuntime } from "../core.js";
import { effectVariance } from "../effectable.js";
import { OP_COMMIT } from "../opCodes/effect.js";
import { SinkTypeId } from "../sink.js";
import * as OpCodes from "./opCodes/stm.js";
import * as TExitOpCodes from "./opCodes/tExit.js";
import * as TryCommitOpCodes from "./opCodes/tryCommit.js";
import * as Journal from "./stm/journal.js";
import * as STMState from "./stm/stmState.js";
import * as TExit from "./stm/tExit.js";
import * as TryCommit from "./stm/tryCommit.js";
import * as TxnId from "./stm/txnId.js";
/** @internal */
const STMSymbolKey = "effect/STM";
/** @internal */
export const STMTypeId = /*#__PURE__*/Symbol.for(STMSymbolKey);
const stmVariance = {
/* c8 ignore next */
_R: _ => _,
/* c8 ignore next */
_E: _ => _,
/* c8 ignore next */
_A: _ => _
};
/** @internal */
class STMPrimitive {
effect_instruction_i0;
_tag = OP_COMMIT;
_op = OP_COMMIT;
effect_instruction_i1 = undefined;
effect_instruction_i2 = undefined;
[Effect.EffectTypeId];
[StreamTypeId];
[SinkTypeId];
[ChannelTypeId];
get [STMTypeId]() {
return stmVariance;
}
constructor(effect_instruction_i0) {
this.effect_instruction_i0 = effect_instruction_i0;
this[Effect.EffectTypeId] = effectVariance;
this[StreamTypeId] = stmVariance;
this[SinkTypeId] = stmVariance;
this[ChannelTypeId] = stmVariance;
}
[Equal.symbol](that) {
return this === that;
}
[Hash.symbol]() {
return Hash.cached(this, Hash.random(this));
}
commit() {
return unsafeAtomically(this, constVoid, constVoid);
}
pipe() {
return pipeArguments(this, arguments);
}
}
/** @internal */
export const isSTM = u => hasProperty(u, STMTypeId);
/** @internal */
export const commit = self => unsafeAtomically(self, constVoid, constVoid);
/** @internal */
export const unsafeAtomically = (self, onDone, onInterrupt) => withFiberRuntime(state => {
const fiberId = state.id();
const env = state.getFiberRef(FiberRef.currentContext);
const scheduler = state.getFiberRef(FiberRef.currentScheduler);
const priority = state.getFiberRef(FiberRef.currentSchedulingPriority);
const commitResult = tryCommitSync(fiberId, self, env, scheduler, priority);
switch (commitResult._tag) {
case TryCommitOpCodes.OP_DONE:
{
onDone(commitResult.exit);
return commitResult.exit;
}
case TryCommitOpCodes.OP_SUSPEND:
{
const txnId = TxnId.make();
const state = {
value: STMState.running
};
const effect = Effect.async(k => tryCommitAsync(fiberId, self, txnId, state, env, scheduler, priority, k));
return Effect.uninterruptibleMask(restore => pipe(restore(effect), Effect.catchAllCause(cause => {
let currentState = state.value;
if (STMState.isRunning(currentState)) {
state.value = STMState.interrupted;
}
currentState = state.value;
if (STMState.isDone(currentState)) {
onDone(currentState.exit);
return currentState.exit;
}
onInterrupt();
return Effect.failCause(cause);
})));
}
}
});
/** @internal */
const tryCommit = (fiberId, stm, state, env, scheduler, priority) => {
const journal = new Map();
const tExit = new STMDriver(stm, journal, fiberId, env).run();
const analysis = Journal.analyzeJournal(journal);
if (analysis === Journal.JournalAnalysisReadWrite) {
Journal.commitJournal(journal);
} else if (analysis === Journal.JournalAnalysisInvalid) {
throw new Error("BUG: STM.TryCommit.tryCommit - please report an issue at https://github.com/Effect-TS/effect/issues");
}
switch (tExit._tag) {
case TExitOpCodes.OP_SUCCEED:
{
state.value = STMState.fromTExit(tExit);
return completeTodos(Exit.succeed(tExit.value), journal, scheduler, priority);
}
case TExitOpCodes.OP_FAIL:
{
state.value = STMState.fromTExit(tExit);
const cause = Cause.fail(tExit.error);
return completeTodos(Exit.failCause(cause), journal, scheduler, priority);
}
case TExitOpCodes.OP_DIE:
{
state.value = STMState.fromTExit(tExit);
const cause = Cause.die(tExit.defect);
return completeTodos(Exit.failCause(cause), journal, scheduler, priority);
}
case TExitOpCodes.OP_INTERRUPT:
{
state.value = STMState.fromTExit(tExit);
const cause = Cause.interrupt(fiberId);
return completeTodos(Exit.failCause(cause), journal, scheduler, priority);
}
case TExitOpCodes.OP_RETRY:
{
return TryCommit.suspend(journal);
}
}
};
/** @internal */
const tryCommitSync = (fiberId, stm, env, scheduler, priority) => {
const journal = new Map();
const tExit = new STMDriver(stm, journal, fiberId, env).run();
const analysis = Journal.analyzeJournal(journal);
if (analysis === Journal.JournalAnalysisReadWrite && TExit.isSuccess(tExit)) {
Journal.commitJournal(journal);
} else if (analysis === Journal.JournalAnalysisInvalid) {
throw new Error("BUG: STM.TryCommit.tryCommitSync - please report an issue at https://github.com/Effect-TS/effect/issues");
}
switch (tExit._tag) {
case TExitOpCodes.OP_SUCCEED:
{
return completeTodos(Exit.succeed(tExit.value), journal, scheduler, priority);
}
case TExitOpCodes.OP_FAIL:
{
const cause = Cause.fail(tExit.error);
return completeTodos(Exit.failCause(cause), journal, scheduler, priority);
}
case TExitOpCodes.OP_DIE:
{
const cause = Cause.die(tExit.defect);
return completeTodos(Exit.failCause(cause), journal, scheduler, priority);
}
case TExitOpCodes.OP_INTERRUPT:
{
const cause = Cause.interrupt(fiberId);
return completeTodos(Exit.failCause(cause), journal, scheduler, priority);
}
case TExitOpCodes.OP_RETRY:
{
return TryCommit.suspend(journal);
}
}
};
/** @internal */
const tryCommitAsync = (fiberId, self, txnId, state, context, scheduler, priority, k) => {
if (STMState.isRunning(state.value)) {
const result = tryCommit(fiberId, self, state, context, scheduler, priority);
switch (result._tag) {
case TryCommitOpCodes.OP_DONE:
{
completeTryCommit(result.exit, k);
break;
}
case TryCommitOpCodes.OP_SUSPEND:
{
Journal.addTodo(txnId, result.journal, () => tryCommitAsync(fiberId, self, txnId, state, context, scheduler, priority, k));
break;
}
}
}
};
/** @internal */
const completeTodos = (exit, journal, scheduler, priority) => {
const todos = Journal.collectTodos(journal);
if (todos.size > 0) {
scheduler.scheduleTask(() => Journal.execTodos(todos), priority);
}
return TryCommit.done(exit);
};
/** @internal */
const completeTryCommit = (exit, k) => {
k(exit);
};
/** @internal */
export const context = () => effect((_, __, env) => env);
/** @internal */
export const contextWith = f => map(context(), f);
/** @internal */
export const contextWithSTM = f => flatMap(context(), f);
/** @internal */
export class STMDriver {
self;
journal;
fiberId;
contStack = [];
env;
constructor(self, journal, fiberId, r0) {
this.self = self;
this.journal = journal;
this.fiberId = fiberId;
this.env = r0;
}
getEnv() {
return this.env;
}
pushStack(cont) {
this.contStack.push(cont);
}
popStack() {
return this.contStack.pop();
}
nextSuccess() {
let current = this.popStack();
while (current !== undefined && current.effect_instruction_i0 !== OpCodes.OP_ON_SUCCESS) {
current = this.popStack();
}
return current;
}
nextFailure() {
let current = this.popStack();
while (current !== undefined && current.effect_instruction_i0 !== OpCodes.OP_ON_FAILURE) {
current = this.popStack();
}
return current;
}
nextRetry() {
let current = this.popStack();
while (current !== undefined && current.effect_instruction_i0 !== OpCodes.OP_ON_RETRY) {
current = this.popStack();
}
return current;
}
run() {
let curr = this.self;
let exit = undefined;
while (exit === undefined && curr !== undefined) {
try {
const current = curr;
if (current) {
switch (current._tag) {
case "Tag":
{
curr = effect((_, __, env) => Context.unsafeGet(env, current));
break;
}
case "Left":
{
curr = fail(current.left);
break;
}
case "None":
{
curr = fail(new Cause.NoSuchElementException());
break;
}
case "Right":
{
curr = succeed(current.right);
break;
}
case "Some":
{
curr = succeed(current.value);
break;
}
case "Commit":
{
switch (current.effect_instruction_i0) {
case OpCodes.OP_DIE:
{
exit = TExit.die(current.effect_instruction_i1());
break;
}
case OpCodes.OP_FAIL:
{
const cont = this.nextFailure();
if (cont === undefined) {
exit = TExit.fail(current.effect_instruction_i1());
} else {
curr = cont.effect_instruction_i2(current.effect_instruction_i1());
}
break;
}
case OpCodes.OP_RETRY:
{
const cont = this.nextRetry();
if (cont === undefined) {
exit = TExit.retry;
} else {
curr = cont.effect_instruction_i2();
}
break;
}
case OpCodes.OP_INTERRUPT:
{
exit = TExit.interrupt(this.fiberId);
break;
}
case OpCodes.OP_WITH_STM_RUNTIME:
{
curr = current.effect_instruction_i1(this);
break;
}
case OpCodes.OP_ON_SUCCESS:
case OpCodes.OP_ON_FAILURE:
case OpCodes.OP_ON_RETRY:
{
this.pushStack(current);
curr = current.effect_instruction_i1;
break;
}
case OpCodes.OP_PROVIDE:
{
const env = this.env;
this.env = current.effect_instruction_i2(env);
curr = pipe(current.effect_instruction_i1, ensuring(sync(() => this.env = env)));
break;
}
case OpCodes.OP_SUCCEED:
{
const value = current.effect_instruction_i1;
const cont = this.nextSuccess();
if (cont === undefined) {
exit = TExit.succeed(value);
} else {
curr = cont.effect_instruction_i2(value);
}
break;
}
case OpCodes.OP_SYNC:
{
const value = current.effect_instruction_i1();
const cont = this.nextSuccess();
if (cont === undefined) {
exit = TExit.succeed(value);
} else {
curr = cont.effect_instruction_i2(value);
}
break;
}
}
break;
}
}
}
} catch (e) {
curr = die(e);
}
}
return exit;
}
}
/** @internal */
export const catchAll = /*#__PURE__*/dual(2, (self, f) => {
const stm = new STMPrimitive(OpCodes.OP_ON_FAILURE);
stm.effect_instruction_i1 = self;
stm.effect_instruction_i2 = f;
return stm;
});
/** @internal */
export const mapInputContext = /*#__PURE__*/dual(2, (self, f) => {
const stm = new STMPrimitive(OpCodes.OP_PROVIDE);
stm.effect_instruction_i1 = self;
stm.effect_instruction_i2 = f;
return stm;
});
/** @internal */
export const die = defect => dieSync(() => defect);
/** @internal */
export const dieMessage = message => dieSync(() => new Cause.RuntimeException(message));
/** @internal */
export const dieSync = evaluate => {
const stm = new STMPrimitive(OpCodes.OP_DIE);
stm.effect_instruction_i1 = evaluate;
return stm;
};
/** @internal */
export const effect = f => withSTMRuntime(_ => succeed(f(_.journal, _.fiberId, _.getEnv())));
/** @internal */
export const ensuring = /*#__PURE__*/dual(2, (self, finalizer) => matchSTM(self, {
onFailure: e => zipRight(finalizer, fail(e)),
onSuccess: a => zipRight(finalizer, succeed(a))
}));
/** @internal */
export const fail = error => failSync(() => error);
/** @internal */
export const failSync = evaluate => {
const stm = new STMPrimitive(OpCodes.OP_FAIL);
stm.effect_instruction_i1 = evaluate;
return stm;
};
/** @internal */
export const flatMap = /*#__PURE__*/dual(2, (self, f) => {
const stm = new STMPrimitive(OpCodes.OP_ON_SUCCESS);
stm.effect_instruction_i1 = self;
stm.effect_instruction_i2 = f;
return stm;
});
/** @internal */
export const matchSTM = /*#__PURE__*/dual(2, (self, {
onFailure,
onSuccess
}) => pipe(self, map(Either.right), catchAll(e => pipe(onFailure(e), map(Either.left))), flatMap(either => {
switch (either._tag) {
case "Left":
{
return succeed(either.left);
}
case "Right":
{
return onSuccess(either.right);
}
}
})));
/** @internal */
export const withSTMRuntime = f => {
const stm = new STMPrimitive(OpCodes.OP_WITH_STM_RUNTIME);
stm.effect_instruction_i1 = f;
return stm;
};
/** @internal */
export const interrupt = /*#__PURE__*/withSTMRuntime(_ => {
const stm = new STMPrimitive(OpCodes.OP_INTERRUPT);
stm.effect_instruction_i1 = _.fiberId;
return stm;
});
/** @internal */
export const interruptAs = fiberId => {
const stm = new STMPrimitive(OpCodes.OP_INTERRUPT);
stm.effect_instruction_i1 = fiberId;
return stm;
};
/** @internal */
export const map = /*#__PURE__*/dual(2, (self, f) => pipe(self, flatMap(a => sync(() => f(a)))));
/** @internal */
export const orTry = /*#__PURE__*/dual(2, (self, that) => {
const stm = new STMPrimitive(OpCodes.OP_ON_RETRY);
stm.effect_instruction_i1 = self;
stm.effect_instruction_i2 = that;
return stm;
});
/** @internal */
export const retry = /*#__PURE__*/new STMPrimitive(OpCodes.OP_RETRY);
/** @internal */
export const succeed = value => {
const stm = new STMPrimitive(OpCodes.OP_SUCCEED);
stm.effect_instruction_i1 = value;
return stm;
};
/** @internal */
export const sync = evaluate => {
const stm = new STMPrimitive(OpCodes.OP_SYNC);
stm.effect_instruction_i1 = evaluate;
return stm;
};
/** @internal */
export const zip = /*#__PURE__*/dual(2, (self, that) => pipe(self, zipWith(that, (a, a1) => [a, a1])));
/** @internal */
export const zipLeft = /*#__PURE__*/dual(2, (self, that) => pipe(self, flatMap(a => pipe(that, map(() => a)))));
/** @internal */
export const zipRight = /*#__PURE__*/dual(2, (self, that) => pipe(self, flatMap(() => that)));
/** @internal */
export const zipWith = /*#__PURE__*/dual(3, (self, that, f) => pipe(self, flatMap(a => pipe(that, map(b => f(a, b))))));
//# sourceMappingURL=core.js.map