Skip to content

Releases: vitalics/rslike

@rslike/std@3.3.0

20 Feb 14:25
6f52e27

Choose a tag to compare

Minor Changes

  • 2c89d8f: fix(std): correct Rust semantic mismatches in Option, Result, and match; add Option.take()

    fix(result): isErrAnd always passed undefined to the predicate instead of the actual error

    The predicate referenced a non-existent field this.err; the real field is this.error.

    Before:

    const res = predicate(this.err as TErr); // always undefined

    After:

    const res = predicate(this.error as TErr);

    fix(result): expect() silently returned null when reason was an empty string

    The reason && short-circuit caused Err("x").expect("") to skip the throw entirely and
    return null. Rust's expect always panics on Err regardless of the message content.

    Before:

    if (reason && this.status === Status.Err) {
      throw new Error(reason, { cause: this.error });
    }
    // Err("x").expect("") → returned null silently

    After:

    if (this.status === Status.Err) {
      throw new Error(reason, { cause: this.error });
    }
    // Err("x").expect("") → throws Error("")

    fix(result): Result.fromPromise produced a different type than Async()

    Async() returns Result<Option<T>, E>. fromPromise returned Result<T, E> with no Option
    wrapper, making the two Promise-to-Result helpers type-incompatible even though they are
    documented as equivalent.

    Before:

    const a = await Async(Promise.resolve(42));
    a.unwrap().unwrap(); // 42  — Option wrapper present
    
    const b = await Result.fromPromise(Promise.resolve(42));
    b.unwrap(); // 42  — no Option wrapper (inconsistent)

    After:

    const b = await Result.fromPromise(Promise.resolve(42));
    b.unwrap().unwrap(); // 42  — Option wrapper now present, consistent with Async()

    fix(option): xor() returned Some when both operands were Some

    Rust's xor returns Some only if exactly one of the two operands is Some. When both are
    Some the result must be None. The missing check caused the wrong value to be returned.

    Before:

    Some(1).xor(Some(2)); // → Some(1)  ❌ should be None()
    Some(1).xor(None()); // → Some(1)  ✅
    None().xor(Some(2)); // → Some(2)  ✅
    None().xor(None()); // → None()   ✅

    After:

    Some(1).xor(Some(2)); // → None()   ✅
    Some(1).xor(None()); // → Some(1)  ✅
    None().xor(Some(2)); // → Some(2)  ✅
    None().xor(None()); // → None()   ✅

    fix(option): flatten() relied on accidental Some(undefined) → None coercion for the None path

    None().flatten() produced the correct result only because Some(undefined) happens to coerce
    to None. An explicit isNone() guard now makes the intent clear and removes the fragile
    dependency on the coercion side-effect.

    Before:

    flatten() {
      if (this.value instanceof Option) {
        return Some(this.value.value) as never; // accessed private .value directly
      }
      return Some(this.value) as never; // None path: Some(undefined) → None by accident
    }

    After:

    flatten() {
      if (this.isNone()) return None() as never; // explicit, intentional
      if (this.value instanceof Option) {
        return Some((this.value as Option<any>).valueOf()) as never;
      }
      return Some(this.value) as never;
    }

    fix(match): JSDoc comment claimed Ok(None()) triggers the Ok callback — code does the opposite

    The implementation correctly routes Ok(None()) to the Err/None callback (absence of value
    inside an Ok wrapper = effectively None). Only the doc comment was wrong.

    Before:

    Ok(None) - will trigger Ok callback
    

    After:

    Ok(None()) - will trigger Err/None callback
    

    feat(option): add missing take() method

    Rust's Option::take moves the value out of the option, leaving None in its place. This
    method was absent from the implementation.

    Before:

    // method did not exist — runtime error
    Some(2).take(); // TypeError: Some(...).take is not a function

    After:

    const x = Some(2);
    const y = x.take();
    x.isNone(); // true  — original is now None
    y.unwrap(); // 2     — taken value is preserved
    
    const a = None<number>();
    const b = a.take();
    a.isNone(); // true
    b.isNone(); // true

@rslike/iter@1.0.0

20 Feb 14:25
6f52e27

Choose a tag to compare

Major Changes

Patch Changes

  • Updated dependencies [2c89d8f]
  • Updated dependencies [2c89d8f]
    • @rslike/cmp@3.2.1
    • @rslike/std@3.3.0

@rslike/dbg@3.2.0

20 Feb 14:24
6f52e27

Choose a tag to compare

Minor Changes

  • 2c89d8f: feat(dbg): add automatic Proxy detection and expose isProxy utility

    feat(dbg): intercept global Proxy constructor to track all created proxies

    On module import, @rslike/dbg replaces the global Proxy constructor with a transparent
    intercepting version that registers every new Proxy(...) and Proxy.revocable(...) call in a
    WeakSet. No user-side changes are required — any proxy created after the module is first
    imported is automatically tracked.

    Before:

    import { dbg } from "@rslike/dbg";
    
    const store = new Proxy({ count: 0 }, {});
    dbg(() => store);
    // dbg | store: {"count":0}
    // — no indication that `store` is a Proxy

    After:

    import { dbg } from "@rslike/dbg";
    
    const store = new Proxy({ count: 0 }, {});
    dbg(() => store);
    // dbg | store (Proxy): {"count":0}
    // — Proxy is detected and annotated automatically

    feat(dbg): expose isProxy(value) utility function

    Returns true if the value was created via new Proxy(...) after @rslike/dbg was imported.
    Returns false for plain objects, primitives, or proxies created before the module was loaded.

    Before:

    // no way to check programmatically

    After:

    import { isProxy } from "@rslike/dbg";
    
    const p = new Proxy({ x: 1 }, {});
    isProxy(p); // true
    isProxy({ x: 1 }); // false
    isProxy(null); // false
    isProxy(42); // false

    feat(dbg): dbg() return value now includes isProxy field

    The inspection result object returned by dbg() now exposes whether the inspected value is a
    known Proxy, allowing callers to branch on it programmatically.

    Before:

    const result = dbg(() => store);
    // result = { name, type, value, message, delimiter, prefix }

    After:

    const result = dbg(() => store);
    // result = { name, type, value, message, delimiter, prefix, isProxy: true }
    result.isProxy; // true for Proxy values, false for plain values

    fix(dbg): label extraction regex stripped () => only with exact spacing

    The old regex /(\(\) => )/g required a space before and after =>. Bundlers such as esbuild
    and swc emit ()=> without spaces, causing the arrow prefix to appear in the output label.

    Before:

    dbg(() => double(21));
    // dbg | ()=>double(21): 42   ❌ prefix not stripped

    After:

    dbg(() => double(21));
    // dbg | double(21): 42       ✅

@rslike/cmp@3.2.1

20 Feb 14:25
6f52e27

Choose a tag to compare

Patch Changes

  • 2c89d8f: test(cmp): add missing branch coverage for primitives and utils

    test(cmp/primitives): cover Symbol.equals and Symbol.partialEquals on all primitive types

    Added tests for previously untested branches on Number, String, Boolean, and Date
    prototype extensions:

    • Number[Symbol.equals] strict equality and inequality
    • Number[Symbol.partialEquals] loose equality with coercion
    • String[Symbol.equals] inequality path
    • String[Symbol.partialEquals] cross-type comparison via ==
    • Boolean[Symbol.equals] with plain boolean literals
    • Boolean[Symbol.partialEquals] with undefined (returns false)
    • Date[Symbol.compare] with ISO string argument (greater / less paths)
    • Date[Symbol.compare] with invalid date string → throws UndefinedBehaviorError

    test(cmp/utils): document known source bugs discovered during coverage analysis

    • compare() with a valid compareFn: after validating the return type, the result is never
      returned — execution falls through to throw UndefinedBehaviorError. Test documents this as a
      known source bug.
    • partialEquals(null, ...): no null guard before a[kPartialEquals], causing
      TypeError: Cannot read properties of null. Test documents this crash.
    • partialEquals() falsy-value coercion: 0 == false and 1 == true via == fallback.
    • equals() with two string primitives uses strict ===.
    • equals(null, null) falls through to a === b (returns true).
  • Updated dependencies [2c89d8f]

    • @rslike/std@3.3.0

@rslike/std@3.2.0

15 Oct 04:28
f963e44

Choose a tag to compare

Minor Changes

  • bb3ced8: ## Fixes

    [@rslike/cmp]

    • Number[Symbol.compare] is not a function error.

    Before:

    (9)[Symbol.compare](3); // Symbol.compare is not a function

    After:

    (9)[Symbol.compare](3); // works correctly now, no error

    [@rslike/std]

    • unwrap typings now returns correct result.

    NOTE: behavior is not changed, only inferred type

    Before:

    const a = None();
    a.unwrap(); // unknown

    After:

    const a = None();
    a.unwrap(); // never, since this throws an error

    Other

    • Migrate from jest to vitest

@rslike/dbg@3.1.1

15 Oct 04:28
f963e44

Choose a tag to compare

Patch Changes

  • bb3ced8: ## Fixes

    [@rslike/cmp]

    • Number[Symbol.compare] is not a function error.

    Before:

    (9)[Symbol.compare](3); // Symbol.compare is not a function

    After:

    (9)[Symbol.compare](3); // works correctly now, no error

    [@rslike/std]

    • unwrap typings now returns correct result.

    NOTE: behavior is not changed, only inferred type

    Before:

    const a = None();
    a.unwrap(); // unknown

    After:

    const a = None();
    a.unwrap(); // never, since this throws an error

    Other

    • Migrate from jest to vitest

@rslike/cmp@3.2.0

15 Oct 04:28
f963e44

Choose a tag to compare

Minor Changes

  • bb3ced8: ## Fixes

    [@rslike/cmp]

    • Number[Symbol.compare] is not a function error.

    Before:

    (9)[Symbol.compare](3); // Symbol.compare is not a function

    After:

    (9)[Symbol.compare](3); // works correctly now, no error

    [@rslike/std]

    • unwrap typings now returns correct result.

    NOTE: behavior is not changed, only inferred type

    Before:

    const a = None();
    a.unwrap(); // unknown

    After:

    const a = None();
    a.unwrap(); // never, since this throws an error

    Other

    • Migrate from jest to vitest

Patch Changes

  • Updated dependencies [bb3ced8]
    • @rslike/std@3.2.0

@rslike/std@3.1.1

10 Mar 12:21
de4ecc8

Choose a tag to compare

Patch Changes

  • b4303ba: What's new in @rslike/std:

    • Improved documentation for constructor parameters for Option and Result.
    • Update types and JSDoc for toString, valueOf, toJSON for both Option and Result.
    • Make Status frozen and readonly object instead of mutable for Option and Result.
    • Improved docs for Some, None, Ok, Err descriptions expecial usage with instanceof keyword.

@rslike/cmp@3.1.1

10 Mar 12:21
de4ecc8

Choose a tag to compare

Patch Changes

  • Updated dependencies [b4303ba]
    • @rslike/std@3.1.1

@rslike/std@3.1.0

02 Mar 19:56
be0514d

Choose a tag to compare

Minor Changes

  • 501b261: ## New Features

    Result & Option constructors control flow

    From this version you can create Result or Option instances as Promises or by returning a value.

    import { Result, Option } from "@rslike/std";
    
    // constructor control flow
    const result = new Result((ok, err) => {
      if (Math.random() > 0.5) {
        ok(42);
      } else {
        err("error");
      }
    });
    
    // return control flow
    const result2 = new Result(() => {
      if (Math.random() > 0.5) {
        return 42;
      } else {
        throw new Error("error");
      }
    });
    
    console.log(result2); // Result<42, Error> or Result<number, Error>
    
    const option = new Option((some, none) => {
      if (Math.random() > 0.5) {
        some(42);
      } else {
        none();
      }
    });
    
    const opt2 = new Option(() => {
      throw new Error("error");
    });
    
    console.log(opt2.isNone()); // true

    NOTE: Async functions and Promise return statements are not supported and throw an error for Result and Option.

    import { Result } from "@rslike/std";
    
    // constructor control flow
    new Result((ok, err) => {
      ok(Promise.resolve(42)); // throw an error. Use `Result.fromPromise` instead
    });
    
    // return control flow
    new Result(() => {
      return Promise.resolve(42); // throw an error. Use `Result.fromPromise` instead
    });
    
    // return control flow
    new Result(async () => {
      return "qwe"; // throw an error. Use `Result.fromPromise` instead
    });

    Option.fromPromise & Option.fromAsync methods

    From this version you are able to create an Option from a Promise or an async function.

    Fixes

    • migrate Status for Result and Option from enum to object.
    • add assertion for passing constructor executor argument in Result and Option.
    • fix types for well known Symbols: Symbol.split, Symbol.search, Symbol.iterator and Symbol.asyncIterator
    • fix inspection output result for Result and Option in node.js

    Chore

    • migrate from eslint to biome
    • bump typescript to 5.8.2
    • move typedoc in devDependencies
    • bump typedoc to 0.27.9
    • update nodejs version in CI
    • fix lint issues