Skip to content

Commit

Permalink
feat: rename unknown to exception (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
andogq authored Jul 15, 2024
2 parents c02f977 + 7300858 commit 06bc4c3
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 61 deletions.
5 changes: 5 additions & 0 deletions .changeset/real-kangaroos-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"windpipe": patch
---

rename `unknown` to `exception`, and deprecate all calls to `unknown`
23 changes: 16 additions & 7 deletions src/atom.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
export const VALUE = Symbol.for("VALUE");
export const ERROR = Symbol.for("ERROR");
export const UNKNOWN = Symbol.for("UNKNOWN");
export const EXCEPTION = Symbol.for("EXCEPTION");
/** @deprecated use `EXCEPTION` instead */
export const UNKNOWN = EXCEPTION;

export type AtomOk<T> = { type: typeof VALUE; value: T };
export type AtomError<E> = { type: typeof ERROR; value: E };
export type AtomUnknown = { type: typeof UNKNOWN; value: unknown; trace: Array<string> };
export type AtomException = { type: typeof EXCEPTION; value: unknown; trace: Array<string> };
/** @deprecated use `AtomException` instead */
export type AtomUnknown = AtomException;

export type Atom<T, E> = AtomOk<T> | AtomError<E> | AtomUnknown;
export type Atom<T, E> = AtomOk<T> | AtomError<E> | AtomException;
export type MaybeAtom<T, E> = T | Atom<T, E>;

export const ok = <T, E>(value: T): Atom<T, E> => ({ type: VALUE, value });
export const error = <T, E>(error: E): Atom<T, E> => ({ type: ERROR, value: error });
export const unknown = <T, E>(error: unknown, trace: Array<string>): Atom<T, E> => ({
type: UNKNOWN,
export const exception = <T, E>(error: unknown, trace: Array<string>): Atom<T, E> => ({
type: EXCEPTION,
value: error,
trace: [...trace],
});
/** @deprecated use `exception` instead */
export const unknown = exception;

export const isOk = <T, E>(atom: Atom<T, E>): atom is AtomOk<T> => atom.type === VALUE;
export const isError = <T, E>(atom: Atom<T, E>): atom is AtomError<E> => atom.type === ERROR;
export const isUnknown = <T, E>(atom: Atom<T, E>): atom is AtomUnknown => atom.type === UNKNOWN;
export const isException = <T, E>(atom: Atom<T, E>): atom is AtomException =>
atom.type === EXCEPTION;
/** @deprecated use `isUnknown` instead */
export const isUnknown = isException;

/**
* Given some value (which may or may not be an atom), convert it into an atom. If it is not an
Expand All @@ -30,7 +39,7 @@ export function normalise<T, E>(value: MaybeAtom<T, E>): Atom<T, E> {
value !== null &&
typeof value === "object" &&
"type" in value &&
(isOk(value) || isError(value) || isUnknown(value))
(isOk(value) || isError(value) || isException(value))
) {
return value;
} else {
Expand Down
16 changes: 8 additions & 8 deletions src/handler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
normalise,
ok,
unknown,
isUnknown,
exception,
isException,
type Atom,
type AtomOk,
type AtomUnknown,
type AtomException,
type MaybeAtom,
} from "./atom";
import type { MaybePromise } from "./util";
Expand All @@ -25,7 +25,7 @@ async function normalisePromise<T>(value: MaybePromise<T>): Promise<T> {

/**
* Run the given handler, then the returned value will be normalised as an atom and returned. If an
* unhandled error is thrown during the handler, then it will be caught and returned as an `unknown`
* unhandled error is thrown during the handler, then it will be caught and returned as an `exception`
* atom.
*/
export async function handler<T, E>(
Expand All @@ -34,7 +34,7 @@ export async function handler<T, E>(
): Promise<Atom<T, E>> {
const result = await run(handler, trace);

if (isUnknown(result)) {
if (isException(result)) {
return result;
}

Expand All @@ -43,16 +43,16 @@ export async function handler<T, E>(

/**
* Run some callback. If it completes successfully, the value will be returned as `AtomOk`. If an
* error is thrown, it will be caught and returned as an `AtomUnknown`. `AtomError` will never be
* error is thrown, it will be caught and returned as an `AtomException`. `AtomError` will never be
* produced from this helper.
*/
export async function run<T>(
cb: () => MaybePromise<T>,
trace: string[],
): Promise<AtomOk<T> | AtomUnknown> {
): Promise<AtomOk<T> | AtomException> {
try {
return ok(await normalisePromise(cb())) as AtomOk<T>;
} catch (e) {
return unknown(e, trace) as AtomUnknown;
return exception(e, trace) as AtomException;
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ export type {
Atom,
AtomOk,
AtomError,
AtomException,
AtomUnknown,
VALUE,
ERROR,
EXCEPTION,
UNKNOWN,
MaybeAtom,
} from "./atom";
Expand Down
18 changes: 13 additions & 5 deletions src/stream/base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { normalise, type Atom, type MaybeAtom, error, unknown } from "../atom";
import { normalise, type Atom, type MaybeAtom, error, exception } from "../atom";
import { Stream } from ".";
import { Readable, Writable } from "stream";
import { createNodeCallback } from "../util";
Expand Down Expand Up @@ -214,8 +214,8 @@ export class StreamBase {
this.push(normalise(value));
}
} catch (e) {
// Promise was rejected, add as an unknown error
this.push(unknown(e, []));
// Promise was rejected, add as an exception
this.push(exception(e, []));
}
},
}),
Expand Down Expand Up @@ -250,12 +250,20 @@ export class StreamBase {
}

/**
* Create a new stream containing a single unknown atom.
* Create a new stream containing a single exception atom.
*
* @group Creation
*/
static ofException<T, E>(value: unknown): Stream<T, E> {
return this.of(exception(value, []));
}

/**
* @group Creation
* @deprecated use `ofException` instead
*/
static ofUnknown<T, E>(value: unknown): Stream<T, E> {
return this.of(unknown(value, []));
return this.ofException(value);
}

/**
Expand Down
24 changes: 17 additions & 7 deletions src/stream/higher-order.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Stream } from ".";
import { isError, isOk, type Atom, isUnknown } from "../atom";
import { isError, isOk, type Atom, isException } from "../atom";
import { run } from "../handler";
import { type CallbackOrStream, type MaybePromise, exhaust } from "../util";
import { StreamTransforms } from "./transforms";
Expand Down Expand Up @@ -107,24 +107,34 @@ export class HigherOrderStream<T, E> extends StreamTransforms<T, E> {
}

/**
* Maps over each unknown error in the stream, producing a new stream from it, and flatten all
* Maps over each exception in the stream, producing a new stream from it, and flatten all
* the value streams together.
*
* @group Higher Order
*/
flatMapUnknown(
flatMapException(
cb: (value: unknown, trace: string[]) => MaybePromise<Stream<T, E>>,
): Stream<T, E> {
const trace = this.trace("flatMapUnknown");
const trace = this.trace("flatMapException");

return this.flatMapAtom(
(atom) => (isUnknown(atom) ? accept(atom) : reject(atom)),
(atom) => (isException(atom) ? accept(atom) : reject(atom)),
(atom) => {
return cb(atom.value, trace);
},
);
}

/**
* @group Higher Order
* @deprecated use `flatMapException` instead
*/
flatMapUnknown(
cb: (value: unknown, trace: string[]) => MaybePromise<Stream<T, E>>,
): Stream<T, E> {
return this.flatMapException(cb);
}

/**
* Map over each value in the stream, produce a stream from it, cache the resultant stream
* and flatten all the value streams together
Expand Down Expand Up @@ -235,7 +245,7 @@ export class HigherOrderStream<T, E> extends StreamTransforms<T, E> {
/**
* Emit items from provided stream if this stream is completely empty.
*
* @note If there are any errors (known or unknown) on the stream, then the new stream won't be
* @note If there are any errors or exceptions on the stream, then the new stream won't be
* consumed.
*
* @group Higher Order
Expand All @@ -262,7 +272,7 @@ export class HigherOrderStream<T, E> extends StreamTransforms<T, E> {

/**
* Consume the entire stream, and completely replace it with a new stream. This will remove
* any errors currently on the stream (both known and unknown).
* any errors and exceptions currently on the stream.
*
* Equivalent to:
*
Expand Down
32 changes: 24 additions & 8 deletions src/stream/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {
ok,
error,
unknown,
exception,
isOk,
isError,
isUnknown,
isException,
type Atom,
type AtomOk,
type AtomError,
type AtomUnknown,
type AtomException,
} from "../atom";
import { HigherOrderStream } from "./higher-order";

Expand Down Expand Up @@ -39,12 +39,20 @@ export class Stream<T, E> extends HigherOrderStream<T, E> {
}

/**
* Create an `unknown` atom with the provided value.
* Create an `exception` atom with the provided value.
*
* @group Atom
*/
static exception<T, E>(value: unknown, trace: string[]): Atom<T, E> {
return exception(value, trace);
}

/**
* @group Atom
* @deprecated use `exception` instead
*/
static unknown<T, E>(value: unknown, trace: string[]): Atom<T, E> {
return unknown(value, trace);
return this.exception(value, trace);
}

/**
Expand All @@ -66,11 +74,19 @@ export class Stream<T, E> extends HigherOrderStream<T, E> {
}

/**
* Verify if the provided atom is of the `unknown` variant.
* Verify if the provided atom is of the `exception` variant.
*
* @group Atom
*/
static isUnknown<T, E>(atom: Atom<T, E>): atom is AtomUnknown {
return isUnknown(atom);
static isException<T, E>(atom: Atom<T, E>): atom is AtomException {
return isException(atom);
}

/**
* @group Atom
* @deprecated use `isException` instead
*/
static isUnknown<T, E>(atom: Atom<T, E>): atom is AtomException {
return this.isException(atom);
}
}
20 changes: 14 additions & 6 deletions src/stream/transforms.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Stream } from ".";
import { isOk, isUnknown, type MaybeAtom, type Atom, isError, unknown, ok } from "../atom";
import { isOk, isException, type MaybeAtom, type Atom, isError, exception, ok } from "../atom";
import { handler } from "../handler";
import { StreamConsumption } from "./consumption";
import { Readable } from "stream";
Expand Down Expand Up @@ -85,16 +85,16 @@ export class StreamTransforms<T, E> extends StreamConsumption<T, E> {
}

/**
* Map over each unknown in the stream.
* Map over each exception in the stream.
*
* @group Transform
*/
mapUnknown(cb: (error: unknown) => MaybePromise<MaybeAtom<T, E>>): Stream<T, E> {
const trace = this.trace("mapUnknown");
mapException(cb: (error: unknown) => MaybePromise<MaybeAtom<T, E>>): Stream<T, E> {
const trace = this.trace("mapException");

return this.consume(async function* (it) {
for await (const atom of it) {
if (isUnknown(atom)) {
if (isException(atom)) {
yield await handler(() => cb(atom.value), trace);
} else {
yield atom;
Expand All @@ -103,6 +103,14 @@ export class StreamTransforms<T, E> extends StreamConsumption<T, E> {
});
}

/**
* @group Transform
* @deprecated use `mapException` instead
*/
mapUnknown(cb: (error: unknown) => MaybePromise<MaybeAtom<T, E>>): Stream<T, E> {
return this.mapException(cb);
}

/**
* Run a callback for each value in the stream, ideal for side effects on stream items.
*
Expand Down Expand Up @@ -165,7 +173,7 @@ export class StreamTransforms<T, E> extends StreamConsumption<T, E> {
"non-ok value returned from filter condition",
);
error.detail = filter;
yield unknown(error, trace);
yield exception(error, trace);
}
}
});
Expand Down
8 changes: 4 additions & 4 deletions test/consumption.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe.concurrent("stream consumption", () => {
$.error("known"),
2,
3,
$.unknown("$.error", []),
$.exception("$.error", []),
]).toArray();

expect(array).toEqual([1, 2, 3]);
Expand Down Expand Up @@ -50,7 +50,7 @@ describe.concurrent("stream consumption", () => {
$.error("known"),
2,
3,
$.unknown("$.error", []),
$.exception("$.error", []),
]).toArray({
atoms: true,
});
Expand All @@ -60,7 +60,7 @@ describe.concurrent("stream consumption", () => {
$.error("known"),
$.ok(2),
$.ok(3),
$.unknown("$.error", []),
$.exception("$.error", []),
]);
});

Expand Down Expand Up @@ -184,7 +184,7 @@ describe.concurrent("stream consumption", () => {
$.ok("a"),
$.error("an error"),
$.ok("b"),
$.unknown("unknown error", []),
$.exception("unknown error", []),
$.ok("c"),
]).toReadable("object");

Expand Down
Loading

0 comments on commit 06bc4c3

Please sign in to comment.