nalloc 0.0.1

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 (58) hide show
  1. package/README.md +282 -0
  2. package/build/devtools.cjs +79 -0
  3. package/build/devtools.cjs.map +1 -0
  4. package/build/devtools.d.ts +82 -0
  5. package/build/devtools.js +43 -0
  6. package/build/devtools.js.map +1 -0
  7. package/build/index.cjs +76 -0
  8. package/build/index.cjs.map +1 -0
  9. package/build/index.d.ts +4 -0
  10. package/build/index.js +5 -0
  11. package/build/index.js.map +1 -0
  12. package/build/option.cjs +279 -0
  13. package/build/option.cjs.map +1 -0
  14. package/build/option.d.ts +356 -0
  15. package/build/option.js +157 -0
  16. package/build/option.js.map +1 -0
  17. package/build/result.cjs +381 -0
  18. package/build/result.cjs.map +1 -0
  19. package/build/result.d.ts +442 -0
  20. package/build/result.js +229 -0
  21. package/build/result.js.map +1 -0
  22. package/build/safe.cjs +88 -0
  23. package/build/safe.cjs.map +1 -0
  24. package/build/safe.d.ts +29 -0
  25. package/build/safe.js +18 -0
  26. package/build/safe.js.map +1 -0
  27. package/build/testing.cjs +111 -0
  28. package/build/testing.cjs.map +1 -0
  29. package/build/testing.d.ts +85 -0
  30. package/build/testing.js +81 -0
  31. package/build/testing.js.map +1 -0
  32. package/build/types.cjs +78 -0
  33. package/build/types.cjs.map +1 -0
  34. package/build/types.d.ts +137 -0
  35. package/build/types.js +36 -0
  36. package/build/types.js.map +1 -0
  37. package/build/unsafe.cjs +82 -0
  38. package/build/unsafe.cjs.map +1 -0
  39. package/build/unsafe.d.ts +27 -0
  40. package/build/unsafe.js +11 -0
  41. package/build/unsafe.js.map +1 -0
  42. package/package.json +93 -0
  43. package/src/__tests__/index.ts +13 -0
  44. package/src/__tests__/option.ts +610 -0
  45. package/src/__tests__/option.types.ts +120 -0
  46. package/src/__tests__/result.ts +721 -0
  47. package/src/__tests__/result.types.ts +249 -0
  48. package/src/__tests__/safe.ts +24 -0
  49. package/src/__tests__/tooling.ts +86 -0
  50. package/src/__tests__/unsafe.ts +24 -0
  51. package/src/devtools.ts +97 -0
  52. package/src/index.ts +18 -0
  53. package/src/option.ts +510 -0
  54. package/src/result.ts +676 -0
  55. package/src/safe.ts +58 -0
  56. package/src/testing.ts +159 -0
  57. package/src/types.ts +201 -0
  58. package/src/unsafe.ts +47 -0
@@ -0,0 +1,249 @@
1
+ import { assert, type Not, type Equals } from 'tsafe';
2
+ import {
3
+ isOk,
4
+ isErr,
5
+ map,
6
+ mapErr,
7
+ flatMap,
8
+ bimap,
9
+ unwrap,
10
+ unwrapErr,
11
+ unwrapOr,
12
+ unwrapOrElse,
13
+ expect,
14
+ expectErr,
15
+ and,
16
+ or,
17
+ orElse,
18
+ flatten,
19
+ match,
20
+ partition,
21
+ collect,
22
+ collectAll,
23
+ transpose,
24
+ isOkAnd,
25
+ isErrAnd,
26
+ of,
27
+ ofAsync,
28
+ tryCatch,
29
+ tryAsync,
30
+ fromPromise,
31
+ unwrapOrReturn,
32
+ assertOk,
33
+ assertErr,
34
+ all,
35
+ any,
36
+ partitionAsync
37
+ } from '../result.js';
38
+ import { Result, Ok, Err, ok, err, Option, type InferErr } from '../types.js';
39
+
40
+ const okValue = ok<number>(42);
41
+ assert<Equals<typeof okValue, Ok<number>>>;
42
+ assert<Not<Equals<typeof okValue, Err<any>>>>;
43
+
44
+ const okValue2 = ok<number>(5);
45
+ assert<Equals<typeof okValue2, Ok<number>>>;
46
+
47
+ const errValue = err("error");
48
+ assert<Equals<typeof errValue, Err<string>>>;
49
+ assert<Not<Equals<typeof errValue, Ok<any>>>>;
50
+
51
+ const errValue2 = err<number>(404);
52
+ assert<Equals<typeof errValue2, Err<number>>>;
53
+
54
+ const result: Result<number, string> = Math.random() > 0 ? ok(42) : err("error");
55
+ if (isOk(result)) {
56
+ assert<Equals<typeof result, Ok<number>>>;
57
+ const value: number = result;
58
+ } else {
59
+ assert<Equals<typeof result, Err<string>>>;
60
+ const error: string = result.error;
61
+ }
62
+
63
+ const knownOk = ok(123);
64
+ const isOkResult = isOk(knownOk);
65
+ assert<Equals<typeof isOkResult, true>>;
66
+ assert<Not<Equals<typeof isOkResult, false>>>;
67
+ assert<Not<Equals<typeof isOkResult, boolean>>>;
68
+
69
+ const knownErr = err("error");
70
+ const isErrResult = isErr(knownErr);
71
+ assert<Equals<typeof isErrResult, true>>;
72
+ assert<Not<Equals<typeof isErrResult, false>>>;
73
+ assert<Not<Equals<typeof isErrResult, boolean>>>;
74
+
75
+ const isOkOnErr = isOk(knownErr);
76
+ assert<Equals<typeof isOkOnErr, false>>;
77
+ assert<Not<Equals<typeof isOkOnErr, true>>>;
78
+
79
+ const isErrOnOk = isErr(knownOk);
80
+ assert<Equals<typeof isErrOnOk, false>>;
81
+ assert<Not<Equals<typeof isErrOnOk, true>>>;
82
+
83
+ const mapped = map(ok<number>(5) as Result<number, never>, x => x * 2);
84
+ assert<Equals<typeof mapped, Result<number, never>>>;
85
+
86
+ const mappedErr = map(err<string>("error") as Result<number, string>, (x: number) => x * 2);
87
+ assert<Equals<typeof mappedErr, Result<number, string>>>;
88
+
89
+ const errorMapped = mapErr(err<string>("error") as Result<never, string>, e => e.length);
90
+ // errorMapped has type Result<never, number>
91
+
92
+ const errorMappedOk = mapErr(ok<number>(42) as Result<number, string>, e => e.length);
93
+ assert<Equals<typeof errorMappedOk, Result<number, number>>>;
94
+
95
+ const chained = flatMap(ok<number>(5) as Result<number, string>, x =>
96
+ x > 0 ? ok(x * 2) : err("negative")
97
+ );
98
+ assert<Equals<typeof chained, Result<number, string>>>;
99
+
100
+ const flatMapped = flatMap(ok<number>(5) as Result<number, string>, x => ok(x.toString()));
101
+ assert<Equals<typeof flatMapped, Result<string, string>>>;
102
+
103
+ const bimapped = bimap(
104
+ result,
105
+ v => v.toString(),
106
+ e => e.length
107
+ );
108
+ assert<Equals<typeof bimapped, Result<string, number>>>;
109
+
110
+ const andResult = and(ok<number>(1) as Result<number, string>, ok<number>(2) as Result<number, string>);
111
+ assert<Equals<typeof andResult, Result<number, string>>>;
112
+
113
+ const orResult = or(err<string>("error") as Result<number, string>, ok<number>(42) as Result<number, boolean>);
114
+ assert<Equals<typeof orResult, Result<number, boolean>>>;
115
+
116
+ const orElseResult = orElse(err<string>("error") as Result<number, string>, e => ok<number>(e.length) as Result<number, number>);
117
+ assert<Equals<typeof orElseResult, Result<number, number>>>;
118
+
119
+ const numErr: Result<string, number> = err(404);
120
+ const strErr: Result<string, string> = err("not found");
121
+ assert<Not<Equals<typeof numErr, typeof strErr>>>;
122
+
123
+ const okNum: Ok<number> = ok(100);
124
+ const plainNum: number = okNum;
125
+ assert<Equals<typeof plainNum, number>>;
126
+
127
+ const nested: Result<Result<number, string>, Error> = ok(ok(42));
128
+ const flattened = flatten(nested as Result<Result<number, string>, string>);
129
+ assert<Equals<typeof flattened, Result<number, string>>>;
130
+
131
+
132
+ const matchResult = match(
133
+ result,
134
+ v => v.toString(),
135
+ e => e,
136
+ );
137
+ assert<Equals<typeof matchResult, string>>;
138
+
139
+ const results: Result<number, string>[] = [ok(1), err("error"), ok(2)];
140
+ const [oks, errs] = partition(results);
141
+ assert<Equals<typeof oks, number[]>>;
142
+ assert<Equals<typeof errs, string[]>>;
143
+
144
+ const collected = collect(results);
145
+ assert<Equals<typeof collected, Result<number[], string>>>;
146
+
147
+ const collectedAll = collectAll(results);
148
+ assert<Equals<typeof collectedAll, Result<number[], string[]>>>;
149
+
150
+ const transposed = transpose(ok(42 as Option<number>) as Result<Option<number>, string>);
151
+ assert<Equals<typeof transposed, Option<Result<number, string>>>>;
152
+
153
+ const isOkAndResult = isOkAnd(ok<number>(42) as Result<number, never>, x => x > 0);
154
+ assert<Equals<typeof isOkAndResult, boolean>>;
155
+
156
+ const isErrAndResult = isErrAnd(err<string>("error") as Result<never, string>, e => e.length > 0);
157
+ assert<Equals<typeof isErrAndResult, boolean>>;
158
+
159
+ const tryResult = of(() => {
160
+ if (Math.random() > 0.5) throw new Error("oops");
161
+ return 42;
162
+ });
163
+ assert<Equals<typeof tryResult, Result<number, unknown>>>;
164
+
165
+ const tryResultTyped = of<number, Error>(() => {
166
+ if (Math.random() > 0.5) throw new Error("oops");
167
+ return 42;
168
+ });
169
+ assert<Equals<typeof tryResultTyped, Result<number, Error>>>;
170
+
171
+ const asyncResult = ofAsync<number>(async () => {
172
+ return 42;
173
+ });
174
+ assert<Equals<typeof asyncResult, Promise<Result<number, unknown>>>>;
175
+
176
+
177
+
178
+ const unwrapOrResult = unwrapOr(err<string>("error") as Result<number, string>, 42);
179
+ assert<Equals<typeof unwrapOrResult, number>>;
180
+
181
+ const unwrapOrElseResult = unwrapOrElse(err<string>("error") as Result<number, string>, e => e.length);
182
+ assert<Equals<typeof unwrapOrElseResult, number>>;
183
+
184
+ const complexResult: Result<{ id: number; name: string }, Error> =
185
+ Math.random() > 0.5
186
+ ? ok({ id: 1, name: "test" })
187
+ : err(new Error("failed"));
188
+
189
+ if (isOk(complexResult)) {
190
+ assert<Equals<typeof complexResult, Ok<{ id: number; name: string }>>>;
191
+ const obj: { id: number; name: string } = complexResult;
192
+ const id: number = complexResult.id;
193
+ const name: string = complexResult.name;
194
+ }
195
+
196
+ if (isErr(complexResult)) {
197
+ // complexResult is narrowed to Err<Error> here
198
+ const error: Error = (complexResult as Err<Error>).error;
199
+ }
200
+
201
+ const okString: Result<string, never> = ok<string>("hello");
202
+ const mappedString = map(okString, s => s.length);
203
+ // mappedString is Result<number, never>
204
+
205
+ const errString: Result<never, string> = err<string>("error");
206
+ const mappedErrString = mapErr(errString, s => s.length);
207
+ // mappedErrString is Result<never, number>
208
+
209
+ const tryCatchResult = tryCatch(() => 1);
210
+ assert<Equals<typeof tryCatchResult, Result<number, unknown>>>;
211
+
212
+ const tryCatchTyped = tryCatch<number, string>(() => {
213
+ throw new Error('boom');
214
+ }, error => (error as Error).message);
215
+ assert<Equals<typeof tryCatchTyped, Result<number, string>>>;
216
+
217
+ const tryAsyncResult = tryAsync(async () => 1);
218
+ assert<Equals<typeof tryAsyncResult, Promise<Result<number, unknown>>>>;
219
+
220
+ const tryAsyncTyped = tryAsync<number, string>(async () => {
221
+ throw new Error('boom');
222
+ }, error => (error as Error).message);
223
+ assert<Equals<typeof tryAsyncTyped, Promise<Result<number, string>>>>;
224
+
225
+ const fromPromiseResult = fromPromise(Promise.resolve(1));
226
+ assert<Equals<typeof fromPromiseResult, Promise<Result<number, unknown>>>>;
227
+
228
+ const unwrapFallback = unwrapOrReturn(ok(1) as Result<number, string>, () => 'fallback');
229
+ assert<Equals<typeof unwrapFallback, number | string>>;
230
+
231
+ declare const maybeResult: Result<number, string>;
232
+ assertOk(maybeResult);
233
+ const narrowedOk: Ok<number> = maybeResult;
234
+
235
+ declare const maybeErr: Result<number, string>;
236
+ assertErr(maybeErr);
237
+ const narrowedErr: Err<string> = maybeErr;
238
+
239
+ const allResult = all([ok(1), ok(2)]);
240
+ assert<Equals<typeof allResult, Result<number[], unknown>>>;
241
+
242
+ const anyResult = any([err('a'), ok(3)]);
243
+ assert<Equals<typeof anyResult, Result<number, string[]>>>;
244
+
245
+ const partitionAsyncResult = partitionAsync([Promise.resolve(ok(1)), Promise.resolve(err('a'))]);
246
+ assert<Equals<typeof partitionAsyncResult, Promise<[number[], string[]]>>>;
247
+
248
+ type ExtractedErr = InferErr<Result<number, string>>;
249
+ assert<Equals<ExtractedErr, Err<string>>>;
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import * as safe from '../safe.js';
3
+ import { err } from '../types.js';
4
+
5
+ describe('nalloc/safe exports', () => {
6
+ it('exports match nalloc', () => {
7
+ expect(safe.some).toBeDefined();
8
+ expect('none' in safe).toBe(true);
9
+ expect(safe.ok).toBeDefined();
10
+ expect(safe.err).toBeDefined();
11
+ expect(safe.Option).toBeDefined();
12
+ expect(safe.Result).toBeDefined();
13
+ });
14
+
15
+ it('safe some throws on null/undefined', () => {
16
+ expect(() => safe.some(null as any)).toThrow();
17
+ expect(() => safe.some(undefined as any)).toThrow();
18
+ });
19
+
20
+ it('safe ok throws on Err', () => {
21
+ const error = err('test');
22
+ expect(() => safe.ok(error)).toThrow();
23
+ });
24
+ });
@@ -0,0 +1,86 @@
1
+ import { beforeAll, describe, expect, it } from 'vitest';
2
+ import { ok, err, none, some } from '../types.js';
3
+ import {
4
+ expectOk,
5
+ expectErr,
6
+ expectSome,
7
+ expectNone,
8
+ extendExpect,
9
+ } from '../testing.js';
10
+ import {
11
+ formatOption,
12
+ formatResult,
13
+ inspectOption,
14
+ inspectResult,
15
+ logOption,
16
+ logResult,
17
+ toJSONOption,
18
+ toJSONResult,
19
+ } from '../devtools.js';
20
+
21
+ describe('testing utilities', () => {
22
+ beforeAll(() => {
23
+ extendExpect(expect as any);
24
+ });
25
+
26
+ it('expectOk returns Ok payload or throws', () => {
27
+ const value = expectOk(ok(5));
28
+ expect(value).toBe(5);
29
+ expect(() => expectOk(err('nope'))).toThrow(/Err/);
30
+ });
31
+
32
+ it('expectErr returns error payload or throws', () => {
33
+ const error = expectErr(err('boom'));
34
+ expect(error).toBe('boom');
35
+ expect(() => expectErr(ok(1))).toThrow(/Ok/);
36
+ });
37
+
38
+ it('expectSome / expectNone validate Option', () => {
39
+ const value = expectSome(some(3));
40
+ expect(value).toBe(3);
41
+ expect(() => expectSome(none)).toThrow(/None/);
42
+ expect(() => expectNone(some(2))).toThrow(/Some/);
43
+ });
44
+
45
+ it('adds matchers to expect', () => {
46
+ expect(ok(1)).toBeOk();
47
+ expect(err('oops')).toBeErr();
48
+ expect(some(4)).toBeSome();
49
+ expect(none).toBeNone();
50
+ });
51
+ });
52
+
53
+ describe('devtools utilities', () => {
54
+ it('formats option', () => {
55
+ expect(formatOption(some('x'))).toBe('Some(x)');
56
+ expect(formatOption(none)).toBe('None');
57
+ });
58
+
59
+ it('formats result', () => {
60
+ expect(formatResult(ok(7))).toBe('Ok(7)');
61
+ expect(formatResult(err('fail'))).toBe('Err(fail)');
62
+ });
63
+
64
+ it('inspects option/result', () => {
65
+ expect(inspectOption(some('x'))).toEqual({ kind: 'some', value: 'x' });
66
+ expect(inspectOption(none)).toEqual({ kind: 'none' });
67
+ expect(inspectResult(ok('y'))).toEqual({ status: 'ok', value: 'y' });
68
+ expect(inspectResult(err('fail'))).toEqual({ status: 'err', error: 'fail' });
69
+ });
70
+
71
+ it('logs option/result via logger', () => {
72
+ const calls: unknown[][] = [];
73
+ const logger = (...args: unknown[]) => calls.push(args);
74
+ logOption(some(1), logger);
75
+ logResult(err('fail'), logger);
76
+ expect(calls[0]).toEqual(['Some(1)']);
77
+ expect(calls[1]).toEqual(['Err', 'fail']);
78
+ });
79
+
80
+ it('serialises option/result to JSON friendly data', () => {
81
+ expect(toJSONOption(some(2))).toEqual({ kind: 'some', value: 2 });
82
+ expect(toJSONOption(none)).toEqual({ kind: 'none' });
83
+ expect(toJSONResult(ok(3))).toEqual({ status: 'ok', value: 3 });
84
+ expect(toJSONResult(err('fail'))).toEqual({ status: 'err', error: 'fail' });
85
+ });
86
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import * as unsafe from '../unsafe.js';
3
+ import { err } from '../types.js';
4
+
5
+ describe('nalloc/unsafe exports', () => {
6
+ it('exports match nalloc', () => {
7
+ expect(unsafe.some).toBeDefined();
8
+ expect('none' in unsafe).toBe(true);
9
+ expect(unsafe.ok).toBeDefined();
10
+ expect(unsafe.err).toBeDefined();
11
+ expect(unsafe.Option).toBeDefined();
12
+ expect(unsafe.Result).toBeDefined();
13
+ });
14
+
15
+ it('unsafe some does not throw on null/undefined', () => {
16
+ expect(() => unsafe.some(null)).not.toThrow();
17
+ expect(() => unsafe.some(undefined)).not.toThrow();
18
+ });
19
+
20
+ it('unsafe ok does not throw on Err', () => {
21
+ const error = err('test');
22
+ expect(() => unsafe.ok(error)).not.toThrow();
23
+ });
24
+ });
@@ -0,0 +1,97 @@
1
+ import { Option, Result, isSome, isOk } from './types.js';
2
+
3
+ /** Logger function signature for custom logging. */
4
+ type Logger = (message: string, ...args: unknown[]) => void;
5
+
6
+ /**
7
+ * Formats an Option as a human-readable string.
8
+ * @param opt - The Option to format
9
+ * @returns "Some(value)" or "None"
10
+ * @example
11
+ * formatOption(42) // "Some(42)"
12
+ * formatOption(null) // "None"
13
+ * formatOption(undefined) // "None"
14
+ */
15
+ export function formatOption<T>(opt: Option<T>): string {
16
+ return isSome(opt) ? `Some(${String(opt)})` : 'None';
17
+ }
18
+
19
+ /**
20
+ * Formats a Result as a human-readable string.
21
+ * For Err containing an Error, displays the error message.
22
+ * @param result - The Result to format
23
+ * @returns "Ok(value)" or "Err(error)"
24
+ * @example
25
+ * formatResult(42) // "Ok(42)"
26
+ * formatResult(err('fail')) // "Err(fail)"
27
+ * formatResult(err(new Error('oops'))) // "Err(oops)"
28
+ */
29
+ export function formatResult<T, E>(result: Result<T, E>): string {
30
+ if (isOk(result)) {
31
+ return `Ok(${String(result)})`;
32
+ }
33
+ const error = (result as { error: E }).error;
34
+ return `Err(${error instanceof Error ? error.message : String(error)})`;
35
+ }
36
+
37
+ /**
38
+ * Inspects an Option, returning a tagged object for debugging or serialization.
39
+ * @param opt - The Option to inspect
40
+ * @returns `{ kind: 'some', value: T }` or `{ kind: 'none' }`
41
+ * @example
42
+ * inspectOption(42) // { kind: 'some', value: 42 }
43
+ * inspectOption(null) // { kind: 'none' }
44
+ */
45
+ export function inspectOption<T>(opt: Option<T>) {
46
+ return isSome(opt) ? { kind: 'some' as const, value: opt } : { kind: 'none' as const };
47
+ }
48
+
49
+ /**
50
+ * Inspects a Result, returning a tagged object for debugging or serialization.
51
+ * @param result - The Result to inspect
52
+ * @returns `{ status: 'ok', value: T }` or `{ status: 'err', error: E }`
53
+ * @example
54
+ * inspectResult(42) // { status: 'ok', value: 42 }
55
+ * inspectResult(err('fail')) // { status: 'err', error: 'fail' }
56
+ */
57
+ export function inspectResult<T, E>(result: Result<T, E>) {
58
+ return isOk(result) ? { status: 'ok' as const, value: result } : { status: 'err' as const, error: (result as { error: E }).error };
59
+ }
60
+
61
+ /**
62
+ * Logs an Option using the provided logger (defaults to console.log).
63
+ * @param opt - The Option to log
64
+ * @param logger - The logging function to use
65
+ * @example
66
+ * logOption(42) // logs "Some(42)"
67
+ * logOption(null) // logs "None"
68
+ * logOption(42, console.warn) // logs "Some(42)" as warning
69
+ */
70
+ export function logOption<T>(opt: Option<T>, logger: Logger = console.log): void {
71
+ logger(formatOption(opt));
72
+ }
73
+
74
+ /**
75
+ * Logs a Result using the provided logger (defaults to console.log).
76
+ * Logs "Ok" with the value or "Err" with the error.
77
+ * @param result - The Result to log
78
+ * @param logger - The logging function to use
79
+ * @example
80
+ * logResult(42) // logs "Ok", 42
81
+ * logResult(err('fail')) // logs "Err", "fail"
82
+ * logResult(42, console.warn) // logs "Ok", 42 as warning
83
+ */
84
+ export function logResult<T, E>(result: Result<T, E>, logger: Logger = console.log): void {
85
+ const tagged = inspectResult(result);
86
+ if (tagged.status === 'ok') {
87
+ logger(`Ok`, tagged.value);
88
+ return;
89
+ }
90
+ logger(`Err`, tagged.error);
91
+ }
92
+
93
+ /** Alias for inspectOption. Returns a JSON-serializable representation. */
94
+ export const toJSONOption = inspectOption;
95
+
96
+ /** Alias for inspectResult. Returns a JSON-serializable representation. */
97
+ export const toJSONResult = inspectResult;
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ export { some, none, ok, err } from './types.js';
2
+ export type {
3
+ Some,
4
+ None,
5
+ Ok,
6
+ Err,
7
+ Option as OptionType,
8
+ OptionValue,
9
+ IsOption,
10
+ InferSome,
11
+ Result as ResultType,
12
+ ResultValue,
13
+ ResultErrorType,
14
+ IsResult,
15
+ InferErr,
16
+ } from './types.js';
17
+ export * as Option from './option.js';
18
+ export * as Result from './result.js';