nalloc 0.0.1 → 0.0.2

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 (64) hide show
  1. package/README.md +124 -38
  2. package/build/index.cjs +12 -68
  3. package/build/index.cjs.map +1 -1
  4. package/build/index.d.ts +1 -4
  5. package/build/index.js +1 -3
  6. package/build/index.js.map +1 -1
  7. package/build/iter.cjs +105 -0
  8. package/build/iter.cjs.map +1 -0
  9. package/build/iter.d.ts +61 -0
  10. package/build/iter.js +78 -0
  11. package/build/iter.js.map +1 -0
  12. package/build/option.cjs +19 -5
  13. package/build/option.cjs.map +1 -1
  14. package/build/option.d.ts +22 -1
  15. package/build/option.js +14 -6
  16. package/build/option.js.map +1 -1
  17. package/build/result.cjs +125 -54
  18. package/build/result.cjs.map +1 -1
  19. package/build/result.d.ts +83 -53
  20. package/build/result.js +100 -38
  21. package/build/result.js.map +1 -1
  22. package/build/safe.cjs +34 -15
  23. package/build/safe.cjs.map +1 -1
  24. package/build/safe.d.ts +4 -27
  25. package/build/safe.js +3 -14
  26. package/build/safe.js.map +1 -1
  27. package/build/types.cjs +38 -7
  28. package/build/types.cjs.map +1 -1
  29. package/build/types.d.ts +26 -4
  30. package/build/types.js +23 -7
  31. package/build/types.js.map +1 -1
  32. package/build/unsafe.cjs +14 -61
  33. package/build/unsafe.cjs.map +1 -1
  34. package/build/unsafe.d.ts +2 -27
  35. package/build/unsafe.js +2 -9
  36. package/build/unsafe.js.map +1 -1
  37. package/package.json +13 -16
  38. package/src/__tests__/index.ts +42 -0
  39. package/src/__tests__/iter.ts +218 -0
  40. package/src/__tests__/option.ts +48 -19
  41. package/src/__tests__/result.ts +286 -91
  42. package/src/__tests__/result.types.ts +3 -22
  43. package/src/__tests__/safe.ts +9 -15
  44. package/src/__tests__/unsafe.ts +11 -12
  45. package/src/index.ts +1 -18
  46. package/src/iter.ts +129 -0
  47. package/src/option.ts +36 -7
  48. package/src/result.ts +216 -113
  49. package/src/safe.ts +5 -42
  50. package/src/types.ts +52 -14
  51. package/src/unsafe.ts +2 -47
  52. package/build/devtools.cjs +0 -79
  53. package/build/devtools.cjs.map +0 -1
  54. package/build/devtools.d.ts +0 -82
  55. package/build/devtools.js +0 -43
  56. package/build/devtools.js.map +0 -1
  57. package/build/testing.cjs +0 -111
  58. package/build/testing.cjs.map +0 -1
  59. package/build/testing.d.ts +0 -85
  60. package/build/testing.js +0 -81
  61. package/build/testing.js.map +0 -1
  62. package/src/__tests__/tooling.ts +0 -86
  63. package/src/devtools.ts +0 -97
  64. package/src/testing.ts +0 -159
package/src/iter.ts ADDED
@@ -0,0 +1,129 @@
1
+ import { isSome, isErr, err as ERR } from './types.js';
2
+ import type { Option, Result, Ok } from './types.js';
3
+
4
+ /**
5
+ * Yields mapped values while the mapping function returns Some, stops at the first None.
6
+ * @param source - The iterable to map over
7
+ * @param fn - Mapping function returning Some(value) to continue or None to stop
8
+ * @returns Generator of mapped values
9
+ * @example
10
+ * [...mapWhile([1, 2, 3, 4], n => n < 3 ? some(n * 10) : none)] // [10, 20]
11
+ */
12
+ export function* mapWhile<T, U>(source: Iterable<T>, fn: (value: T) => Option<U>): Generator<U> {
13
+ for (const item of source) {
14
+ const mapped = fn(item);
15
+ if (!isSome(mapped)) return;
16
+ yield mapped as U;
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Wraps an iterable so that each yielded value becomes Ok and any throw becomes a single Err.
22
+ * Stops after the first error - the source's internal state is unknown after an exception.
23
+ *
24
+ * Uses a manual iterator instead of a generator to avoid coroutine suspend/resume overhead.
25
+ *
26
+ * Follows the for-of IteratorClose spec:
27
+ * - iter.return() is never called on natural exhaustion or after a caught error
28
+ * - iter.return() is only called on early consumer exit (break/return)
29
+ * - Cleanup errors from iter.return() are suppressed
30
+ *
31
+ * @param source - The iterable to wrap
32
+ * @returns Iterable iterator of Result values
33
+ * @example
34
+ * [...safeIter([1, 2, 3])] // [Ok(1), Ok(2), Ok(3)]
35
+ * [...safeIter(throwingIter())] // [Ok(1), Err(error)]
36
+ */
37
+ export function safeIter<T>(source: Iterable<T>): IterableIterator<Result<T, unknown>> {
38
+ const iter = source[Symbol.iterator]();
39
+ let done = false;
40
+ return {
41
+ [Symbol.iterator]() {
42
+ return this;
43
+ },
44
+ next(): IteratorResult<Result<T, unknown>> {
45
+ if (done) return { value: undefined, done: true };
46
+ try {
47
+ const next = iter.next();
48
+ if (next.done) {
49
+ done = true;
50
+ return next;
51
+ }
52
+ return { value: next.value as Ok<T>, done: false };
53
+ } catch (e) {
54
+ done = true;
55
+ return { value: ERR(e), done: false };
56
+ }
57
+ },
58
+ return(): IteratorResult<Result<T, unknown>> {
59
+ if (!done) {
60
+ done = true;
61
+ try {
62
+ iter.return?.();
63
+ } catch {
64
+ // suppressed
65
+ }
66
+ }
67
+ return { value: undefined, done: true };
68
+ },
69
+ };
70
+ }
71
+
72
+ // -- Result-oriented terminal operations --
73
+
74
+ /**
75
+ * Collects an iterable of Results into a single Result containing an array.
76
+ * Short-circuits on the first Err.
77
+ * @param source - Iterable of Result values
78
+ * @returns Ok(values[]) if all Ok, or the first Err encountered
79
+ * @example
80
+ * tryCollect([ok(1), ok(2), ok(3)]) // Ok([1, 2, 3])
81
+ * tryCollect([ok(1), err('x')]) // Err('x')
82
+ */
83
+ export function tryCollect<T, E>(source: Iterable<Result<T, E>>): Result<T[], E> {
84
+ const collected: T[] = [];
85
+ for (const result of source) {
86
+ if (isErr(result)) return result;
87
+ collected.push(result as T);
88
+ }
89
+ return collected as Ok<T[]>;
90
+ }
91
+
92
+ /**
93
+ * Folds an iterable with a fallible accumulator function.
94
+ * Short-circuits on the first Err returned by fn.
95
+ * @param source - The iterable to fold over
96
+ * @param init - Initial accumulator value
97
+ * @param fn - Folding function returning Ok(newAcc) or Err
98
+ * @returns Ok(finalAcc) if all steps succeed, or the first Err
99
+ * @example
100
+ * tryFold([1, 2, 3], 0, (acc, n) => ok(acc + n)) // Ok(6)
101
+ * tryFold([1, 2, 3], 0, (acc, n) => n === 2 ? err('stop') : ok(acc + n)) // Err('stop')
102
+ */
103
+ export function tryFold<T, Acc, E>(source: Iterable<T>, init: Acc, fn: (acc: Acc, item: T) => Result<Acc, E>): Result<Acc, E> {
104
+ let acc = init;
105
+ for (const item of source) {
106
+ const result = fn(acc, item);
107
+ if (isErr(result)) return result;
108
+ acc = result as Acc;
109
+ }
110
+ return acc as Ok<Acc>;
111
+ }
112
+
113
+ /**
114
+ * Iterates over a source, calling a fallible function for each item.
115
+ * Short-circuits on the first Err returned by fn.
116
+ * @param source - The iterable to iterate over
117
+ * @param fn - Function to call for each item, returning Ok(void) or Err
118
+ * @returns Ok(void) if all calls succeed, or the first Err
119
+ * @example
120
+ * tryForEach([1, 2, 3], n => ok(console.log(n))) // Ok(void), logs 1, 2, 3
121
+ * tryForEach([1, 2, 3], n => n === 2 ? err('stop') : ok(undefined)) // Err('stop')
122
+ */
123
+ export function tryForEach<T, E>(source: Iterable<T>, fn: (item: T) => Result<void, E>): Result<void, E> {
124
+ for (const item of source) {
125
+ const result = fn(item);
126
+ if (isErr(result)) return result;
127
+ }
128
+ return undefined as Ok<void>;
129
+ }
package/src/option.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NONE, isSome, isNone, optionOf as of, err, isOk, isErr } from './types.js';
1
+ import { NONE, EMPTY, isSome, isNone, optionOf as of, err, isOk, isErr } from './types.js';
2
2
  import type { Some, None, Option, NoneValueType, ValueType, Result, Ok, Widen } from './types.js';
3
3
 
4
4
  export type { Some, None, Option };
@@ -97,13 +97,28 @@ export function filterMap<T, U>(values: Iterable<T>, fn: (value: T) => Option<U>
97
97
  const collected: U[] = [];
98
98
  for (const value of values) {
99
99
  const mapped = fn(value);
100
- if (isSome(mapped)) {
101
- collected.push(mapped);
102
- }
100
+ if (isSome(mapped)) collected.push(mapped);
103
101
  }
104
102
  return collected;
105
103
  }
106
104
 
105
+ /**
106
+ * Finds the first element that maps to Some, returning that value.
107
+ * @param values - Iterable to search
108
+ * @param fn - Function that returns Some for matches
109
+ * @returns The first Some value, or None if no match
110
+ * @example
111
+ * findMap([1, 2, 3], n => n > 1 ? some(n * 2) : none) // Some(4)
112
+ * findMap([1], n => n > 5 ? some(n) : none) // None
113
+ */
114
+ export function findMap<T, U>(values: Iterable<T>, fn: (value: T) => Option<U>): Option<U> {
115
+ for (const value of values) {
116
+ const mapped = fn(value);
117
+ if (isSome(mapped)) return mapped;
118
+ }
119
+ return NONE;
120
+ }
121
+
107
122
  /**
108
123
  * Transforms the value inside a Some, or returns None.
109
124
  * @param opt - The Option to map
@@ -146,7 +161,7 @@ export function flatMap<T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Opti
146
161
  export function andThen<T, U>(opt: None, fn: (value: T) => Option<U>): None;
147
162
  export function andThen<T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Option<U>;
148
163
  export function andThen<T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Option<U> {
149
- return flatMap(opt, fn);
164
+ return isNone(opt) ? NONE : fn(opt);
150
165
  }
151
166
 
152
167
  /**
@@ -167,6 +182,20 @@ export function tap<T>(opt: Option<T>, fn: (value: T) => void): Option<T> {
167
182
  return opt;
168
183
  }
169
184
 
185
+ /**
186
+ * Returns true if None, or if Some and predicate returns true.
187
+ * @param opt - The Option to check
188
+ * @param predicate - Test function
189
+ * @returns true if None or predicate(value) is true
190
+ * @example
191
+ * isNoneOr(none, x => x > 2) // true
192
+ * isNoneOr(some(4), x => x > 2) // true
193
+ * isNoneOr(some(1), x => x > 2) // false
194
+ */
195
+ export function isNoneOr<T>(opt: Option<T>, predicate: (value: T) => boolean): boolean {
196
+ return isNone(opt) || predicate(opt);
197
+ }
198
+
170
199
  /**
171
200
  * Returns Some if the value passes the predicate, None otherwise.
172
201
  * @param opt - The Option to filter
@@ -407,8 +436,8 @@ export function isSomeAnd<T>(opt: Option<T>, predicate: (value: T) => boolean):
407
436
  * toArray(some(42)) // [42]
408
437
  * toArray(none) // []
409
438
  */
410
- export function toArray<T>(opt: Option<T>): T[] {
411
- return isSome(opt) ? [opt] : [];
439
+ export function toArray<T>(opt: Option<T>): readonly T[] {
440
+ return isSome(opt) ? [opt] : (EMPTY as readonly T[]);
412
441
  }
413
442
 
414
443
  /**