import type { FromRef, FromRefs, Ref, Refs } from './types'; /** * Wraps a single value in a Ref object. * * If the value is already a Ref, returns it as-is (idempotent). * * @example * ```ts * const r = ref(123); // { '~ref': 123 } * console.log(r['~ref']); // 123 * * const r2 = ref(r); // { '~ref': 123 } (not double-wrapped) * ``` */ export const ref = (value: T): Ref => { if (isRef(value)) { return value as Ref; } return { '~ref': value } as Ref; }; /** * Converts a plain object to an object of Refs (deep, per property). * * @example * ```ts * const obj = { a: 1, b: "x" }; * const refs = refs(obj); // { a: { '~ref': 1 }, b: { '~ref': "x" } } * ``` */ export const refs = >(obj: T): Refs => { const result = {} as Refs; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { result[key] = ref(obj[key]); } } return result; }; /** * Unwraps a single Ref object to its value. * * @example * ```ts * const r = { '~ref': 42 }; * const n = fromRef(r); // 42 * console.log(n); // 42 * ``` */ export const fromRef = | undefined>(ref: T): FromRef => ref?.['~ref'] as FromRef; /** * Converts an object of Refs back to a plain object (unwraps all refs). * * @example * ```ts * const refs = { a: { '~ref': 1 }, b: { '~ref': "x" } }; * const plain = fromRefs(refs); // { a: 1, b: "x" } * ``` */ export const fromRefs = >>(obj: T): FromRefs => { const result = {} as FromRefs; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { result[key] = fromRef(obj[key]!) as (typeof result)[typeof key]; } } return result; }; /** * Checks whether a value is a Ref object. * * @param value Value to check * @returns True if the value is a Ref object. */ export const isRef = (value: unknown): value is Ref => typeof value === 'object' && value !== null && '~ref' in value;