@pvorona/failable 0.0.1 → 0.0.3

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 (2) hide show
  1. package/README.md +283 -0
  2. package/package.json +7 -3
package/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  A typed result type for expected failures. `Failable<T, E>` is a discriminated union of `Success<T>` and `Failure<E>`, with ergonomic accessors and structured-clone support.
4
4
 
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i @pvorona/failable
9
+ ```
10
+
5
11
  ## Usage
6
12
 
7
13
  ```ts
@@ -19,6 +25,8 @@ if (result.isSuccess) {
19
25
  ### Factories
20
26
 
21
27
  ```ts
28
+ import { Failable } from '@pvorona/failable';
29
+
22
30
  const success = Failable.ofSuccess(42);
23
31
  const failure = Failable.ofError(new Error('boom'));
24
32
  ```
@@ -26,6 +34,10 @@ const failure = Failable.ofError(new Error('boom'));
26
34
  ### Fallbacks
27
35
 
28
36
  ```ts
37
+ import { Failable } from '@pvorona/failable';
38
+
39
+ const failure = Failable.ofError(new Error('boom'));
40
+
29
41
  const value = failure.getOr('default'); // 'default'
30
42
  const recovered = failure.or('fallback'); // Success<'fallback'>
31
43
  ```
@@ -33,6 +45,8 @@ const recovered = failure.or('fallback'); // Success<'fallback'>
33
45
  ### Wrapping async work
34
46
 
35
47
  ```ts
48
+ import { Failable } from '@pvorona/failable';
49
+
36
50
  const result = await Failable.from(fetch('/api'));
37
51
  ```
38
52
 
@@ -41,6 +55,8 @@ const result = await Failable.from(fetch('/api'));
41
55
  `Failable` instances use Symbols and prototype methods that do not survive structured cloning (`postMessage`, `chrome.runtime.sendMessage`, etc.). Convert to a plain object first:
42
56
 
43
57
  ```ts
58
+ import { Failable } from '@pvorona/failable';
59
+
44
60
  // sender
45
61
  const wire = Failable.toFailableLike(result);
46
62
  postMessage(wire);
@@ -48,3 +64,270 @@ postMessage(wire);
48
64
  // receiver
49
65
  const hydrated = Failable.from(wire);
50
66
  ```
67
+
68
+ ## API
69
+
70
+ ### `const enum FailableStatus`
71
+
72
+ The discriminant for `FailableLike` and `Failable` instances.
73
+
74
+ ```ts
75
+ export const enum FailableStatus {
76
+ Success = 'success',
77
+ Failure = 'failure',
78
+ }
79
+ ```
80
+
81
+ Example:
82
+
83
+ ```ts
84
+ import { FailableStatus, type FailableLike } from '@pvorona/failable';
85
+
86
+ const ok: FailableLike<number, string> = {
87
+ status: FailableStatus.Success,
88
+ data: 1,
89
+ };
90
+ ```
91
+
92
+ ### `type Failable<T, E>`
93
+
94
+ Alias for `Success<T> | Failure<E>`.
95
+
96
+ Example:
97
+
98
+ ```ts
99
+ import type { Failable } from '@pvorona/failable';
100
+ import { Failable as FailableNS } from '@pvorona/failable';
101
+
102
+ export function parseIntSafe(input: string): Failable<number, Error> {
103
+ const n = Number(input);
104
+ if (!Number.isInteger(n)) return FailableNS.ofError(new Error('Not an int'));
105
+
106
+ return FailableNS.ofSuccess(n);
107
+ }
108
+ ```
109
+
110
+ ### `type Success<T>`
111
+
112
+ The success variant.
113
+
114
+ Key fields/methods:
115
+
116
+ - `status: 'success'`, `isSuccess: true`, `isError: false`
117
+ - `data: T`, `error: null`
118
+ - `or(value)` returns itself
119
+ - `getOr(_)` returns `data`
120
+ - `getOrThrow()` returns `data`
121
+
122
+ Example:
123
+
124
+ ```ts
125
+ import { Failable } from '@pvorona/failable';
126
+
127
+ const s = Failable.ofSuccess(123);
128
+ s.getOr(0); // 123
129
+ s.or('x'); // Success<number>
130
+ ```
131
+
132
+ ### `type Failure<E>`
133
+
134
+ The failure variant.
135
+
136
+ Key fields/methods:
137
+
138
+ - `status: 'failure'`, `isSuccess: false`, `isError: true`
139
+ - `error: E`, `data: null`
140
+ - `or(value)` converts to `Success<typeof value>`
141
+ - `getOr(fallback)` returns `fallback`
142
+ - `getOrThrow()` throws `error`
143
+
144
+ Example:
145
+
146
+ ```ts
147
+ import { Failable } from '@pvorona/failable';
148
+
149
+ const f = Failable.ofError(new Error('boom'));
150
+ f.getOr('default'); // 'default'
151
+ f.or(42).data; // 42
152
+ ```
153
+
154
+ ### `type FailableLike<T, E>`
155
+
156
+ Structured-clone-friendly representation of a result:
157
+
158
+ - `{ status: 'success', data }`
159
+ - `{ status: 'failure', error }`
160
+
161
+ Example:
162
+
163
+ ```ts
164
+ import type { FailableLike } from '@pvorona/failable';
165
+
166
+ type Wire = FailableLike<{ id: string }, { code: string }>;
167
+ ```
168
+
169
+ ### `type FailableLikeSuccess<T>`
170
+
171
+ The success-shaped `FailableLike`.
172
+
173
+ Example:
174
+
175
+ ```ts
176
+ import { FailableStatus, type FailableLikeSuccess } from '@pvorona/failable';
177
+
178
+ const wireOk: FailableLikeSuccess<number> = {
179
+ status: FailableStatus.Success,
180
+ data: 1,
181
+ };
182
+ ```
183
+
184
+ ### `type FailableLikeFailure<E>`
185
+
186
+ The failure-shaped `FailableLike`.
187
+
188
+ Example:
189
+
190
+ ```ts
191
+ import { FailableStatus, type FailableLikeFailure } from '@pvorona/failable';
192
+
193
+ const wireErr: FailableLikeFailure<string> = {
194
+ status: FailableStatus.Failure,
195
+ error: 'bad_request',
196
+ };
197
+ ```
198
+
199
+ ### `FailableTag`, `SuccessTag`, `FailureTag` (Symbols)
200
+
201
+ Low-level Symbol tags used to mark hydrated `Failable` instances at runtime.
202
+
203
+ Most code should prefer `result.isSuccess` / `result.isError` or the guards `Failable.isSuccess(...)` / `Failable.isFailure(...)`.
204
+
205
+ Example (advanced):
206
+
207
+ ```ts
208
+ import { FailableTag } from '@pvorona/failable';
209
+
210
+ export function isHydratedFailable(value: unknown): boolean {
211
+ return (
212
+ typeof value === 'object' &&
213
+ value !== null &&
214
+ (value as any)[FailableTag] === true
215
+ );
216
+ }
217
+ ```
218
+
219
+ ### `const Failable`
220
+
221
+ Namespace-style factory + utilities for producing and working with `Failable<T, E>`.
222
+
223
+ #### `Failable.ofSuccess<T>(data: T): Success<T>`
224
+
225
+ Example:
226
+
227
+ ```ts
228
+ import { Failable } from '@pvorona/failable';
229
+
230
+ const ok = Failable.ofSuccess({ id: '1' });
231
+ ```
232
+
233
+ #### `Failable.ofError<E>(error: E): Failure<E>`
234
+
235
+ Example:
236
+
237
+ ```ts
238
+ import { Failable } from '@pvorona/failable';
239
+
240
+ const err = Failable.ofError({ code: 'bad_request' });
241
+ ```
242
+
243
+ #### `Failable.isFailable(value): value is Failable<unknown, unknown>`
244
+
245
+ Checks whether a value is a hydrated `Failable` instance (Symbol-tagged).
246
+
247
+ Example:
248
+
249
+ ```ts
250
+ import { Failable } from '@pvorona/failable';
251
+
252
+ const maybe: unknown = Failable.ofSuccess(1);
253
+ if (Failable.isFailable(maybe)) {
254
+ // narrowed
255
+ }
256
+ ```
257
+
258
+ #### `Failable.isSuccess(value): value is Success<unknown>`
259
+
260
+ Example:
261
+
262
+ ```ts
263
+ import { Failable } from '@pvorona/failable';
264
+
265
+ const maybe: unknown = Failable.ofSuccess(1);
266
+ if (Failable.isSuccess(maybe)) {
267
+ maybe.data; // ok
268
+ }
269
+ ```
270
+
271
+ #### `Failable.isFailure(value): value is Failure<unknown>`
272
+
273
+ Example:
274
+
275
+ ```ts
276
+ import { Failable } from '@pvorona/failable';
277
+
278
+ const maybe: unknown = Failable.ofError('nope');
279
+ if (Failable.isFailure(maybe)) {
280
+ console.log(maybe.error);
281
+ }
282
+ ```
283
+
284
+ #### `Failable.toFailableLike(result): FailableLike<...>`
285
+
286
+ Converts a hydrated `Failable` into a structured-clone-friendly representation.
287
+
288
+ Example:
289
+
290
+ ```ts
291
+ import { Failable } from '@pvorona/failable';
292
+
293
+ const res = Failable.ofSuccess(1);
294
+ const wire = Failable.toFailableLike(res);
295
+ ```
296
+
297
+ #### `Failable.isFailableLike(value): value is FailableLike<unknown, unknown>`
298
+
299
+ Strictly checks for `{ status, data }` or `{ status, error }` with no extra enumerable keys.
300
+
301
+ Example:
302
+
303
+ ```ts
304
+ import { Failable } from '@pvorona/failable';
305
+
306
+ const wire: unknown = { status: 'success', data: 1 };
307
+ Failable.isFailableLike(wire); // true
308
+ ```
309
+
310
+ #### `Failable.from(...)`
311
+
312
+ Overloads:
313
+
314
+ - `from(failable)` → returns the same instance
315
+ - `from(failableLike)` → rehydrates into a real `Success` / `Failure`
316
+ - `from(() => value)` → captures throws into `Failure`
317
+ - `from(promise)` → captures rejections into `Failure`
318
+
319
+ Examples:
320
+
321
+ ```ts
322
+ import { Failable } from '@pvorona/failable';
323
+
324
+ // function wrapper (captures throws)
325
+ const res1 = Failable.from(() => JSON.parse('{'));
326
+
327
+ // promise wrapper (captures rejections)
328
+ const res2 = await Failable.from(fetch('https://example.com'));
329
+
330
+ // rehydrate from structured clone
331
+ const wire = Failable.toFailableLike(Failable.ofError('bad'));
332
+ const hydrated = Failable.from(wire);
333
+ ```
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@pvorona/failable",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
+ "license": "MIT",
4
5
  "type": "module",
5
6
  "main": "./dist/index.js",
6
7
  "module": "./dist/index.js",
@@ -18,8 +19,11 @@
18
19
  "dist",
19
20
  "!**/*.tsbuildinfo"
20
21
  ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
21
25
  "dependencies": {
22
- "@pvorona/assert": "^0.0.1",
23
- "@pvorona/not-implemented": "^0.0.1"
26
+ "@pvorona/assert": "~0.0.2",
27
+ "@pvorona/not-implemented": "~0.0.1"
24
28
  }
25
29
  }