@evolu/common 6.0.1-preview.20 → 6.0.1-preview.21

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/src/Result.ts CHANGED
@@ -228,23 +228,26 @@
228
228
  *
229
229
  * ### FAQ
230
230
  *
231
- * #### What if my function doesn't return a value on success?
231
+ * #### When should a function return a plain value instead of `Result<T, E>`?
232
232
  *
233
- * If your function performs an operation but doesn't need to return a value on
234
- * success, you can use `Result<void, E>`. Using `Result<void, E>` is clearer
235
- * than using `Result<true, E>` or `Result<null, E>` because it communicates
236
- * that the function doesn't produce a value but can produce errors.
233
+ * Use `Result<T, E>` only when a function can fail with **known, expected
234
+ * errors** that callers need to handle. If a function cannot fail with a known
235
+ * error, return the value directly.
236
+ *
237
+ * - ✅ Return `Result<User, UserNotFoundError>` - can fail with a known error
238
+ * - ✅ Return `User` - cannot fail with a known error
239
+ * - ❌ Don't return `Result<User, never>` - unnecessary wrapper
237
240
  *
238
- * #### When can a function return `void` instead of `Result<void, E>`?
241
+ * This keeps the codebase clean and makes error handling intentional. The type
242
+ * system communicates which operations can fail and which cannot.
239
243
  *
240
- * A function can safely return `void` (instead of `Result<void, E>`) when all
241
- * unsafe code within it is properly wrapped with `trySync` or `tryAsync`. If
242
- * developers consistently wrap all potentially throwing operations, then any
243
- * function returning `void` is guaranteed not to throw and can be called
244
- * without error handling.
244
+ * Unsafe code from external libraries (not under our control) should be wrapped
245
+ * with `trySync` or `tryAsync` at the boundaries. Once wrapped, if the error is
246
+ * not important to callers, functions can safely return plain values. If the
247
+ * error matters, use `Result` with a typed error.
245
248
  *
246
249
  * ```ts
247
- * // ✅ Safe to return void - all unsafe code is wrapped
250
+ * // ✅ Safe to return void - unsafe code is wrapped and error is handled
248
251
  * const processData = (data: string): void => {
249
252
  * const parseResult = trySync(
250
253
  * () => JSON.parse(data),
@@ -252,7 +255,7 @@
252
255
  * );
253
256
  *
254
257
  * if (!parseResult.ok) {
255
- * logError(parseResult.error); // Handle error appropriately
258
+ * logError(parseResult.error);
256
259
  * return;
257
260
  * }
258
261
  *
@@ -263,9 +266,12 @@
263
266
  * processData(jsonString);
264
267
  * ```
265
268
  *
266
- * This approach creates a clear contract: functions returning `void` are safe
267
- * to call, while functions returning `Result<T, E>` require explicit error
268
- * handling.
269
+ * #### What if my function doesn't return a value on success?
270
+ *
271
+ * If your function performs an operation but doesn't need to return a value on
272
+ * success, you can use `Result<void, E>`. Using `Result<void, E>` is clearer
273
+ * than using `Result<true, E>` or `Result<null, E>` because it communicates
274
+ * that the function doesn't produce a value but can produce errors.
269
275
  *
270
276
  * #### How do I short-circuit processing of an array on the first error?
271
277
  *
@@ -460,6 +466,31 @@ export const getOrThrow = <T, E>(result: Result<T, E>): T => {
460
466
  }
461
467
  };
462
468
 
469
+ /**
470
+ * Extracts the value from a {@link Result} if it is an `Ok`, or returns `null`
471
+ * if it is an `Err`.
472
+ *
473
+ * **Intended usage:**
474
+ *
475
+ * - When you need to convert a `Result` to a nullable value for APIs that expect
476
+ * `T | null`.
477
+ * - When the error is not important and you just want the value or nothing.
478
+ *
479
+ * ### Example
480
+ *
481
+ * ```ts
482
+ * const parseResult = parseJson('{"key": "value"}');
483
+ * const value = getOrNull(parseResult);
484
+ * // value is unknown | null
485
+ *
486
+ * if (value != null) {
487
+ * console.log("Parsed value:", value);
488
+ * }
489
+ * ```
490
+ */
491
+ export const getOrNull = <T, E>(result: Result<T, E>): T | null =>
492
+ result.ok ? result.value : null;
493
+
463
494
  /**
464
495
  * Wraps synchronous functions that may throw exceptions, returning a
465
496
  * {@link Result}.
package/src/Task.ts CHANGED
@@ -484,10 +484,10 @@ export interface RetryError<E> {
484
484
  export const retry = <T, E>(
485
485
  {
486
486
  retries,
487
- initialDelay = "100ms",
488
- maxDelay = "10s",
487
+ initialDelay = "1s",
488
+ maxDelay = "30s",
489
489
  factor = 2,
490
- jitter = 0.1,
490
+ jitter = 0.5,
491
491
  retryable = (error: E | AbortError) => !isAbortError(error),
492
492
  onRetry,
493
493
  }: RetryOptions<E>,
package/src/Type.ts CHANGED
@@ -1503,6 +1503,18 @@ export const formatSimplePasswordError = (
1503
1503
  * characters), standard and native string serialization (Base64Url), and no
1504
1504
  * privacy leaks.
1505
1505
  *
1506
+ * ### Future Consideration
1507
+ *
1508
+ * For database-heavy workloads where insert performance is critical, a hybrid
1509
+ * approach could be considered: `timestamp ^ H(cluster_id, timestamp >> N)`
1510
+ * where H is a keyed hash function and N is a configurable parameter. This
1511
+ * would maintain spatial locality for database caches (improving insert
1512
+ * performance by an order of magnitude) while adding entropy to prevent
1513
+ * timestamp leakage and correlation across systems. The parameter N would allow
1514
+ * trading off cache locality (larger N = better locality) versus entropy
1515
+ * distribution. See https://brooker.co.za/blog/2025/10/22/uuidv7.html for
1516
+ * details on this approach.
1517
+ *
1506
1518
  * @category String
1507
1519
  */
1508
1520
  export const Id = brand("Id", String, (value) =>