@onrails/maybe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +47 -0
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/fluent.cjs +53 -0
- package/dist/fluent.cjs.map +1 -0
- package/dist/fluent.d.cts +13 -0
- package/dist/fluent.d.ts +13 -0
- package/dist/fluent.js +51 -0
- package/dist/fluent.js.map +1 -0
- package/dist/index.cjs +68 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/interop.cjs +27 -0
- package/dist/interop.cjs.map +1 -0
- package/dist/interop.d.cts +18 -0
- package/dist/interop.d.ts +18 -0
- package/dist/interop.js +21 -0
- package/dist/interop.js.map +1 -0
- package/dist/types-vkiMAo4w.d.cts +15 -0
- package/dist/types-vkiMAo4w.d.ts +15 -0
- package/package.json +82 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @onrails/maybe — design
|
|
2
|
+
|
|
3
|
+
Expected absence as a tagged union — not failure. Pairs with `@onrails/result` at boundaries.
|
|
4
|
+
|
|
5
|
+
## Goals
|
|
6
|
+
|
|
7
|
+
- Same runtime model as `@onrails/result`: `{ _tag }` variants, dual-form module functions, `sideEffects: false`.
|
|
8
|
+
- **Some / None** — not Ok/Err. Do not use Maybe for parse/IO/auth failures.
|
|
9
|
+
- Optional `@onrails/maybe/interop` bridge (`toResult`, `fromResult`) keyed on `@onrails/result`.
|
|
10
|
+
|
|
11
|
+
## Runtime model
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
type Maybe<T> =
|
|
15
|
+
| { readonly _tag: "Some"; readonly value: T }
|
|
16
|
+
| { readonly _tag: "None" };
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## API parity with `@onrails/result`
|
|
20
|
+
|
|
21
|
+
| Result | Maybe |
|
|
22
|
+
|--------|-------|
|
|
23
|
+
| `ok` / `err` | `some` / `none` |
|
|
24
|
+
| `of` | `of` (= `some`) |
|
|
25
|
+
| `map` (dual) | `map` (dual) |
|
|
26
|
+
| `flatMap` (dual) | `flatMap` (dual) |
|
|
27
|
+
| `match` (positional, dual) | `match` (positional, dual) |
|
|
28
|
+
| `matchResult` | `matchMaybe` (collision-free alias) |
|
|
29
|
+
| `unwrapOr` | `unwrapOr` (= `getOrElse`) |
|
|
30
|
+
| — | `compactMap` (`compact` ∘ `map`) |
|
|
31
|
+
| — | `optional` (`flatMap` ∘ `fromNullable`) |
|
|
32
|
+
|
|
33
|
+
## Type tests
|
|
34
|
+
|
|
35
|
+
`test/types.spec.ts` uses `ts-expect` (`expectType` + `TypeEqual`) — compile-time assertions, no runtime cost.
|
|
36
|
+
|
|
37
|
+
## Exports
|
|
38
|
+
|
|
39
|
+
| Subpath | Purpose |
|
|
40
|
+
|---------|---------|
|
|
41
|
+
| `@onrails/maybe` | Core |
|
|
42
|
+
| `@onrails/maybe/fluent` | Dot-style chains |
|
|
43
|
+
| `@onrails/maybe/interop` | `Result` bridge (peer: `@onrails/result`) |
|
|
44
|
+
|
|
45
|
+
## Deferred
|
|
46
|
+
|
|
47
|
+
- Fantasy Land instances.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alan R. Soares
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @onrails/maybe
|
|
2
|
+
|
|
3
|
+
Tagged `Maybe` for **expected absence** — cache miss, optional field, not-ready-yet. Not for failures (use `@onrails/result`).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @onrails/maybe
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { compactMap, fromNullable, isSome, match, some } from "@onrails/maybe";
|
|
15
|
+
|
|
16
|
+
const row = fromNullable(db.get(id));
|
|
17
|
+
|
|
18
|
+
const name = match(
|
|
19
|
+
row,
|
|
20
|
+
(r) => r.name,
|
|
21
|
+
() => "guest",
|
|
22
|
+
);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Result boundary
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { toResult } from "@onrails/maybe/interop";
|
|
29
|
+
|
|
30
|
+
const user = toResult(fromNullable(row), () => ({ kind: "not_found" as const }));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Subpaths
|
|
34
|
+
|
|
35
|
+
| Path | Contents |
|
|
36
|
+
|------|----------|
|
|
37
|
+
| `@onrails/maybe` | Core |
|
|
38
|
+
| `@onrails/maybe/fluent` | `fluent(maybe)` chains |
|
|
39
|
+
| `@onrails/maybe/interop` | `toResult`, `fromResult`, `NoneError` |
|
|
40
|
+
|
|
41
|
+
See [DESIGN.md](./DESIGN.md).
|
package/dist/fluent.cjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/maybe.ts
|
|
4
|
+
var some = (value) => ({ _tag: "Some", value });
|
|
5
|
+
var none = () => ({ _tag: "None" });
|
|
6
|
+
var isSome = (maybe) => maybe._tag === "Some";
|
|
7
|
+
var mapImpl = (maybe, fn) => isSome(maybe) ? some(fn(maybe.value)) : none();
|
|
8
|
+
function map(...args) {
|
|
9
|
+
if (args.length === 2) return mapImpl(args[0], args[1]);
|
|
10
|
+
const fn = args[0];
|
|
11
|
+
return (maybe) => mapImpl(maybe, fn);
|
|
12
|
+
}
|
|
13
|
+
var flatMapImpl = (maybe, fn) => isSome(maybe) ? fn(maybe.value) : none();
|
|
14
|
+
function flatMap(...args) {
|
|
15
|
+
if (args.length === 2) return flatMapImpl(args[0], args[1]);
|
|
16
|
+
const fn = args[0];
|
|
17
|
+
return (maybe) => flatMapImpl(maybe, fn);
|
|
18
|
+
}
|
|
19
|
+
var andThen = flatMap;
|
|
20
|
+
var matchImpl = (maybe, onSome, onNone) => isSome(maybe) ? onSome(maybe.value) : onNone();
|
|
21
|
+
function match(...args) {
|
|
22
|
+
if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
|
|
23
|
+
const [onSome, onNone] = args;
|
|
24
|
+
return (maybe) => matchImpl(maybe, onSome, onNone);
|
|
25
|
+
}
|
|
26
|
+
var getOrElse = (maybe, defaultValue) => isSome(maybe) ? maybe.value : defaultValue;
|
|
27
|
+
var unwrapOr = getOrElse;
|
|
28
|
+
|
|
29
|
+
// src/fluent.ts
|
|
30
|
+
var fluent = (maybe) => Object.assign(maybe, {
|
|
31
|
+
map(fn) {
|
|
32
|
+
return fluent(map(maybe, fn));
|
|
33
|
+
},
|
|
34
|
+
flatMap(fn) {
|
|
35
|
+
return fluent(flatMap(maybe, fn));
|
|
36
|
+
},
|
|
37
|
+
andThen(fn) {
|
|
38
|
+
return fluent(andThen(maybe, fn));
|
|
39
|
+
},
|
|
40
|
+
match(onSome, onNone) {
|
|
41
|
+
return match(maybe, onSome, onNone);
|
|
42
|
+
},
|
|
43
|
+
getOrElse(defaultValue) {
|
|
44
|
+
return getOrElse(maybe, defaultValue);
|
|
45
|
+
},
|
|
46
|
+
unwrapOr(defaultValue) {
|
|
47
|
+
return unwrapOr(maybe, defaultValue);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
exports.fluent = fluent;
|
|
52
|
+
//# sourceMappingURL=fluent.cjs.map
|
|
53
|
+
//# sourceMappingURL=fluent.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/maybe.ts","../src/fluent.ts"],"names":[],"mappings":";;;AAYO,IAAM,OAAO,CAAI,KAAA,MAAwB,EAAE,IAAA,EAAM,QAAQ,KAAA,EAAM,CAAA;AAa/D,IAAM,IAAA,GAAO,OAA4B,EAAE,IAAA,EAAM,MAAA,EAAO,CAAA;AAYxD,IAAM,MAAA,GAAS,CAAI,KAAA,KAAsC,KAAA,CAAM,IAAA,KAAS,MAAA;AA0B/E,IAAM,OAAA,GAAU,CAAO,KAAA,EAAiB,EAAA,KACtC,MAAA,CAAO,KAAK,CAAA,GAAI,IAAA,CAAK,EAAA,CAAG,KAAA,CAAM,KAAK,CAAC,IAAI,IAAA,EAAQ;AAa3C,SAAS,OACX,IAAA,EACM;AACT,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,OAAA,CAAQ,KAAK,CAAC,CAAA,EAAG,IAAA,CAAK,CAAC,CAAC,CAAA;AACtD,EAAA,MAAM,EAAA,GAAK,KAAK,CAAC,CAAA;AACjB,EAAA,OAAO,CAAC,KAAA,KAA0B,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACrD;AAEA,IAAM,WAAA,GAAc,CAAO,KAAA,EAAiB,EAAA,KAC1C,MAAA,CAAO,KAAK,CAAA,GAAI,EAAA,CAAG,KAAA,CAAM,KAAK,CAAA,GAAI,IAAA,EAAQ;AAerC,SAAS,WACX,IAAA,EAGM;AACT,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,WAAA,CAAY,KAAK,CAAC,CAAA,EAAG,IAAA,CAAK,CAAC,CAAC,CAAA;AAC1D,EAAA,MAAM,EAAA,GAAK,KAAK,CAAC,CAAA;AACjB,EAAA,OAAO,CAAC,KAAA,KAA0B,WAAA,CAAY,KAAA,EAAO,EAAE,CAAA;AACzD;AAGO,IAAM,OAAA,GAAU,OAAA;AAEvB,IAAM,SAAA,GAAY,CAAO,KAAA,EAAiB,MAAA,EAAyB,MAAA,KACjE,MAAA,CAAO,KAAK,CAAA,GAAI,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,GAAI,MAAA,EAAO;AAoBxC,SAAS,SACX,IAAA,EAGM;AACT,EAAA,IAAI,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG,OAAO,SAAA,CAAU,IAAA,CAAK,CAAC,CAAA,EAAG,IAAA,CAAK,CAAC,CAAA,EAAG,IAAA,CAAK,CAAC,CAAC,CAAA;AACjE,EAAA,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,GAAI,IAAA;AACzB,EAAA,OAAO,CAAC,KAAA,KAA0B,SAAA,CAAU,KAAA,EAAO,QAAQ,MAAM,CAAA;AACnE;AAaO,IAAM,SAAA,GAAY,CAAI,KAAA,EAAiB,YAAA,KAC5C,OAAO,KAAK,CAAA,GAAI,MAAM,KAAA,GAAQ,YAAA;AAGzB,IAAM,QAAA,GAAW,SAAA;;;ACrJjB,IAAM,MAAA,GAAS,CAAI,KAAA,KACxB,MAAA,CAAO,OAAO,KAAA,EAAO;AAAA,EACnB,IAAO,EAAA,EAAqB;AAC1B,IAAA,OAAO,MAAA,CAAO,GAAA,CAAI,KAAA,EAAO,EAAE,CAAC,CAAA;AAAA,EAC9B,CAAA;AAAA,EACA,QAAW,EAAA,EAA4B;AACrC,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA;AAAA,EAClC,CAAA;AAAA,EACA,QAAW,EAAA,EAA4B;AACrC,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA;AAAA,EAClC,CAAA;AAAA,EACA,KAAA,CAAS,QAAyB,MAAA,EAAiB;AACjD,IAAA,OAAO,KAAA,CAAM,KAAA,EAAO,MAAA,EAAQ,MAAM,CAAA;AAAA,EACpC,CAAA;AAAA,EACA,UAAU,YAAA,EAAiB;AACzB,IAAA,OAAO,SAAA,CAAU,OAAO,YAAY,CAAA;AAAA,EACtC,CAAA;AAAA,EACA,SAAS,YAAA,EAAiB;AACxB,IAAA,OAAO,QAAA,CAAS,OAAO,YAAY,CAAA;AAAA,EACrC;AACF,CAAC","file":"fluent.cjs","sourcesContent":["import type { Maybe, None, Some } from \"./types.js\";\n\nexport type { Maybe, None, Some } from \"./types.js\";\n\n/**\n * Lifts a value into the `Some` branch.\n *\n * @example\n * ```ts\n * const m = some(1); // Maybe<number>\n * ```\n */\nexport const some = <T>(value: T): Maybe<T> => ({ _tag: \"Some\", value });\n\n/** Fantasy Land `of` — alias of {@link some}. */\nexport const of = some;\n\n/**\n * The `None` value — represents expected absence. Not a failure.\n *\n * @example\n * ```ts\n * const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();\n * ```\n */\nexport const none = <T = never>(): Maybe<T> => ({ _tag: \"None\" });\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `Some`.\n *\n * @example\n * ```ts\n * if (isSome(m)) {\n * console.log(m.value); // narrowed to Some branch\n * }\n * ```\n */\nexport const isSome = <T>(maybe: Maybe<T>): maybe is Some<T> => maybe._tag === \"Some\";\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `None`.\n */\nexport const isNone = <T>(maybe: Maybe<T>): maybe is None => maybe._tag === \"None\";\n\n/**\n * Lift a nullable value into `Maybe<T>` — `null` and `undefined` become\n * `None`; anything else becomes `Some`.\n *\n * @example\n * ```ts\n * fromNullable(map.get(key)); // Maybe<V>\n * fromNullable(null); // None\n * ```\n */\nexport const fromNullable = <T>(value: T | null | undefined): Maybe<T> =>\n value == null ? none<T>() : some(value);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each accepts either shape:\n// data-first: `map(maybe, fn)`\n// curried: `map(fn)(maybe)`\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U> =>\n isSome(maybe) ? some(fn(maybe.value)) : none<U>();\n\n/**\n * Transform the `Some` value, passing `None` through unchanged. Dual-form.\n *\n * @example\n * ```ts\n * map(some(2), (n) => n * 3); // Some 6 — data-first\n * pipe(fromNullable(input), map((s) => s.trim())); // curried\n * ```\n */\nexport function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;\nexport function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;\nexport function map(\n ...args: [Maybe<unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => mapImpl(maybe, fn);\n}\n\nconst flatMapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> =>\n isSome(maybe) ? fn(maybe.value) : none<U>();\n\n/**\n * Canonical bind for `Maybe`. Chains a `Maybe`-returning step;\n * short-circuits on `None`. Dual-form.\n *\n * @example\n * ```ts\n * flatMap(fromNullable(user), (u) =>\n * u.active ? some(u) : none(),\n * );\n * ```\n */\nexport function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;\nexport function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;\nexport function flatMap(\n ...args:\n | [Maybe<unknown>, (value: unknown) => Maybe<unknown>]\n | [(value: unknown) => Maybe<unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => flatMapImpl(maybe, fn);\n}\n\n/** Alias for {@link flatMap} — same dual-form shape. */\nexport const andThen = flatMap;\n\nconst matchImpl = <T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U =>\n isSome(maybe) ? onSome(maybe.value) : onNone();\n\n/**\n * Terminal collapse — fold both branches into a single value. Positional,\n * dual-form. Same shape as `Result.match` for sibling consistency.\n *\n * For files that also import `match` from `ts-pattern`, the alias\n * {@link matchMaybe} is identical.\n *\n * @example\n * ```ts\n * const greeting = match(\n * fromNullable(user),\n * (u) => `hello ${u.name}`,\n * () => \"hello guest\",\n * );\n * ```\n */\nexport function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;\nexport function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;\nexport function match(\n ...args:\n | [Maybe<unknown>, (value: unknown) => unknown, () => unknown]\n | [(value: unknown) => unknown, () => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onSome, onNone] = args;\n return (maybe: Maybe<unknown>) => matchImpl(maybe, onSome, onNone);\n}\n\n/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */\nexport const matchMaybe = match;\n\n/**\n * Returns the `Some` value, or `defaultValue` when the value is `None`.\n *\n * @example\n * ```ts\n * getOrElse(fromNullable(user), { name: \"guest\" });\n * ```\n */\nexport const getOrElse = <T>(maybe: Maybe<T>, defaultValue: T): T =>\n isSome(maybe) ? maybe.value : defaultValue;\n\n/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */\nexport const unwrapOr = getOrElse;\n\n/**\n * Unwraps the `Some` value or throws if the value is `None`. Use in tests\n * or at the boundary of an assertion; prefer `match` / `getOrElse` in\n * production code paths.\n *\n * @example\n * ```ts\n * expect(unwrap(parseUser(raw))).toEqual(expected);\n * ```\n */\nexport const unwrap = <T>(maybe: Maybe<T>): T => {\n if (isNone(maybe)) {\n throw new Error(\"Called unwrap on None\");\n }\n return maybe.value;\n};\n\n/**\n * Drop every `None` and collect the remaining `Some` values into an array.\n *\n * @example\n * ```ts\n * compact([some(1), none(), some(2)]); // [1, 2]\n * ```\n */\nexport const compact = <T>(maybes: readonly Maybe<T>[]): T[] => {\n const out: T[] = [];\n for (const m of maybes) {\n if (isSome(m)) {\n out.push(m.value);\n }\n }\n return out;\n};\n\n/**\n * `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,\n * collect the rest.\n *\n * @example\n * ```ts\n * compactMap(rawRows, (row) =>\n * row.deletedAt ? none() : some(row.id),\n * );\n * // string[]\n * ```\n */\nexport const compactMap = <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>): U[] =>\n compact(items.map(fn));\n\n/**\n * `flatMap(fromNullable(value), fn)` — lift a nullable then bind through\n * a `Maybe`-returning step. Convenient for chaining lookups.\n *\n * @example\n * ```ts\n * optional(rawId, (id) => fromNullable(cache.get(id)));\n * ```\n */\nexport const optional = <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>): Maybe<U> =>\n flatMapImpl(fromNullable(value), fn);\n","import { andThen, flatMap, getOrElse, type Maybe, map, match, unwrapOr } from \"./maybe.js\";\n\nexport type FluentMaybe<T> = Maybe<T> & {\n map<U>(fn: (value: T) => U): FluentMaybe<U>;\n flatMap<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;\n andThen<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;\n match<U>(onSome: (value: T) => U, onNone: () => U): U;\n getOrElse(defaultValue: T): T;\n unwrapOr(defaultValue: T): T;\n};\n\nexport const fluent = <T>(maybe: Maybe<T>): FluentMaybe<T> =>\n Object.assign(maybe, {\n map<U>(fn: (value: T) => U) {\n return fluent(map(maybe, fn));\n },\n flatMap<U>(fn: (value: T) => Maybe<U>) {\n return fluent(flatMap(maybe, fn));\n },\n andThen<U>(fn: (value: T) => Maybe<U>) {\n return fluent(andThen(maybe, fn));\n },\n match<U>(onSome: (value: T) => U, onNone: () => U) {\n return match(maybe, onSome, onNone);\n },\n getOrElse(defaultValue: T) {\n return getOrElse(maybe, defaultValue);\n },\n unwrapOr(defaultValue: T) {\n return unwrapOr(maybe, defaultValue);\n },\n });\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { M as Maybe } from './types-vkiMAo4w.cjs';
|
|
2
|
+
|
|
3
|
+
type FluentMaybe<T> = Maybe<T> & {
|
|
4
|
+
map<U>(fn: (value: T) => U): FluentMaybe<U>;
|
|
5
|
+
flatMap<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;
|
|
6
|
+
andThen<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;
|
|
7
|
+
match<U>(onSome: (value: T) => U, onNone: () => U): U;
|
|
8
|
+
getOrElse(defaultValue: T): T;
|
|
9
|
+
unwrapOr(defaultValue: T): T;
|
|
10
|
+
};
|
|
11
|
+
declare const fluent: <T>(maybe: Maybe<T>) => FluentMaybe<T>;
|
|
12
|
+
|
|
13
|
+
export { type FluentMaybe, fluent };
|
package/dist/fluent.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { M as Maybe } from './types-vkiMAo4w.js';
|
|
2
|
+
|
|
3
|
+
type FluentMaybe<T> = Maybe<T> & {
|
|
4
|
+
map<U>(fn: (value: T) => U): FluentMaybe<U>;
|
|
5
|
+
flatMap<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;
|
|
6
|
+
andThen<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;
|
|
7
|
+
match<U>(onSome: (value: T) => U, onNone: () => U): U;
|
|
8
|
+
getOrElse(defaultValue: T): T;
|
|
9
|
+
unwrapOr(defaultValue: T): T;
|
|
10
|
+
};
|
|
11
|
+
declare const fluent: <T>(maybe: Maybe<T>) => FluentMaybe<T>;
|
|
12
|
+
|
|
13
|
+
export { type FluentMaybe, fluent };
|
package/dist/fluent.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/maybe.ts
|
|
2
|
+
var some = (value) => ({ _tag: "Some", value });
|
|
3
|
+
var none = () => ({ _tag: "None" });
|
|
4
|
+
var isSome = (maybe) => maybe._tag === "Some";
|
|
5
|
+
var mapImpl = (maybe, fn) => isSome(maybe) ? some(fn(maybe.value)) : none();
|
|
6
|
+
function map(...args) {
|
|
7
|
+
if (args.length === 2) return mapImpl(args[0], args[1]);
|
|
8
|
+
const fn = args[0];
|
|
9
|
+
return (maybe) => mapImpl(maybe, fn);
|
|
10
|
+
}
|
|
11
|
+
var flatMapImpl = (maybe, fn) => isSome(maybe) ? fn(maybe.value) : none();
|
|
12
|
+
function flatMap(...args) {
|
|
13
|
+
if (args.length === 2) return flatMapImpl(args[0], args[1]);
|
|
14
|
+
const fn = args[0];
|
|
15
|
+
return (maybe) => flatMapImpl(maybe, fn);
|
|
16
|
+
}
|
|
17
|
+
var andThen = flatMap;
|
|
18
|
+
var matchImpl = (maybe, onSome, onNone) => isSome(maybe) ? onSome(maybe.value) : onNone();
|
|
19
|
+
function match(...args) {
|
|
20
|
+
if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
|
|
21
|
+
const [onSome, onNone] = args;
|
|
22
|
+
return (maybe) => matchImpl(maybe, onSome, onNone);
|
|
23
|
+
}
|
|
24
|
+
var getOrElse = (maybe, defaultValue) => isSome(maybe) ? maybe.value : defaultValue;
|
|
25
|
+
var unwrapOr = getOrElse;
|
|
26
|
+
|
|
27
|
+
// src/fluent.ts
|
|
28
|
+
var fluent = (maybe) => Object.assign(maybe, {
|
|
29
|
+
map(fn) {
|
|
30
|
+
return fluent(map(maybe, fn));
|
|
31
|
+
},
|
|
32
|
+
flatMap(fn) {
|
|
33
|
+
return fluent(flatMap(maybe, fn));
|
|
34
|
+
},
|
|
35
|
+
andThen(fn) {
|
|
36
|
+
return fluent(andThen(maybe, fn));
|
|
37
|
+
},
|
|
38
|
+
match(onSome, onNone) {
|
|
39
|
+
return match(maybe, onSome, onNone);
|
|
40
|
+
},
|
|
41
|
+
getOrElse(defaultValue) {
|
|
42
|
+
return getOrElse(maybe, defaultValue);
|
|
43
|
+
},
|
|
44
|
+
unwrapOr(defaultValue) {
|
|
45
|
+
return unwrapOr(maybe, defaultValue);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export { fluent };
|
|
50
|
+
//# sourceMappingURL=fluent.js.map
|
|
51
|
+
//# sourceMappingURL=fluent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/maybe.ts","../src/fluent.ts"],"names":[],"mappings":"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","file":"fluent.js","sourcesContent":["import type { Maybe, None, Some } from \"./types.js\";\n\nexport type { Maybe, None, Some } from \"./types.js\";\n\n/**\n * Lifts a value into the `Some` branch.\n *\n * @example\n * ```ts\n * const m = some(1); // Maybe<number>\n * ```\n */\nexport const some = <T>(value: T): Maybe<T> => ({ _tag: \"Some\", value });\n\n/** Fantasy Land `of` — alias of {@link some}. */\nexport const of = some;\n\n/**\n * The `None` value — represents expected absence. Not a failure.\n *\n * @example\n * ```ts\n * const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();\n * ```\n */\nexport const none = <T = never>(): Maybe<T> => ({ _tag: \"None\" });\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `Some`.\n *\n * @example\n * ```ts\n * if (isSome(m)) {\n * console.log(m.value); // narrowed to Some branch\n * }\n * ```\n */\nexport const isSome = <T>(maybe: Maybe<T>): maybe is Some<T> => maybe._tag === \"Some\";\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `None`.\n */\nexport const isNone = <T>(maybe: Maybe<T>): maybe is None => maybe._tag === \"None\";\n\n/**\n * Lift a nullable value into `Maybe<T>` — `null` and `undefined` become\n * `None`; anything else becomes `Some`.\n *\n * @example\n * ```ts\n * fromNullable(map.get(key)); // Maybe<V>\n * fromNullable(null); // None\n * ```\n */\nexport const fromNullable = <T>(value: T | null | undefined): Maybe<T> =>\n value == null ? none<T>() : some(value);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each accepts either shape:\n// data-first: `map(maybe, fn)`\n// curried: `map(fn)(maybe)`\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U> =>\n isSome(maybe) ? some(fn(maybe.value)) : none<U>();\n\n/**\n * Transform the `Some` value, passing `None` through unchanged. Dual-form.\n *\n * @example\n * ```ts\n * map(some(2), (n) => n * 3); // Some 6 — data-first\n * pipe(fromNullable(input), map((s) => s.trim())); // curried\n * ```\n */\nexport function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;\nexport function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;\nexport function map(\n ...args: [Maybe<unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => mapImpl(maybe, fn);\n}\n\nconst flatMapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> =>\n isSome(maybe) ? fn(maybe.value) : none<U>();\n\n/**\n * Canonical bind for `Maybe`. Chains a `Maybe`-returning step;\n * short-circuits on `None`. Dual-form.\n *\n * @example\n * ```ts\n * flatMap(fromNullable(user), (u) =>\n * u.active ? some(u) : none(),\n * );\n * ```\n */\nexport function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;\nexport function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;\nexport function flatMap(\n ...args:\n | [Maybe<unknown>, (value: unknown) => Maybe<unknown>]\n | [(value: unknown) => Maybe<unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => flatMapImpl(maybe, fn);\n}\n\n/** Alias for {@link flatMap} — same dual-form shape. */\nexport const andThen = flatMap;\n\nconst matchImpl = <T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U =>\n isSome(maybe) ? onSome(maybe.value) : onNone();\n\n/**\n * Terminal collapse — fold both branches into a single value. Positional,\n * dual-form. Same shape as `Result.match` for sibling consistency.\n *\n * For files that also import `match` from `ts-pattern`, the alias\n * {@link matchMaybe} is identical.\n *\n * @example\n * ```ts\n * const greeting = match(\n * fromNullable(user),\n * (u) => `hello ${u.name}`,\n * () => \"hello guest\",\n * );\n * ```\n */\nexport function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;\nexport function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;\nexport function match(\n ...args:\n | [Maybe<unknown>, (value: unknown) => unknown, () => unknown]\n | [(value: unknown) => unknown, () => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onSome, onNone] = args;\n return (maybe: Maybe<unknown>) => matchImpl(maybe, onSome, onNone);\n}\n\n/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */\nexport const matchMaybe = match;\n\n/**\n * Returns the `Some` value, or `defaultValue` when the value is `None`.\n *\n * @example\n * ```ts\n * getOrElse(fromNullable(user), { name: \"guest\" });\n * ```\n */\nexport const getOrElse = <T>(maybe: Maybe<T>, defaultValue: T): T =>\n isSome(maybe) ? maybe.value : defaultValue;\n\n/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */\nexport const unwrapOr = getOrElse;\n\n/**\n * Unwraps the `Some` value or throws if the value is `None`. Use in tests\n * or at the boundary of an assertion; prefer `match` / `getOrElse` in\n * production code paths.\n *\n * @example\n * ```ts\n * expect(unwrap(parseUser(raw))).toEqual(expected);\n * ```\n */\nexport const unwrap = <T>(maybe: Maybe<T>): T => {\n if (isNone(maybe)) {\n throw new Error(\"Called unwrap on None\");\n }\n return maybe.value;\n};\n\n/**\n * Drop every `None` and collect the remaining `Some` values into an array.\n *\n * @example\n * ```ts\n * compact([some(1), none(), some(2)]); // [1, 2]\n * ```\n */\nexport const compact = <T>(maybes: readonly Maybe<T>[]): T[] => {\n const out: T[] = [];\n for (const m of maybes) {\n if (isSome(m)) {\n out.push(m.value);\n }\n }\n return out;\n};\n\n/**\n * `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,\n * collect the rest.\n *\n * @example\n * ```ts\n * compactMap(rawRows, (row) =>\n * row.deletedAt ? none() : some(row.id),\n * );\n * // string[]\n * ```\n */\nexport const compactMap = <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>): U[] =>\n compact(items.map(fn));\n\n/**\n * `flatMap(fromNullable(value), fn)` — lift a nullable then bind through\n * a `Maybe`-returning step. Convenient for chaining lookups.\n *\n * @example\n * ```ts\n * optional(rawId, (id) => fromNullable(cache.get(id)));\n * ```\n */\nexport const optional = <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>): Maybe<U> =>\n flatMapImpl(fromNullable(value), fn);\n","import { andThen, flatMap, getOrElse, type Maybe, map, match, unwrapOr } from \"./maybe.js\";\n\nexport type FluentMaybe<T> = Maybe<T> & {\n map<U>(fn: (value: T) => U): FluentMaybe<U>;\n flatMap<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;\n andThen<U>(fn: (value: T) => Maybe<U>): FluentMaybe<U>;\n match<U>(onSome: (value: T) => U, onNone: () => U): U;\n getOrElse(defaultValue: T): T;\n unwrapOr(defaultValue: T): T;\n};\n\nexport const fluent = <T>(maybe: Maybe<T>): FluentMaybe<T> =>\n Object.assign(maybe, {\n map<U>(fn: (value: T) => U) {\n return fluent(map(maybe, fn));\n },\n flatMap<U>(fn: (value: T) => Maybe<U>) {\n return fluent(flatMap(maybe, fn));\n },\n andThen<U>(fn: (value: T) => Maybe<U>) {\n return fluent(andThen(maybe, fn));\n },\n match<U>(onSome: (value: T) => U, onNone: () => U) {\n return match(maybe, onSome, onNone);\n },\n getOrElse(defaultValue: T) {\n return getOrElse(maybe, defaultValue);\n },\n unwrapOr(defaultValue: T) {\n return unwrapOr(maybe, defaultValue);\n },\n });\n"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/maybe.ts
|
|
4
|
+
var some = (value) => ({ _tag: "Some", value });
|
|
5
|
+
var of = some;
|
|
6
|
+
var none = () => ({ _tag: "None" });
|
|
7
|
+
var isSome = (maybe) => maybe._tag === "Some";
|
|
8
|
+
var isNone = (maybe) => maybe._tag === "None";
|
|
9
|
+
var fromNullable = (value) => value == null ? none() : some(value);
|
|
10
|
+
var mapImpl = (maybe, fn) => isSome(maybe) ? some(fn(maybe.value)) : none();
|
|
11
|
+
function map(...args) {
|
|
12
|
+
if (args.length === 2) return mapImpl(args[0], args[1]);
|
|
13
|
+
const fn = args[0];
|
|
14
|
+
return (maybe) => mapImpl(maybe, fn);
|
|
15
|
+
}
|
|
16
|
+
var flatMapImpl = (maybe, fn) => isSome(maybe) ? fn(maybe.value) : none();
|
|
17
|
+
function flatMap(...args) {
|
|
18
|
+
if (args.length === 2) return flatMapImpl(args[0], args[1]);
|
|
19
|
+
const fn = args[0];
|
|
20
|
+
return (maybe) => flatMapImpl(maybe, fn);
|
|
21
|
+
}
|
|
22
|
+
var andThen = flatMap;
|
|
23
|
+
var matchImpl = (maybe, onSome, onNone) => isSome(maybe) ? onSome(maybe.value) : onNone();
|
|
24
|
+
function match(...args) {
|
|
25
|
+
if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
|
|
26
|
+
const [onSome, onNone] = args;
|
|
27
|
+
return (maybe) => matchImpl(maybe, onSome, onNone);
|
|
28
|
+
}
|
|
29
|
+
var matchMaybe = match;
|
|
30
|
+
var getOrElse = (maybe, defaultValue) => isSome(maybe) ? maybe.value : defaultValue;
|
|
31
|
+
var unwrapOr = getOrElse;
|
|
32
|
+
var unwrap = (maybe) => {
|
|
33
|
+
if (isNone(maybe)) {
|
|
34
|
+
throw new Error("Called unwrap on None");
|
|
35
|
+
}
|
|
36
|
+
return maybe.value;
|
|
37
|
+
};
|
|
38
|
+
var compact = (maybes) => {
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const m of maybes) {
|
|
41
|
+
if (isSome(m)) {
|
|
42
|
+
out.push(m.value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
};
|
|
47
|
+
var compactMap = (items, fn) => compact(items.map(fn));
|
|
48
|
+
var optional = (value, fn) => flatMapImpl(fromNullable(value), fn);
|
|
49
|
+
|
|
50
|
+
exports.andThen = andThen;
|
|
51
|
+
exports.compact = compact;
|
|
52
|
+
exports.compactMap = compactMap;
|
|
53
|
+
exports.flatMap = flatMap;
|
|
54
|
+
exports.fromNullable = fromNullable;
|
|
55
|
+
exports.getOrElse = getOrElse;
|
|
56
|
+
exports.isNone = isNone;
|
|
57
|
+
exports.isSome = isSome;
|
|
58
|
+
exports.map = map;
|
|
59
|
+
exports.match = match;
|
|
60
|
+
exports.matchMaybe = matchMaybe;
|
|
61
|
+
exports.none = none;
|
|
62
|
+
exports.of = of;
|
|
63
|
+
exports.optional = optional;
|
|
64
|
+
exports.some = some;
|
|
65
|
+
exports.unwrap = unwrap;
|
|
66
|
+
exports.unwrapOr = unwrapOr;
|
|
67
|
+
//# sourceMappingURL=index.cjs.map
|
|
68
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/maybe.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Maybe, None, Some } from \"./types.js\";\n\nexport type { Maybe, None, Some } from \"./types.js\";\n\n/**\n * Lifts a value into the `Some` branch.\n *\n * @example\n * ```ts\n * const m = some(1); // Maybe<number>\n * ```\n */\nexport const some = <T>(value: T): Maybe<T> => ({ _tag: \"Some\", value });\n\n/** Fantasy Land `of` — alias of {@link some}. */\nexport const of = some;\n\n/**\n * The `None` value — represents expected absence. Not a failure.\n *\n * @example\n * ```ts\n * const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();\n * ```\n */\nexport const none = <T = never>(): Maybe<T> => ({ _tag: \"None\" });\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `Some`.\n *\n * @example\n * ```ts\n * if (isSome(m)) {\n * console.log(m.value); // narrowed to Some branch\n * }\n * ```\n */\nexport const isSome = <T>(maybe: Maybe<T>): maybe is Some<T> => maybe._tag === \"Some\";\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `None`.\n */\nexport const isNone = <T>(maybe: Maybe<T>): maybe is None => maybe._tag === \"None\";\n\n/**\n * Lift a nullable value into `Maybe<T>` — `null` and `undefined` become\n * `None`; anything else becomes `Some`.\n *\n * @example\n * ```ts\n * fromNullable(map.get(key)); // Maybe<V>\n * fromNullable(null); // None\n * ```\n */\nexport const fromNullable = <T>(value: T | null | undefined): Maybe<T> =>\n value == null ? none<T>() : some(value);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each accepts either shape:\n// data-first: `map(maybe, fn)`\n// curried: `map(fn)(maybe)`\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U> =>\n isSome(maybe) ? some(fn(maybe.value)) : none<U>();\n\n/**\n * Transform the `Some` value, passing `None` through unchanged. Dual-form.\n *\n * @example\n * ```ts\n * map(some(2), (n) => n * 3); // Some 6 — data-first\n * pipe(fromNullable(input), map((s) => s.trim())); // curried\n * ```\n */\nexport function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;\nexport function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;\nexport function map(\n ...args: [Maybe<unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => mapImpl(maybe, fn);\n}\n\nconst flatMapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> =>\n isSome(maybe) ? fn(maybe.value) : none<U>();\n\n/**\n * Canonical bind for `Maybe`. Chains a `Maybe`-returning step;\n * short-circuits on `None`. Dual-form.\n *\n * @example\n * ```ts\n * flatMap(fromNullable(user), (u) =>\n * u.active ? some(u) : none(),\n * );\n * ```\n */\nexport function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;\nexport function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;\nexport function flatMap(\n ...args:\n | [Maybe<unknown>, (value: unknown) => Maybe<unknown>]\n | [(value: unknown) => Maybe<unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => flatMapImpl(maybe, fn);\n}\n\n/** Alias for {@link flatMap} — same dual-form shape. */\nexport const andThen = flatMap;\n\nconst matchImpl = <T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U =>\n isSome(maybe) ? onSome(maybe.value) : onNone();\n\n/**\n * Terminal collapse — fold both branches into a single value. Positional,\n * dual-form. Same shape as `Result.match` for sibling consistency.\n *\n * For files that also import `match` from `ts-pattern`, the alias\n * {@link matchMaybe} is identical.\n *\n * @example\n * ```ts\n * const greeting = match(\n * fromNullable(user),\n * (u) => `hello ${u.name}`,\n * () => \"hello guest\",\n * );\n * ```\n */\nexport function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;\nexport function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;\nexport function match(\n ...args:\n | [Maybe<unknown>, (value: unknown) => unknown, () => unknown]\n | [(value: unknown) => unknown, () => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onSome, onNone] = args;\n return (maybe: Maybe<unknown>) => matchImpl(maybe, onSome, onNone);\n}\n\n/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */\nexport const matchMaybe = match;\n\n/**\n * Returns the `Some` value, or `defaultValue` when the value is `None`.\n *\n * @example\n * ```ts\n * getOrElse(fromNullable(user), { name: \"guest\" });\n * ```\n */\nexport const getOrElse = <T>(maybe: Maybe<T>, defaultValue: T): T =>\n isSome(maybe) ? maybe.value : defaultValue;\n\n/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */\nexport const unwrapOr = getOrElse;\n\n/**\n * Unwraps the `Some` value or throws if the value is `None`. Use in tests\n * or at the boundary of an assertion; prefer `match` / `getOrElse` in\n * production code paths.\n *\n * @example\n * ```ts\n * expect(unwrap(parseUser(raw))).toEqual(expected);\n * ```\n */\nexport const unwrap = <T>(maybe: Maybe<T>): T => {\n if (isNone(maybe)) {\n throw new Error(\"Called unwrap on None\");\n }\n return maybe.value;\n};\n\n/**\n * Drop every `None` and collect the remaining `Some` values into an array.\n *\n * @example\n * ```ts\n * compact([some(1), none(), some(2)]); // [1, 2]\n * ```\n */\nexport const compact = <T>(maybes: readonly Maybe<T>[]): T[] => {\n const out: T[] = [];\n for (const m of maybes) {\n if (isSome(m)) {\n out.push(m.value);\n }\n }\n return out;\n};\n\n/**\n * `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,\n * collect the rest.\n *\n * @example\n * ```ts\n * compactMap(rawRows, (row) =>\n * row.deletedAt ? none() : some(row.id),\n * );\n * // string[]\n * ```\n */\nexport const compactMap = <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>): U[] =>\n compact(items.map(fn));\n\n/**\n * `flatMap(fromNullable(value), fn)` — lift a nullable then bind through\n * a `Maybe`-returning step. Convenient for chaining lookups.\n *\n * @example\n * ```ts\n * optional(rawId, (id) => fromNullable(cache.get(id)));\n * ```\n */\nexport const optional = <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>): Maybe<U> =>\n flatMapImpl(fromNullable(value), fn);\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { M as Maybe, N as None, S as Some } from './types-vkiMAo4w.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lifts a value into the `Some` branch.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const m = some(1); // Maybe<number>
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
declare const some: <T>(value: T) => Maybe<T>;
|
|
12
|
+
/** Fantasy Land `of` — alias of {@link some}. */
|
|
13
|
+
declare const of: <T>(value: T) => Maybe<T>;
|
|
14
|
+
/**
|
|
15
|
+
* The `None` value — represents expected absence. Not a failure.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare const none: <T = never>() => Maybe<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Type-narrowing predicate: returns `true` when the value is `Some`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* if (isSome(m)) {
|
|
29
|
+
* console.log(m.value); // narrowed to Some branch
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare const isSome: <T>(maybe: Maybe<T>) => maybe is Some<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Type-narrowing predicate: returns `true` when the value is `None`.
|
|
36
|
+
*/
|
|
37
|
+
declare const isNone: <T>(maybe: Maybe<T>) => maybe is None;
|
|
38
|
+
/**
|
|
39
|
+
* Lift a nullable value into `Maybe<T>` — `null` and `undefined` become
|
|
40
|
+
* `None`; anything else becomes `Some`.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* fromNullable(map.get(key)); // Maybe<V>
|
|
45
|
+
* fromNullable(null); // None
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare const fromNullable: <T>(value: T | null | undefined) => Maybe<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Transform the `Some` value, passing `None` through unchanged. Dual-form.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* map(some(2), (n) => n * 3); // Some 6 — data-first
|
|
55
|
+
* pipe(fromNullable(input), map((s) => s.trim())); // curried
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;
|
|
59
|
+
declare function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;
|
|
60
|
+
/**
|
|
61
|
+
* Canonical bind for `Maybe`. Chains a `Maybe`-returning step;
|
|
62
|
+
* short-circuits on `None`. Dual-form.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* flatMap(fromNullable(user), (u) =>
|
|
67
|
+
* u.active ? some(u) : none(),
|
|
68
|
+
* );
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
declare function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;
|
|
72
|
+
declare function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;
|
|
73
|
+
/** Alias for {@link flatMap} — same dual-form shape. */
|
|
74
|
+
declare const andThen: typeof flatMap;
|
|
75
|
+
/**
|
|
76
|
+
* Terminal collapse — fold both branches into a single value. Positional,
|
|
77
|
+
* dual-form. Same shape as `Result.match` for sibling consistency.
|
|
78
|
+
*
|
|
79
|
+
* For files that also import `match` from `ts-pattern`, the alias
|
|
80
|
+
* {@link matchMaybe} is identical.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const greeting = match(
|
|
85
|
+
* fromNullable(user),
|
|
86
|
+
* (u) => `hello ${u.name}`,
|
|
87
|
+
* () => "hello guest",
|
|
88
|
+
* );
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;
|
|
92
|
+
declare function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;
|
|
93
|
+
/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */
|
|
94
|
+
declare const matchMaybe: typeof match;
|
|
95
|
+
/**
|
|
96
|
+
* Returns the `Some` value, or `defaultValue` when the value is `None`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* getOrElse(fromNullable(user), { name: "guest" });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare const getOrElse: <T>(maybe: Maybe<T>, defaultValue: T) => T;
|
|
104
|
+
/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */
|
|
105
|
+
declare const unwrapOr: <T>(maybe: Maybe<T>, defaultValue: T) => T;
|
|
106
|
+
/**
|
|
107
|
+
* Unwraps the `Some` value or throws if the value is `None`. Use in tests
|
|
108
|
+
* or at the boundary of an assertion; prefer `match` / `getOrElse` in
|
|
109
|
+
* production code paths.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* expect(unwrap(parseUser(raw))).toEqual(expected);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare const unwrap: <T>(maybe: Maybe<T>) => T;
|
|
117
|
+
/**
|
|
118
|
+
* Drop every `None` and collect the remaining `Some` values into an array.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* compact([some(1), none(), some(2)]); // [1, 2]
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare const compact: <T>(maybes: readonly Maybe<T>[]) => T[];
|
|
126
|
+
/**
|
|
127
|
+
* `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,
|
|
128
|
+
* collect the rest.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* compactMap(rawRows, (row) =>
|
|
133
|
+
* row.deletedAt ? none() : some(row.id),
|
|
134
|
+
* );
|
|
135
|
+
* // string[]
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare const compactMap: <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>) => U[];
|
|
139
|
+
/**
|
|
140
|
+
* `flatMap(fromNullable(value), fn)` — lift a nullable then bind through
|
|
141
|
+
* a `Maybe`-returning step. Convenient for chaining lookups.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* optional(rawId, (id) => fromNullable(cache.get(id)));
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare const optional: <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>) => Maybe<U>;
|
|
149
|
+
|
|
150
|
+
export { Maybe, None, Some, andThen, compact, compactMap, flatMap, fromNullable, getOrElse, isNone, isSome, map, match, matchMaybe, none, of, optional, some, unwrap, unwrapOr };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { M as Maybe, N as None, S as Some } from './types-vkiMAo4w.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lifts a value into the `Some` branch.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const m = some(1); // Maybe<number>
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
declare const some: <T>(value: T) => Maybe<T>;
|
|
12
|
+
/** Fantasy Land `of` — alias of {@link some}. */
|
|
13
|
+
declare const of: <T>(value: T) => Maybe<T>;
|
|
14
|
+
/**
|
|
15
|
+
* The `None` value — represents expected absence. Not a failure.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare const none: <T = never>() => Maybe<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Type-narrowing predicate: returns `true` when the value is `Some`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* if (isSome(m)) {
|
|
29
|
+
* console.log(m.value); // narrowed to Some branch
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare const isSome: <T>(maybe: Maybe<T>) => maybe is Some<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Type-narrowing predicate: returns `true` when the value is `None`.
|
|
36
|
+
*/
|
|
37
|
+
declare const isNone: <T>(maybe: Maybe<T>) => maybe is None;
|
|
38
|
+
/**
|
|
39
|
+
* Lift a nullable value into `Maybe<T>` — `null` and `undefined` become
|
|
40
|
+
* `None`; anything else becomes `Some`.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* fromNullable(map.get(key)); // Maybe<V>
|
|
45
|
+
* fromNullable(null); // None
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare const fromNullable: <T>(value: T | null | undefined) => Maybe<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Transform the `Some` value, passing `None` through unchanged. Dual-form.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* map(some(2), (n) => n * 3); // Some 6 — data-first
|
|
55
|
+
* pipe(fromNullable(input), map((s) => s.trim())); // curried
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;
|
|
59
|
+
declare function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;
|
|
60
|
+
/**
|
|
61
|
+
* Canonical bind for `Maybe`. Chains a `Maybe`-returning step;
|
|
62
|
+
* short-circuits on `None`. Dual-form.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* flatMap(fromNullable(user), (u) =>
|
|
67
|
+
* u.active ? some(u) : none(),
|
|
68
|
+
* );
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
declare function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;
|
|
72
|
+
declare function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;
|
|
73
|
+
/** Alias for {@link flatMap} — same dual-form shape. */
|
|
74
|
+
declare const andThen: typeof flatMap;
|
|
75
|
+
/**
|
|
76
|
+
* Terminal collapse — fold both branches into a single value. Positional,
|
|
77
|
+
* dual-form. Same shape as `Result.match` for sibling consistency.
|
|
78
|
+
*
|
|
79
|
+
* For files that also import `match` from `ts-pattern`, the alias
|
|
80
|
+
* {@link matchMaybe} is identical.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* const greeting = match(
|
|
85
|
+
* fromNullable(user),
|
|
86
|
+
* (u) => `hello ${u.name}`,
|
|
87
|
+
* () => "hello guest",
|
|
88
|
+
* );
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;
|
|
92
|
+
declare function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;
|
|
93
|
+
/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */
|
|
94
|
+
declare const matchMaybe: typeof match;
|
|
95
|
+
/**
|
|
96
|
+
* Returns the `Some` value, or `defaultValue` when the value is `None`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* getOrElse(fromNullable(user), { name: "guest" });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare const getOrElse: <T>(maybe: Maybe<T>, defaultValue: T) => T;
|
|
104
|
+
/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */
|
|
105
|
+
declare const unwrapOr: <T>(maybe: Maybe<T>, defaultValue: T) => T;
|
|
106
|
+
/**
|
|
107
|
+
* Unwraps the `Some` value or throws if the value is `None`. Use in tests
|
|
108
|
+
* or at the boundary of an assertion; prefer `match` / `getOrElse` in
|
|
109
|
+
* production code paths.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* expect(unwrap(parseUser(raw))).toEqual(expected);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare const unwrap: <T>(maybe: Maybe<T>) => T;
|
|
117
|
+
/**
|
|
118
|
+
* Drop every `None` and collect the remaining `Some` values into an array.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* compact([some(1), none(), some(2)]); // [1, 2]
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare const compact: <T>(maybes: readonly Maybe<T>[]) => T[];
|
|
126
|
+
/**
|
|
127
|
+
* `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,
|
|
128
|
+
* collect the rest.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* compactMap(rawRows, (row) =>
|
|
133
|
+
* row.deletedAt ? none() : some(row.id),
|
|
134
|
+
* );
|
|
135
|
+
* // string[]
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare const compactMap: <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>) => U[];
|
|
139
|
+
/**
|
|
140
|
+
* `flatMap(fromNullable(value), fn)` — lift a nullable then bind through
|
|
141
|
+
* a `Maybe`-returning step. Convenient for chaining lookups.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* optional(rawId, (id) => fromNullable(cache.get(id)));
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare const optional: <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>) => Maybe<U>;
|
|
149
|
+
|
|
150
|
+
export { Maybe, None, Some, andThen, compact, compactMap, flatMap, fromNullable, getOrElse, isNone, isSome, map, match, matchMaybe, none, of, optional, some, unwrap, unwrapOr };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/maybe.ts
|
|
2
|
+
var some = (value) => ({ _tag: "Some", value });
|
|
3
|
+
var of = some;
|
|
4
|
+
var none = () => ({ _tag: "None" });
|
|
5
|
+
var isSome = (maybe) => maybe._tag === "Some";
|
|
6
|
+
var isNone = (maybe) => maybe._tag === "None";
|
|
7
|
+
var fromNullable = (value) => value == null ? none() : some(value);
|
|
8
|
+
var mapImpl = (maybe, fn) => isSome(maybe) ? some(fn(maybe.value)) : none();
|
|
9
|
+
function map(...args) {
|
|
10
|
+
if (args.length === 2) return mapImpl(args[0], args[1]);
|
|
11
|
+
const fn = args[0];
|
|
12
|
+
return (maybe) => mapImpl(maybe, fn);
|
|
13
|
+
}
|
|
14
|
+
var flatMapImpl = (maybe, fn) => isSome(maybe) ? fn(maybe.value) : none();
|
|
15
|
+
function flatMap(...args) {
|
|
16
|
+
if (args.length === 2) return flatMapImpl(args[0], args[1]);
|
|
17
|
+
const fn = args[0];
|
|
18
|
+
return (maybe) => flatMapImpl(maybe, fn);
|
|
19
|
+
}
|
|
20
|
+
var andThen = flatMap;
|
|
21
|
+
var matchImpl = (maybe, onSome, onNone) => isSome(maybe) ? onSome(maybe.value) : onNone();
|
|
22
|
+
function match(...args) {
|
|
23
|
+
if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
|
|
24
|
+
const [onSome, onNone] = args;
|
|
25
|
+
return (maybe) => matchImpl(maybe, onSome, onNone);
|
|
26
|
+
}
|
|
27
|
+
var matchMaybe = match;
|
|
28
|
+
var getOrElse = (maybe, defaultValue) => isSome(maybe) ? maybe.value : defaultValue;
|
|
29
|
+
var unwrapOr = getOrElse;
|
|
30
|
+
var unwrap = (maybe) => {
|
|
31
|
+
if (isNone(maybe)) {
|
|
32
|
+
throw new Error("Called unwrap on None");
|
|
33
|
+
}
|
|
34
|
+
return maybe.value;
|
|
35
|
+
};
|
|
36
|
+
var compact = (maybes) => {
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const m of maybes) {
|
|
39
|
+
if (isSome(m)) {
|
|
40
|
+
out.push(m.value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
};
|
|
45
|
+
var compactMap = (items, fn) => compact(items.map(fn));
|
|
46
|
+
var optional = (value, fn) => flatMapImpl(fromNullable(value), fn);
|
|
47
|
+
|
|
48
|
+
export { andThen, compact, compactMap, flatMap, fromNullable, getOrElse, isNone, isSome, map, match, matchMaybe, none, of, optional, some, unwrap, unwrapOr };
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/maybe.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Maybe, None, Some } from \"./types.js\";\n\nexport type { Maybe, None, Some } from \"./types.js\";\n\n/**\n * Lifts a value into the `Some` branch.\n *\n * @example\n * ```ts\n * const m = some(1); // Maybe<number>\n * ```\n */\nexport const some = <T>(value: T): Maybe<T> => ({ _tag: \"Some\", value });\n\n/** Fantasy Land `of` — alias of {@link some}. */\nexport const of = some;\n\n/**\n * The `None` value — represents expected absence. Not a failure.\n *\n * @example\n * ```ts\n * const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();\n * ```\n */\nexport const none = <T = never>(): Maybe<T> => ({ _tag: \"None\" });\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `Some`.\n *\n * @example\n * ```ts\n * if (isSome(m)) {\n * console.log(m.value); // narrowed to Some branch\n * }\n * ```\n */\nexport const isSome = <T>(maybe: Maybe<T>): maybe is Some<T> => maybe._tag === \"Some\";\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `None`.\n */\nexport const isNone = <T>(maybe: Maybe<T>): maybe is None => maybe._tag === \"None\";\n\n/**\n * Lift a nullable value into `Maybe<T>` — `null` and `undefined` become\n * `None`; anything else becomes `Some`.\n *\n * @example\n * ```ts\n * fromNullable(map.get(key)); // Maybe<V>\n * fromNullable(null); // None\n * ```\n */\nexport const fromNullable = <T>(value: T | null | undefined): Maybe<T> =>\n value == null ? none<T>() : some(value);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each accepts either shape:\n// data-first: `map(maybe, fn)`\n// curried: `map(fn)(maybe)`\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U> =>\n isSome(maybe) ? some(fn(maybe.value)) : none<U>();\n\n/**\n * Transform the `Some` value, passing `None` through unchanged. Dual-form.\n *\n * @example\n * ```ts\n * map(some(2), (n) => n * 3); // Some 6 — data-first\n * pipe(fromNullable(input), map((s) => s.trim())); // curried\n * ```\n */\nexport function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;\nexport function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;\nexport function map(\n ...args: [Maybe<unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => mapImpl(maybe, fn);\n}\n\nconst flatMapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> =>\n isSome(maybe) ? fn(maybe.value) : none<U>();\n\n/**\n * Canonical bind for `Maybe`. Chains a `Maybe`-returning step;\n * short-circuits on `None`. Dual-form.\n *\n * @example\n * ```ts\n * flatMap(fromNullable(user), (u) =>\n * u.active ? some(u) : none(),\n * );\n * ```\n */\nexport function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;\nexport function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;\nexport function flatMap(\n ...args:\n | [Maybe<unknown>, (value: unknown) => Maybe<unknown>]\n | [(value: unknown) => Maybe<unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => flatMapImpl(maybe, fn);\n}\n\n/** Alias for {@link flatMap} — same dual-form shape. */\nexport const andThen = flatMap;\n\nconst matchImpl = <T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U =>\n isSome(maybe) ? onSome(maybe.value) : onNone();\n\n/**\n * Terminal collapse — fold both branches into a single value. Positional,\n * dual-form. Same shape as `Result.match` for sibling consistency.\n *\n * For files that also import `match` from `ts-pattern`, the alias\n * {@link matchMaybe} is identical.\n *\n * @example\n * ```ts\n * const greeting = match(\n * fromNullable(user),\n * (u) => `hello ${u.name}`,\n * () => \"hello guest\",\n * );\n * ```\n */\nexport function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;\nexport function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;\nexport function match(\n ...args:\n | [Maybe<unknown>, (value: unknown) => unknown, () => unknown]\n | [(value: unknown) => unknown, () => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onSome, onNone] = args;\n return (maybe: Maybe<unknown>) => matchImpl(maybe, onSome, onNone);\n}\n\n/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */\nexport const matchMaybe = match;\n\n/**\n * Returns the `Some` value, or `defaultValue` when the value is `None`.\n *\n * @example\n * ```ts\n * getOrElse(fromNullable(user), { name: \"guest\" });\n * ```\n */\nexport const getOrElse = <T>(maybe: Maybe<T>, defaultValue: T): T =>\n isSome(maybe) ? maybe.value : defaultValue;\n\n/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */\nexport const unwrapOr = getOrElse;\n\n/**\n * Unwraps the `Some` value or throws if the value is `None`. Use in tests\n * or at the boundary of an assertion; prefer `match` / `getOrElse` in\n * production code paths.\n *\n * @example\n * ```ts\n * expect(unwrap(parseUser(raw))).toEqual(expected);\n * ```\n */\nexport const unwrap = <T>(maybe: Maybe<T>): T => {\n if (isNone(maybe)) {\n throw new Error(\"Called unwrap on None\");\n }\n return maybe.value;\n};\n\n/**\n * Drop every `None` and collect the remaining `Some` values into an array.\n *\n * @example\n * ```ts\n * compact([some(1), none(), some(2)]); // [1, 2]\n * ```\n */\nexport const compact = <T>(maybes: readonly Maybe<T>[]): T[] => {\n const out: T[] = [];\n for (const m of maybes) {\n if (isSome(m)) {\n out.push(m.value);\n }\n }\n return out;\n};\n\n/**\n * `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,\n * collect the rest.\n *\n * @example\n * ```ts\n * compactMap(rawRows, (row) =>\n * row.deletedAt ? none() : some(row.id),\n * );\n * // string[]\n * ```\n */\nexport const compactMap = <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>): U[] =>\n compact(items.map(fn));\n\n/**\n * `flatMap(fromNullable(value), fn)` — lift a nullable then bind through\n * a `Maybe`-returning step. Convenient for chaining lookups.\n *\n * @example\n * ```ts\n * optional(rawId, (id) => fromNullable(cache.get(id)));\n * ```\n */\nexport const optional = <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>): Maybe<U> =>\n flatMapImpl(fromNullable(value), fn);\n"]}
|
package/dist/interop.cjs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var result = require('@onrails/result');
|
|
4
|
+
|
|
5
|
+
// src/interop.ts
|
|
6
|
+
|
|
7
|
+
// src/maybe.ts
|
|
8
|
+
var some = (value) => ({ _tag: "Some", value });
|
|
9
|
+
var none = () => ({ _tag: "None" });
|
|
10
|
+
var isSome = (maybe) => maybe._tag === "Some";
|
|
11
|
+
var fromNullable = (value) => value == null ? none() : some(value);
|
|
12
|
+
|
|
13
|
+
// src/interop.ts
|
|
14
|
+
var noneError = () => ({ _tag: "None" });
|
|
15
|
+
var toResult = (maybe, onNone) => isSome(maybe) ? result.ok(maybe.value) : result.err(onNone());
|
|
16
|
+
var fromResult = (result$1) => result.isOk(result$1) ? some(result$1.value) : none();
|
|
17
|
+
var nullableToResult = (value, onNone) => toResult(fromNullable(value), onNone);
|
|
18
|
+
var isNoneError = (result$1) => !result.isOk(result$1) && isNoneErrorValue(result$1.error);
|
|
19
|
+
var isNoneErrorValue = (error) => typeof error === "object" && error !== null && "_tag" in error && error._tag === "None";
|
|
20
|
+
|
|
21
|
+
exports.fromResult = fromResult;
|
|
22
|
+
exports.isNoneError = isNoneError;
|
|
23
|
+
exports.noneError = noneError;
|
|
24
|
+
exports.nullableToResult = nullableToResult;
|
|
25
|
+
exports.toResult = toResult;
|
|
26
|
+
//# sourceMappingURL=interop.cjs.map
|
|
27
|
+
//# sourceMappingURL=interop.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/maybe.ts","../src/interop.ts"],"names":["ok","err","result","isOk"],"mappings":";;;;;;;AAYO,IAAM,OAAO,CAAI,KAAA,MAAwB,EAAE,IAAA,EAAM,QAAQ,KAAA,EAAM,CAAA;AAa/D,IAAM,IAAA,GAAO,OAA4B,EAAE,IAAA,EAAM,MAAA,EAAO,CAAA;AAYxD,IAAM,MAAA,GAAS,CAAI,KAAA,KAAsC,KAAA,CAAM,IAAA,KAAS,MAAA;AAiBxE,IAAM,YAAA,GAAe,CAAI,KAAA,KAC9B,KAAA,IAAS,OAAO,IAAA,EAAQ,GAAI,KAAK,KAAK,CAAA;;;ACjDjC,IAAM,SAAA,GAAY,OAAkB,EAAE,IAAA,EAAM,MAAA,EAAO;AAGnD,IAAM,QAAA,GAAW,CAAO,KAAA,EAAiB,MAAA,KAC9C,MAAA,CAAO,KAAK,CAAA,GAAIA,SAAA,CAAG,KAAA,CAAM,KAAK,CAAA,GAAIC,UAAA,CAAI,QAAQ;AAGzC,IAAM,UAAA,GAAa,CAAOC,QAAA,KAC/BC,WAAA,CAAKD,QAAM,IAAI,IAAA,CAAKA,QAAA,CAAO,KAAK,CAAA,GAAI,IAAA;AAG/B,IAAM,gBAAA,GAAmB,CAC9B,KAAA,EACA,MAAA,KACiB,SAAS,YAAA,CAAa,KAAK,GAAG,MAAM;AAGhD,IAAM,WAAA,GAAc,CAACA,QAAA,KAC1B,CAACC,YAAKD,QAAM,CAAA,IAAK,gBAAA,CAAiBA,QAAA,CAAO,KAAK;AAEhD,IAAM,gBAAA,GAAmB,CAAC,KAAA,KACxB,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,MAAA,IAAU,KAAA,IACT,KAAA,CAA4B,IAAA,KAAS,MAAA","file":"interop.cjs","sourcesContent":["import type { Maybe, None, Some } from \"./types.js\";\n\nexport type { Maybe, None, Some } from \"./types.js\";\n\n/**\n * Lifts a value into the `Some` branch.\n *\n * @example\n * ```ts\n * const m = some(1); // Maybe<number>\n * ```\n */\nexport const some = <T>(value: T): Maybe<T> => ({ _tag: \"Some\", value });\n\n/** Fantasy Land `of` — alias of {@link some}. */\nexport const of = some;\n\n/**\n * The `None` value — represents expected absence. Not a failure.\n *\n * @example\n * ```ts\n * const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();\n * ```\n */\nexport const none = <T = never>(): Maybe<T> => ({ _tag: \"None\" });\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `Some`.\n *\n * @example\n * ```ts\n * if (isSome(m)) {\n * console.log(m.value); // narrowed to Some branch\n * }\n * ```\n */\nexport const isSome = <T>(maybe: Maybe<T>): maybe is Some<T> => maybe._tag === \"Some\";\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `None`.\n */\nexport const isNone = <T>(maybe: Maybe<T>): maybe is None => maybe._tag === \"None\";\n\n/**\n * Lift a nullable value into `Maybe<T>` — `null` and `undefined` become\n * `None`; anything else becomes `Some`.\n *\n * @example\n * ```ts\n * fromNullable(map.get(key)); // Maybe<V>\n * fromNullable(null); // None\n * ```\n */\nexport const fromNullable = <T>(value: T | null | undefined): Maybe<T> =>\n value == null ? none<T>() : some(value);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each accepts either shape:\n// data-first: `map(maybe, fn)`\n// curried: `map(fn)(maybe)`\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U> =>\n isSome(maybe) ? some(fn(maybe.value)) : none<U>();\n\n/**\n * Transform the `Some` value, passing `None` through unchanged. Dual-form.\n *\n * @example\n * ```ts\n * map(some(2), (n) => n * 3); // Some 6 — data-first\n * pipe(fromNullable(input), map((s) => s.trim())); // curried\n * ```\n */\nexport function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;\nexport function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;\nexport function map(\n ...args: [Maybe<unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => mapImpl(maybe, fn);\n}\n\nconst flatMapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> =>\n isSome(maybe) ? fn(maybe.value) : none<U>();\n\n/**\n * Canonical bind for `Maybe`. Chains a `Maybe`-returning step;\n * short-circuits on `None`. Dual-form.\n *\n * @example\n * ```ts\n * flatMap(fromNullable(user), (u) =>\n * u.active ? some(u) : none(),\n * );\n * ```\n */\nexport function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;\nexport function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;\nexport function flatMap(\n ...args:\n | [Maybe<unknown>, (value: unknown) => Maybe<unknown>]\n | [(value: unknown) => Maybe<unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => flatMapImpl(maybe, fn);\n}\n\n/** Alias for {@link flatMap} — same dual-form shape. */\nexport const andThen = flatMap;\n\nconst matchImpl = <T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U =>\n isSome(maybe) ? onSome(maybe.value) : onNone();\n\n/**\n * Terminal collapse — fold both branches into a single value. Positional,\n * dual-form. Same shape as `Result.match` for sibling consistency.\n *\n * For files that also import `match` from `ts-pattern`, the alias\n * {@link matchMaybe} is identical.\n *\n * @example\n * ```ts\n * const greeting = match(\n * fromNullable(user),\n * (u) => `hello ${u.name}`,\n * () => \"hello guest\",\n * );\n * ```\n */\nexport function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;\nexport function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;\nexport function match(\n ...args:\n | [Maybe<unknown>, (value: unknown) => unknown, () => unknown]\n | [(value: unknown) => unknown, () => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onSome, onNone] = args;\n return (maybe: Maybe<unknown>) => matchImpl(maybe, onSome, onNone);\n}\n\n/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */\nexport const matchMaybe = match;\n\n/**\n * Returns the `Some` value, or `defaultValue` when the value is `None`.\n *\n * @example\n * ```ts\n * getOrElse(fromNullable(user), { name: \"guest\" });\n * ```\n */\nexport const getOrElse = <T>(maybe: Maybe<T>, defaultValue: T): T =>\n isSome(maybe) ? maybe.value : defaultValue;\n\n/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */\nexport const unwrapOr = getOrElse;\n\n/**\n * Unwraps the `Some` value or throws if the value is `None`. Use in tests\n * or at the boundary of an assertion; prefer `match` / `getOrElse` in\n * production code paths.\n *\n * @example\n * ```ts\n * expect(unwrap(parseUser(raw))).toEqual(expected);\n * ```\n */\nexport const unwrap = <T>(maybe: Maybe<T>): T => {\n if (isNone(maybe)) {\n throw new Error(\"Called unwrap on None\");\n }\n return maybe.value;\n};\n\n/**\n * Drop every `None` and collect the remaining `Some` values into an array.\n *\n * @example\n * ```ts\n * compact([some(1), none(), some(2)]); // [1, 2]\n * ```\n */\nexport const compact = <T>(maybes: readonly Maybe<T>[]): T[] => {\n const out: T[] = [];\n for (const m of maybes) {\n if (isSome(m)) {\n out.push(m.value);\n }\n }\n return out;\n};\n\n/**\n * `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,\n * collect the rest.\n *\n * @example\n * ```ts\n * compactMap(rawRows, (row) =>\n * row.deletedAt ? none() : some(row.id),\n * );\n * // string[]\n * ```\n */\nexport const compactMap = <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>): U[] =>\n compact(items.map(fn));\n\n/**\n * `flatMap(fromNullable(value), fn)` — lift a nullable then bind through\n * a `Maybe`-returning step. Convenient for chaining lookups.\n *\n * @example\n * ```ts\n * optional(rawId, (id) => fromNullable(cache.get(id)));\n * ```\n */\nexport const optional = <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>): Maybe<U> =>\n flatMapImpl(fromNullable(value), fn);\n","import { err, isOk, ok, type Result } from \"@onrails/result\";\nimport { fromNullable, isSome, type Maybe, none, some } from \"./maybe.js\";\n\n/** Sentinel error tag — `Maybe<T>` bridges to `Result<T, NoneError>`. */\nexport type NoneError = { readonly _tag: \"None\" };\n\nexport const noneError = (): NoneError => ({ _tag: \"None\" });\n\n/** Lift absence into a domain error. */\nexport const toResult = <T, E>(maybe: Maybe<T>, onNone: () => E): Result<T, E> =>\n isSome(maybe) ? ok(maybe.value) : err(onNone());\n\n/** Ok values become Some; any Err becomes None (errors are not absence). */\nexport const fromResult = <T, E>(result: Result<T, E>): Maybe<T> =>\n isOk(result) ? some(result.value) : none<T>();\n\n/** Nullable edge → Maybe; explicit None error at the boundary. */\nexport const nullableToResult = <T, E>(\n value: T | null | undefined,\n onNone: () => E,\n): Result<T, E> => toResult(fromNullable(value), onNone);\n\n/** True when a Result failed specifically with {@link NoneError}. */\nexport const isNoneError = (result: Result<unknown, unknown>): result is Result<never, NoneError> =>\n !isOk(result) && isNoneErrorValue(result.error);\n\nconst isNoneErrorValue = (error: unknown): error is NoneError =>\n typeof error === \"object\" &&\n error !== null &&\n \"_tag\" in error &&\n (error as { _tag: unknown })._tag === \"None\";\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Result } from '@onrails/result';
|
|
2
|
+
import { M as Maybe } from './types-vkiMAo4w.cjs';
|
|
3
|
+
|
|
4
|
+
/** Sentinel error tag — `Maybe<T>` bridges to `Result<T, NoneError>`. */
|
|
5
|
+
type NoneError = {
|
|
6
|
+
readonly _tag: "None";
|
|
7
|
+
};
|
|
8
|
+
declare const noneError: () => NoneError;
|
|
9
|
+
/** Lift absence into a domain error. */
|
|
10
|
+
declare const toResult: <T, E>(maybe: Maybe<T>, onNone: () => E) => Result<T, E>;
|
|
11
|
+
/** Ok values become Some; any Err becomes None (errors are not absence). */
|
|
12
|
+
declare const fromResult: <T, E>(result: Result<T, E>) => Maybe<T>;
|
|
13
|
+
/** Nullable edge → Maybe; explicit None error at the boundary. */
|
|
14
|
+
declare const nullableToResult: <T, E>(value: T | null | undefined, onNone: () => E) => Result<T, E>;
|
|
15
|
+
/** True when a Result failed specifically with {@link NoneError}. */
|
|
16
|
+
declare const isNoneError: (result: Result<unknown, unknown>) => result is Result<never, NoneError>;
|
|
17
|
+
|
|
18
|
+
export { type NoneError, fromResult, isNoneError, noneError, nullableToResult, toResult };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Result } from '@onrails/result';
|
|
2
|
+
import { M as Maybe } from './types-vkiMAo4w.js';
|
|
3
|
+
|
|
4
|
+
/** Sentinel error tag — `Maybe<T>` bridges to `Result<T, NoneError>`. */
|
|
5
|
+
type NoneError = {
|
|
6
|
+
readonly _tag: "None";
|
|
7
|
+
};
|
|
8
|
+
declare const noneError: () => NoneError;
|
|
9
|
+
/** Lift absence into a domain error. */
|
|
10
|
+
declare const toResult: <T, E>(maybe: Maybe<T>, onNone: () => E) => Result<T, E>;
|
|
11
|
+
/** Ok values become Some; any Err becomes None (errors are not absence). */
|
|
12
|
+
declare const fromResult: <T, E>(result: Result<T, E>) => Maybe<T>;
|
|
13
|
+
/** Nullable edge → Maybe; explicit None error at the boundary. */
|
|
14
|
+
declare const nullableToResult: <T, E>(value: T | null | undefined, onNone: () => E) => Result<T, E>;
|
|
15
|
+
/** True when a Result failed specifically with {@link NoneError}. */
|
|
16
|
+
declare const isNoneError: (result: Result<unknown, unknown>) => result is Result<never, NoneError>;
|
|
17
|
+
|
|
18
|
+
export { type NoneError, fromResult, isNoneError, noneError, nullableToResult, toResult };
|
package/dist/interop.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ok, err, isOk } from '@onrails/result';
|
|
2
|
+
|
|
3
|
+
// src/interop.ts
|
|
4
|
+
|
|
5
|
+
// src/maybe.ts
|
|
6
|
+
var some = (value) => ({ _tag: "Some", value });
|
|
7
|
+
var none = () => ({ _tag: "None" });
|
|
8
|
+
var isSome = (maybe) => maybe._tag === "Some";
|
|
9
|
+
var fromNullable = (value) => value == null ? none() : some(value);
|
|
10
|
+
|
|
11
|
+
// src/interop.ts
|
|
12
|
+
var noneError = () => ({ _tag: "None" });
|
|
13
|
+
var toResult = (maybe, onNone) => isSome(maybe) ? ok(maybe.value) : err(onNone());
|
|
14
|
+
var fromResult = (result) => isOk(result) ? some(result.value) : none();
|
|
15
|
+
var nullableToResult = (value, onNone) => toResult(fromNullable(value), onNone);
|
|
16
|
+
var isNoneError = (result) => !isOk(result) && isNoneErrorValue(result.error);
|
|
17
|
+
var isNoneErrorValue = (error) => typeof error === "object" && error !== null && "_tag" in error && error._tag === "None";
|
|
18
|
+
|
|
19
|
+
export { fromResult, isNoneError, noneError, nullableToResult, toResult };
|
|
20
|
+
//# sourceMappingURL=interop.js.map
|
|
21
|
+
//# sourceMappingURL=interop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/maybe.ts","../src/interop.ts"],"names":[],"mappings":";;;;;AAYO,IAAM,OAAO,CAAI,KAAA,MAAwB,EAAE,IAAA,EAAM,QAAQ,KAAA,EAAM,CAAA;AAa/D,IAAM,IAAA,GAAO,OAA4B,EAAE,IAAA,EAAM,MAAA,EAAO,CAAA;AAYxD,IAAM,MAAA,GAAS,CAAI,KAAA,KAAsC,KAAA,CAAM,IAAA,KAAS,MAAA;AAiBxE,IAAM,YAAA,GAAe,CAAI,KAAA,KAC9B,KAAA,IAAS,OAAO,IAAA,EAAQ,GAAI,KAAK,KAAK,CAAA;;;ACjDjC,IAAM,SAAA,GAAY,OAAkB,EAAE,IAAA,EAAM,MAAA,EAAO;AAGnD,IAAM,QAAA,GAAW,CAAO,KAAA,EAAiB,MAAA,KAC9C,MAAA,CAAO,KAAK,CAAA,GAAI,EAAA,CAAG,KAAA,CAAM,KAAK,CAAA,GAAI,GAAA,CAAI,QAAQ;AAGzC,IAAM,UAAA,GAAa,CAAO,MAAA,KAC/B,IAAA,CAAK,MAAM,IAAI,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA,GAAI,IAAA;AAG/B,IAAM,gBAAA,GAAmB,CAC9B,KAAA,EACA,MAAA,KACiB,SAAS,YAAA,CAAa,KAAK,GAAG,MAAM;AAGhD,IAAM,WAAA,GAAc,CAAC,MAAA,KAC1B,CAAC,KAAK,MAAM,CAAA,IAAK,gBAAA,CAAiB,MAAA,CAAO,KAAK;AAEhD,IAAM,gBAAA,GAAmB,CAAC,KAAA,KACxB,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACV,MAAA,IAAU,KAAA,IACT,KAAA,CAA4B,IAAA,KAAS,MAAA","file":"interop.js","sourcesContent":["import type { Maybe, None, Some } from \"./types.js\";\n\nexport type { Maybe, None, Some } from \"./types.js\";\n\n/**\n * Lifts a value into the `Some` branch.\n *\n * @example\n * ```ts\n * const m = some(1); // Maybe<number>\n * ```\n */\nexport const some = <T>(value: T): Maybe<T> => ({ _tag: \"Some\", value });\n\n/** Fantasy Land `of` — alias of {@link some}. */\nexport const of = some;\n\n/**\n * The `None` value — represents expected absence. Not a failure.\n *\n * @example\n * ```ts\n * const cached: Maybe<User> = cache.has(id) ? some(cache.get(id)) : none();\n * ```\n */\nexport const none = <T = never>(): Maybe<T> => ({ _tag: \"None\" });\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `Some`.\n *\n * @example\n * ```ts\n * if (isSome(m)) {\n * console.log(m.value); // narrowed to Some branch\n * }\n * ```\n */\nexport const isSome = <T>(maybe: Maybe<T>): maybe is Some<T> => maybe._tag === \"Some\";\n\n/**\n * Type-narrowing predicate: returns `true` when the value is `None`.\n */\nexport const isNone = <T>(maybe: Maybe<T>): maybe is None => maybe._tag === \"None\";\n\n/**\n * Lift a nullable value into `Maybe<T>` — `null` and `undefined` become\n * `None`; anything else becomes `Some`.\n *\n * @example\n * ```ts\n * fromNullable(map.get(key)); // Maybe<V>\n * fromNullable(null); // None\n * ```\n */\nexport const fromNullable = <T>(value: T | null | undefined): Maybe<T> =>\n value == null ? none<T>() : some(value);\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each accepts either shape:\n// data-first: `map(maybe, fn)`\n// curried: `map(fn)(maybe)`\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U> =>\n isSome(maybe) ? some(fn(maybe.value)) : none<U>();\n\n/**\n * Transform the `Some` value, passing `None` through unchanged. Dual-form.\n *\n * @example\n * ```ts\n * map(some(2), (n) => n * 3); // Some 6 — data-first\n * pipe(fromNullable(input), map((s) => s.trim())); // curried\n * ```\n */\nexport function map<T, U>(maybe: Maybe<T>, fn: (value: T) => U): Maybe<U>;\nexport function map<T, U>(fn: (value: T) => U): (maybe: Maybe<T>) => Maybe<U>;\nexport function map(\n ...args: [Maybe<unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => mapImpl(maybe, fn);\n}\n\nconst flatMapImpl = <T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U> =>\n isSome(maybe) ? fn(maybe.value) : none<U>();\n\n/**\n * Canonical bind for `Maybe`. Chains a `Maybe`-returning step;\n * short-circuits on `None`. Dual-form.\n *\n * @example\n * ```ts\n * flatMap(fromNullable(user), (u) =>\n * u.active ? some(u) : none(),\n * );\n * ```\n */\nexport function flatMap<T, U>(maybe: Maybe<T>, fn: (value: T) => Maybe<U>): Maybe<U>;\nexport function flatMap<T, U>(fn: (value: T) => Maybe<U>): (maybe: Maybe<T>) => Maybe<U>;\nexport function flatMap(\n ...args:\n | [Maybe<unknown>, (value: unknown) => Maybe<unknown>]\n | [(value: unknown) => Maybe<unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (maybe: Maybe<unknown>) => flatMapImpl(maybe, fn);\n}\n\n/** Alias for {@link flatMap} — same dual-form shape. */\nexport const andThen = flatMap;\n\nconst matchImpl = <T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U =>\n isSome(maybe) ? onSome(maybe.value) : onNone();\n\n/**\n * Terminal collapse — fold both branches into a single value. Positional,\n * dual-form. Same shape as `Result.match` for sibling consistency.\n *\n * For files that also import `match` from `ts-pattern`, the alias\n * {@link matchMaybe} is identical.\n *\n * @example\n * ```ts\n * const greeting = match(\n * fromNullable(user),\n * (u) => `hello ${u.name}`,\n * () => \"hello guest\",\n * );\n * ```\n */\nexport function match<T, U>(maybe: Maybe<T>, onSome: (value: T) => U, onNone: () => U): U;\nexport function match<T, U>(onSome: (value: T) => U, onNone: () => U): (maybe: Maybe<T>) => U;\nexport function match(\n ...args:\n | [Maybe<unknown>, (value: unknown) => unknown, () => unknown]\n | [(value: unknown) => unknown, () => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onSome, onNone] = args;\n return (maybe: Maybe<unknown>) => matchImpl(maybe, onSome, onNone);\n}\n\n/** Collision-free alias — mirrors `matchResult` on `@onrails/result`. */\nexport const matchMaybe = match;\n\n/**\n * Returns the `Some` value, or `defaultValue` when the value is `None`.\n *\n * @example\n * ```ts\n * getOrElse(fromNullable(user), { name: \"guest\" });\n * ```\n */\nexport const getOrElse = <T>(maybe: Maybe<T>, defaultValue: T): T =>\n isSome(maybe) ? maybe.value : defaultValue;\n\n/** Alias — mirrors `@onrails/result` `unwrapOr`. Same as {@link getOrElse}. */\nexport const unwrapOr = getOrElse;\n\n/**\n * Unwraps the `Some` value or throws if the value is `None`. Use in tests\n * or at the boundary of an assertion; prefer `match` / `getOrElse` in\n * production code paths.\n *\n * @example\n * ```ts\n * expect(unwrap(parseUser(raw))).toEqual(expected);\n * ```\n */\nexport const unwrap = <T>(maybe: Maybe<T>): T => {\n if (isNone(maybe)) {\n throw new Error(\"Called unwrap on None\");\n }\n return maybe.value;\n};\n\n/**\n * Drop every `None` and collect the remaining `Some` values into an array.\n *\n * @example\n * ```ts\n * compact([some(1), none(), some(2)]); // [1, 2]\n * ```\n */\nexport const compact = <T>(maybes: readonly Maybe<T>[]): T[] => {\n const out: T[] = [];\n for (const m of maybes) {\n if (isSome(m)) {\n out.push(m.value);\n }\n }\n return out;\n};\n\n/**\n * `compact(items.map(fn))` — map each item to `Maybe`, drop `None`,\n * collect the rest.\n *\n * @example\n * ```ts\n * compactMap(rawRows, (row) =>\n * row.deletedAt ? none() : some(row.id),\n * );\n * // string[]\n * ```\n */\nexport const compactMap = <T, U>(items: readonly T[], fn: (item: T) => Maybe<U>): U[] =>\n compact(items.map(fn));\n\n/**\n * `flatMap(fromNullable(value), fn)` — lift a nullable then bind through\n * a `Maybe`-returning step. Convenient for chaining lookups.\n *\n * @example\n * ```ts\n * optional(rawId, (id) => fromNullable(cache.get(id)));\n * ```\n */\nexport const optional = <T, U>(value: T | null | undefined, fn: (value: T) => Maybe<U>): Maybe<U> =>\n flatMapImpl(fromNullable(value), fn);\n","import { err, isOk, ok, type Result } from \"@onrails/result\";\nimport { fromNullable, isSome, type Maybe, none, some } from \"./maybe.js\";\n\n/** Sentinel error tag — `Maybe<T>` bridges to `Result<T, NoneError>`. */\nexport type NoneError = { readonly _tag: \"None\" };\n\nexport const noneError = (): NoneError => ({ _tag: \"None\" });\n\n/** Lift absence into a domain error. */\nexport const toResult = <T, E>(maybe: Maybe<T>, onNone: () => E): Result<T, E> =>\n isSome(maybe) ? ok(maybe.value) : err(onNone());\n\n/** Ok values become Some; any Err becomes None (errors are not absence). */\nexport const fromResult = <T, E>(result: Result<T, E>): Maybe<T> =>\n isOk(result) ? some(result.value) : none<T>();\n\n/** Nullable edge → Maybe; explicit None error at the boundary. */\nexport const nullableToResult = <T, E>(\n value: T | null | undefined,\n onNone: () => E,\n): Result<T, E> => toResult(fromNullable(value), onNone);\n\n/** True when a Result failed specifically with {@link NoneError}. */\nexport const isNoneError = (result: Result<unknown, unknown>): result is Result<never, NoneError> =>\n !isOk(result) && isNoneErrorValue(result.error);\n\nconst isNoneErrorValue = (error: unknown): error is NoneError =>\n typeof error === \"object\" &&\n error !== null &&\n \"_tag\" in error &&\n (error as { _tag: unknown })._tag === \"None\";\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Tagged optional value — absence is not failure. */
|
|
2
|
+
type Maybe<T> = {
|
|
3
|
+
readonly _tag: "Some";
|
|
4
|
+
readonly value: T;
|
|
5
|
+
} | {
|
|
6
|
+
readonly _tag: "None";
|
|
7
|
+
};
|
|
8
|
+
type Some<T> = Extract<Maybe<T>, {
|
|
9
|
+
_tag: "Some";
|
|
10
|
+
}>;
|
|
11
|
+
type None = Extract<Maybe<never>, {
|
|
12
|
+
_tag: "None";
|
|
13
|
+
}>;
|
|
14
|
+
|
|
15
|
+
export type { Maybe as M, None as N, Some as S };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Tagged optional value — absence is not failure. */
|
|
2
|
+
type Maybe<T> = {
|
|
3
|
+
readonly _tag: "Some";
|
|
4
|
+
readonly value: T;
|
|
5
|
+
} | {
|
|
6
|
+
readonly _tag: "None";
|
|
7
|
+
};
|
|
8
|
+
type Some<T> = Extract<Maybe<T>, {
|
|
9
|
+
_tag: "Some";
|
|
10
|
+
}>;
|
|
11
|
+
type None = Extract<Maybe<never>, {
|
|
12
|
+
_tag: "None";
|
|
13
|
+
}>;
|
|
14
|
+
|
|
15
|
+
export type { Maybe as M, None as N, Some as S };
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onrails/maybe",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tagged Maybe for expected absence — pure tagged unions, Result interop, tree-shakeable",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Alan R. Soares",
|
|
7
|
+
"homepage": "https://alanrsoares.github.io/onrails/modules/_onrails_maybe.html",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/alanrsoares/onrails.git",
|
|
11
|
+
"directory": "packages/maybe"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/alanrsoares/onrails/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"maybe",
|
|
18
|
+
"optional",
|
|
19
|
+
"tagged-union",
|
|
20
|
+
"discriminated-union",
|
|
21
|
+
"typescript",
|
|
22
|
+
"bun",
|
|
23
|
+
"functional"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18",
|
|
27
|
+
"bun": ">=1.0"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"provenance": true
|
|
32
|
+
},
|
|
33
|
+
"type": "module",
|
|
34
|
+
"sideEffects": false,
|
|
35
|
+
"main": "./dist/index.cjs",
|
|
36
|
+
"module": "./dist/index.js",
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"DESIGN.md",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"types": "./dist/index.d.ts",
|
|
47
|
+
"import": "./dist/index.js",
|
|
48
|
+
"require": "./dist/index.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./fluent": {
|
|
51
|
+
"types": "./dist/fluent.d.ts",
|
|
52
|
+
"import": "./dist/fluent.js",
|
|
53
|
+
"require": "./dist/fluent.cjs"
|
|
54
|
+
},
|
|
55
|
+
"./interop": {
|
|
56
|
+
"types": "./dist/interop.d.ts",
|
|
57
|
+
"import": "./dist/interop.js",
|
|
58
|
+
"require": "./dist/interop.cjs"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@onrails/result": "0.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"@onrails/result": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"typecheck": "tsc --noEmit",
|
|
71
|
+
"test": "bun test",
|
|
72
|
+
"check": "bun typecheck && bun test",
|
|
73
|
+
"build": "tsup",
|
|
74
|
+
"clean": "rm -rf dist",
|
|
75
|
+
"prepublishOnly": "bun run build"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@onrails/result": "0.0.0",
|
|
79
|
+
"ts-expect": "^1.3.0",
|
|
80
|
+
"tsup": "^8.5.1"
|
|
81
|
+
}
|
|
82
|
+
}
|