happy-rusty 1.9.0 → 1.9.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.
- package/CHANGELOG.md +26 -0
- package/dist/main.cjs +2255 -1453
- package/dist/main.cjs.map +1 -1
- package/dist/main.mjs +2254 -1449
- package/dist/main.mjs.map +1 -1
- package/dist/types.d.ts +54 -103
- package/package.json +13 -13
package/dist/main.cjs
CHANGED
|
@@ -1,1507 +1,2308 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/core/option/symbols.ts
|
|
3
|
+
/**
|
|
4
|
+
* @module
|
|
5
|
+
* Internal symbol used to identify `Option` type variants.
|
|
6
|
+
*
|
|
7
|
+
* This symbol is used as a property key to distinguish between `Some` and `None` variants.
|
|
8
|
+
* It provides a reliable way to identify the variant of an `Option` instance without
|
|
9
|
+
* relying on method calls or duck typing.
|
|
10
|
+
*
|
|
11
|
+
* Note: This symbol is an internal implementation detail and is not exported as part of the public API.
|
|
12
|
+
* Use the `isOption` utility function for type checking instead.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* A unique symbol used as a property key to identify the variant of an `Option` instance.
|
|
16
|
+
*
|
|
17
|
+
* When accessed on an `Option`, returns `'Some'` if the Option contains a value,
|
|
18
|
+
* or `'None'` if it represents the absence of a value.
|
|
19
|
+
*
|
|
20
|
+
* This symbol is used internally by the `isOption` utility function to verify
|
|
21
|
+
* that an object is a valid `Option` instance.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
5
25
|
const OptionKindSymbol = /* @__PURE__ */ Symbol("Option kind");
|
|
6
|
-
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/core/option/guards.ts
|
|
28
|
+
/**
|
|
29
|
+
* Checks if a value is an `Option`.
|
|
30
|
+
*
|
|
31
|
+
* @typeParam T - The expected type of the value contained within the `Option`.
|
|
32
|
+
* @param o - The value to be checked as an `Option`.
|
|
33
|
+
* @returns `true` if the value is an `Option`, otherwise `false`.
|
|
34
|
+
* @since 1.2.0
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const x = Some(5);
|
|
38
|
+
* console.log(isOption(x)); // true
|
|
39
|
+
* console.log(isOption(null)); // false
|
|
40
|
+
* console.log(isOption({ value: 5 })); // false
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
7
43
|
function isOption(o) {
|
|
8
|
-
|
|
44
|
+
return o != null && typeof o === "object" && OptionKindSymbol in o;
|
|
9
45
|
}
|
|
10
|
-
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/core/result/symbols.ts
|
|
48
|
+
/**
|
|
49
|
+
* @module
|
|
50
|
+
* Internal symbol used to identify `Result` type variants.
|
|
51
|
+
*
|
|
52
|
+
* This symbol is used as a property key to distinguish between `Ok` and `Err` variants.
|
|
53
|
+
* It provides a reliable way to identify the variant of a `Result` instance without
|
|
54
|
+
* relying on method calls or duck typing.
|
|
55
|
+
*
|
|
56
|
+
* Note: This symbol is an internal implementation detail and is not exported as part of the public API.
|
|
57
|
+
* Use the `isResult` utility function for type checking instead.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* A unique symbol used as a property key to identify the variant of a `Result` instance.
|
|
61
|
+
*
|
|
62
|
+
* When accessed on a `Result`, returns `'Ok'` if the Result represents success,
|
|
63
|
+
* or `'Err'` if it represents failure.
|
|
64
|
+
*
|
|
65
|
+
* This symbol is used internally by the `isResult` utility function to verify
|
|
66
|
+
* that an object is a valid `Result` instance.
|
|
67
|
+
*
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
11
70
|
const ResultKindSymbol = /* @__PURE__ */ Symbol("Result kind");
|
|
12
|
-
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/core/result/guards.ts
|
|
73
|
+
/**
|
|
74
|
+
* Checks if a value is a `Result`.
|
|
75
|
+
*
|
|
76
|
+
* @typeParam T - The expected type of the success value contained within the `Result`.
|
|
77
|
+
* @typeParam E - The expected type of the error value contained within the `Result`.
|
|
78
|
+
* @param r - The value to be checked as a `Result`.
|
|
79
|
+
* @returns `true` if the value is a `Result`, otherwise `false`.
|
|
80
|
+
* @since 1.2.0
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const x = Ok(5);
|
|
84
|
+
* console.log(isResult(x)); // true
|
|
85
|
+
* console.log(isResult(null)); // false
|
|
86
|
+
* console.log(isResult({ value: 5 })); // false
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
13
89
|
function isResult(r) {
|
|
14
|
-
|
|
90
|
+
return r != null && typeof r === "object" && ResultKindSymbol in r;
|
|
15
91
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/core/prelude.ts
|
|
94
|
+
/**
|
|
95
|
+
* @module
|
|
96
|
+
* Constructors and factory functions for creating `Option` and `Result` types.
|
|
97
|
+
*
|
|
98
|
+
* This module exports:
|
|
99
|
+
* - `Some<T>(value)` - Creates an Option containing a value
|
|
100
|
+
* - `None` - Constant representing absence of value
|
|
101
|
+
* - `Ok<T, E>(value)` - Creates a successful Result
|
|
102
|
+
* - `Err<T, E>(error)` - Creates a failed Result
|
|
103
|
+
* - `None` interface - Type overrides for better type inference
|
|
104
|
+
*/
|
|
105
|
+
const ASYNC_TRUE$1 = /* @__PURE__ */ Promise.resolve(true);
|
|
106
|
+
const ASYNC_FALSE$1 = /* @__PURE__ */ Promise.resolve(false);
|
|
107
|
+
/**
|
|
108
|
+
* Creates an `Option<T>` representing the presence of a value.
|
|
109
|
+
* This function is typically used to construct an `Option` that contains a value, indicating that the operation yielding the value was successful.
|
|
110
|
+
*
|
|
111
|
+
* @typeParam T - The type of the value to be wrapped in a `Some`.
|
|
112
|
+
* @param value - The value to wrap as a `Some` option.
|
|
113
|
+
* @returns An `Option<T>` that contains the provided value, representing the `Some` case.
|
|
114
|
+
* @since 1.0.0
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* const maybeValue = Some(1); // Option<number> with a value
|
|
118
|
+
* if (maybeValue.isSome()) {
|
|
119
|
+
* console.log(maybeValue.unwrap()); // Outputs: 1
|
|
120
|
+
* }
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
19
123
|
function Some(value) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
});
|
|
140
|
-
return some;
|
|
124
|
+
const some = Object.freeze({
|
|
125
|
+
[Symbol.toStringTag]: "Option",
|
|
126
|
+
[OptionKindSymbol]: "Some",
|
|
127
|
+
*[Symbol.iterator]() {
|
|
128
|
+
yield value;
|
|
129
|
+
},
|
|
130
|
+
toString() {
|
|
131
|
+
return `Some(${value})`;
|
|
132
|
+
},
|
|
133
|
+
isSome() {
|
|
134
|
+
return true;
|
|
135
|
+
},
|
|
136
|
+
isNone() {
|
|
137
|
+
return false;
|
|
138
|
+
},
|
|
139
|
+
isSomeAnd(predicate) {
|
|
140
|
+
return predicate(value);
|
|
141
|
+
},
|
|
142
|
+
isSomeAndAsync(predicate) {
|
|
143
|
+
return Promise.resolve(predicate(value));
|
|
144
|
+
},
|
|
145
|
+
isNoneOr(predicate) {
|
|
146
|
+
return predicate(value);
|
|
147
|
+
},
|
|
148
|
+
isNoneOrAsync(predicate) {
|
|
149
|
+
return Promise.resolve(predicate(value));
|
|
150
|
+
},
|
|
151
|
+
expect(_msg) {
|
|
152
|
+
return value;
|
|
153
|
+
},
|
|
154
|
+
unwrap() {
|
|
155
|
+
return value;
|
|
156
|
+
},
|
|
157
|
+
unwrapOr(_defaultValue) {
|
|
158
|
+
return value;
|
|
159
|
+
},
|
|
160
|
+
unwrapOrElse(_fn) {
|
|
161
|
+
return value;
|
|
162
|
+
},
|
|
163
|
+
unwrapOrElseAsync(_fn) {
|
|
164
|
+
return Promise.resolve(value);
|
|
165
|
+
},
|
|
166
|
+
okOr(_error) {
|
|
167
|
+
return Ok(value);
|
|
168
|
+
},
|
|
169
|
+
okOrElse(_err) {
|
|
170
|
+
return Ok(value);
|
|
171
|
+
},
|
|
172
|
+
transpose() {
|
|
173
|
+
assertResult(value);
|
|
174
|
+
return value.isOk() ? Ok(Some(value.unwrap())) : Err(value.unwrapErr());
|
|
175
|
+
},
|
|
176
|
+
filter(predicate) {
|
|
177
|
+
return predicate(value) ? some : None;
|
|
178
|
+
},
|
|
179
|
+
flatten() {
|
|
180
|
+
assertOption(value);
|
|
181
|
+
return value;
|
|
182
|
+
},
|
|
183
|
+
map(fn) {
|
|
184
|
+
return Some(fn(value));
|
|
185
|
+
},
|
|
186
|
+
mapOr(_defaultValue, fn) {
|
|
187
|
+
return fn(value);
|
|
188
|
+
},
|
|
189
|
+
mapOrElse(_defaultFn, fn) {
|
|
190
|
+
return fn(value);
|
|
191
|
+
},
|
|
192
|
+
zip(other) {
|
|
193
|
+
assertOption(other);
|
|
194
|
+
return other.isSome() ? Some([value, other.unwrap()]) : None;
|
|
195
|
+
},
|
|
196
|
+
zipWith(other, fn) {
|
|
197
|
+
assertOption(other);
|
|
198
|
+
return other.isSome() ? Some(fn(value, other.unwrap())) : None;
|
|
199
|
+
},
|
|
200
|
+
unzip() {
|
|
201
|
+
const tuple = value;
|
|
202
|
+
if (!Array.isArray(tuple) || tuple.length !== 2) throw new TypeError(`Option::unzip() requires a 2-element tuple, received ${Array.isArray(tuple) ? `array with ${tuple.length} elements` : typeof tuple}`);
|
|
203
|
+
const [a, b] = tuple;
|
|
204
|
+
return [Some(a), Some(b)];
|
|
205
|
+
},
|
|
206
|
+
reduce(other, fn) {
|
|
207
|
+
assertOption(other);
|
|
208
|
+
return other.isSome() ? Some(fn(value, other.unwrap())) : some;
|
|
209
|
+
},
|
|
210
|
+
and(other) {
|
|
211
|
+
assertOption(other);
|
|
212
|
+
return other;
|
|
213
|
+
},
|
|
214
|
+
andThen(fn) {
|
|
215
|
+
return fn(value);
|
|
216
|
+
},
|
|
217
|
+
andThenAsync(fn) {
|
|
218
|
+
return Promise.resolve(fn(value));
|
|
219
|
+
},
|
|
220
|
+
or(_other) {
|
|
221
|
+
return some;
|
|
222
|
+
},
|
|
223
|
+
orElse(_fn) {
|
|
224
|
+
return some;
|
|
225
|
+
},
|
|
226
|
+
orElseAsync(_fn) {
|
|
227
|
+
return Promise.resolve(some);
|
|
228
|
+
},
|
|
229
|
+
xor(other) {
|
|
230
|
+
assertOption(other);
|
|
231
|
+
return other.isSome() ? None : some;
|
|
232
|
+
},
|
|
233
|
+
inspect(fn) {
|
|
234
|
+
fn(value);
|
|
235
|
+
return some;
|
|
236
|
+
},
|
|
237
|
+
eq(other) {
|
|
238
|
+
assertOption(other);
|
|
239
|
+
return other.isSome() && other.unwrap() === value;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return some;
|
|
141
243
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
244
|
+
/**
|
|
245
|
+
* A constant representing the `None` case of an `Option`, indicating the absence of a value.
|
|
246
|
+
* This constant is frozen to ensure it is immutable and cannot be altered, preserving the integrity of `None` throughout the application.
|
|
247
|
+
*
|
|
248
|
+
* @since 1.0.0
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* // Use None to represent absence of a value
|
|
252
|
+
* function findUser(id: number): Option<User> {
|
|
253
|
+
* const user = users.find(u => u.id === id);
|
|
254
|
+
* return user ? Some(user) : None;
|
|
255
|
+
* }
|
|
256
|
+
*
|
|
257
|
+
* // None is a singleton, so you can compare by reference
|
|
258
|
+
* const result = findUser(999);
|
|
259
|
+
* if (result === None) {
|
|
260
|
+
* console.log('User not found');
|
|
261
|
+
* }
|
|
262
|
+
*
|
|
263
|
+
* // Use with Option methods
|
|
264
|
+
* const name = None.unwrapOr('Anonymous'); // 'Anonymous'
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
const None = /* @__PURE__ */ Object.freeze({
|
|
268
|
+
[Symbol.toStringTag]: "Option",
|
|
269
|
+
[OptionKindSymbol]: "None",
|
|
270
|
+
*[Symbol.iterator]() {},
|
|
271
|
+
toString() {
|
|
272
|
+
return "None";
|
|
273
|
+
},
|
|
274
|
+
isSome() {
|
|
275
|
+
return false;
|
|
276
|
+
},
|
|
277
|
+
isNone() {
|
|
278
|
+
return true;
|
|
279
|
+
},
|
|
280
|
+
isSomeAnd(_predicate) {
|
|
281
|
+
return false;
|
|
282
|
+
},
|
|
283
|
+
isSomeAndAsync(_predicate) {
|
|
284
|
+
return ASYNC_FALSE$1;
|
|
285
|
+
},
|
|
286
|
+
isNoneOr(_predicate) {
|
|
287
|
+
return true;
|
|
288
|
+
},
|
|
289
|
+
isNoneOrAsync(_predicate) {
|
|
290
|
+
return ASYNC_TRUE$1;
|
|
291
|
+
},
|
|
292
|
+
expect(msg) {
|
|
293
|
+
throw new TypeError(msg);
|
|
294
|
+
},
|
|
295
|
+
unwrap() {
|
|
296
|
+
throw new TypeError("Option::unwrap() called on a `None` value");
|
|
297
|
+
},
|
|
298
|
+
unwrapOr(defaultValue) {
|
|
299
|
+
return defaultValue;
|
|
300
|
+
},
|
|
301
|
+
unwrapOrElse(fn) {
|
|
302
|
+
return fn();
|
|
303
|
+
},
|
|
304
|
+
unwrapOrElseAsync(fn) {
|
|
305
|
+
return Promise.resolve(fn());
|
|
306
|
+
},
|
|
307
|
+
okOr(error) {
|
|
308
|
+
return Err(error);
|
|
309
|
+
},
|
|
310
|
+
okOrElse(err) {
|
|
311
|
+
return Err(err());
|
|
312
|
+
},
|
|
313
|
+
transpose() {
|
|
314
|
+
return Ok(None);
|
|
315
|
+
},
|
|
316
|
+
filter(_predicate) {
|
|
317
|
+
return None;
|
|
318
|
+
},
|
|
319
|
+
flatten() {
|
|
320
|
+
return None;
|
|
321
|
+
},
|
|
322
|
+
map(_fn) {
|
|
323
|
+
return None;
|
|
324
|
+
},
|
|
325
|
+
mapOr(defaultValue, _fn) {
|
|
326
|
+
return defaultValue;
|
|
327
|
+
},
|
|
328
|
+
mapOrElse(defaultFn, _fn) {
|
|
329
|
+
return defaultFn();
|
|
330
|
+
},
|
|
331
|
+
zip(_other) {
|
|
332
|
+
return None;
|
|
333
|
+
},
|
|
334
|
+
zipWith(_other, _fn) {
|
|
335
|
+
return None;
|
|
336
|
+
},
|
|
337
|
+
unzip() {
|
|
338
|
+
return [None, None];
|
|
339
|
+
},
|
|
340
|
+
reduce(other, _fn) {
|
|
341
|
+
assertOption(other);
|
|
342
|
+
return other;
|
|
343
|
+
},
|
|
344
|
+
and(_other) {
|
|
345
|
+
return None;
|
|
346
|
+
},
|
|
347
|
+
andThen(_fn) {
|
|
348
|
+
return None;
|
|
349
|
+
},
|
|
350
|
+
andThenAsync(_fn) {
|
|
351
|
+
return ASYNC_NONE;
|
|
352
|
+
},
|
|
353
|
+
or(other) {
|
|
354
|
+
assertOption(other);
|
|
355
|
+
return other;
|
|
356
|
+
},
|
|
357
|
+
orElse(fn) {
|
|
358
|
+
return fn();
|
|
359
|
+
},
|
|
360
|
+
orElseAsync(fn) {
|
|
361
|
+
return Promise.resolve(fn());
|
|
362
|
+
},
|
|
363
|
+
xor(other) {
|
|
364
|
+
assertOption(other);
|
|
365
|
+
return other.isSome() ? other : None;
|
|
366
|
+
},
|
|
367
|
+
inspect(_fn) {
|
|
368
|
+
return None;
|
|
369
|
+
},
|
|
370
|
+
eq(other) {
|
|
371
|
+
assertOption(other);
|
|
372
|
+
return other === None;
|
|
373
|
+
}
|
|
250
374
|
});
|
|
251
|
-
|
|
375
|
+
/**
|
|
376
|
+
* Async Option constant for `None`.
|
|
377
|
+
* A pre-resolved `Promise<None>` that can be reused to avoid creating
|
|
378
|
+
* new Promise instances when returning `None` from async functions.
|
|
379
|
+
*
|
|
380
|
+
* Since `None extends Option<never>`, this constant can be assigned to any
|
|
381
|
+
* `AsyncOption<T>` (i.e., `Promise<Option<T>>`) due to TypeScript's covariance.
|
|
382
|
+
*
|
|
383
|
+
* @since 1.8.0
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* async function findUser(id: number): AsyncOption<User> {
|
|
387
|
+
* if (id < 0) {
|
|
388
|
+
* return ASYNC_NONE;
|
|
389
|
+
* }
|
|
390
|
+
* const user = await db.findUser(id);
|
|
391
|
+
* return user ? Some(user) : ASYNC_NONE;
|
|
392
|
+
* }
|
|
393
|
+
* ```
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```ts
|
|
397
|
+
* // Useful in conditional async returns
|
|
398
|
+
* function maybeLoadAsync(shouldLoad: boolean): AsyncOption<Data> {
|
|
399
|
+
* if (!shouldLoad) {
|
|
400
|
+
* return ASYNC_NONE;
|
|
401
|
+
* }
|
|
402
|
+
* return loadDataAsync();
|
|
403
|
+
* }
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
406
|
+
const ASYNC_NONE = /* @__PURE__ */ Promise.resolve(None);
|
|
252
407
|
function Ok(value) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
return ok;
|
|
408
|
+
const ok = Object.freeze({
|
|
409
|
+
[Symbol.toStringTag]: "Result",
|
|
410
|
+
[ResultKindSymbol]: "Ok",
|
|
411
|
+
*[Symbol.iterator]() {
|
|
412
|
+
yield value;
|
|
413
|
+
},
|
|
414
|
+
toString() {
|
|
415
|
+
return `Ok(${value})`;
|
|
416
|
+
},
|
|
417
|
+
isOk() {
|
|
418
|
+
return true;
|
|
419
|
+
},
|
|
420
|
+
isErr() {
|
|
421
|
+
return false;
|
|
422
|
+
},
|
|
423
|
+
isOkAnd(predicate) {
|
|
424
|
+
return predicate(value);
|
|
425
|
+
},
|
|
426
|
+
isOkAndAsync(predicate) {
|
|
427
|
+
return Promise.resolve(predicate(value));
|
|
428
|
+
},
|
|
429
|
+
isErrAnd(_predicate) {
|
|
430
|
+
return false;
|
|
431
|
+
},
|
|
432
|
+
isErrAndAsync(_predicate) {
|
|
433
|
+
return ASYNC_FALSE$1;
|
|
434
|
+
},
|
|
435
|
+
expect(_msg) {
|
|
436
|
+
return value;
|
|
437
|
+
},
|
|
438
|
+
unwrap() {
|
|
439
|
+
return value;
|
|
440
|
+
},
|
|
441
|
+
unwrapOr(_defaultValue) {
|
|
442
|
+
return value;
|
|
443
|
+
},
|
|
444
|
+
unwrapOrElse(_fn) {
|
|
445
|
+
return value;
|
|
446
|
+
},
|
|
447
|
+
unwrapOrElseAsync(_fn) {
|
|
448
|
+
return Promise.resolve(value);
|
|
449
|
+
},
|
|
450
|
+
expectErr(msg) {
|
|
451
|
+
throw new TypeError(`${msg}: ${value}`);
|
|
452
|
+
},
|
|
453
|
+
unwrapErr() {
|
|
454
|
+
throw new TypeError("Result::unwrapErr() called on an `Ok` value");
|
|
455
|
+
},
|
|
456
|
+
intoOk() {
|
|
457
|
+
return value;
|
|
458
|
+
},
|
|
459
|
+
intoErr() {
|
|
460
|
+
throw new TypeError("Result::intoErr() called on an `Ok` value");
|
|
461
|
+
},
|
|
462
|
+
ok() {
|
|
463
|
+
return Some(value);
|
|
464
|
+
},
|
|
465
|
+
err() {
|
|
466
|
+
return None;
|
|
467
|
+
},
|
|
468
|
+
transpose() {
|
|
469
|
+
assertOption(value);
|
|
470
|
+
return value.isSome() ? Some(Ok(value.unwrap())) : None;
|
|
471
|
+
},
|
|
472
|
+
map(fn) {
|
|
473
|
+
return Ok(fn(value));
|
|
474
|
+
},
|
|
475
|
+
mapErr(_fn) {
|
|
476
|
+
return ok;
|
|
477
|
+
},
|
|
478
|
+
mapOr(_defaultValue, fn) {
|
|
479
|
+
return fn(value);
|
|
480
|
+
},
|
|
481
|
+
mapOrElse(_defaultFn, fn) {
|
|
482
|
+
return fn(value);
|
|
483
|
+
},
|
|
484
|
+
flatten() {
|
|
485
|
+
assertResult(value);
|
|
486
|
+
return value;
|
|
487
|
+
},
|
|
488
|
+
and(other) {
|
|
489
|
+
assertResult(other);
|
|
490
|
+
return other;
|
|
491
|
+
},
|
|
492
|
+
or(_other) {
|
|
493
|
+
return ok;
|
|
494
|
+
},
|
|
495
|
+
andThen(fn) {
|
|
496
|
+
return fn(value);
|
|
497
|
+
},
|
|
498
|
+
andThenAsync(fn) {
|
|
499
|
+
return Promise.resolve(fn(value));
|
|
500
|
+
},
|
|
501
|
+
orElse(_fn) {
|
|
502
|
+
return ok;
|
|
503
|
+
},
|
|
504
|
+
orElseAsync(_fn) {
|
|
505
|
+
return Promise.resolve(ok);
|
|
506
|
+
},
|
|
507
|
+
inspect(fn) {
|
|
508
|
+
fn(value);
|
|
509
|
+
return ok;
|
|
510
|
+
},
|
|
511
|
+
inspectErr(_fn) {
|
|
512
|
+
return ok;
|
|
513
|
+
},
|
|
514
|
+
eq(other) {
|
|
515
|
+
assertResult(other);
|
|
516
|
+
return other.isOk() && other.unwrap() === value;
|
|
517
|
+
},
|
|
518
|
+
asOk() {
|
|
519
|
+
return ok;
|
|
520
|
+
},
|
|
521
|
+
asErr() {
|
|
522
|
+
throw new TypeError("Result::asErr() called on an `Ok` value");
|
|
523
|
+
},
|
|
524
|
+
andTryAsync(fn) {
|
|
525
|
+
try {
|
|
526
|
+
const result = fn(value);
|
|
527
|
+
return Promise.resolve(result).then(Ok, Err);
|
|
528
|
+
} catch (e) {
|
|
529
|
+
return Promise.resolve(Err(e));
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
orTryAsync(_fn) {
|
|
533
|
+
return Promise.resolve(ok);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
return ok;
|
|
385
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* Creates a `Result<T, E>` representing a failed outcome containing an error.
|
|
540
|
+
* This function is used to construct a `Result` that signifies the operation failed by containing the error `E`.
|
|
541
|
+
*
|
|
542
|
+
* @typeParam T - The type of the value that the result could potentially contain (not used in this case).
|
|
543
|
+
* @typeParam E - The type of the error to be wrapped in the `Err` result.
|
|
544
|
+
* @param error - The error to wrap as an `Err` result.
|
|
545
|
+
* @returns A `Result<T, E>` that contains the provided error, representing the `Err` case.
|
|
546
|
+
* @since 1.0.0
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* const badResult = Err<number, Error>(new Error('Something went wrong'));
|
|
550
|
+
* if (badResult.isErr()) {
|
|
551
|
+
* console.error(badResult.unwrapErr()); // Outputs: Error: Something went wrong
|
|
552
|
+
* }
|
|
553
|
+
* ```
|
|
554
|
+
*/
|
|
386
555
|
function Err(error) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
return err;
|
|
556
|
+
const err = Object.freeze({
|
|
557
|
+
[Symbol.toStringTag]: "Result",
|
|
558
|
+
[ResultKindSymbol]: "Err",
|
|
559
|
+
*[Symbol.iterator]() {},
|
|
560
|
+
toString() {
|
|
561
|
+
return `Err(${error})`;
|
|
562
|
+
},
|
|
563
|
+
isOk() {
|
|
564
|
+
return false;
|
|
565
|
+
},
|
|
566
|
+
isErr() {
|
|
567
|
+
return true;
|
|
568
|
+
},
|
|
569
|
+
isOkAnd(_predicate) {
|
|
570
|
+
return false;
|
|
571
|
+
},
|
|
572
|
+
isOkAndAsync(_predicate) {
|
|
573
|
+
return ASYNC_FALSE$1;
|
|
574
|
+
},
|
|
575
|
+
isErrAnd(predicate) {
|
|
576
|
+
return predicate(error);
|
|
577
|
+
},
|
|
578
|
+
isErrAndAsync(predicate) {
|
|
579
|
+
return Promise.resolve(predicate(error));
|
|
580
|
+
},
|
|
581
|
+
expect(msg) {
|
|
582
|
+
throw new TypeError(`${msg}: ${error}`);
|
|
583
|
+
},
|
|
584
|
+
unwrap() {
|
|
585
|
+
throw new TypeError("Result::unwrap() called on an `Err` value");
|
|
586
|
+
},
|
|
587
|
+
unwrapOr(defaultValue) {
|
|
588
|
+
return defaultValue;
|
|
589
|
+
},
|
|
590
|
+
unwrapOrElse(fn) {
|
|
591
|
+
return fn(error);
|
|
592
|
+
},
|
|
593
|
+
unwrapOrElseAsync(fn) {
|
|
594
|
+
return Promise.resolve(fn(error));
|
|
595
|
+
},
|
|
596
|
+
expectErr(_msg) {
|
|
597
|
+
return error;
|
|
598
|
+
},
|
|
599
|
+
unwrapErr() {
|
|
600
|
+
return error;
|
|
601
|
+
},
|
|
602
|
+
intoOk() {
|
|
603
|
+
throw new TypeError("Result::intoOk() called on an `Err` value");
|
|
604
|
+
},
|
|
605
|
+
intoErr() {
|
|
606
|
+
return error;
|
|
607
|
+
},
|
|
608
|
+
ok() {
|
|
609
|
+
return None;
|
|
610
|
+
},
|
|
611
|
+
err() {
|
|
612
|
+
return Some(error);
|
|
613
|
+
},
|
|
614
|
+
transpose() {
|
|
615
|
+
return Some(err);
|
|
616
|
+
},
|
|
617
|
+
map(_fn) {
|
|
618
|
+
return err;
|
|
619
|
+
},
|
|
620
|
+
mapErr(fn) {
|
|
621
|
+
return Err(fn(error));
|
|
622
|
+
},
|
|
623
|
+
mapOr(defaultValue, _fn) {
|
|
624
|
+
return defaultValue;
|
|
625
|
+
},
|
|
626
|
+
mapOrElse(defaultFn, _fn) {
|
|
627
|
+
return defaultFn(error);
|
|
628
|
+
},
|
|
629
|
+
flatten() {
|
|
630
|
+
return err;
|
|
631
|
+
},
|
|
632
|
+
and(_other) {
|
|
633
|
+
return err;
|
|
634
|
+
},
|
|
635
|
+
or(other) {
|
|
636
|
+
assertResult(other);
|
|
637
|
+
return other;
|
|
638
|
+
},
|
|
639
|
+
andThen(_fn) {
|
|
640
|
+
return err;
|
|
641
|
+
},
|
|
642
|
+
andThenAsync(_fn) {
|
|
643
|
+
return Promise.resolve(err);
|
|
644
|
+
},
|
|
645
|
+
orElse(fn) {
|
|
646
|
+
return fn(error);
|
|
647
|
+
},
|
|
648
|
+
orElseAsync(fn) {
|
|
649
|
+
return Promise.resolve(fn(error));
|
|
650
|
+
},
|
|
651
|
+
inspect(_fn) {
|
|
652
|
+
return err;
|
|
653
|
+
},
|
|
654
|
+
inspectErr(fn) {
|
|
655
|
+
fn(error);
|
|
656
|
+
return err;
|
|
657
|
+
},
|
|
658
|
+
eq(other) {
|
|
659
|
+
assertResult(other);
|
|
660
|
+
return other.isErr() && other.unwrapErr() === error;
|
|
661
|
+
},
|
|
662
|
+
asOk() {
|
|
663
|
+
throw new TypeError("Result::asOk() called on an `Err` value");
|
|
664
|
+
},
|
|
665
|
+
asErr() {
|
|
666
|
+
return err;
|
|
667
|
+
},
|
|
668
|
+
andTryAsync(_fn) {
|
|
669
|
+
return Promise.resolve(err);
|
|
670
|
+
},
|
|
671
|
+
orTryAsync(fn) {
|
|
672
|
+
try {
|
|
673
|
+
const result = fn(error);
|
|
674
|
+
return Promise.resolve(result).then(Ok, Err);
|
|
675
|
+
} catch (e) {
|
|
676
|
+
return Promise.resolve(Err(e));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
return err;
|
|
516
681
|
}
|
|
682
|
+
/**
|
|
683
|
+
* Safely converts a value to a string representation for error messages.
|
|
684
|
+
* Handles cases where `toString()` might throw or values are null/undefined.
|
|
685
|
+
*
|
|
686
|
+
* @param value - The value to stringify.
|
|
687
|
+
* @returns A safe string representation of the value.
|
|
688
|
+
*/
|
|
517
689
|
function safeStringify(value) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return Object.prototype.toString.call(value);
|
|
527
|
-
}
|
|
528
|
-
return String(value);
|
|
529
|
-
} catch {
|
|
530
|
-
return "[unable to stringify]";
|
|
531
|
-
}
|
|
690
|
+
try {
|
|
691
|
+
if (value === null) return "null";
|
|
692
|
+
if (value === void 0) return "undefined";
|
|
693
|
+
if (typeof value === "object") return Object.prototype.toString.call(value);
|
|
694
|
+
return String(value);
|
|
695
|
+
} catch {
|
|
696
|
+
return "[unable to stringify]";
|
|
697
|
+
}
|
|
532
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* Asserts that a given value is an `Option`.
|
|
701
|
+
*
|
|
702
|
+
* @typeParam T - The expected type of the value contained within the `Option`.
|
|
703
|
+
* @param o - The value to be checked as an `Option`.
|
|
704
|
+
* @throws {TypeError} If the value is not an `Option`.
|
|
705
|
+
* @see isOption
|
|
706
|
+
*/
|
|
533
707
|
function assertOption(o) {
|
|
534
|
-
|
|
535
|
-
throw new TypeError(`Expected an Option, but received: ${safeStringify(o)}`);
|
|
536
|
-
}
|
|
708
|
+
if (!isOption(o)) throw new TypeError(`Expected an Option, but received: ${safeStringify(o)}`);
|
|
537
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* Asserts that a given value is a `Result`.
|
|
712
|
+
*
|
|
713
|
+
* @typeParam T - The expected type of the success value contained within the `Result`.
|
|
714
|
+
* @typeParam E - The expected type of the error value contained within the `Result`.
|
|
715
|
+
* @param r - The value to be checked as a `Result`.
|
|
716
|
+
* @throws {TypeError} If the value is not a `Result`.
|
|
717
|
+
* @see isResult
|
|
718
|
+
*/
|
|
538
719
|
function assertResult(r) {
|
|
539
|
-
|
|
540
|
-
throw new TypeError(`Expected a Result, but received: ${safeStringify(r)}`);
|
|
541
|
-
}
|
|
720
|
+
if (!isResult(r)) throw new TypeError(`Expected a Result, but received: ${safeStringify(r)}`);
|
|
542
721
|
}
|
|
543
|
-
|
|
722
|
+
//#endregion
|
|
723
|
+
//#region src/core/option/extensions.ts
|
|
724
|
+
/**
|
|
725
|
+
* @module
|
|
726
|
+
* Extension functions for bridging standard JavaScript patterns with Option types.
|
|
727
|
+
*
|
|
728
|
+
* This module provides utilities for:
|
|
729
|
+
* - Converting try-catch patterns to Option-based handling
|
|
730
|
+
* - Integrating async/await patterns with Option types
|
|
731
|
+
*/
|
|
732
|
+
/**
|
|
733
|
+
* Executes a function and returns `Some` with the result if successful, or `None` if it throws.
|
|
734
|
+
*
|
|
735
|
+
* This converts try-catch patterns to Option-based handling, where you only care
|
|
736
|
+
* about success/failure, not the error details.
|
|
737
|
+
*
|
|
738
|
+
* Similar to `Promise.try`, this function accepts optional arguments that are passed to the function.
|
|
739
|
+
*
|
|
740
|
+
* @typeParam T - The type of the value returned by the function.
|
|
741
|
+
* @typeParam Args - The types of the arguments to pass to the function.
|
|
742
|
+
* @param fn - A function that may throw an exception.
|
|
743
|
+
* @param args - Arguments to pass to the function.
|
|
744
|
+
* @returns `Some<T>` if the function succeeds, or `None` if it throws.
|
|
745
|
+
* @since 1.7.0
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* // Parse JSON, ignore error details
|
|
749
|
+
* const data = tryOption(JSON.parse, jsonString);
|
|
750
|
+
* console.log(data.unwrapOr(defaultData));
|
|
751
|
+
* ```
|
|
752
|
+
*
|
|
753
|
+
* @example
|
|
754
|
+
* ```ts
|
|
755
|
+
* // Validate URL - using closure form
|
|
756
|
+
* const url = tryOption(() => new URL(input));
|
|
757
|
+
* url.inspect(u => console.log('Valid URL:', u.href));
|
|
758
|
+
* ```
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```ts
|
|
762
|
+
* // Decode URI component with arguments
|
|
763
|
+
* const decoded = tryOption(decodeURIComponent, str);
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
544
766
|
function tryOption(fn, ...args) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
767
|
+
try {
|
|
768
|
+
return Some(fn(...args));
|
|
769
|
+
} catch {
|
|
770
|
+
return None;
|
|
771
|
+
}
|
|
550
772
|
}
|
|
551
773
|
async function tryAsyncOption(task, ...args) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
774
|
+
try {
|
|
775
|
+
return Some(await (typeof task === "function" ? task(...args) : task));
|
|
776
|
+
} catch {
|
|
777
|
+
return None;
|
|
778
|
+
}
|
|
558
779
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region src/core/result/constants.ts
|
|
782
|
+
/**
|
|
783
|
+
* @module
|
|
784
|
+
* Pre-defined Result constants for common return values.
|
|
785
|
+
*
|
|
786
|
+
* These immutable constants can be reused throughout the application to avoid
|
|
787
|
+
* creating new Result instances for common values like `true`, `false`, `0`, and `void`.
|
|
788
|
+
*
|
|
789
|
+
* The error type is `never` because these are always `Ok` values that can never
|
|
790
|
+
* contain an error. This allows them to be assigned to any `Result<T, E>` type.
|
|
791
|
+
*/
|
|
792
|
+
/**
|
|
793
|
+
* Result constant for `true`.
|
|
794
|
+
* Can be used anywhere due to immutability.
|
|
795
|
+
* @since 1.3.0
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* function validate(): Result<boolean, Error> {
|
|
799
|
+
* return RESULT_TRUE;
|
|
800
|
+
* }
|
|
801
|
+
*
|
|
802
|
+
* const result: Result<boolean, string> = RESULT_TRUE;
|
|
803
|
+
* const value: boolean = RESULT_TRUE.intoOk(); // Safe extraction
|
|
804
|
+
* ```
|
|
805
|
+
*/
|
|
806
|
+
const RESULT_TRUE = /* @__PURE__ */ Ok(true);
|
|
807
|
+
/**
|
|
808
|
+
* Result constant for `false`.
|
|
809
|
+
* Can be used anywhere due to immutability.
|
|
810
|
+
* @since 1.3.0
|
|
811
|
+
* @example
|
|
812
|
+
* ```ts
|
|
813
|
+
* function validate(): Result<boolean, Error> {
|
|
814
|
+
* return RESULT_FALSE;
|
|
815
|
+
* }
|
|
816
|
+
*
|
|
817
|
+
* const result: Result<boolean, string> = RESULT_FALSE;
|
|
818
|
+
* const value: boolean = RESULT_FALSE.intoOk(); // Safe extraction
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
const RESULT_FALSE = /* @__PURE__ */ Ok(false);
|
|
822
|
+
/**
|
|
823
|
+
* Result constant for `0`.
|
|
824
|
+
* Can be used anywhere due to immutability.
|
|
825
|
+
* @since 1.3.0
|
|
826
|
+
* @example
|
|
827
|
+
* ```ts
|
|
828
|
+
* function count(): Result<number, Error> {
|
|
829
|
+
* return RESULT_ZERO;
|
|
830
|
+
* }
|
|
831
|
+
*
|
|
832
|
+
* const result: Result<number, string> = RESULT_ZERO;
|
|
833
|
+
* const value: number = RESULT_ZERO.intoOk(); // Safe extraction
|
|
834
|
+
* ```
|
|
835
|
+
*/
|
|
836
|
+
const RESULT_ZERO = /* @__PURE__ */ Ok(0);
|
|
837
|
+
/**
|
|
838
|
+
* Result constant for `void` or `()`.
|
|
839
|
+
* Can be used anywhere due to immutability.
|
|
840
|
+
* @since 1.4.0
|
|
841
|
+
* @example
|
|
842
|
+
* ```ts
|
|
843
|
+
* function doSomething(): Result<void, Error> {
|
|
844
|
+
* return RESULT_VOID;
|
|
845
|
+
* }
|
|
846
|
+
*
|
|
847
|
+
* const result: Result<void, string> = RESULT_VOID;
|
|
848
|
+
* RESULT_VOID.intoOk(); // Safe extraction (returns undefined)
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
851
|
+
const RESULT_VOID = /* @__PURE__ */ Ok();
|
|
852
|
+
//#endregion
|
|
853
|
+
//#region src/core/result/extensions.ts
|
|
854
|
+
/**
|
|
855
|
+
* @module
|
|
856
|
+
* Extension functions for bridging standard JavaScript patterns with Result types.
|
|
857
|
+
*
|
|
858
|
+
* This module provides utilities for:
|
|
859
|
+
* - Converting try-catch patterns to Result-based error handling
|
|
860
|
+
* - Integrating async/await patterns with Result types
|
|
861
|
+
*/
|
|
862
|
+
/**
|
|
863
|
+
* Executes a function and captures any thrown exception as an `Err`.
|
|
864
|
+
* If the function executes successfully, returns `Ok` with the result.
|
|
865
|
+
*
|
|
866
|
+
* Use this to convert traditional try-catch error handling to Result-based handling.
|
|
867
|
+
*
|
|
868
|
+
* Similar to `Promise.try`, this function accepts optional arguments that are passed to the function.
|
|
869
|
+
*
|
|
870
|
+
* @typeParam T - The type of the value returned by the function.
|
|
871
|
+
* @typeParam E - The type of the error that may be thrown, defaults to `Error`.
|
|
872
|
+
* @typeParam Args - The types of the arguments to pass to the function.
|
|
873
|
+
* @param fn - A function that may throw an exception.
|
|
874
|
+
* @param args - Arguments to pass to the function.
|
|
875
|
+
* @returns `Ok<T>` if the function succeeds, or `Err<E>` if it throws.
|
|
876
|
+
* @since 1.7.0
|
|
877
|
+
* @example
|
|
878
|
+
* ```ts
|
|
879
|
+
* // Parse JSON safely with arguments
|
|
880
|
+
* const result = tryResult(JSON.parse, jsonString);
|
|
881
|
+
* result.inspect(data => console.log('Parsed:', data))
|
|
882
|
+
* .inspectErr(err => console.error('Invalid JSON:', err));
|
|
883
|
+
* ```
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
* ```ts
|
|
887
|
+
* // Validate URL - using closure form
|
|
888
|
+
* const result = tryResult(() => new URL(input));
|
|
889
|
+
* if (result.isOk()) {
|
|
890
|
+
* console.log('Valid URL:', result.unwrap().href);
|
|
891
|
+
* }
|
|
892
|
+
* ```
|
|
893
|
+
*
|
|
894
|
+
* @example
|
|
895
|
+
* ```ts
|
|
896
|
+
* // With custom error type
|
|
897
|
+
* const result = tryResult<Config, ConfigError, [string]>(parseConfig, raw);
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
565
900
|
function tryResult(fn, ...args) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
901
|
+
try {
|
|
902
|
+
return Ok(fn(...args));
|
|
903
|
+
} catch (err) {
|
|
904
|
+
return Err(err);
|
|
905
|
+
}
|
|
571
906
|
}
|
|
572
907
|
async function tryAsyncResult(task, ...args) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}
|
|
908
|
+
try {
|
|
909
|
+
return Ok(await (typeof task === "function" ? task(...args) : task));
|
|
910
|
+
} catch (err) {
|
|
911
|
+
return Err(err);
|
|
912
|
+
}
|
|
579
913
|
}
|
|
580
|
-
|
|
914
|
+
//#endregion
|
|
915
|
+
//#region src/std/ops/symbols.ts
|
|
916
|
+
/**
|
|
917
|
+
* @module
|
|
918
|
+
* Internal symbols used to identify `ControlFlow` type variants.
|
|
919
|
+
*
|
|
920
|
+
* These symbols are used as property keys to distinguish between `Break` and `Continue` variants.
|
|
921
|
+
* They provide a reliable way to identify the variant of a `ControlFlow` instance without
|
|
922
|
+
* relying on method calls or duck typing.
|
|
923
|
+
*
|
|
924
|
+
* Note: These symbols are internal implementation details and are not exported as part of the public API.
|
|
925
|
+
* Use the `isControlFlow` utility function for type checking instead.
|
|
926
|
+
*/
|
|
927
|
+
/**
|
|
928
|
+
* A unique symbol used as a property key to identify the variant of a `ControlFlow` instance.
|
|
929
|
+
*
|
|
930
|
+
* When accessed on a `ControlFlow`, returns `'Break'` if the ControlFlow signals early exit,
|
|
931
|
+
* or `'Continue'` if it signals to proceed as normal.
|
|
932
|
+
*
|
|
933
|
+
* This symbol is used internally by the `isControlFlow` utility function to verify
|
|
934
|
+
* that an object is a valid `ControlFlow` instance.
|
|
935
|
+
*
|
|
936
|
+
* @internal
|
|
937
|
+
*/
|
|
581
938
|
const ControlFlowKindSymbol = /* @__PURE__ */ Symbol("ControlFlow kind");
|
|
582
|
-
|
|
939
|
+
//#endregion
|
|
940
|
+
//#region src/std/ops/control_flow.ts
|
|
941
|
+
/**
|
|
942
|
+
* @module
|
|
943
|
+
* Rust-inspired [ControlFlow](https://doc.rust-lang.org/std/ops/enum.ControlFlow.html) for control flow handling.
|
|
944
|
+
*/
|
|
583
945
|
function Break(value) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
946
|
+
const brk = Object.freeze({
|
|
947
|
+
[Symbol.toStringTag]: "ControlFlow",
|
|
948
|
+
[ControlFlowKindSymbol]: "Break",
|
|
949
|
+
toString() {
|
|
950
|
+
return `Break(${value})`;
|
|
951
|
+
},
|
|
952
|
+
isBreak() {
|
|
953
|
+
return true;
|
|
954
|
+
},
|
|
955
|
+
isContinue() {
|
|
956
|
+
return false;
|
|
957
|
+
},
|
|
958
|
+
breakValue() {
|
|
959
|
+
return Some(value);
|
|
960
|
+
},
|
|
961
|
+
continueValue() {
|
|
962
|
+
return None;
|
|
963
|
+
},
|
|
964
|
+
mapBreak(fn) {
|
|
965
|
+
return Break(fn(value));
|
|
966
|
+
},
|
|
967
|
+
mapContinue(_fn) {
|
|
968
|
+
return brk;
|
|
969
|
+
},
|
|
970
|
+
breakOk() {
|
|
971
|
+
return Ok(value);
|
|
972
|
+
},
|
|
973
|
+
continueOk() {
|
|
974
|
+
return Err(value);
|
|
975
|
+
},
|
|
976
|
+
intoValue() {
|
|
977
|
+
return value;
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
return brk;
|
|
619
981
|
}
|
|
620
982
|
function Continue(value) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
983
|
+
const cont = Object.freeze({
|
|
984
|
+
[Symbol.toStringTag]: "ControlFlow",
|
|
985
|
+
[ControlFlowKindSymbol]: "Continue",
|
|
986
|
+
toString() {
|
|
987
|
+
return `Continue(${value})`;
|
|
988
|
+
},
|
|
989
|
+
isBreak() {
|
|
990
|
+
return false;
|
|
991
|
+
},
|
|
992
|
+
isContinue() {
|
|
993
|
+
return true;
|
|
994
|
+
},
|
|
995
|
+
breakValue() {
|
|
996
|
+
return None;
|
|
997
|
+
},
|
|
998
|
+
continueValue() {
|
|
999
|
+
return Some(value);
|
|
1000
|
+
},
|
|
1001
|
+
mapBreak(_fn) {
|
|
1002
|
+
return cont;
|
|
1003
|
+
},
|
|
1004
|
+
mapContinue(fn) {
|
|
1005
|
+
return Continue(fn(value));
|
|
1006
|
+
},
|
|
1007
|
+
breakOk() {
|
|
1008
|
+
return Err(value);
|
|
1009
|
+
},
|
|
1010
|
+
continueOk() {
|
|
1011
|
+
return Ok(value);
|
|
1012
|
+
},
|
|
1013
|
+
intoValue() {
|
|
1014
|
+
return value;
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
return cont;
|
|
656
1018
|
}
|
|
657
|
-
|
|
1019
|
+
//#endregion
|
|
1020
|
+
//#region src/std/ops/fn_once.ts
|
|
1021
|
+
/**
|
|
1022
|
+
* @module
|
|
1023
|
+
* Rust-inspired [FnOnce](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) for one-time callable functions.
|
|
1024
|
+
*
|
|
1025
|
+
* **When to use `FnOnce` vs `FnOnceAsync`:**
|
|
1026
|
+
* - Use `FnOnce` for sync functions
|
|
1027
|
+
* - Use `FnOnceAsync` for async functions
|
|
1028
|
+
*/
|
|
1029
|
+
/**
|
|
1030
|
+
* Creates a `FnOnce` wrapper around a function, making it callable only once.
|
|
1031
|
+
*
|
|
1032
|
+
* @typeParam A - Tuple type of the function arguments.
|
|
1033
|
+
* @typeParam R - Return type of the function.
|
|
1034
|
+
* @param fn - The function to wrap.
|
|
1035
|
+
* @returns A `FnOnce` instance that wraps the function.
|
|
1036
|
+
* @example
|
|
1037
|
+
* ```ts
|
|
1038
|
+
* const initialize = FnOnce(() => {
|
|
1039
|
+
* console.log('Initializing...');
|
|
1040
|
+
* return { ready: true };
|
|
1041
|
+
* });
|
|
1042
|
+
*
|
|
1043
|
+
* const result = initialize.call(); // Logs 'Initializing...', returns { ready: true }
|
|
1044
|
+
* // initialize.call(); // Throws Error: FnOnce has already been consumed
|
|
1045
|
+
* ```
|
|
1046
|
+
*/
|
|
658
1047
|
function FnOnce(fn) {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
isConsumed() {
|
|
680
|
-
return consumed;
|
|
681
|
-
}
|
|
682
|
-
});
|
|
1048
|
+
let consumed = false;
|
|
1049
|
+
return Object.freeze({
|
|
1050
|
+
[Symbol.toStringTag]: "FnOnce",
|
|
1051
|
+
toString() {
|
|
1052
|
+
return `FnOnce(${consumed ? "consumed" : "pending"})`;
|
|
1053
|
+
},
|
|
1054
|
+
call(...args) {
|
|
1055
|
+
if (consumed) throw new Error("FnOnce has already been consumed");
|
|
1056
|
+
consumed = true;
|
|
1057
|
+
return fn(...args);
|
|
1058
|
+
},
|
|
1059
|
+
tryCall(...args) {
|
|
1060
|
+
if (consumed) return None;
|
|
1061
|
+
consumed = true;
|
|
1062
|
+
return Some(fn(...args));
|
|
1063
|
+
},
|
|
1064
|
+
isConsumed() {
|
|
1065
|
+
return consumed;
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
683
1068
|
}
|
|
684
|
-
|
|
1069
|
+
//#endregion
|
|
1070
|
+
//#region src/std/ops/fn_once_async.ts
|
|
1071
|
+
/**
|
|
1072
|
+
* @module
|
|
1073
|
+
* Rust-inspired [AsyncFnOnce](https://doc.rust-lang.org/std/ops/trait.AsyncFnOnce.html) for one-time callable async functions.
|
|
1074
|
+
*
|
|
1075
|
+
* **When to use `FnOnce` vs `FnOnceAsync`:**
|
|
1076
|
+
* - Use `FnOnce` for sync functions
|
|
1077
|
+
* - Use `FnOnceAsync` for async functions
|
|
1078
|
+
*/
|
|
1079
|
+
/**
|
|
1080
|
+
* Creates a `FnOnceAsync` wrapper around an async function, making it callable only once.
|
|
1081
|
+
*
|
|
1082
|
+
* @typeParam A - Tuple type of the function arguments.
|
|
1083
|
+
* @typeParam R - The resolved type of the Promise returned by the async function.
|
|
1084
|
+
* @param fn - A function that returns `PromiseLike<R>` or `R`.
|
|
1085
|
+
* @returns A `FnOnceAsync` instance that wraps the function.
|
|
1086
|
+
* @example
|
|
1087
|
+
* ```ts
|
|
1088
|
+
* const initialize = FnOnceAsync(async () => {
|
|
1089
|
+
* console.log('Initializing...');
|
|
1090
|
+
* await loadResources();
|
|
1091
|
+
* return { ready: true };
|
|
1092
|
+
* });
|
|
1093
|
+
*
|
|
1094
|
+
* const result = await initialize.call(); // Logs 'Initializing...', returns { ready: true }
|
|
1095
|
+
* // await initialize.call(); // Throws Error: FnOnceAsync has already been consumed
|
|
1096
|
+
* ```
|
|
1097
|
+
*/
|
|
685
1098
|
function FnOnceAsync(fn) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
return Promise.resolve(fn(...args)).then(Some);
|
|
707
|
-
},
|
|
708
|
-
isConsumed() {
|
|
709
|
-
return consumed;
|
|
710
|
-
}
|
|
711
|
-
});
|
|
1099
|
+
let consumed = false;
|
|
1100
|
+
return Object.freeze({
|
|
1101
|
+
[Symbol.toStringTag]: "FnOnceAsync",
|
|
1102
|
+
toString() {
|
|
1103
|
+
return `FnOnceAsync(${consumed ? "consumed" : "pending"})`;
|
|
1104
|
+
},
|
|
1105
|
+
call(...args) {
|
|
1106
|
+
if (consumed) throw new Error("FnOnceAsync has already been consumed");
|
|
1107
|
+
consumed = true;
|
|
1108
|
+
return Promise.resolve(fn(...args));
|
|
1109
|
+
},
|
|
1110
|
+
tryCall(...args) {
|
|
1111
|
+
if (consumed) return ASYNC_NONE;
|
|
1112
|
+
consumed = true;
|
|
1113
|
+
return Promise.resolve(fn(...args)).then(Some);
|
|
1114
|
+
},
|
|
1115
|
+
isConsumed() {
|
|
1116
|
+
return consumed;
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
712
1119
|
}
|
|
713
|
-
|
|
1120
|
+
//#endregion
|
|
1121
|
+
//#region src/std/ops/guards.ts
|
|
1122
|
+
/**
|
|
1123
|
+
* Checks if a value is a `ControlFlow`.
|
|
1124
|
+
*
|
|
1125
|
+
* @typeParam B - The expected type of the break value contained within the `ControlFlow`.
|
|
1126
|
+
* @typeParam C - The expected type of the continue value contained within the `ControlFlow`.
|
|
1127
|
+
* @param cf - The value to be checked as a `ControlFlow`.
|
|
1128
|
+
* @returns `true` if the value is a `ControlFlow`, otherwise `false`.
|
|
1129
|
+
* @since 1.6.0
|
|
1130
|
+
* @example
|
|
1131
|
+
* ```ts
|
|
1132
|
+
* const x = Break(5);
|
|
1133
|
+
* console.log(isControlFlow(x)); // true
|
|
1134
|
+
* console.log(isControlFlow(null)); // false
|
|
1135
|
+
* console.log(isControlFlow({ isBreak: () => true })); // false
|
|
1136
|
+
* ```
|
|
1137
|
+
*/
|
|
714
1138
|
function isControlFlow(cf) {
|
|
715
|
-
|
|
1139
|
+
return cf != null && typeof cf === "object" && ControlFlowKindSymbol in cf;
|
|
716
1140
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
1141
|
+
//#endregion
|
|
1142
|
+
//#region src/std/sync/channel.ts
|
|
1143
|
+
/**
|
|
1144
|
+
* @module
|
|
1145
|
+
* Rust-inspired MPMC (multi-producer multi-consumer) channel for async message passing.
|
|
1146
|
+
*
|
|
1147
|
+
* Provides a type-safe channel with optional bounded capacity and backpressure support.
|
|
1148
|
+
* Supports rendezvous (capacity=0) for synchronous handoff between sender and receiver.
|
|
1149
|
+
*
|
|
1150
|
+
*/
|
|
1151
|
+
const ASYNC_TRUE = /* @__PURE__ */ Promise.resolve(true);
|
|
1152
|
+
const ASYNC_FALSE = /* @__PURE__ */ Promise.resolve(false);
|
|
1153
|
+
/**
|
|
1154
|
+
* Creates a new MPMC channel with the specified capacity.
|
|
1155
|
+
*
|
|
1156
|
+
* @typeParam T - The type of values that can be sent through the channel.
|
|
1157
|
+
* @param capacity - Maximum buffer size. Defaults to `Infinity` (unbounded).
|
|
1158
|
+
* Use `0` for a rendezvous channel (direct handoff).
|
|
1159
|
+
* @returns A new `Channel<T>` instance.
|
|
1160
|
+
* @throws {RangeError} If capacity is negative or not an integer (except Infinity).
|
|
1161
|
+
* @example
|
|
1162
|
+
* ```ts
|
|
1163
|
+
* // Unbounded channel (default)
|
|
1164
|
+
* const unbounded = Channel<string>();
|
|
1165
|
+
*
|
|
1166
|
+
* // Bounded channel with backpressure
|
|
1167
|
+
* const bounded = Channel<string>(100);
|
|
1168
|
+
*
|
|
1169
|
+
* // Rendezvous channel (synchronous handoff)
|
|
1170
|
+
* const rendezvous = Channel<string>(0);
|
|
1171
|
+
* ```
|
|
1172
|
+
*
|
|
1173
|
+
* @example
|
|
1174
|
+
* ```ts
|
|
1175
|
+
* // Task queue with backpressure
|
|
1176
|
+
* const taskQueue = Channel<() => Promise<void>>(10);
|
|
1177
|
+
*
|
|
1178
|
+
* // Worker
|
|
1179
|
+
* (async () => {
|
|
1180
|
+
* for await (const task of taskQueue) {
|
|
1181
|
+
* await task();
|
|
1182
|
+
* }
|
|
1183
|
+
* })();
|
|
1184
|
+
*
|
|
1185
|
+
* // Producer - will wait if queue is full
|
|
1186
|
+
* await taskQueue.send(async () => {
|
|
1187
|
+
* console.log('Processing...');
|
|
1188
|
+
* });
|
|
1189
|
+
* ```
|
|
1190
|
+
*
|
|
1191
|
+
* @example
|
|
1192
|
+
* ```ts
|
|
1193
|
+
* // Log aggregation with multiple producers
|
|
1194
|
+
* const logs = Channel<string>(1000);
|
|
1195
|
+
*
|
|
1196
|
+
* // Multiple producers
|
|
1197
|
+
* async function logFromService(name: string) {
|
|
1198
|
+
* const sender = logs.sender();
|
|
1199
|
+
* await sender.send(`[${name}] Started`);
|
|
1200
|
+
* // ... do work ...
|
|
1201
|
+
* await sender.send(`[${name}] Finished`);
|
|
1202
|
+
* }
|
|
1203
|
+
*
|
|
1204
|
+
* // Single consumer writing to file
|
|
1205
|
+
* async function writeLogsToFile() {
|
|
1206
|
+
* const receiver = logs.receiver();
|
|
1207
|
+
* for await (const log of receiver) {
|
|
1208
|
+
* await fs.appendFile('app.log', log + '\n');
|
|
1209
|
+
* }
|
|
1210
|
+
* }
|
|
1211
|
+
* ```
|
|
1212
|
+
*/
|
|
720
1213
|
function Channel(capacity = Infinity) {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
return `Channel(${buffer.length}/∞)`;
|
|
927
|
-
}
|
|
928
|
-
return `Channel(${buffer.length}/${capacity})`;
|
|
929
|
-
},
|
|
930
|
-
get capacity() {
|
|
931
|
-
return capacity;
|
|
932
|
-
},
|
|
933
|
-
get length() {
|
|
934
|
-
return buffer.length;
|
|
935
|
-
},
|
|
936
|
-
get isClosed() {
|
|
937
|
-
return closed;
|
|
938
|
-
},
|
|
939
|
-
get isEmpty() {
|
|
940
|
-
return buffer.length === 0;
|
|
941
|
-
},
|
|
942
|
-
get isFull() {
|
|
943
|
-
return buffer.length >= capacity;
|
|
944
|
-
},
|
|
945
|
-
get sender() {
|
|
946
|
-
return cachedSender ??= createSender();
|
|
947
|
-
},
|
|
948
|
-
get receiver() {
|
|
949
|
-
return cachedReceiver ??= createReceiver();
|
|
950
|
-
},
|
|
951
|
-
send,
|
|
952
|
-
trySend,
|
|
953
|
-
sendTimeout,
|
|
954
|
-
receive,
|
|
955
|
-
tryReceive,
|
|
956
|
-
receiveTimeout,
|
|
957
|
-
close
|
|
958
|
-
});
|
|
1214
|
+
if (capacity < 0 || !Number.isInteger(capacity) && capacity !== Infinity) throw new RangeError("Channel capacity must be a non-negative integer or Infinity");
|
|
1215
|
+
const buffer = new Queue();
|
|
1216
|
+
let closed = false;
|
|
1217
|
+
const sendWaitQueue = [];
|
|
1218
|
+
const receiveWaitQueue = [];
|
|
1219
|
+
let cachedSender;
|
|
1220
|
+
let cachedReceiver;
|
|
1221
|
+
function send(value) {
|
|
1222
|
+
if (closed) return ASYNC_FALSE;
|
|
1223
|
+
if (trySend(value)) return ASYNC_TRUE;
|
|
1224
|
+
return new Promise((resolve) => {
|
|
1225
|
+
sendWaitQueue.push({
|
|
1226
|
+
value,
|
|
1227
|
+
resolve
|
|
1228
|
+
});
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
function trySend(value) {
|
|
1232
|
+
if (closed) return false;
|
|
1233
|
+
if (receiveWaitQueue.length > 0) {
|
|
1234
|
+
receiveWaitQueue.shift()(Some(value));
|
|
1235
|
+
return true;
|
|
1236
|
+
}
|
|
1237
|
+
if (buffer.length < capacity) {
|
|
1238
|
+
buffer.push(value);
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
1241
|
+
return false;
|
|
1242
|
+
}
|
|
1243
|
+
function receive() {
|
|
1244
|
+
const result = tryReceive();
|
|
1245
|
+
if (result.isSome()) return Promise.resolve(result);
|
|
1246
|
+
if (closed) return ASYNC_NONE;
|
|
1247
|
+
return new Promise((resolve) => {
|
|
1248
|
+
receiveWaitQueue.push(resolve);
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
function tryReceive() {
|
|
1252
|
+
if (buffer.length > 0) {
|
|
1253
|
+
const value = buffer.shift();
|
|
1254
|
+
if (sendWaitQueue.length > 0) {
|
|
1255
|
+
const sender = sendWaitQueue.shift();
|
|
1256
|
+
buffer.push(sender.value);
|
|
1257
|
+
sender.resolve(true);
|
|
1258
|
+
}
|
|
1259
|
+
return Some(value);
|
|
1260
|
+
}
|
|
1261
|
+
if (sendWaitQueue.length > 0) {
|
|
1262
|
+
const sender = sendWaitQueue.shift();
|
|
1263
|
+
sender.resolve(true);
|
|
1264
|
+
return Some(sender.value);
|
|
1265
|
+
}
|
|
1266
|
+
return None;
|
|
1267
|
+
}
|
|
1268
|
+
function close() {
|
|
1269
|
+
if (closed) return;
|
|
1270
|
+
closed = true;
|
|
1271
|
+
while (sendWaitQueue.length > 0) sendWaitQueue.shift().resolve(false);
|
|
1272
|
+
while (receiveWaitQueue.length > 0) receiveWaitQueue.shift()(None);
|
|
1273
|
+
}
|
|
1274
|
+
function sendTimeout(value, ms) {
|
|
1275
|
+
if (closed) return ASYNC_FALSE;
|
|
1276
|
+
if (trySend(value)) return ASYNC_TRUE;
|
|
1277
|
+
return new Promise((resolve) => {
|
|
1278
|
+
const waiter = {
|
|
1279
|
+
value,
|
|
1280
|
+
resolve
|
|
1281
|
+
};
|
|
1282
|
+
sendWaitQueue.push(waiter);
|
|
1283
|
+
const timeoutId = setTimeout(() => {
|
|
1284
|
+
const index = sendWaitQueue.indexOf(waiter);
|
|
1285
|
+
sendWaitQueue.splice(index, 1);
|
|
1286
|
+
resolve(false);
|
|
1287
|
+
}, ms);
|
|
1288
|
+
const originalResolve = waiter.resolve;
|
|
1289
|
+
waiter.resolve = (success) => {
|
|
1290
|
+
clearTimeout(timeoutId);
|
|
1291
|
+
originalResolve(success);
|
|
1292
|
+
};
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
function receiveTimeout(ms) {
|
|
1296
|
+
const result = tryReceive();
|
|
1297
|
+
if (result.isSome()) return Promise.resolve(result);
|
|
1298
|
+
if (closed) return ASYNC_NONE;
|
|
1299
|
+
return new Promise((resolve) => {
|
|
1300
|
+
const wrappedWaiter = (value) => {
|
|
1301
|
+
clearTimeout(timeoutId);
|
|
1302
|
+
resolve(value);
|
|
1303
|
+
};
|
|
1304
|
+
receiveWaitQueue.push(wrappedWaiter);
|
|
1305
|
+
const timeoutId = setTimeout(() => {
|
|
1306
|
+
const index = receiveWaitQueue.indexOf(wrappedWaiter);
|
|
1307
|
+
receiveWaitQueue.splice(index, 1);
|
|
1308
|
+
resolve(None);
|
|
1309
|
+
}, ms);
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
function asyncIterator() {
|
|
1313
|
+
return { async next() {
|
|
1314
|
+
const result = await receive();
|
|
1315
|
+
if (result.isNone()) return {
|
|
1316
|
+
done: true,
|
|
1317
|
+
value: void 0
|
|
1318
|
+
};
|
|
1319
|
+
return {
|
|
1320
|
+
done: false,
|
|
1321
|
+
value: result.unwrap()
|
|
1322
|
+
};
|
|
1323
|
+
} };
|
|
1324
|
+
}
|
|
1325
|
+
function createSender() {
|
|
1326
|
+
return Object.freeze({
|
|
1327
|
+
[Symbol.toStringTag]: "Sender",
|
|
1328
|
+
toString() {
|
|
1329
|
+
if (closed) return "Sender(<closed>)";
|
|
1330
|
+
if (capacity === Infinity) return `Sender(${buffer.length}/∞)`;
|
|
1331
|
+
return `Sender(${buffer.length}/${capacity})`;
|
|
1332
|
+
},
|
|
1333
|
+
get capacity() {
|
|
1334
|
+
return capacity;
|
|
1335
|
+
},
|
|
1336
|
+
get length() {
|
|
1337
|
+
return buffer.length;
|
|
1338
|
+
},
|
|
1339
|
+
get isClosed() {
|
|
1340
|
+
return closed;
|
|
1341
|
+
},
|
|
1342
|
+
get isEmpty() {
|
|
1343
|
+
return buffer.length === 0;
|
|
1344
|
+
},
|
|
1345
|
+
get isFull() {
|
|
1346
|
+
return buffer.length >= capacity;
|
|
1347
|
+
},
|
|
1348
|
+
send,
|
|
1349
|
+
trySend,
|
|
1350
|
+
sendTimeout
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
function createReceiver() {
|
|
1354
|
+
return Object.freeze({
|
|
1355
|
+
[Symbol.toStringTag]: "Receiver",
|
|
1356
|
+
[Symbol.asyncIterator]: asyncIterator,
|
|
1357
|
+
toString() {
|
|
1358
|
+
if (closed) return "Receiver(<closed>)";
|
|
1359
|
+
if (capacity === Infinity) return `Receiver(${buffer.length}/∞)`;
|
|
1360
|
+
return `Receiver(${buffer.length}/${capacity})`;
|
|
1361
|
+
},
|
|
1362
|
+
get capacity() {
|
|
1363
|
+
return capacity;
|
|
1364
|
+
},
|
|
1365
|
+
get length() {
|
|
1366
|
+
return buffer.length;
|
|
1367
|
+
},
|
|
1368
|
+
get isClosed() {
|
|
1369
|
+
return closed;
|
|
1370
|
+
},
|
|
1371
|
+
get isEmpty() {
|
|
1372
|
+
return buffer.length === 0;
|
|
1373
|
+
},
|
|
1374
|
+
get isFull() {
|
|
1375
|
+
return buffer.length >= capacity;
|
|
1376
|
+
},
|
|
1377
|
+
receive,
|
|
1378
|
+
tryReceive,
|
|
1379
|
+
receiveTimeout
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
return Object.freeze({
|
|
1383
|
+
[Symbol.toStringTag]: "Channel",
|
|
1384
|
+
[Symbol.asyncIterator]: asyncIterator,
|
|
1385
|
+
toString() {
|
|
1386
|
+
if (closed) return "Channel(<closed>)";
|
|
1387
|
+
if (capacity === Infinity) return `Channel(${buffer.length}/∞)`;
|
|
1388
|
+
return `Channel(${buffer.length}/${capacity})`;
|
|
1389
|
+
},
|
|
1390
|
+
get capacity() {
|
|
1391
|
+
return capacity;
|
|
1392
|
+
},
|
|
1393
|
+
get length() {
|
|
1394
|
+
return buffer.length;
|
|
1395
|
+
},
|
|
1396
|
+
get isClosed() {
|
|
1397
|
+
return closed;
|
|
1398
|
+
},
|
|
1399
|
+
get isEmpty() {
|
|
1400
|
+
return buffer.length === 0;
|
|
1401
|
+
},
|
|
1402
|
+
get isFull() {
|
|
1403
|
+
return buffer.length >= capacity;
|
|
1404
|
+
},
|
|
1405
|
+
get sender() {
|
|
1406
|
+
return cachedSender ??= createSender();
|
|
1407
|
+
},
|
|
1408
|
+
get receiver() {
|
|
1409
|
+
return cachedReceiver ??= createReceiver();
|
|
1410
|
+
},
|
|
1411
|
+
send,
|
|
1412
|
+
trySend,
|
|
1413
|
+
sendTimeout,
|
|
1414
|
+
receive,
|
|
1415
|
+
tryReceive,
|
|
1416
|
+
receiveTimeout,
|
|
1417
|
+
close
|
|
1418
|
+
});
|
|
959
1419
|
}
|
|
960
|
-
|
|
1420
|
+
/**
|
|
1421
|
+
* A fast FIFO queue implementation using an array and an offset pointer.
|
|
1422
|
+
* This avoids the O(n) overhead of Array.shift() by simply incrementing the offset.
|
|
1423
|
+
*
|
|
1424
|
+
* When the offset exceeds a threshold (1024) and more than half the array is empty,
|
|
1425
|
+
* the array is compacted by slicing. The threshold 1024 was chosen empirically:
|
|
1426
|
+
* - Too small: frequent compaction overhead
|
|
1427
|
+
* - Too large: excessive memory waste
|
|
1428
|
+
* - 1024 balances compaction frequency and memory usage across typical workloads
|
|
1429
|
+
*
|
|
1430
|
+
* Benchmark results show minimal performance difference between thresholds (64-16384),
|
|
1431
|
+
* so 1024 is a reasonable default that works well for most Channel use cases.
|
|
1432
|
+
*/
|
|
1433
|
+
var Queue = class {
|
|
1434
|
+
data = [];
|
|
1435
|
+
offset = 0;
|
|
1436
|
+
get length() {
|
|
1437
|
+
return this.data.length - this.offset;
|
|
1438
|
+
}
|
|
1439
|
+
push(item) {
|
|
1440
|
+
this.data.push(item);
|
|
1441
|
+
}
|
|
1442
|
+
shift() {
|
|
1443
|
+
const item = this.data[this.offset];
|
|
1444
|
+
this.data[this.offset] = void 0;
|
|
1445
|
+
this.offset++;
|
|
1446
|
+
if (this.offset > 1024 && this.offset * 2 > this.data.length) {
|
|
1447
|
+
this.data = this.data.slice(this.offset);
|
|
1448
|
+
this.offset = 0;
|
|
1449
|
+
}
|
|
1450
|
+
return item;
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
//#endregion
|
|
1454
|
+
//#region src/std/sync/lazy.ts
|
|
1455
|
+
/**
|
|
1456
|
+
* @module
|
|
1457
|
+
* Rust-inspired [LazyLock](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) for lazy initialization.
|
|
1458
|
+
*
|
|
1459
|
+
* Unlike `Once<T>`, which allows setting values manually or with different
|
|
1460
|
+
* initializers, `Lazy<T>` binds the initializer at creation time.
|
|
1461
|
+
*
|
|
1462
|
+
* **When to use `Lazy<T>` vs `LazyAsync<T>`:**
|
|
1463
|
+
* - Use `Lazy<T>` for sync-only initialization
|
|
1464
|
+
* - Use `LazyAsync<T>` for async initialization with concurrent call handling
|
|
1465
|
+
*/
|
|
1466
|
+
/**
|
|
1467
|
+
* Creates a new `Lazy<T>` with the given synchronous initialization function.
|
|
1468
|
+
*
|
|
1469
|
+
* The function is called at most once, on first access via `force()`.
|
|
1470
|
+
*
|
|
1471
|
+
* @typeParam T - The type of value to store.
|
|
1472
|
+
* @param fn - The initialization function that produces the value.
|
|
1473
|
+
* @returns A new `Lazy<T>` instance.
|
|
1474
|
+
* @example
|
|
1475
|
+
* ```ts
|
|
1476
|
+
* // Basic usage
|
|
1477
|
+
* const lazy = Lazy(() => {
|
|
1478
|
+
* console.log('Initializing');
|
|
1479
|
+
* return 42;
|
|
1480
|
+
* });
|
|
1481
|
+
*
|
|
1482
|
+
* console.log(lazy.isInitialized()); // false
|
|
1483
|
+
* console.log(lazy.force()); // logs "Initializing", returns 42
|
|
1484
|
+
* console.log(lazy.isInitialized()); // true
|
|
1485
|
+
* console.log(lazy.force()); // returns 42 (no log)
|
|
1486
|
+
* ```
|
|
1487
|
+
*
|
|
1488
|
+
* @example
|
|
1489
|
+
* ```ts
|
|
1490
|
+
* // Lazy singleton pattern
|
|
1491
|
+
* const logger = Lazy(() => new Logger('app'));
|
|
1492
|
+
*
|
|
1493
|
+
* function getLogger(): Logger {
|
|
1494
|
+
* return logger.force();
|
|
1495
|
+
* }
|
|
1496
|
+
* ```
|
|
1497
|
+
*
|
|
1498
|
+
* @example
|
|
1499
|
+
* ```ts
|
|
1500
|
+
* // Expensive computation
|
|
1501
|
+
* const fibonacci = Lazy(() => {
|
|
1502
|
+
* function fib(n: number): number {
|
|
1503
|
+
* if (n <= 1) return n;
|
|
1504
|
+
* return fib(n - 1) + fib(n - 2);
|
|
1505
|
+
* }
|
|
1506
|
+
* return fib(40); // Only computed once
|
|
1507
|
+
* });
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
961
1510
|
function Lazy(fn) {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1511
|
+
let value;
|
|
1512
|
+
let initialized = false;
|
|
1513
|
+
return Object.freeze({
|
|
1514
|
+
[Symbol.toStringTag]: "Lazy",
|
|
1515
|
+
toString() {
|
|
1516
|
+
return initialized ? `Lazy(${value})` : "Lazy(<uninitialized>)";
|
|
1517
|
+
},
|
|
1518
|
+
force() {
|
|
1519
|
+
if (!initialized) {
|
|
1520
|
+
value = fn();
|
|
1521
|
+
initialized = true;
|
|
1522
|
+
}
|
|
1523
|
+
return value;
|
|
1524
|
+
},
|
|
1525
|
+
get() {
|
|
1526
|
+
return initialized ? Some(value) : None;
|
|
1527
|
+
},
|
|
1528
|
+
isInitialized() {
|
|
1529
|
+
return initialized;
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
983
1532
|
}
|
|
984
|
-
|
|
1533
|
+
//#endregion
|
|
1534
|
+
//#region src/std/sync/lazy_async.ts
|
|
1535
|
+
/**
|
|
1536
|
+
* @module
|
|
1537
|
+
* Rust-inspired [LazyLock](https://doc.rust-lang.org/std/sync/struct.LazyLock.html) for async lazy initialization.
|
|
1538
|
+
*
|
|
1539
|
+
* **When to use `Lazy<T>` vs `LazyAsync<T>`:**
|
|
1540
|
+
* - Use `Lazy<T>` for sync-only initialization
|
|
1541
|
+
* - Use `LazyAsync<T>` for async initialization with concurrent call handling
|
|
1542
|
+
*/
|
|
1543
|
+
/**
|
|
1544
|
+
* Creates a new `LazyAsync<T>` with the given async initialization function.
|
|
1545
|
+
*
|
|
1546
|
+
* The function is called at most once, on first access via `force()`.
|
|
1547
|
+
* Concurrent calls to `force()` before initialization completes will
|
|
1548
|
+
* wait for the single initialization to finish.
|
|
1549
|
+
*
|
|
1550
|
+
* @typeParam T - The type of value to store.
|
|
1551
|
+
* @param fn - A function that returns `PromiseLike<T>` or `T` to initialize.
|
|
1552
|
+
* @returns A new `LazyAsync<T>` instance.
|
|
1553
|
+
* @example
|
|
1554
|
+
* ```ts
|
|
1555
|
+
* // Basic usage
|
|
1556
|
+
* const lazy = LazyAsync(async () => {
|
|
1557
|
+
* const response = await fetch('/api/data');
|
|
1558
|
+
* return await response.json();
|
|
1559
|
+
* });
|
|
1560
|
+
*
|
|
1561
|
+
* const data = await lazy.force();
|
|
1562
|
+
* ```
|
|
1563
|
+
*
|
|
1564
|
+
* @example
|
|
1565
|
+
* ```ts
|
|
1566
|
+
* // Database connection singleton
|
|
1567
|
+
* const db = LazyAsync(async () => {
|
|
1568
|
+
* console.log('Connecting to database...');
|
|
1569
|
+
* return await Database.connect(connectionString);
|
|
1570
|
+
* });
|
|
1571
|
+
*
|
|
1572
|
+
* async function getDb(): Promise<Database> {
|
|
1573
|
+
* return await db.force();
|
|
1574
|
+
* }
|
|
1575
|
+
*
|
|
1576
|
+
* // Multiple calls - connection happens only once
|
|
1577
|
+
* const [db1, db2] = await Promise.all([getDb(), getDb()]);
|
|
1578
|
+
* console.log(db1 === db2); // true
|
|
1579
|
+
* ```
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```ts
|
|
1583
|
+
* // Configuration loader
|
|
1584
|
+
* const config = LazyAsync(async () => {
|
|
1585
|
+
* const response = await fetch('/api/config');
|
|
1586
|
+
* if (!response.ok) {
|
|
1587
|
+
* throw new Error(`Failed to load config: ${response.status}`);
|
|
1588
|
+
* }
|
|
1589
|
+
* return await response.json() as Config;
|
|
1590
|
+
* });
|
|
1591
|
+
*
|
|
1592
|
+
* // Used throughout the app
|
|
1593
|
+
* async function getApiEndpoint(): Promise<string> {
|
|
1594
|
+
* const cfg = await config.force();
|
|
1595
|
+
* return cfg.apiEndpoint;
|
|
1596
|
+
* }
|
|
1597
|
+
* ```
|
|
1598
|
+
*/
|
|
985
1599
|
function LazyAsync(fn) {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
},
|
|
1015
|
-
get() {
|
|
1016
|
-
return initialized ? Some(value) : None;
|
|
1017
|
-
},
|
|
1018
|
-
isInitialized() {
|
|
1019
|
-
return initialized;
|
|
1020
|
-
}
|
|
1021
|
-
});
|
|
1600
|
+
let value;
|
|
1601
|
+
let initialized = false;
|
|
1602
|
+
let pendingPromise;
|
|
1603
|
+
return Object.freeze({
|
|
1604
|
+
[Symbol.toStringTag]: "LazyAsync",
|
|
1605
|
+
toString() {
|
|
1606
|
+
return initialized ? `LazyAsync(${value})` : "LazyAsync(<uninitialized>)";
|
|
1607
|
+
},
|
|
1608
|
+
force() {
|
|
1609
|
+
if (initialized) return pendingPromise ??= Promise.resolve(value);
|
|
1610
|
+
if (pendingPromise) return pendingPromise;
|
|
1611
|
+
pendingPromise = Promise.resolve(fn()).then((result) => {
|
|
1612
|
+
value = result;
|
|
1613
|
+
initialized = true;
|
|
1614
|
+
return result;
|
|
1615
|
+
}).catch((err) => {
|
|
1616
|
+
pendingPromise = void 0;
|
|
1617
|
+
throw err;
|
|
1618
|
+
});
|
|
1619
|
+
return pendingPromise;
|
|
1620
|
+
},
|
|
1621
|
+
get() {
|
|
1622
|
+
return initialized ? Some(value) : None;
|
|
1623
|
+
},
|
|
1624
|
+
isInitialized() {
|
|
1625
|
+
return initialized;
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1022
1628
|
}
|
|
1023
|
-
|
|
1629
|
+
//#endregion
|
|
1630
|
+
//#region src/std/sync/mutex.ts
|
|
1631
|
+
/**
|
|
1632
|
+
* @module
|
|
1633
|
+
* Rust-inspired [Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html) for async mutual exclusion.
|
|
1634
|
+
*
|
|
1635
|
+
* In JavaScript's single-threaded environment, `Mutex<T>` is used to serialize
|
|
1636
|
+
* async operations, ensuring only one async task accesses the protected resource at a time.
|
|
1637
|
+
*/
|
|
1638
|
+
/**
|
|
1639
|
+
* Creates a new `Mutex<T>` protecting the given value.
|
|
1640
|
+
*
|
|
1641
|
+
* @typeParam T - The type of the protected value.
|
|
1642
|
+
* @param value - The initial value to protect.
|
|
1643
|
+
* @returns A new `Mutex<T>` instance.
|
|
1644
|
+
* @example
|
|
1645
|
+
* ```ts
|
|
1646
|
+
* // Protect a simple value
|
|
1647
|
+
* const counter = Mutex(0);
|
|
1648
|
+
*
|
|
1649
|
+
* // Protect an object
|
|
1650
|
+
* const state = Mutex({ users: [], lastUpdate: Date.now() });
|
|
1651
|
+
*
|
|
1652
|
+
* // Protect a resource
|
|
1653
|
+
* const db = Mutex(await createConnection());
|
|
1654
|
+
* ```
|
|
1655
|
+
*
|
|
1656
|
+
* @example
|
|
1657
|
+
* ```ts
|
|
1658
|
+
* // Database transaction safety
|
|
1659
|
+
* const connection = Mutex(db);
|
|
1660
|
+
*
|
|
1661
|
+
* async function transfer(from: string, to: string, amount: number) {
|
|
1662
|
+
* await connection.withLock(async (conn) => {
|
|
1663
|
+
* await conn.beginTransaction();
|
|
1664
|
+
* try {
|
|
1665
|
+
* const balance = await conn.query('SELECT balance FROM accounts WHERE id = ?', [from]);
|
|
1666
|
+
* if (balance < amount) {
|
|
1667
|
+
* throw new Error('Insufficient funds');
|
|
1668
|
+
* }
|
|
1669
|
+
* await conn.query('UPDATE accounts SET balance = balance - ? WHERE id = ?', [amount, from]);
|
|
1670
|
+
* await conn.query('UPDATE accounts SET balance = balance + ? WHERE id = ?', [amount, to]);
|
|
1671
|
+
* await conn.commit();
|
|
1672
|
+
* } catch (e) {
|
|
1673
|
+
* await conn.rollback();
|
|
1674
|
+
* throw e;
|
|
1675
|
+
* }
|
|
1676
|
+
* });
|
|
1677
|
+
* }
|
|
1678
|
+
* ```
|
|
1679
|
+
*
|
|
1680
|
+
* @example
|
|
1681
|
+
* ```ts
|
|
1682
|
+
* // Token refresh with mutex
|
|
1683
|
+
* const authState = Mutex({ token: '', expiresAt: 0 });
|
|
1684
|
+
*
|
|
1685
|
+
* async function getToken(): Promise<string> {
|
|
1686
|
+
* return await authState.withLock(async (state) => {
|
|
1687
|
+
* if (Date.now() > state.expiresAt) {
|
|
1688
|
+
* const response = await fetch('/api/refresh');
|
|
1689
|
+
* const data = await response.json();
|
|
1690
|
+
* state.token = data.token;
|
|
1691
|
+
* state.expiresAt = Date.now() + data.expiresIn * 1000;
|
|
1692
|
+
* }
|
|
1693
|
+
* return state.token;
|
|
1694
|
+
* });
|
|
1695
|
+
* }
|
|
1696
|
+
* ```
|
|
1697
|
+
*
|
|
1698
|
+
* @example
|
|
1699
|
+
* ```ts
|
|
1700
|
+
* // File write serialization
|
|
1701
|
+
* const fileLock = Mutex('/path/to/file.json');
|
|
1702
|
+
*
|
|
1703
|
+
* async function appendToFile(data: string) {
|
|
1704
|
+
* await fileLock.withLock(async (path) => {
|
|
1705
|
+
* const content = await fs.readFile(path, 'utf-8');
|
|
1706
|
+
* const json = JSON.parse(content);
|
|
1707
|
+
* json.entries.push(data);
|
|
1708
|
+
* await fs.writeFile(path, JSON.stringify(json, null, 2));
|
|
1709
|
+
* });
|
|
1710
|
+
* }
|
|
1711
|
+
* ```
|
|
1712
|
+
*/
|
|
1024
1713
|
function Mutex(value) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
guard.unlock();
|
|
1116
|
-
}
|
|
1117
|
-
},
|
|
1118
|
-
async replace(value2) {
|
|
1119
|
-
const guard = await lock();
|
|
1120
|
-
try {
|
|
1121
|
-
const old = guard.value;
|
|
1122
|
-
guard.value = value2;
|
|
1123
|
-
return old;
|
|
1124
|
-
} finally {
|
|
1125
|
-
guard.unlock();
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
});
|
|
1714
|
+
let currentValue = value;
|
|
1715
|
+
let locked = false;
|
|
1716
|
+
const waitQueue = [];
|
|
1717
|
+
function unlock() {
|
|
1718
|
+
if (waitQueue.length > 0) waitQueue.shift()();
|
|
1719
|
+
else locked = false;
|
|
1720
|
+
}
|
|
1721
|
+
function createGuard() {
|
|
1722
|
+
let released = false;
|
|
1723
|
+
return Object.freeze({
|
|
1724
|
+
[Symbol.toStringTag]: "MutexGuard",
|
|
1725
|
+
toString() {
|
|
1726
|
+
if (released) return "MutexGuard(<released>)";
|
|
1727
|
+
return `MutexGuard(${currentValue})`;
|
|
1728
|
+
},
|
|
1729
|
+
get value() {
|
|
1730
|
+
if (released) throw new Error("MutexGuard has been released");
|
|
1731
|
+
return currentValue;
|
|
1732
|
+
},
|
|
1733
|
+
set value(newValue) {
|
|
1734
|
+
if (released) throw new Error("MutexGuard has been released");
|
|
1735
|
+
currentValue = newValue;
|
|
1736
|
+
},
|
|
1737
|
+
unlock() {
|
|
1738
|
+
if (released) return;
|
|
1739
|
+
released = true;
|
|
1740
|
+
unlock();
|
|
1741
|
+
}
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
function lock() {
|
|
1745
|
+
if (!locked) {
|
|
1746
|
+
locked = true;
|
|
1747
|
+
return Promise.resolve(createGuard());
|
|
1748
|
+
}
|
|
1749
|
+
return new Promise((resolve) => {
|
|
1750
|
+
waitQueue.push(() => {
|
|
1751
|
+
resolve(createGuard());
|
|
1752
|
+
});
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
return Object.freeze({
|
|
1756
|
+
[Symbol.toStringTag]: "Mutex",
|
|
1757
|
+
toString() {
|
|
1758
|
+
return locked ? "Mutex(<locked>)" : "Mutex(<unlocked>)";
|
|
1759
|
+
},
|
|
1760
|
+
async withLock(fn) {
|
|
1761
|
+
const guard = await lock();
|
|
1762
|
+
try {
|
|
1763
|
+
return await fn(guard.value);
|
|
1764
|
+
} finally {
|
|
1765
|
+
guard.unlock();
|
|
1766
|
+
}
|
|
1767
|
+
},
|
|
1768
|
+
lock,
|
|
1769
|
+
tryLock() {
|
|
1770
|
+
if (locked) return None;
|
|
1771
|
+
locked = true;
|
|
1772
|
+
return Some(createGuard());
|
|
1773
|
+
},
|
|
1774
|
+
isLocked() {
|
|
1775
|
+
return locked;
|
|
1776
|
+
},
|
|
1777
|
+
async get() {
|
|
1778
|
+
const guard = await lock();
|
|
1779
|
+
try {
|
|
1780
|
+
return guard.value;
|
|
1781
|
+
} finally {
|
|
1782
|
+
guard.unlock();
|
|
1783
|
+
}
|
|
1784
|
+
},
|
|
1785
|
+
async set(value) {
|
|
1786
|
+
const guard = await lock();
|
|
1787
|
+
try {
|
|
1788
|
+
guard.value = value;
|
|
1789
|
+
} finally {
|
|
1790
|
+
guard.unlock();
|
|
1791
|
+
}
|
|
1792
|
+
},
|
|
1793
|
+
async replace(value) {
|
|
1794
|
+
const guard = await lock();
|
|
1795
|
+
try {
|
|
1796
|
+
const old = guard.value;
|
|
1797
|
+
guard.value = value;
|
|
1798
|
+
return old;
|
|
1799
|
+
} finally {
|
|
1800
|
+
guard.unlock();
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1129
1804
|
}
|
|
1130
|
-
|
|
1805
|
+
//#endregion
|
|
1806
|
+
//#region src/std/sync/once.ts
|
|
1807
|
+
/**
|
|
1808
|
+
* @module
|
|
1809
|
+
* Rust-inspired [OnceLock](https://doc.rust-lang.org/std/sync/struct.OnceLock.html) for one-time initialization.
|
|
1810
|
+
*
|
|
1811
|
+
* `Once<T>` is a container which can be written to only once. It provides safe access
|
|
1812
|
+
* to lazily initialized data with synchronous initialization.
|
|
1813
|
+
*
|
|
1814
|
+
* **When to use `Once<T>` vs `OnceAsync<T>`:**
|
|
1815
|
+
* - Use `Once<T>` for sync-only initialization
|
|
1816
|
+
* - Use `OnceAsync<T>` for async initialization with concurrent call handling
|
|
1817
|
+
*/
|
|
1818
|
+
/**
|
|
1819
|
+
* Creates a new empty `Once<T>`.
|
|
1820
|
+
*
|
|
1821
|
+
* @typeParam T - The type of value to store.
|
|
1822
|
+
* @returns A new uninitialized `Once`.
|
|
1823
|
+
* @example
|
|
1824
|
+
* ```ts
|
|
1825
|
+
* // Basic usage
|
|
1826
|
+
* const once = Once<string>();
|
|
1827
|
+
* once.set('hello');
|
|
1828
|
+
* console.log(once.get().unwrap()); // 'hello'
|
|
1829
|
+
* ```
|
|
1830
|
+
*
|
|
1831
|
+
* @example
|
|
1832
|
+
* ```ts
|
|
1833
|
+
* // Sync lazy singleton pattern
|
|
1834
|
+
* const logger = Once<Logger>();
|
|
1835
|
+
*
|
|
1836
|
+
* function getLogger(): Logger {
|
|
1837
|
+
* return logger.getOrInit(() => new Logger('app'));
|
|
1838
|
+
* }
|
|
1839
|
+
* ```
|
|
1840
|
+
*
|
|
1841
|
+
* @example
|
|
1842
|
+
* ```ts
|
|
1843
|
+
* // Fallible sync initialization
|
|
1844
|
+
* const config = Once<Config>();
|
|
1845
|
+
*
|
|
1846
|
+
* function loadConfig(): Result<Config, Error> {
|
|
1847
|
+
* return config.getOrTryInit(() => {
|
|
1848
|
+
* const data = readFileSync('config.json');
|
|
1849
|
+
* return data ? Ok(JSON.parse(data)) : Err(new Error('Config not found'));
|
|
1850
|
+
* });
|
|
1851
|
+
* }
|
|
1852
|
+
* ```
|
|
1853
|
+
*/
|
|
1131
1854
|
function Once() {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
const taken = value;
|
|
1181
|
-
value = void 0;
|
|
1182
|
-
initialized = false;
|
|
1183
|
-
return Some(taken);
|
|
1184
|
-
},
|
|
1185
|
-
isInitialized() {
|
|
1186
|
-
return initialized;
|
|
1187
|
-
}
|
|
1188
|
-
});
|
|
1855
|
+
let value;
|
|
1856
|
+
let initialized = false;
|
|
1857
|
+
/**
|
|
1858
|
+
* Sets the value and marks as initialized.
|
|
1859
|
+
*/
|
|
1860
|
+
function setValue(val) {
|
|
1861
|
+
value = val;
|
|
1862
|
+
initialized = true;
|
|
1863
|
+
}
|
|
1864
|
+
return Object.freeze({
|
|
1865
|
+
[Symbol.toStringTag]: "Once",
|
|
1866
|
+
toString() {
|
|
1867
|
+
return initialized ? `Once(${value})` : "Once(<uninitialized>)";
|
|
1868
|
+
},
|
|
1869
|
+
get() {
|
|
1870
|
+
return initialized ? Some(value) : None;
|
|
1871
|
+
},
|
|
1872
|
+
set(newValue) {
|
|
1873
|
+
if (initialized) return Err(newValue);
|
|
1874
|
+
setValue(newValue);
|
|
1875
|
+
return RESULT_VOID;
|
|
1876
|
+
},
|
|
1877
|
+
tryInsert(newValue) {
|
|
1878
|
+
if (initialized) return Err([value, newValue]);
|
|
1879
|
+
setValue(newValue);
|
|
1880
|
+
return Ok(newValue);
|
|
1881
|
+
},
|
|
1882
|
+
getOrInit(fn) {
|
|
1883
|
+
if (!initialized) setValue(fn());
|
|
1884
|
+
return value;
|
|
1885
|
+
},
|
|
1886
|
+
getOrTryInit(fn) {
|
|
1887
|
+
if (initialized) return Ok(value);
|
|
1888
|
+
const result = fn();
|
|
1889
|
+
if (result.isOk()) setValue(result.unwrap());
|
|
1890
|
+
return result;
|
|
1891
|
+
},
|
|
1892
|
+
take() {
|
|
1893
|
+
if (!initialized) return None;
|
|
1894
|
+
const taken = value;
|
|
1895
|
+
value = void 0;
|
|
1896
|
+
initialized = false;
|
|
1897
|
+
return Some(taken);
|
|
1898
|
+
},
|
|
1899
|
+
isInitialized() {
|
|
1900
|
+
return initialized;
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1189
1903
|
}
|
|
1190
|
-
|
|
1904
|
+
//#endregion
|
|
1905
|
+
//#region src/std/sync/once_async.ts
|
|
1906
|
+
/**
|
|
1907
|
+
* @module
|
|
1908
|
+
* Async-first one-time initialization container.
|
|
1909
|
+
*
|
|
1910
|
+
* `OnceAsync<T>` is designed for async initialization where the stored value
|
|
1911
|
+
* is always `Awaited<T>`, matching JavaScript's Promise flattening behavior.
|
|
1912
|
+
*
|
|
1913
|
+
* **When to use `OnceAsync<T>` vs `Once<T>`:**
|
|
1914
|
+
* - Use `Once<T>` for sync-only initialization
|
|
1915
|
+
* - Use `OnceAsync<T>` for async initialization with concurrent call handling
|
|
1916
|
+
*/
|
|
1917
|
+
/**
|
|
1918
|
+
* Creates a new empty `OnceAsync<T>`.
|
|
1919
|
+
*
|
|
1920
|
+
* `OnceAsync<T>` stores `Awaited<T>`, matching JavaScript's Promise flattening behavior.
|
|
1921
|
+
* This means `OnceAsync<Promise<number>>` and `OnceAsync<number>` behave identically.
|
|
1922
|
+
*
|
|
1923
|
+
* @typeParam T - The type parameter. The actual stored type is `Awaited<T>`.
|
|
1924
|
+
* @returns A new uninitialized `OnceAsync`.
|
|
1925
|
+
* @example
|
|
1926
|
+
* ```ts
|
|
1927
|
+
* // Basic usage
|
|
1928
|
+
* const once = OnceAsync<string>();
|
|
1929
|
+
* await once.getOrInit(async () => 'hello');
|
|
1930
|
+
* console.log(once.get().unwrap()); // 'hello'
|
|
1931
|
+
* ```
|
|
1932
|
+
*
|
|
1933
|
+
* @example
|
|
1934
|
+
* ```ts
|
|
1935
|
+
* // Async lazy singleton pattern
|
|
1936
|
+
* const db = OnceAsync<Database>();
|
|
1937
|
+
*
|
|
1938
|
+
* async function getDb(): Promise<Database> {
|
|
1939
|
+
* return await db.getOrInit(async () => {
|
|
1940
|
+
* console.log('Connecting to database...');
|
|
1941
|
+
* return await Database.connect(connectionString);
|
|
1942
|
+
* });
|
|
1943
|
+
* }
|
|
1944
|
+
*
|
|
1945
|
+
* // Multiple calls - connection happens only once
|
|
1946
|
+
* const [db1, db2] = await Promise.all([getDb(), getDb()]);
|
|
1947
|
+
* console.log(db1 === db2); // true
|
|
1948
|
+
* ```
|
|
1949
|
+
*
|
|
1950
|
+
* @example
|
|
1951
|
+
* ```ts
|
|
1952
|
+
* // Fallible async initialization
|
|
1953
|
+
* const config = OnceAsync<Config>();
|
|
1954
|
+
*
|
|
1955
|
+
* async function loadConfig(): Promise<Result<Config, Error>> {
|
|
1956
|
+
* return await config.getOrTryInit(async () => {
|
|
1957
|
+
* try {
|
|
1958
|
+
* const response = await fetch('/api/config');
|
|
1959
|
+
* if (!response.ok) {
|
|
1960
|
+
* return Err(new Error(`HTTP ${response.status}`));
|
|
1961
|
+
* }
|
|
1962
|
+
* return Ok(await response.json());
|
|
1963
|
+
* } catch (e) {
|
|
1964
|
+
* return Err(e as Error);
|
|
1965
|
+
* }
|
|
1966
|
+
* });
|
|
1967
|
+
* }
|
|
1968
|
+
* ```
|
|
1969
|
+
*/
|
|
1191
1970
|
function OnceAsync() {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
).finally(() => {
|
|
1274
|
-
pendingPromise = void 0;
|
|
1275
|
-
});
|
|
1276
|
-
return pendingPromise;
|
|
1277
|
-
},
|
|
1278
|
-
getOrTryInit,
|
|
1279
|
-
take() {
|
|
1280
|
-
if (!initialized) {
|
|
1281
|
-
return None;
|
|
1282
|
-
}
|
|
1283
|
-
const taken = value;
|
|
1284
|
-
value = void 0;
|
|
1285
|
-
initialized = false;
|
|
1286
|
-
resolvedPromise = void 0;
|
|
1287
|
-
resolvedResultPromise = void 0;
|
|
1288
|
-
return Some(taken);
|
|
1289
|
-
},
|
|
1290
|
-
isInitialized() {
|
|
1291
|
-
return initialized;
|
|
1292
|
-
},
|
|
1293
|
-
wait() {
|
|
1294
|
-
if (initialized) {
|
|
1295
|
-
return resolvedPromise ??= Promise.resolve(value);
|
|
1296
|
-
}
|
|
1297
|
-
if (pendingPromise) {
|
|
1298
|
-
return pendingPromise;
|
|
1299
|
-
}
|
|
1300
|
-
return new Promise((resolve) => {
|
|
1301
|
-
waiters.push(resolve);
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
});
|
|
1971
|
+
let value;
|
|
1972
|
+
let initialized = false;
|
|
1973
|
+
let pendingPromise;
|
|
1974
|
+
let resolvedPromise;
|
|
1975
|
+
let resolvedResultPromise;
|
|
1976
|
+
let waiters = [];
|
|
1977
|
+
/**
|
|
1978
|
+
* Sets the value, marks as initialized, and notifies all waiters.
|
|
1979
|
+
*/
|
|
1980
|
+
function setValue(val) {
|
|
1981
|
+
value = val;
|
|
1982
|
+
initialized = true;
|
|
1983
|
+
for (const waiter of waiters) waiter(val);
|
|
1984
|
+
waiters = [];
|
|
1985
|
+
}
|
|
1986
|
+
function getOrTryInit(fn) {
|
|
1987
|
+
if (initialized) return resolvedResultPromise ??= Promise.resolve(Ok(value));
|
|
1988
|
+
if (pendingPromise) return pendingPromise.then(() => Ok(value), () => getOrTryInit(fn));
|
|
1989
|
+
const resultPromise = Promise.resolve(fn());
|
|
1990
|
+
pendingPromise = resultPromise.then((result) => {
|
|
1991
|
+
if (result.isOk()) {
|
|
1992
|
+
const val = result.unwrap();
|
|
1993
|
+
setValue(val);
|
|
1994
|
+
return val;
|
|
1995
|
+
}
|
|
1996
|
+
throw result;
|
|
1997
|
+
}).finally(() => {
|
|
1998
|
+
pendingPromise = void 0;
|
|
1999
|
+
});
|
|
2000
|
+
return pendingPromise.then(() => resultPromise, () => resultPromise);
|
|
2001
|
+
}
|
|
2002
|
+
return Object.freeze({
|
|
2003
|
+
[Symbol.toStringTag]: "OnceAsync",
|
|
2004
|
+
toString() {
|
|
2005
|
+
return initialized ? `OnceAsync(${value})` : "OnceAsync(<uninitialized>)";
|
|
2006
|
+
},
|
|
2007
|
+
get() {
|
|
2008
|
+
return initialized ? Some(value) : None;
|
|
2009
|
+
},
|
|
2010
|
+
set(newValue) {
|
|
2011
|
+
if (initialized) return Err(newValue);
|
|
2012
|
+
setValue(newValue);
|
|
2013
|
+
return RESULT_VOID;
|
|
2014
|
+
},
|
|
2015
|
+
tryInsert(newValue) {
|
|
2016
|
+
if (initialized) return Err([value, newValue]);
|
|
2017
|
+
setValue(newValue);
|
|
2018
|
+
return Ok(newValue);
|
|
2019
|
+
},
|
|
2020
|
+
getOrInit(fn) {
|
|
2021
|
+
if (initialized) return resolvedPromise ??= Promise.resolve(value);
|
|
2022
|
+
if (pendingPromise) return pendingPromise;
|
|
2023
|
+
pendingPromise = Promise.resolve(fn()).then((result) => {
|
|
2024
|
+
setValue(result);
|
|
2025
|
+
return result;
|
|
2026
|
+
}).finally(() => {
|
|
2027
|
+
pendingPromise = void 0;
|
|
2028
|
+
});
|
|
2029
|
+
return pendingPromise;
|
|
2030
|
+
},
|
|
2031
|
+
getOrTryInit,
|
|
2032
|
+
take() {
|
|
2033
|
+
if (!initialized) return None;
|
|
2034
|
+
const taken = value;
|
|
2035
|
+
value = void 0;
|
|
2036
|
+
initialized = false;
|
|
2037
|
+
resolvedPromise = void 0;
|
|
2038
|
+
resolvedResultPromise = void 0;
|
|
2039
|
+
return Some(taken);
|
|
2040
|
+
},
|
|
2041
|
+
isInitialized() {
|
|
2042
|
+
return initialized;
|
|
2043
|
+
},
|
|
2044
|
+
wait() {
|
|
2045
|
+
if (initialized) return resolvedPromise ??= Promise.resolve(value);
|
|
2046
|
+
if (pendingPromise) return pendingPromise;
|
|
2047
|
+
return new Promise((resolve) => {
|
|
2048
|
+
waiters.push(resolve);
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
1305
2052
|
}
|
|
1306
|
-
|
|
2053
|
+
//#endregion
|
|
2054
|
+
//#region src/std/sync/rwlock.ts
|
|
2055
|
+
/**
|
|
2056
|
+
* @module
|
|
2057
|
+
* Rust-inspired [RwLock](https://doc.rust-lang.org/std/sync/struct.RwLock.html) for async read-write locking.
|
|
2058
|
+
*
|
|
2059
|
+
* In JavaScript's single-threaded environment, `RwLock<T>` allows multiple
|
|
2060
|
+
* concurrent readers or a single exclusive writer. This is useful when
|
|
2061
|
+
* read operations are frequent and write operations are rare.
|
|
2062
|
+
*
|
|
2063
|
+
* **When to use RwLock vs Mutex:**
|
|
2064
|
+
* - Use `RwLock` when reads are frequent, writes are rare, and read operations
|
|
2065
|
+
* contain `await` statements that could benefit from concurrent access.
|
|
2066
|
+
* - Use `Mutex` for simpler cases or when read/write frequency is balanced.
|
|
2067
|
+
*
|
|
2068
|
+
* **Important:** In JS, the benefit of RwLock is limited because:
|
|
2069
|
+
* - JS is single-threaded, so "parallel reads" don't truly execute simultaneously
|
|
2070
|
+
* - If read operations don't contain `await`, there's no opportunity for concurrency
|
|
2071
|
+
* - RwLock adds complexity over Mutex with marginal performance benefit
|
|
2072
|
+
*/
|
|
2073
|
+
/**
|
|
2074
|
+
* Creates a new `RwLock<T>` protecting the given value.
|
|
2075
|
+
*
|
|
2076
|
+
* @typeParam T - The type of the protected value.
|
|
2077
|
+
* @param value - The initial value to protect.
|
|
2078
|
+
* @returns A new `RwLock<T>` instance.
|
|
2079
|
+
* @example
|
|
2080
|
+
* ```ts
|
|
2081
|
+
* // Basic usage
|
|
2082
|
+
* const counter = RwLock(0);
|
|
2083
|
+
*
|
|
2084
|
+
* // Read (multiple can proceed)
|
|
2085
|
+
* const value = await counter.withRead(v => v);
|
|
2086
|
+
*
|
|
2087
|
+
* // Write (exclusive)
|
|
2088
|
+
* await counter.withWrite(v => v + 1);
|
|
2089
|
+
* ```
|
|
2090
|
+
*
|
|
2091
|
+
* @example
|
|
2092
|
+
* ```ts
|
|
2093
|
+
* // Cache with read-heavy workload
|
|
2094
|
+
* const cache = RwLock(new Map<string, Data>());
|
|
2095
|
+
*
|
|
2096
|
+
* // Many concurrent reads
|
|
2097
|
+
* async function get(key: string): Promise<Data | undefined> {
|
|
2098
|
+
* return cache.withRead(map => map.get(key));
|
|
2099
|
+
* }
|
|
2100
|
+
*
|
|
2101
|
+
* // Occasional writes
|
|
2102
|
+
* async function set(key: string, value: Data): Promise<void> {
|
|
2103
|
+
* await cache.withWrite(map => { map.set(key, value); });
|
|
2104
|
+
* }
|
|
2105
|
+
* ```
|
|
2106
|
+
*
|
|
2107
|
+
* @example
|
|
2108
|
+
* ```ts
|
|
2109
|
+
* // Configuration that's read frequently, updated rarely
|
|
2110
|
+
* interface AppConfig {
|
|
2111
|
+
* apiUrl: string;
|
|
2112
|
+
* timeout: number;
|
|
2113
|
+
* features: string[];
|
|
2114
|
+
* }
|
|
2115
|
+
*
|
|
2116
|
+
* const config = RwLock<AppConfig>({
|
|
2117
|
+
* apiUrl: 'https://api.example.com',
|
|
2118
|
+
* timeout: 5000,
|
|
2119
|
+
* features: ['feature-a', 'feature-b'],
|
|
2120
|
+
* });
|
|
2121
|
+
*
|
|
2122
|
+
* // Read config (concurrent)
|
|
2123
|
+
* async function isFeatureEnabled(feature: string): Promise<boolean> {
|
|
2124
|
+
* return config.withRead(cfg => cfg.features.includes(feature));
|
|
2125
|
+
* }
|
|
2126
|
+
*
|
|
2127
|
+
* // Update config (exclusive)
|
|
2128
|
+
* async function enableFeature(feature: string): Promise<void> {
|
|
2129
|
+
* await config.withWrite(cfg => {
|
|
2130
|
+
* if (!cfg.features.includes(feature)) {
|
|
2131
|
+
* cfg.features.push(feature);
|
|
2132
|
+
* }
|
|
2133
|
+
* });
|
|
2134
|
+
* }
|
|
2135
|
+
* ```
|
|
2136
|
+
*/
|
|
1307
2137
|
function RwLock(value) {
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
return writer;
|
|
1475
|
-
},
|
|
1476
|
-
async get() {
|
|
1477
|
-
const guard = await read();
|
|
1478
|
-
try {
|
|
1479
|
-
return guard.value;
|
|
1480
|
-
} finally {
|
|
1481
|
-
guard.unlock();
|
|
1482
|
-
}
|
|
1483
|
-
},
|
|
1484
|
-
async set(value2) {
|
|
1485
|
-
const guard = await write();
|
|
1486
|
-
try {
|
|
1487
|
-
guard.value = value2;
|
|
1488
|
-
} finally {
|
|
1489
|
-
guard.unlock();
|
|
1490
|
-
}
|
|
1491
|
-
},
|
|
1492
|
-
async replace(value2) {
|
|
1493
|
-
const guard = await write();
|
|
1494
|
-
try {
|
|
1495
|
-
const old = guard.value;
|
|
1496
|
-
guard.value = value2;
|
|
1497
|
-
return old;
|
|
1498
|
-
} finally {
|
|
1499
|
-
guard.unlock();
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
});
|
|
2138
|
+
let currentValue = value;
|
|
2139
|
+
let readers = 0;
|
|
2140
|
+
let writer = false;
|
|
2141
|
+
let pendingWriters = 0;
|
|
2142
|
+
const readWaitQueue = [];
|
|
2143
|
+
const writeWaitQueue = [];
|
|
2144
|
+
function tryAcquireRead() {
|
|
2145
|
+
if (!writer && pendingWriters === 0) {
|
|
2146
|
+
readers++;
|
|
2147
|
+
return true;
|
|
2148
|
+
}
|
|
2149
|
+
return false;
|
|
2150
|
+
}
|
|
2151
|
+
function tryAcquireWrite() {
|
|
2152
|
+
if (readers === 0 && !writer) {
|
|
2153
|
+
writer = true;
|
|
2154
|
+
return true;
|
|
2155
|
+
}
|
|
2156
|
+
return false;
|
|
2157
|
+
}
|
|
2158
|
+
function releaseRead() {
|
|
2159
|
+
readers--;
|
|
2160
|
+
if (readers === 0 && writeWaitQueue.length > 0) {
|
|
2161
|
+
const next = writeWaitQueue.shift();
|
|
2162
|
+
writer = true;
|
|
2163
|
+
pendingWriters--;
|
|
2164
|
+
next();
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
function releaseWrite() {
|
|
2168
|
+
writer = false;
|
|
2169
|
+
if (writeWaitQueue.length > 0) {
|
|
2170
|
+
const next = writeWaitQueue.shift();
|
|
2171
|
+
writer = true;
|
|
2172
|
+
pendingWriters--;
|
|
2173
|
+
next();
|
|
2174
|
+
} else while (readWaitQueue.length > 0) {
|
|
2175
|
+
readers++;
|
|
2176
|
+
readWaitQueue.shift()();
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
function createReadGuard() {
|
|
2180
|
+
let released = false;
|
|
2181
|
+
return Object.freeze({
|
|
2182
|
+
[Symbol.toStringTag]: "RwLockReadGuard",
|
|
2183
|
+
toString() {
|
|
2184
|
+
if (released) return "RwLockReadGuard(<released>)";
|
|
2185
|
+
return `RwLockReadGuard(${currentValue})`;
|
|
2186
|
+
},
|
|
2187
|
+
get value() {
|
|
2188
|
+
if (released) throw new Error("RwLockReadGuard has been released");
|
|
2189
|
+
return currentValue;
|
|
2190
|
+
},
|
|
2191
|
+
unlock() {
|
|
2192
|
+
if (released) return;
|
|
2193
|
+
released = true;
|
|
2194
|
+
releaseRead();
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
function createWriteGuard() {
|
|
2199
|
+
let released = false;
|
|
2200
|
+
return Object.freeze({
|
|
2201
|
+
[Symbol.toStringTag]: "RwLockWriteGuard",
|
|
2202
|
+
toString() {
|
|
2203
|
+
if (released) return "RwLockWriteGuard(<released>)";
|
|
2204
|
+
return `RwLockWriteGuard(${currentValue})`;
|
|
2205
|
+
},
|
|
2206
|
+
get value() {
|
|
2207
|
+
if (released) throw new Error("RwLockWriteGuard has been released");
|
|
2208
|
+
return currentValue;
|
|
2209
|
+
},
|
|
2210
|
+
set value(newValue) {
|
|
2211
|
+
if (released) throw new Error("RwLockWriteGuard has been released");
|
|
2212
|
+
currentValue = newValue;
|
|
2213
|
+
},
|
|
2214
|
+
unlock() {
|
|
2215
|
+
if (released) return;
|
|
2216
|
+
released = true;
|
|
2217
|
+
releaseWrite();
|
|
2218
|
+
}
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
function read() {
|
|
2222
|
+
if (tryAcquireRead()) return Promise.resolve(createReadGuard());
|
|
2223
|
+
return new Promise((resolve) => {
|
|
2224
|
+
readWaitQueue.push(() => {
|
|
2225
|
+
resolve(createReadGuard());
|
|
2226
|
+
});
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
function write() {
|
|
2230
|
+
if (tryAcquireWrite()) return Promise.resolve(createWriteGuard());
|
|
2231
|
+
pendingWriters++;
|
|
2232
|
+
return new Promise((resolve) => {
|
|
2233
|
+
writeWaitQueue.push(() => {
|
|
2234
|
+
resolve(createWriteGuard());
|
|
2235
|
+
});
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
return Object.freeze({
|
|
2239
|
+
[Symbol.toStringTag]: "RwLock",
|
|
2240
|
+
toString() {
|
|
2241
|
+
if (writer) return "RwLock(<write-locked>)";
|
|
2242
|
+
if (readers > 0) return `RwLock(<read-locked:${readers}>)`;
|
|
2243
|
+
return "RwLock(<unlocked>)";
|
|
2244
|
+
},
|
|
2245
|
+
async withRead(fn) {
|
|
2246
|
+
const guard = await read();
|
|
2247
|
+
try {
|
|
2248
|
+
return await fn(guard.value);
|
|
2249
|
+
} finally {
|
|
2250
|
+
guard.unlock();
|
|
2251
|
+
}
|
|
2252
|
+
},
|
|
2253
|
+
async withWrite(fn) {
|
|
2254
|
+
const guard = await write();
|
|
2255
|
+
try {
|
|
2256
|
+
return await fn(guard.value);
|
|
2257
|
+
} finally {
|
|
2258
|
+
guard.unlock();
|
|
2259
|
+
}
|
|
2260
|
+
},
|
|
2261
|
+
read,
|
|
2262
|
+
write,
|
|
2263
|
+
tryRead() {
|
|
2264
|
+
if (tryAcquireRead()) return Some(createReadGuard());
|
|
2265
|
+
return None;
|
|
2266
|
+
},
|
|
2267
|
+
tryWrite() {
|
|
2268
|
+
if (tryAcquireWrite()) return Some(createWriteGuard());
|
|
2269
|
+
return None;
|
|
2270
|
+
},
|
|
2271
|
+
readerCount() {
|
|
2272
|
+
return readers;
|
|
2273
|
+
},
|
|
2274
|
+
isWriteLocked() {
|
|
2275
|
+
return writer;
|
|
2276
|
+
},
|
|
2277
|
+
async get() {
|
|
2278
|
+
const guard = await read();
|
|
2279
|
+
try {
|
|
2280
|
+
return guard.value;
|
|
2281
|
+
} finally {
|
|
2282
|
+
guard.unlock();
|
|
2283
|
+
}
|
|
2284
|
+
},
|
|
2285
|
+
async set(value) {
|
|
2286
|
+
const guard = await write();
|
|
2287
|
+
try {
|
|
2288
|
+
guard.value = value;
|
|
2289
|
+
} finally {
|
|
2290
|
+
guard.unlock();
|
|
2291
|
+
}
|
|
2292
|
+
},
|
|
2293
|
+
async replace(value) {
|
|
2294
|
+
const guard = await write();
|
|
2295
|
+
try {
|
|
2296
|
+
const old = guard.value;
|
|
2297
|
+
guard.value = value;
|
|
2298
|
+
return old;
|
|
2299
|
+
} finally {
|
|
2300
|
+
guard.unlock();
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
});
|
|
1503
2304
|
}
|
|
1504
|
-
|
|
2305
|
+
//#endregion
|
|
1505
2306
|
exports.ASYNC_NONE = ASYNC_NONE;
|
|
1506
2307
|
exports.Break = Break;
|
|
1507
2308
|
exports.Channel = Channel;
|
|
@@ -1529,4 +2330,5 @@ exports.tryAsyncOption = tryAsyncOption;
|
|
|
1529
2330
|
exports.tryAsyncResult = tryAsyncResult;
|
|
1530
2331
|
exports.tryOption = tryOption;
|
|
1531
2332
|
exports.tryResult = tryResult;
|
|
1532
|
-
|
|
2333
|
+
|
|
2334
|
+
//# sourceMappingURL=main.cjs.map
|