selaws 0.0.0-stage → 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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +355 -2
  3. package/dist/evidence.d.ts +45 -0
  4. package/dist/evidence.d.ts.map +1 -0
  5. package/dist/evidence.js +22 -0
  6. package/dist/evidence.js.map +1 -0
  7. package/dist/identity.d.ts +52 -0
  8. package/dist/identity.d.ts.map +1 -0
  9. package/dist/identity.js +22 -0
  10. package/dist/identity.js.map +1 -0
  11. package/dist/index.d.ts +35 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +18 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/internal/callback.d.ts +13 -0
  16. package/dist/internal/callback.d.ts.map +1 -0
  17. package/dist/internal/callback.js +2 -0
  18. package/dist/internal/callback.js.map +1 -0
  19. package/dist/internal/promise-like.d.ts +8 -0
  20. package/dist/internal/promise-like.d.ts.map +1 -0
  21. package/dist/internal/promise-like.js +12 -0
  22. package/dist/internal/promise-like.js.map +1 -0
  23. package/dist/internal/scalar.d.ts +18 -0
  24. package/dist/internal/scalar.d.ts.map +1 -0
  25. package/dist/internal/scalar.js +6 -0
  26. package/dist/internal/scalar.js.map +1 -0
  27. package/dist/option.d.ts +90 -0
  28. package/dist/option.d.ts.map +1 -0
  29. package/dist/option.js +96 -0
  30. package/dist/option.js.map +1 -0
  31. package/dist/protocol.d.ts +42 -0
  32. package/dist/protocol.d.ts.map +1 -0
  33. package/dist/protocol.js +67 -0
  34. package/dist/protocol.js.map +1 -0
  35. package/dist/result/capture.d.ts +42 -0
  36. package/dist/result/capture.d.ts.map +1 -0
  37. package/dist/result/capture.js +41 -0
  38. package/dist/result/capture.js.map +1 -0
  39. package/dist/result/core.d.ts +98 -0
  40. package/dist/result/core.d.ts.map +1 -0
  41. package/dist/result/core.js +103 -0
  42. package/dist/result/core.js.map +1 -0
  43. package/dist/result/index.d.ts +4 -0
  44. package/dist/result/index.d.ts.map +1 -0
  45. package/dist/result/index.js +4 -0
  46. package/dist/result/index.js.map +1 -0
  47. package/dist/result/throw.d.ts +4 -0
  48. package/dist/result/throw.d.ts.map +1 -0
  49. package/dist/result/throw.js +8 -0
  50. package/dist/result/throw.js.map +1 -0
  51. package/dist/validation.d.ts +126 -0
  52. package/dist/validation.d.ts.map +1 -0
  53. package/dist/validation.js +240 -0
  54. package/dist/validation.js.map +1 -0
  55. package/dist/variant.d.ts +97 -0
  56. package/dist/variant.d.ts.map +1 -0
  57. package/dist/variant.js +102 -0
  58. package/dist/variant.js.map +1 -0
  59. package/docs/API.md +543 -0
  60. package/docs/GUIDE.md +739 -0
  61. package/docs/SEMANTICS.md +319 -0
  62. package/docs/laws/evidence.md +113 -0
  63. package/docs/laws/identity.md +126 -0
  64. package/docs/laws/match.md +163 -0
  65. package/docs/laws/option.md +100 -0
  66. package/docs/laws/protocol.md +152 -0
  67. package/docs/laws/result.md +124 -0
  68. package/docs/laws/validation.md +111 -0
  69. package/docs/laws/variant.md +251 -0
  70. package/package.json +87 -3
  71. package/src/evidence.ts +120 -0
  72. package/src/identity.ts +129 -0
  73. package/src/index.ts +54 -0
  74. package/src/internal/callback.ts +54 -0
  75. package/src/internal/promise-like.ts +32 -0
  76. package/src/internal/scalar.ts +43 -0
  77. package/src/option.ts +214 -0
  78. package/src/protocol.ts +174 -0
  79. package/src/result/capture.ts +209 -0
  80. package/src/result/core.ts +229 -0
  81. package/src/result/index.ts +3 -0
  82. package/src/result/throw.ts +13 -0
  83. package/src/validation.ts +491 -0
  84. package/src/variant.ts +363 -0
@@ -0,0 +1,32 @@
1
+ type IsAny<T> = 0 extends 1 & T ? true : false;
2
+
3
+ type BroadFunction = FunctionConstructor["prototype"];
4
+
5
+ type HasCallableThen<T> = T extends object
6
+ ? "then" extends keyof T
7
+ ? Extract<T["then"], BroadFunction> extends never
8
+ ? false
9
+ : true
10
+ : false
11
+ : false;
12
+
13
+ export type NotPromiseLike<T> =
14
+ IsAny<T> extends true
15
+ ? T
16
+ : true extends (T extends unknown ? HasCallableThen<T> : never)
17
+ ? never
18
+ : T;
19
+
20
+ export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> => {
21
+ if ((typeof value !== "object" && typeof value !== "function") || value === null) {
22
+ return false;
23
+ }
24
+
25
+ return typeof (value as { readonly then?: unknown }).then === "function";
26
+ };
27
+
28
+ export const assertNotPromiseLike = (value: unknown, message: string): void => {
29
+ if (isPromiseLike(value)) {
30
+ throw new TypeError(message);
31
+ }
32
+ };
@@ -0,0 +1,43 @@
1
+ /** Immutable scalar carriers that can hold Selaws phantom meaning. */
2
+ export type Scalar = string | number | bigint | boolean | symbol;
3
+
4
+ export type Token = string | symbol;
5
+
6
+ type IsUnion<T, Whole = T> = T extends Whole
7
+ ? [Whole] extends [T]
8
+ ? false
9
+ : true
10
+ : never;
11
+
12
+ // An empty record is assignable to broad, patterned, and branded key spaces,
13
+ // but not to one concrete string or unique-symbol key.
14
+ type SingleToken<Key extends Token> =
15
+ true extends IsUnion<Key>
16
+ ? never
17
+ : Record<never, never> extends Record<Key, never>
18
+ ? never
19
+ : Key;
20
+
21
+ type SingletonSymbolMembers<Key extends symbol> = Key extends unknown
22
+ ? SingleToken<Key>
23
+ : never;
24
+
25
+ export type NarrowSymbolSet<Key extends symbol> = [Key] extends [
26
+ SingletonSymbolMembers<Key>,
27
+ ]
28
+ ? Key
29
+ : never;
30
+
31
+ export type SingleSymbol<Key extends symbol> = SingleToken<Key>;
32
+ export type SingleName<Name extends string> = SingleToken<Name>;
33
+
34
+ export const isString = (value: unknown): value is string => typeof value === "string";
35
+
36
+ export const isNumber = (value: unknown): value is number => typeof value === "number";
37
+
38
+ export const isBigint = (value: unknown): value is bigint => typeof value === "bigint";
39
+
40
+ export const isBoolean = (value: unknown): value is boolean =>
41
+ typeof value === "boolean";
42
+
43
+ export const isSymbol = (value: unknown): value is symbol => typeof value === "symbol";
package/src/option.ts ADDED
@@ -0,0 +1,214 @@
1
+ import type { CallbackResult } from "./internal/callback.js";
2
+ import { assertNotPromiseLike, type NotPromiseLike } from "./internal/promise-like.js";
3
+
4
+ /** A present Option value. */
5
+ export type Some<T> = Readonly<{
6
+ some: true;
7
+ value: T;
8
+ }>;
9
+
10
+ /** Reasonless absence. */
11
+ export type None = Readonly<{
12
+ some: false;
13
+ }>;
14
+
15
+ /** Explicit presence or absence. */
16
+ export type Option<T> = Some<T> | None;
17
+
18
+ /** Extracts the present value type from an Option union. */
19
+ export type OptionValue<O> = O extends Some<infer T> ? T : never;
20
+
21
+ type OptionValues<O extends readonly Option<unknown>[]> = {
22
+ [K in keyof O]: OptionValue<O[K]>;
23
+ };
24
+
25
+ type SynchronousReturn<T> = [T] extends [NotPromiseLike<T>] ? unknown : never;
26
+
27
+ type IsMutableArray<Value> = Value extends unknown[] ? true : false;
28
+
29
+ type StableArrayInput<Value extends readonly unknown[]> =
30
+ true extends IsMutableArray<Value> ? never : unknown;
31
+
32
+ /** Constructs a present Option value. */
33
+ export const some = <T>(value: T): Option<T> => ({
34
+ some: true,
35
+ value,
36
+ });
37
+
38
+ /** Constructs reasonless absence. */
39
+ export const none = (): Option<never> => ({
40
+ some: false,
41
+ });
42
+
43
+ /** Narrows an Option to Some. */
44
+ export const isSome = <T>(option: Option<T>): option is Some<T> => option.some;
45
+
46
+ /** Narrows an Option to None. */
47
+ export const isNone = <T>(option: Option<T>): option is None => !option.some;
48
+
49
+ /** Eliminates an Option under the shared Match law. */
50
+ export const match = <
51
+ T,
52
+ Some extends (this: void, value: NoInfer<T>) => unknown,
53
+ None extends (this: void) => unknown,
54
+ >(
55
+ option: Option<T>,
56
+ arms: Readonly<{
57
+ some: Some;
58
+ none: None;
59
+ }>,
60
+ ): CallbackResult<Some> | CallbackResult<None> => {
61
+ if (option.some) {
62
+ const selected = arms.some;
63
+ return selected(option.value) as CallbackResult<Some>;
64
+ }
65
+
66
+ const selected = arms.none;
67
+ return selected() as CallbackResult<None>;
68
+ };
69
+
70
+ /** Transforms the Some value and preserves None. */
71
+ export const map = <T, U>(option: Option<T>, transform: (value: T) => U): Option<U> =>
72
+ option.some ? some(transform(option.value)) : option;
73
+
74
+ /** Sequences an Option-producing computation when a value is present. */
75
+ export const andThen = <T, U>(
76
+ option: Option<T>,
77
+ next: (value: T) => Option<U>,
78
+ ): Option<U> => (option.some ? next(option.value) : option);
79
+
80
+ /** Lazily provides another Option when the input is None. */
81
+ export const orElse = <T, U>(
82
+ option: Option<T>,
83
+ fallback: () => Option<U>,
84
+ ): Option<T | U> => (option.some ? option : fallback());
85
+
86
+ /** Removes one explicit nested Option layer. */
87
+ export const flatten = <T>(option: Option<Option<T>>): Option<T> =>
88
+ option.some ? option.value : option;
89
+
90
+ /** Retains Some when a type-guard predicate accepts its value. */
91
+ export function filter<T, U extends T>(
92
+ option: Option<T>,
93
+ predicate: (value: T) => value is U,
94
+ ): Option<U>;
95
+
96
+ /** Retains Some when the predicate accepts its value. */
97
+ export function filter<T>(
98
+ option: Option<T>,
99
+ predicate: (value: T) => boolean,
100
+ ): Option<T>;
101
+
102
+ export function filter<T>(
103
+ option: Option<T>,
104
+ predicate: (value: T) => boolean,
105
+ ): Option<T> {
106
+ if (!option.some) {
107
+ return option;
108
+ }
109
+ return predicate(option.value) ? option : none();
110
+ }
111
+
112
+ /** Synchronously observes Some and returns the original Option. */
113
+ export const inspect = <T, R>(
114
+ option: Option<T>,
115
+ observe: ((value: T) => R) & SynchronousReturn<R>,
116
+ ): Option<T> => {
117
+ if (option.some) {
118
+ const completion = observe(option.value);
119
+ assertNotPromiseLike(
120
+ completion,
121
+ "Option.inspect() expects a synchronous observer.",
122
+ );
123
+ }
124
+ return option;
125
+ };
126
+
127
+ /** Returns the Some value or an eager fallback. */
128
+ export const unwrapOr = <T, U>(option: Option<T>, fallback: U): T | U =>
129
+ option.some ? option.value : fallback;
130
+
131
+ /** Returns the Some value or evaluates a lazy fallback. */
132
+ export const unwrapOrElse = <T, U>(option: Option<T>, fallback: () => U): T | U =>
133
+ option.some ? option.value : fallback();
134
+
135
+ /**
136
+ * Combines already-materialized Options.
137
+ *
138
+ * All Some values produce a position-preserving tuple; any None produces None.
139
+ */
140
+ export const all = <const O extends readonly Option<unknown>[]>(
141
+ options: O & StableArrayInput<O>,
142
+ ): Option<OptionValues<O>> => {
143
+ const values: unknown[] = [];
144
+ const length = options.length;
145
+
146
+ for (let index = 0; index < length; index += 1) {
147
+ const option = options[index] as Option<unknown>;
148
+ if (!option.some) {
149
+ return none();
150
+ }
151
+ values.push(option.value);
152
+ }
153
+
154
+ return some(values as OptionValues<O>);
155
+ };
156
+
157
+ /** Maps exactly undefined to None and every other value to Some. */
158
+ export const fromUndefined = <T>(value: T): Option<Exclude<T, undefined>> =>
159
+ value === undefined ? none() : some(value as Exclude<T, undefined>);
160
+
161
+ /** Maps null or undefined to None and every other value to Some. */
162
+ export const fromNullable = <T>(value: T): Option<NonNullable<T>> =>
163
+ value === undefined || value === null ? none() : some(value as NonNullable<T>);
164
+
165
+ /** Maps None to undefined and returns the Some value unchanged. */
166
+ export const toUndefined = <T>(option: Option<T>): T | undefined =>
167
+ option.some ? option.value : undefined;
168
+
169
+ /** Maps None to null and returns the Some value unchanged. */
170
+ export const toNullable = <T>(option: Option<T>): T | null =>
171
+ option.some ? option.value : null;
172
+
173
+ type OptionFacade = Readonly<{
174
+ all: typeof all;
175
+ andThen: typeof andThen;
176
+ filter: typeof filter;
177
+ flatten: typeof flatten;
178
+ fromNullable: typeof fromNullable;
179
+ fromUndefined: typeof fromUndefined;
180
+ inspect: typeof inspect;
181
+ isNone: typeof isNone;
182
+ isSome: typeof isSome;
183
+ map: typeof map;
184
+ match: typeof match;
185
+ none: typeof none;
186
+ orElse: typeof orElse;
187
+ some: typeof some;
188
+ toNullable: typeof toNullable;
189
+ toUndefined: typeof toUndefined;
190
+ unwrapOr: typeof unwrapOr;
191
+ unwrapOrElse: typeof unwrapOrElse;
192
+ }>;
193
+
194
+ /** Facade for Option construction, transformation, elimination, and collection. */
195
+ export const Option: OptionFacade = {
196
+ all,
197
+ andThen,
198
+ filter,
199
+ flatten,
200
+ fromNullable,
201
+ fromUndefined,
202
+ inspect,
203
+ isNone,
204
+ isSome,
205
+ map,
206
+ match,
207
+ none,
208
+ orElse,
209
+ some,
210
+ toNullable,
211
+ toUndefined,
212
+ unwrapOr,
213
+ unwrapOrElse,
214
+ };
@@ -0,0 +1,174 @@
1
+ import type { Scalar } from "./internal/scalar.js";
2
+
3
+ /** Scalar identifiers used as Protocol states. */
4
+ export type State = Scalar;
5
+
6
+ /** Scalar identifiers used to distinguish transitions. */
7
+ export type Label = Scalar;
8
+
9
+ /** One admissible labeled transition. */
10
+ export type Transition<
11
+ From extends State = State,
12
+ Via extends Label = Label,
13
+ To extends State = State,
14
+ > = readonly [from: From, label: Via, to: To];
15
+
16
+ type TransitionUnion<Transitions extends readonly Transition[]> = Transitions[number];
17
+
18
+ type SourceOf<One> = One extends readonly [infer From extends State, Label, State]
19
+ ? From
20
+ : never;
21
+
22
+ type LabelOf<One> = One extends readonly [State, infer Via extends Label, State]
23
+ ? Via
24
+ : never;
25
+
26
+ type TargetOf<One> = One extends readonly [State, Label, infer To extends State]
27
+ ? To
28
+ : never;
29
+
30
+ /** All state identifiers that occur as a source or target. */
31
+ export type States<Transitions extends readonly Transition[]> =
32
+ | SourceOf<TransitionUnion<Transitions>>
33
+ | TargetOf<TransitionUnion<Transitions>>;
34
+
35
+ /** All transition labels that occur in a declaration. */
36
+ export type Labels<Transitions extends readonly Transition[]> = LabelOf<
37
+ TransitionUnion<Transitions>
38
+ >;
39
+
40
+ type NextFrom<One, From extends State, Via extends Label> = One extends readonly [
41
+ infer Source extends State,
42
+ infer EdgeLabel extends Label,
43
+ infer To extends State,
44
+ ]
45
+ ? [Source & From] extends [never]
46
+ ? never
47
+ : [EdgeLabel & Via] extends [never]
48
+ ? never
49
+ : To
50
+ : never;
51
+
52
+ /** The statically admissible targets for one source state and transition label. */
53
+ export type Next<
54
+ Transitions extends readonly Transition[],
55
+ From extends States<Transitions>,
56
+ Via extends Labels<Transitions>,
57
+ > = NextFrom<TransitionUnion<Transitions>, From, Via>;
58
+
59
+ type IsMutableArray<Value> = Value extends unknown[] ? true : false;
60
+
61
+ type ClosedTransitions<Transitions extends readonly Transition[]> =
62
+ true extends IsMutableArray<Transitions>
63
+ ? never
64
+ : true extends IsMutableArray<Transitions[number]>
65
+ ? never
66
+ : Transitions;
67
+
68
+ /** Runtime membership for one immutable labeled transition relation. */
69
+ export type Protocol<Transitions extends readonly Transition[]> = Readonly<{
70
+ allows<
71
+ From extends States<Transitions>,
72
+ Via extends Labels<Transitions>,
73
+ To extends States<Transitions>,
74
+ >(from: From, label: Via, to: To): boolean;
75
+ }>;
76
+
77
+ const isScalarIdentifier = (value: unknown): value is Scalar => {
78
+ switch (typeof value) {
79
+ case "string":
80
+ case "number":
81
+ case "bigint":
82
+ case "boolean":
83
+ case "symbol":
84
+ return true;
85
+ default:
86
+ return false;
87
+ }
88
+ };
89
+
90
+ /**
91
+ * Defines one immutable admissible labeled transition relation.
92
+ *
93
+ * The runtime relation snapshots the supplied triples and uses SameValueZero
94
+ * equality through Map and Set.
95
+ */
96
+ export const define = <const Transitions extends readonly Transition[]>(
97
+ transitions: Transitions &
98
+ (ClosedTransitions<Transitions> extends never ? never : unknown),
99
+ ): Protocol<Transitions> => {
100
+ if (!Array.isArray(transitions)) {
101
+ throw new TypeError("Protocol declarations must be arrays.");
102
+ }
103
+
104
+ const relation = new Map<State, Map<Label, Set<State>>>();
105
+
106
+ const length = transitions.length;
107
+
108
+ for (let index = 0; index < length; index += 1) {
109
+ if (!Object.hasOwn(transitions, index)) {
110
+ throw new TypeError("Protocol declarations must contain own transition entries.");
111
+ }
112
+
113
+ const transition = transitions[index] as unknown;
114
+
115
+ if (
116
+ !Array.isArray(transition) ||
117
+ transition.length !== 3 ||
118
+ !Object.hasOwn(transition, 0) ||
119
+ !Object.hasOwn(transition, 1) ||
120
+ !Object.hasOwn(transition, 2)
121
+ ) {
122
+ throw new TypeError(
123
+ "Protocol transitions must be own [from, label, to] triples.",
124
+ );
125
+ }
126
+
127
+ const from = transition[0];
128
+ const label = transition[1];
129
+ const to = transition[2];
130
+
131
+ if (
132
+ !isScalarIdentifier(from) ||
133
+ !isScalarIdentifier(label) ||
134
+ !isScalarIdentifier(to)
135
+ ) {
136
+ throw new TypeError("Protocol states and labels must be scalar identifiers.");
137
+ }
138
+
139
+ let labels = relation.get(from);
140
+
141
+ if (labels === undefined) {
142
+ labels = new Map<Label, Set<State>>();
143
+ relation.set(from, labels);
144
+ }
145
+
146
+ let targets = labels.get(label);
147
+
148
+ if (targets === undefined) {
149
+ targets = new Set<State>();
150
+ labels.set(label, targets);
151
+ }
152
+
153
+ targets.add(to);
154
+ }
155
+
156
+ return Object.freeze({
157
+ allows<
158
+ From extends States<Transitions>,
159
+ Via extends Labels<Transitions>,
160
+ To extends States<Transitions>,
161
+ >(from: From, label: Via, to: To): boolean {
162
+ return relation.get(from)?.get(label)?.has(to) ?? false;
163
+ },
164
+ });
165
+ };
166
+
167
+ type ProtocolFacade = Readonly<{
168
+ define: typeof define;
169
+ }>;
170
+
171
+ /** Admissible labeled transition relations. */
172
+ export const Protocol: ProtocolFacade = {
173
+ define,
174
+ };
@@ -0,0 +1,209 @@
1
+ import { assertNotPromiseLike, type NotPromiseLike } from "../internal/promise-like.js";
2
+ import { err, ok, type Result } from "./core.js";
3
+
4
+ type SyncPrimitive = string | number | boolean | bigint | symbol | null | undefined;
5
+
6
+ type NonThenableObject = object & {
7
+ readonly then?: never;
8
+ };
9
+
10
+ type OverloadUnion<Callable, Partial = unknown> = Callable extends (
11
+ this: infer This,
12
+ ...args: infer Args
13
+ ) => infer Return
14
+ ? Partial extends Callable
15
+ ? never
16
+ :
17
+ | OverloadUnion<
18
+ Partial & Callable,
19
+ Partial & ((this: This, ...args: Args) => Return)
20
+ >
21
+ | ((this: This, ...args: Args) => Return)
22
+ : never;
23
+
24
+ type IsUnion<T, Whole = T> = T extends unknown
25
+ ? [Whole] extends [T]
26
+ ? false
27
+ : true
28
+ : never;
29
+
30
+ type UnionToIntersection<T> = (T extends unknown ? (value: T) => void : never) extends (
31
+ value: infer Intersection,
32
+ ) => void
33
+ ? Intersection
34
+ : never;
35
+
36
+ type HasAsyncOverload<Callable> = true extends (
37
+ OverloadUnion<Callable> extends infer One
38
+ ? One extends (...args: never[]) => infer Return
39
+ ? [Return] extends [NotPromiseLike<Return>]
40
+ ? false
41
+ : true
42
+ : never
43
+ : never
44
+ )
45
+ ? true
46
+ : false;
47
+
48
+ type HasNonPromiseOverload<Callable> = true extends (
49
+ OverloadUnion<Callable> extends infer One
50
+ ? One extends (...args: never[]) => infer Return
51
+ ? [Return] extends [PromiseLike<unknown>]
52
+ ? false
53
+ : true
54
+ : never
55
+ : never
56
+ )
57
+ ? true
58
+ : false;
59
+
60
+ type SyncWrappedOne<Callable, E> = Callable extends (
61
+ this: infer This,
62
+ ...args: infer Args
63
+ ) => infer Return
64
+ ? (this: This, ...args: Args) => Result<Return, E>
65
+ : never;
66
+
67
+ type SyncWrappedOverloads<Callable, E> = UnionToIntersection<
68
+ OverloadUnion<Callable> extends infer One
69
+ ? One extends unknown
70
+ ? SyncWrappedOne<One, E>
71
+ : never
72
+ : never
73
+ >;
74
+
75
+ type AsyncWrappedOne<Callable, E> = Callable extends (
76
+ this: infer This,
77
+ ...args: infer Args
78
+ ) => infer Return
79
+ ? (this: This, ...args: Args) => Promise<Result<Awaited<Return>, E>>
80
+ : never;
81
+
82
+ type AsyncWrappedOverloads<Callable, E> = UnionToIntersection<
83
+ OverloadUnion<Callable> extends infer One
84
+ ? One extends unknown
85
+ ? AsyncWrappedOne<One, E>
86
+ : never
87
+ : never
88
+ >;
89
+
90
+ const attemptRuntime = <T, E>(
91
+ read: () => T,
92
+ mapThrown: (caught: unknown) => E,
93
+ ): Result<T, E> => {
94
+ let value: T;
95
+
96
+ try {
97
+ value = read();
98
+ } catch (caught) {
99
+ return err(mapThrown(caught));
100
+ }
101
+
102
+ assertNotPromiseLike(value, "attempt() expects a synchronous thunk.");
103
+ return ok(value);
104
+ };
105
+
106
+ /**
107
+ * Captures one synchronous invocation boundary as Result.
108
+ *
109
+ * Thrown values are mapped from unknown into the caller's recoverable error
110
+ * type. Promise-like returns are rejected as a sync-boundary contract error.
111
+ * A thrown mapper remains an abrupt JavaScript completion.
112
+ */
113
+ export function attempt<T extends SyncPrimitive, E>(
114
+ read: () => T,
115
+ mapThrown: (caught: unknown) => E,
116
+ ): Result<T, E>;
117
+
118
+ export function attempt<T extends NonThenableObject, E>(
119
+ read: () => T,
120
+ mapThrown: (caught: unknown) => E,
121
+ ): Result<T, E>;
122
+
123
+ export function attempt<T, E>(
124
+ read: () => NotPromiseLike<T>,
125
+ mapThrown: (caught: unknown) => E,
126
+ ): Result<T, E>;
127
+
128
+ export function attempt<T, E>(
129
+ read: () => T,
130
+ mapThrown: (caught: unknown) => E,
131
+ ): Result<T, E> {
132
+ return attemptRuntime(read, mapThrown);
133
+ }
134
+
135
+ /**
136
+ * Captures synchronous invocation plus Promise rejection as Result.
137
+ *
138
+ * The returned Promise resolves to Result; the mapper defines the recoverable
139
+ * error vocabulary for both abrupt paths.
140
+ */
141
+ export const attemptAsync = async <T, E>(
142
+ read: () => PromiseLike<T>,
143
+ mapThrown: (caught: unknown) => E,
144
+ ): Promise<Result<Awaited<T>, E>> => {
145
+ try {
146
+ return ok(await read());
147
+ } catch (caught) {
148
+ return err(mapThrown(caught));
149
+ }
150
+ };
151
+
152
+ /** Wraps a synchronous function with the attempt boundary while preserving this and arguments. */
153
+ export function wrap<Callable extends (...args: never[]) => unknown, E>(
154
+ read: Callable &
155
+ (true extends IsUnion<Callable> ? never : unknown) &
156
+ (true extends IsUnion<OverloadUnion<Callable>> ? unknown : never) &
157
+ (HasAsyncOverload<Callable> extends false ? unknown : never),
158
+ mapThrown: (caught: unknown) => E,
159
+ ): SyncWrappedOverloads<Callable, E>;
160
+
161
+ export function wrap<This, Args extends unknown[], T extends SyncPrimitive, E>(
162
+ read: (this: This, ...args: Args) => T,
163
+ mapThrown: (caught: unknown) => E,
164
+ ): (this: This, ...args: Args) => Result<T, E>;
165
+
166
+ export function wrap<This, Args extends unknown[], T extends NonThenableObject, E>(
167
+ read: (this: This, ...args: Args) => T,
168
+ mapThrown: (caught: unknown) => E,
169
+ ): (this: This, ...args: Args) => Result<T, E>;
170
+
171
+ export function wrap<This, Args extends unknown[], T, E>(
172
+ read: (this: This, ...args: Args) => NotPromiseLike<T>,
173
+ mapThrown: (caught: unknown) => E,
174
+ ): (this: This, ...args: Args) => Result<T, E>;
175
+
176
+ export function wrap<This, Args extends unknown[], T, E>(
177
+ read: (this: This, ...args: Args) => T,
178
+ mapThrown: (caught: unknown) => E,
179
+ ): (this: This, ...args: Args) => Result<T, E> {
180
+ return function wrapped(this: This, ...args: Args): Result<T, E> {
181
+ return attemptRuntime(() => Reflect.apply(read, this, args), mapThrown);
182
+ };
183
+ }
184
+
185
+ /** Wraps an async function with the attemptAsync boundary while preserving this and arguments. */
186
+ export function wrapAsync<Callable extends (...args: never[]) => unknown, E>(
187
+ read: Callable &
188
+ (true extends IsUnion<Callable> ? never : unknown) &
189
+ (true extends IsUnion<OverloadUnion<Callable>> ? unknown : never) &
190
+ (HasNonPromiseOverload<Callable> extends false ? unknown : never),
191
+ mapThrown: (caught: unknown) => E,
192
+ ): AsyncWrappedOverloads<Callable, E>;
193
+
194
+ export function wrapAsync<This, Args extends unknown[], T, E>(
195
+ read: (this: This, ...args: Args) => PromiseLike<T>,
196
+ mapThrown: (caught: unknown) => E,
197
+ ): (this: This, ...args: Args) => Promise<Result<Awaited<T>, E>>;
198
+
199
+ export function wrapAsync<This, Args extends unknown[], T, E>(
200
+ read: (this: This, ...args: Args) => PromiseLike<T>,
201
+ mapThrown: (caught: unknown) => E,
202
+ ): (this: This, ...args: Args) => Promise<Result<Awaited<T>, E>> {
203
+ return function wrappedAsync(
204
+ this: This,
205
+ ...args: Args
206
+ ): Promise<Result<Awaited<T>, E>> {
207
+ return attemptAsync(() => Reflect.apply(read, this, args), mapThrown);
208
+ };
209
+ }