nalloc 0.0.2 → 0.1.0
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/README.md +56 -8
- package/build/option.cjs +5 -6
- package/build/option.cjs.map +1 -1
- package/build/option.d.ts +1 -2
- package/build/option.js +5 -6
- package/build/option.js.map +1 -1
- package/build/result.cjs +45 -17
- package/build/result.cjs.map +1 -1
- package/build/result.d.ts +12 -2
- package/build/result.js +42 -17
- package/build/result.js.map +1 -1
- package/build/types.cjs +4 -4
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +1 -0
- package/build/types.js +4 -4
- package/build/types.js.map +1 -1
- package/package.json +29 -10
- package/src/__tests__/result.ts +37 -1
- package/src/__tests__/result.types.ts +1 -1
- package/src/option.ts +5 -8
- package/src/result.ts +60 -22
- package/src/types.ts +6 -4
package/build/types.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type Some<T> = ValueType<T> & {
|
|
|
15
15
|
export type None = NoneValueType & {
|
|
16
16
|
readonly [SOME_BRAND]: false;
|
|
17
17
|
};
|
|
18
|
+
export type UnOption<V, T extends Option<V>> = T extends Some<V> ? V : None;
|
|
18
19
|
/** Constant representing None. Use this instead of null/undefined for clarity. */
|
|
19
20
|
export declare const NONE: None;
|
|
20
21
|
/** Shared frozen empty array to avoid allocations. */
|
package/build/types.js
CHANGED
|
@@ -25,10 +25,10 @@ function ResultErrorCtor(error) {
|
|
|
25
25
|
}
|
|
26
26
|
ResultErrorCtor.prototype[ERR_BRAND] = true;
|
|
27
27
|
export function isOk(result) {
|
|
28
|
-
return !result
|
|
28
|
+
return typeof result !== 'object' || result === null || !result[ERR_BRAND];
|
|
29
29
|
}
|
|
30
30
|
export function isErr(result) {
|
|
31
|
-
return result
|
|
31
|
+
return typeof result === 'object' && result !== null && result[ERR_BRAND] === true;
|
|
32
32
|
}
|
|
33
33
|
export function okUnchecked(value) {
|
|
34
34
|
return value;
|
|
@@ -43,10 +43,10 @@ export function err(error) {
|
|
|
43
43
|
return new ResultErrorCtor(error);
|
|
44
44
|
}
|
|
45
45
|
export function isThenable(value) {
|
|
46
|
-
return typeof value
|
|
46
|
+
return (typeof value === 'object' || typeof value === 'function') && value !== null && typeof value.then === 'function';
|
|
47
47
|
}
|
|
48
48
|
export function isSync(value) {
|
|
49
|
-
return typeof value
|
|
49
|
+
return !(typeof value === 'object' || typeof value === 'function') || value === null || typeof value.then !== 'function';
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
//# sourceMappingURL=types.js.map
|
package/build/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/** Values that represent None (absence of a value). */\nexport type NoneValueType = null | undefined | void;\n\n/** Widens literal types to their base types for better type inference. */\nexport type Widen<T> = T extends string\n ? string\n : T extends number\n ? number\n : T extends boolean\n ? boolean\n : T extends bigint\n ? bigint\n : T extends symbol\n ? symbol\n : T;\n\n/** Widens never to unknown, otherwise preserves the type. */\nexport type WidenNever<T> = [T] extends [never] ? unknown : T;\n\n/** Excludes None values from a type, leaving only valid Some values. */\nexport type ValueType<T> = Exclude<T, NoneValueType>;\n\nexport declare const SOME_BRAND: unique symbol;\n\n/** Represents a value that is present. The value itself is the Some - no wrapper object. */\nexport type Some<T> = ValueType<T> & { readonly [SOME_BRAND]: true };\n\n/** Represents the absence of a value (null or undefined). */\nexport type None = NoneValueType & { readonly [SOME_BRAND]: false };\n\n/** Constant representing None. Use this instead of null/undefined for clarity. */\nexport const NONE = undefined as None;\n\n/** Shared frozen empty array to avoid allocations. */\nexport const EMPTY: readonly unknown[] = Object.freeze([]);\n\n/** A value that may or may not be present. Some(T) is the value itself; None is null/undefined. */\nexport type Option<T> = Some<ValueType<T>> | None;\n\n/** Extracts the value type from an Option type. */\nexport type OptionValue<TOption> = TOption extends Option<infer TValue> ? TValue : never;\n\n/** Type predicate: true if T is an Option type. */\nexport type IsOption<T> = T extends Option<any> ? true : false;\n\n/** Infers the Some type from a value, preserving the inner type. */\nexport type InferSome<T> = T extends Some<infer TValue> ? Some<ValueType<TValue>> : never;\n\n/**\n * Checks if an Option contains a value (is Some).\n * @param opt - The Option to check\n * @returns true if the Option is Some, false if None\n * @example\n * isSome(42) // true\n * isSome(null) // false\n * isSome(undefined) // false\n */\nexport function isSome<T>(opt: Some<T>): true;\nexport function isSome(opt: None): false;\nexport function isSome<T>(opt: unknown): opt is Some<T>;\nexport function isSome(opt: unknown): boolean {\n return opt !== null && opt !== undefined;\n}\n\n/**\n * Checks if an Option is None (absent).\n * @param opt - The Option to check\n * @returns true if the Option is None, false if Some\n * @example\n * isNone(null) // true\n * isNone(undefined) // true\n * isNone(42) // false\n */\nexport function isNone<T>(opt: Some<T>): false;\nexport function isNone(opt: None): true;\nexport function isNone(opt: unknown): opt is None;\nexport function isNone(opt: unknown): boolean {\n return opt === null || opt === undefined;\n}\n\n/**\n * Creates an Option from a nullable value. Returns None for null/undefined, Some otherwise.\n * @param value - The value to wrap\n * @returns Some(value) if value is non-null, None otherwise\n * @example\n * optionOf(42) // Some(42)\n * optionOf(null) // None\n * optionOf(undefined) // None\n */\nexport function optionOf(value: null): None;\nexport function optionOf(value: undefined): None;\nexport function optionOf<T>(value: T): T extends NoneValueType ? None : Some<T>;\nexport function optionOf<T>(value: T): Option<T> {\n return isNone(value as Option<T>) ? NONE : (value as Some<T>);\n}\n\nexport function someUnchecked<T>(value: T): Some<ValueType<T>> {\n return value as Some<ValueType<T>>;\n}\n\n/**\n * Creates a Some value with runtime validation.\n * @param value - The value to wrap\n * @returns The value typed as Some\n * @throws TypeError if value is null or undefined\n * @example\n * some(42) // Some(42)\n */\nexport function some<T>(value: ValueType<T>): Some<ValueType<T>> {\n if (value === null || value === undefined) {\n throw new TypeError('some() requires a non-nullable value');\n }\n return someUnchecked(value);\n}\n\n/** Constant representing None. Alias for NONE. */\nexport const none: None = NONE;\n\ndeclare const OK_BRAND: unique symbol;\n\n/** Represents a successful Result value. The value itself is the Ok - no wrapper object. */\nexport type Ok<T> = T & { readonly [OK_BRAND]: true };\n\nconst ERR_BRAND = Symbol.for('nalloc.ResultError');\n\ninterface ResultErrorShape<E> {\n readonly error: E;\n readonly [ERR_BRAND]: true;\n}\n\nfunction ResultErrorCtor<E>(this: ResultErrorShape<E>, error: E): void {\n (this as { error: E }).error = error;\n}\n(ResultErrorCtor.prototype as { [ERR_BRAND]: true })[ERR_BRAND] = true;\n\n/** The error wrapper type used internally. */\nexport type ResultError<E> = ResultErrorShape<E>;\n\n/** Represents a failed Result containing an error. */\nexport type Err<E> = ResultError<E>;\n\n/** A value that is either successful (Ok) or failed (Err). Ok is the value itself; Err wraps the error. */\nexport type Result<T, E> = Ok<T> | Err<E>;\n\n/** Extracts the success value type from a Result type. */\nexport type ResultValue<TResult> = TResult extends Result<infer TValue, any> ? TValue : never;\n\n/** Extracts the error type from a Result type. */\nexport type ResultErrorType<TResult> = TResult extends Result<any, infer TError> ? TError : never;\n\n/** Type predicate: true if T is a Result type. */\nexport type IsResult<T> = T extends Result<any, any> ? true : false;\n\n/** Infers the Err type from a value, preserving the error type. */\nexport type InferErr<T> = T extends Err<infer TError> ? Err<TError> : never;\n\n/**\n * Checks if a Result is Ok (successful).\n * @param result - The Result to check\n * @returns true if the Result is Ok, false if Err\n * @example\n * isOk(42) // true (Ok value)\n * isOk(err('fail')) // false\n */\nexport function isOk<T>(result: Ok<T>): true;\nexport function isOk<E>(result: Err<E>): false;\nexport function isOk<T, E>(result: Result<T, E>): result is Ok<T>;\nexport function isOk(result: unknown): boolean;\nexport function isOk(result: unknown): boolean {\n return !(result as Record<symbol, unknown>)?.[ERR_BRAND];\n}\n\n/**\n * Checks if a Result is Err (failed).\n * @param result - The Result to check\n * @returns true if the Result is Err, false if Ok\n * @example\n * isErr(err('fail')) // true\n * isErr(42) // false (Ok value)\n */\nexport function isErr<E>(result: Err<E>): true;\nexport function isErr(result: Ok<unknown>): false;\nexport function isErr<T, E>(result: Result<T, E>): result is Err<E>;\nexport function isErr(result: unknown): result is Err<unknown>;\nexport function isErr(result: unknown): boolean {\n return (result as Record<symbol, unknown>)?.[ERR_BRAND] === true;\n}\n\nexport function okUnchecked<T>(value: T): Ok<T> {\n return value as Ok<T>;\n}\n\n/**\n * Creates an Ok value with runtime validation.\n * @param value - The success value\n * @returns The value typed as Ok\n * @throws TypeError if value is an Err\n * @example\n * ok(42) // Ok(42)\n */\nexport function ok<T>(value: T): Ok<T> {\n if (isErr(value)) {\n throw new TypeError('ok() cannot wrap an Err value');\n }\n return okUnchecked(value);\n}\n\n/**\n * Creates an Err value wrapping an error.\n * @param error - The error value\n * @returns An Err containing the error\n * @example\n * err('something went wrong') // Err('something went wrong')\n * err(new Error('failed')) // Err(Error)\n */\nexport function err<E>(error: E): Err<E> {\n return new (ResultErrorCtor as unknown as new (error: E) => Err<E>)(error);\n}\n\n/** A value that may or may not be a Promise. */\nexport type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;\n\n/**\n * Checks if a value is a thenable (has a .then method).\n * @param value - The value to check\n * @returns true if value is a PromiseLike\n */\nexport function isThenable<T>(value: MaybePromise<T>): value is PromiseLike<T> {\n return typeof (value as PromiseLike<T>)?.then === 'function';\n}\n\n/**\n * Checks if a value is synchronous (not a thenable).\n * @param value - The value to check\n * @returns true if value is not a PromiseLike\n */\nexport function isSync<T>(value: MaybePromise<T>): value is T {\n return typeof (value as PromiseLike<T>)?.then !== 'function';\n}\n"],"names":["NONE","undefined","EMPTY","Object","freeze","isSome","opt","isNone","optionOf","value","someUnchecked","some","TypeError","none","ERR_BRAND","Symbol","for","ResultErrorCtor","error","prototype","isOk","result","isErr","okUnchecked","ok","err","isThenable","then","isSync"],"mappings":"AA+BA,OAAO,MAAMA,OAAOC,UAAkB;AAGtC,OAAO,MAAMC,QAA4BC,OAAOC,MAAM,CAAC,EAAE,EAAE;AA0B3D,OAAO,SAASC,OAAOC,GAAY;IACjC,OAAOA,QAAQ,QAAQA,QAAQL;AACjC;AAcA,OAAO,SAASM,OAAOD,GAAY;IACjC,OAAOA,QAAQ,QAAQA,QAAQL;AACjC;AAcA,OAAO,SAASO,SAAYC,KAAQ;IAClC,OAAOF,OAAOE,SAAsBT,OAAQS;AAC9C;AAEA,OAAO,SAASC,cAAiBD,KAAQ;IACvC,OAAOA;AACT;AAUA,OAAO,SAASE,KAAQF,KAAmB;IACzC,IAAIA,UAAU,QAAQA,UAAUR,WAAW;QACzC,MAAM,IAAIW,UAAU;IACtB;IACA,OAAOF,cAAcD;AACvB;AAGA,OAAO,MAAMI,OAAab,KAAK;AAO/B,MAAMc,YAAYC,OAAOC,GAAG,CAAC;AAO7B,SAASC,gBAA8CC,KAAQ;IAC7D,AAAC,IAAI,CAAkBA,KAAK,GAAGA;AACjC;AACCD,gBAAgBE,SAAS,AAA0B,CAACL,UAAU,GAAG;AAmClE,OAAO,SAASM,KAAKC,MAAe;IAClC,OAAO,CAAEA,QAAoC,CAACP,UAAU;AAC1D;AAcA,OAAO,SAASQ,MAAMD,MAAe;IACnC,OAAO,AAACA,QAAoC,CAACP,UAAU,KAAK;AAC9D;AAEA,OAAO,SAASS,YAAed,KAAQ;IACrC,OAAOA;AACT;AAUA,OAAO,SAASe,GAAMf,KAAQ;IAC5B,IAAIa,MAAMb,QAAQ;QAChB,MAAM,IAAIG,UAAU;IACtB;IACA,OAAOW,YAAYd;AACrB;AAUA,OAAO,SAASgB,IAAOP,KAAQ;IAC7B,OAAO,IAAKD,gBAAwDC;AACtE;AAUA,OAAO,SAASQ,WAAcjB,KAAsB;IAClD,OAAO,OAAQA,OAA0BkB,SAAS;AACpD;AAOA,OAAO,SAASC,OAAUnB,KAAsB;IAC9C,OAAO,OAAQA,OAA0BkB,SAAS;AACpD"}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/** Values that represent None (absence of a value). */\nexport type NoneValueType = null | undefined | void;\n\n/** Widens literal types to their base types for better type inference. */\nexport type Widen<T> = T extends string\n ? string\n : T extends number\n ? number\n : T extends boolean\n ? boolean\n : T extends bigint\n ? bigint\n : T extends symbol\n ? symbol\n : T;\n\n/** Widens never to unknown, otherwise preserves the type. */\nexport type WidenNever<T> = [T] extends [never] ? unknown : T;\n\n/** Excludes None values from a type, leaving only valid Some values. */\nexport type ValueType<T> = Exclude<T, NoneValueType>;\n\nexport declare const SOME_BRAND: unique symbol;\n\n/** Represents a value that is present. The value itself is the Some - no wrapper object. */\nexport type Some<T> = ValueType<T> & { readonly [SOME_BRAND]: true };\n\n/** Represents the absence of a value (null or undefined). */\nexport type None = NoneValueType & { readonly [SOME_BRAND]: false };\n\nexport type UnOption<V, T extends Option<V>> = T extends Some<V> ? V : None;\n\n/** Constant representing None. Use this instead of null/undefined for clarity. */\nexport const NONE = undefined as None;\n\n/** Shared frozen empty array to avoid allocations. */\nexport const EMPTY: readonly unknown[] = Object.freeze([]);\n\n/** A value that may or may not be present. Some(T) is the value itself; None is null/undefined. */\nexport type Option<T> = Some<ValueType<T>> | None;\n\n/** Extracts the value type from an Option type. */\nexport type OptionValue<TOption> = TOption extends Option<infer TValue> ? TValue : never;\n\n/** Type predicate: true if T is an Option type. */\nexport type IsOption<T> = T extends Option<any> ? true : false;\n\n/** Infers the Some type from a value, preserving the inner type. */\nexport type InferSome<T> = T extends Some<infer TValue> ? Some<ValueType<TValue>> : never;\n\n/**\n * Checks if an Option contains a value (is Some).\n * @param opt - The Option to check\n * @returns true if the Option is Some, false if None\n * @example\n * isSome(42) // true\n * isSome(null) // false\n * isSome(undefined) // false\n */\nexport function isSome<T>(opt: Some<T>): true;\nexport function isSome(opt: None): false;\nexport function isSome<T>(opt: unknown): opt is Some<T>;\nexport function isSome(opt: unknown): boolean {\n return opt !== null && opt !== undefined;\n}\n\n/**\n * Checks if an Option is None (absent).\n * @param opt - The Option to check\n * @returns true if the Option is None, false if Some\n * @example\n * isNone(null) // true\n * isNone(undefined) // true\n * isNone(42) // false\n */\nexport function isNone<T>(opt: Some<T>): false;\nexport function isNone(opt: None): true;\nexport function isNone(opt: unknown): opt is None;\nexport function isNone(opt: unknown): boolean {\n return opt === null || opt === undefined;\n}\n\n/**\n * Creates an Option from a nullable value. Returns None for null/undefined, Some otherwise.\n * @param value - The value to wrap\n * @returns Some(value) if value is non-null, None otherwise\n * @example\n * optionOf(42) // Some(42)\n * optionOf(null) // None\n * optionOf(undefined) // None\n */\nexport function optionOf(value: null): None;\nexport function optionOf(value: undefined): None;\nexport function optionOf<T>(value: T): T extends NoneValueType ? None : Some<T>;\nexport function optionOf<T>(value: T): Option<T> {\n return isNone(value as Option<T>) ? NONE : (value as Some<T>);\n}\n\nexport function someUnchecked<T>(value: T): Some<ValueType<T>> {\n return value as Some<ValueType<T>>;\n}\n\n/**\n * Creates a Some value with runtime validation.\n * @param value - The value to wrap\n * @returns The value typed as Some\n * @throws TypeError if value is null or undefined\n * @example\n * some(42) // Some(42)\n */\nexport function some<T>(value: ValueType<T>): Some<ValueType<T>> {\n if (value === null || value === undefined) {\n throw new TypeError('some() requires a non-nullable value');\n }\n return someUnchecked(value);\n}\n\n/** Constant representing None. Alias for NONE. */\nexport const none: None = NONE;\n\ndeclare const OK_BRAND: unique symbol;\n\n/** Represents a successful Result value. The value itself is the Ok - no wrapper object. */\nexport type Ok<T> = T & { readonly [OK_BRAND]: true };\n\nconst ERR_BRAND = Symbol.for('nalloc.ResultError');\n\ninterface ResultErrorShape<E> {\n readonly error: E;\n readonly [ERR_BRAND]: true;\n}\n\nfunction ResultErrorCtor<E>(this: ResultErrorShape<E>, error: E): void {\n (this as { error: E }).error = error;\n}\n(ResultErrorCtor.prototype as { [ERR_BRAND]: true })[ERR_BRAND] = true;\n\n/** The error wrapper type used internally. */\nexport type ResultError<E> = ResultErrorShape<E>;\n\n/** Represents a failed Result containing an error. */\nexport type Err<E> = ResultError<E>;\n\n/** A value that is either successful (Ok) or failed (Err). Ok is the value itself; Err wraps the error. */\nexport type Result<T, E> = Ok<T> | Err<E>;\n\n/** Extracts the success value type from a Result type. */\nexport type ResultValue<TResult> = TResult extends Result<infer TValue, any> ? TValue : never;\n\n/** Extracts the error type from a Result type. */\nexport type ResultErrorType<TResult> = TResult extends Result<any, infer TError> ? TError : never;\n\n/** Type predicate: true if T is a Result type. */\nexport type IsResult<T> = T extends Result<any, any> ? true : false;\n\n/** Infers the Err type from a value, preserving the error type. */\nexport type InferErr<T> = T extends Err<infer TError> ? Err<TError> : never;\n\n/**\n * Checks if a Result is Ok (successful).\n * @param result - The Result to check\n * @returns true if the Result is Ok, false if Err\n * @example\n * isOk(42) // true (Ok value)\n * isOk(err('fail')) // false\n */\nexport function isOk<T>(result: Ok<T>): true;\nexport function isOk<E>(result: Err<E>): false;\nexport function isOk<T, E>(result: Result<T, E>): result is Ok<T>;\nexport function isOk(result: unknown): boolean;\nexport function isOk(result: unknown): boolean {\n return typeof result !== 'object' || result === null || !(result as Record<symbol, boolean>)[ERR_BRAND];\n}\n\n/**\n * Checks if a Result is Err (failed).\n * @param result - The Result to check\n * @returns true if the Result is Err, false if Ok\n * @example\n * isErr(err('fail')) // true\n * isErr(42) // false (Ok value)\n */\nexport function isErr<E>(result: Err<E>): true;\nexport function isErr(result: Ok<unknown>): false;\nexport function isErr<T, E>(result: Result<T, E>): result is Err<E>;\nexport function isErr(result: unknown): result is Err<unknown>;\nexport function isErr(result: unknown): boolean {\n return typeof result === 'object' && result !== null && (result as Record<symbol, boolean>)[ERR_BRAND] === true;\n}\n\nexport function okUnchecked<T>(value: T): Ok<T> {\n return value as Ok<T>;\n}\n\n/**\n * Creates an Ok value with runtime validation.\n * @param value - The success value\n * @returns The value typed as Ok\n * @throws TypeError if value is an Err\n * @example\n * ok(42) // Ok(42)\n */\nexport function ok<T>(value: T): Ok<T> {\n if (isErr(value)) {\n throw new TypeError('ok() cannot wrap an Err value');\n }\n return okUnchecked(value);\n}\n\n/**\n * Creates an Err value wrapping an error.\n * @param error - The error value\n * @returns An Err containing the error\n * @example\n * err('something went wrong') // Err('something went wrong')\n * err(new Error('failed')) // Err(Error)\n */\nexport function err<E>(error: E): Err<E> {\n return new (ResultErrorCtor as unknown as new (error: E) => Err<E>)(error);\n}\n\n/** A value that may or may not be a Promise. */\nexport type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;\n\n/**\n * Checks if a value is a thenable (has a .then method).\n * @param value - The value to check\n * @returns true if value is a PromiseLike\n */\nexport function isThenable<T>(value: MaybePromise<T>): value is PromiseLike<T> {\n return (typeof value === 'object' || typeof value === 'function') && value !== null && typeof (value as PromiseLike<T>).then === 'function';\n}\n\n/**\n * Checks if a value is synchronous (not a thenable).\n * @param value - The value to check\n * @returns true if value is not a PromiseLike\n */\nexport function isSync<T>(value: MaybePromise<T>): value is T {\n return !(typeof value === 'object' || typeof value === 'function') || value === null || typeof (value as PromiseLike<T>).then !== 'function';\n}\n"],"names":["NONE","undefined","EMPTY","Object","freeze","isSome","opt","isNone","optionOf","value","someUnchecked","some","TypeError","none","ERR_BRAND","Symbol","for","ResultErrorCtor","error","prototype","isOk","result","isErr","okUnchecked","ok","err","isThenable","then","isSync"],"mappings":"AAiCA,OAAO,MAAMA,OAAOC,UAAkB;AAGtC,OAAO,MAAMC,QAA4BC,OAAOC,MAAM,CAAC,EAAE,EAAE;AA0B3D,OAAO,SAASC,OAAOC,GAAY;IACjC,OAAOA,QAAQ,QAAQA,QAAQL;AACjC;AAcA,OAAO,SAASM,OAAOD,GAAY;IACjC,OAAOA,QAAQ,QAAQA,QAAQL;AACjC;AAcA,OAAO,SAASO,SAAYC,KAAQ;IAClC,OAAOF,OAAOE,SAAsBT,OAAQS;AAC9C;AAEA,OAAO,SAASC,cAAiBD,KAAQ;IACvC,OAAOA;AACT;AAUA,OAAO,SAASE,KAAQF,KAAmB;IACzC,IAAIA,UAAU,QAAQA,UAAUR,WAAW;QACzC,MAAM,IAAIW,UAAU;IACtB;IACA,OAAOF,cAAcD;AACvB;AAGA,OAAO,MAAMI,OAAab,KAAK;AAO/B,MAAMc,YAAYC,OAAOC,GAAG,CAAC;AAO7B,SAASC,gBAA8CC,KAAQ;IAC7D,AAAC,IAAI,CAAkBA,KAAK,GAAGA;AACjC;AACCD,gBAAgBE,SAAS,AAA0B,CAACL,UAAU,GAAG;AAmClE,OAAO,SAASM,KAAKC,MAAe;IAClC,OAAO,OAAOA,WAAW,YAAYA,WAAW,QAAQ,CAAC,AAACA,MAAkC,CAACP,UAAU;AACzG;AAcA,OAAO,SAASQ,MAAMD,MAAe;IACnC,OAAO,OAAOA,WAAW,YAAYA,WAAW,QAAQ,AAACA,MAAkC,CAACP,UAAU,KAAK;AAC7G;AAEA,OAAO,SAASS,YAAed,KAAQ;IACrC,OAAOA;AACT;AAUA,OAAO,SAASe,GAAMf,KAAQ;IAC5B,IAAIa,MAAMb,QAAQ;QAChB,MAAM,IAAIG,UAAU;IACtB;IACA,OAAOW,YAAYd;AACrB;AAUA,OAAO,SAASgB,IAAOP,KAAQ;IAC7B,OAAO,IAAKD,gBAAwDC;AACtE;AAUA,OAAO,SAASQ,WAAcjB,KAAsB;IAClD,OAAO,AAAC,CAAA,OAAOA,UAAU,YAAY,OAAOA,UAAU,UAAS,KAAMA,UAAU,QAAQ,OAAO,AAACA,MAAyBkB,IAAI,KAAK;AACnI;AAOA,OAAO,SAASC,OAAUnB,KAAsB;IAC9C,OAAO,CAAE,CAAA,OAAOA,UAAU,YAAY,OAAOA,UAAU,UAAS,KAAMA,UAAU,QAAQ,OAAO,AAACA,MAAyBkB,IAAI,KAAK;AACpI"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nalloc",
|
|
3
3
|
"description": "Rust-like Option and Result for TypeScript with near-zero allocations for extreme performance",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"main": "build/index.cjs",
|
|
@@ -50,15 +50,32 @@
|
|
|
50
50
|
"result",
|
|
51
51
|
"maybe",
|
|
52
52
|
"either",
|
|
53
|
+
"ok",
|
|
54
|
+
"err",
|
|
55
|
+
"some",
|
|
56
|
+
"none",
|
|
57
|
+
"unwrap",
|
|
53
58
|
"rust",
|
|
54
59
|
"typescript",
|
|
55
60
|
"type-safe",
|
|
56
61
|
"monad",
|
|
57
62
|
"functional",
|
|
63
|
+
"fp",
|
|
64
|
+
"adt",
|
|
65
|
+
"algebraic-data-types",
|
|
66
|
+
"discriminated-union",
|
|
58
67
|
"error-handling",
|
|
68
|
+
"try-catch",
|
|
69
|
+
"safe",
|
|
70
|
+
"nullable",
|
|
71
|
+
"null-safety",
|
|
59
72
|
"pattern-matching",
|
|
60
73
|
"zero-allocation",
|
|
61
|
-
"
|
|
74
|
+
"no-allocation",
|
|
75
|
+
"gc-friendly",
|
|
76
|
+
"performance",
|
|
77
|
+
"neverthrow-alternative",
|
|
78
|
+
"fp-ts-alternative"
|
|
62
79
|
],
|
|
63
80
|
"author": "Ivan Zakharchanka",
|
|
64
81
|
"license": "MIT",
|
|
@@ -68,23 +85,25 @@
|
|
|
68
85
|
"homepage": "https://github.com/3axap4eHko/nalloc#readme",
|
|
69
86
|
"devDependencies": {
|
|
70
87
|
"@eslint/js": "^10.0.1",
|
|
71
|
-
"@vitest/coverage-v8": "^4.
|
|
72
|
-
"eslint": "^10.0
|
|
88
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
89
|
+
"eslint": "^10.1.0",
|
|
73
90
|
"eslint-plugin-prettier": "^5.5.5",
|
|
74
91
|
"inop": "^0.9.0",
|
|
75
|
-
"overtake": "^1.
|
|
92
|
+
"overtake": "^2.1.1",
|
|
76
93
|
"prettier": "^3.8.1",
|
|
77
94
|
"tsafe": "^1.8.12",
|
|
78
95
|
"tslib": "^2.8.1",
|
|
79
|
-
"typescript": "^
|
|
80
|
-
"typescript-eslint": "^8.
|
|
81
|
-
"vitest": "^4.
|
|
96
|
+
"typescript": "^6.0.2",
|
|
97
|
+
"typescript-eslint": "^8.58.0",
|
|
98
|
+
"vitest": "^4.1.2"
|
|
82
99
|
},
|
|
83
100
|
"scripts": {
|
|
84
|
-
"check": "tsc
|
|
101
|
+
"check": "tsc --noEmit -p tsconfig.check.json",
|
|
85
102
|
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
86
103
|
"lint": "eslint .",
|
|
87
104
|
"test": "vitest run",
|
|
88
|
-
"bench": "overtake benchmarks/*"
|
|
105
|
+
"bench": "overtake benchmarks/* --pin-cores -w 2 --progress",
|
|
106
|
+
"bench:save": "pnpm bench --save-baseline .bench-baseline.json",
|
|
107
|
+
"bench:test": "pnpm bench --compare-baseline .bench-baseline.json"
|
|
89
108
|
}
|
|
90
109
|
}
|
package/src/__tests__/result.ts
CHANGED
|
@@ -45,7 +45,8 @@ import {
|
|
|
45
45
|
filterOk,
|
|
46
46
|
filterErr,
|
|
47
47
|
safeTry,
|
|
48
|
-
safeTryAsync
|
|
48
|
+
safeTryAsync,
|
|
49
|
+
fromPromise
|
|
49
50
|
} from '../result.js';
|
|
50
51
|
import { ok, err, isOk, isErr, optionOf as optOf, none } from '../types.js';
|
|
51
52
|
|
|
@@ -913,4 +914,39 @@ describe('Result', () => {
|
|
|
913
914
|
});
|
|
914
915
|
});
|
|
915
916
|
|
|
917
|
+
describe('fromPromise', () => {
|
|
918
|
+
it('returns Ok for resolved promise', async () => {
|
|
919
|
+
const result = await fromPromise(Promise.resolve(42));
|
|
920
|
+
expect(isOk(result)).toBe(true);
|
|
921
|
+
expect(result).toBe(42);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it('returns Err for rejected promise', async () => {
|
|
925
|
+
const result = await fromPromise(Promise.reject(new Error('boom')));
|
|
926
|
+
expect(isErr(result)).toBe(true);
|
|
927
|
+
expect((result as { error: Error }).error.message).toBe('boom');
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it('uses onError mapper for rejected promise', async () => {
|
|
931
|
+
const result = await fromPromise(
|
|
932
|
+
Promise.reject(new Error('boom')),
|
|
933
|
+
(e) => `mapped: ${(e as Error).message}`,
|
|
934
|
+
);
|
|
935
|
+
expect(isErr(result)).toBe(true);
|
|
936
|
+
expect((result as { error: string }).error).toBe('mapped: boom');
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
it('handles Ok(null) as valid success', async () => {
|
|
940
|
+
const result = await fromPromise(Promise.resolve(null));
|
|
941
|
+
expect(isOk(result)).toBe(true);
|
|
942
|
+
expect(result).toBe(null);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
it('returns Err with unknown type when no onError provided', async () => {
|
|
946
|
+
const result = await fromPromise(Promise.reject('string error'));
|
|
947
|
+
expect(isErr(result)).toBe(true);
|
|
948
|
+
expect((result as { error: string }).error).toBe('string error');
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
|
|
916
952
|
});
|
|
@@ -207,7 +207,7 @@ assert<Equals<typeof tryCatchTyped, Result<number, string>>>;
|
|
|
207
207
|
|
|
208
208
|
|
|
209
209
|
const unwrapFallback = unwrapOrReturn(ok(1) as Result<number, string>, () => 'fallback');
|
|
210
|
-
assert<Equals<typeof unwrapFallback, number |
|
|
210
|
+
assert<Equals<typeof unwrapFallback, number | 'fallback'>>;
|
|
211
211
|
|
|
212
212
|
declare const maybeResult: Result<number, string>;
|
|
213
213
|
assertOk(maybeResult);
|
package/src/option.ts
CHANGED
|
@@ -158,11 +158,7 @@ export function flatMap<T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Opti
|
|
|
158
158
|
* @param fn - Function returning an Option
|
|
159
159
|
* @returns The result of fn(value) if Some, None otherwise
|
|
160
160
|
*/
|
|
161
|
-
export
|
|
162
|
-
export function andThen<T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Option<U>;
|
|
163
|
-
export function andThen<T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Option<U> {
|
|
164
|
-
return isNone(opt) ? NONE : fn(opt);
|
|
165
|
-
}
|
|
161
|
+
export const andThen: typeof flatMap = flatMap;
|
|
166
162
|
|
|
167
163
|
/**
|
|
168
164
|
* Executes a side effect if Some, then returns the original Option.
|
|
@@ -312,8 +308,9 @@ export function orElse<T>(opt: Option<T>, fn: () => Option<T>): Option<T> {
|
|
|
312
308
|
* xor(none, none) // None
|
|
313
309
|
*/
|
|
314
310
|
export function xor<T>(opt: Option<T>, optb: Option<T>): Option<T> {
|
|
315
|
-
|
|
316
|
-
|
|
311
|
+
const a = isSome(opt);
|
|
312
|
+
const b = isSome(optb);
|
|
313
|
+
if (a !== b) return a ? opt : optb;
|
|
317
314
|
return NONE;
|
|
318
315
|
}
|
|
319
316
|
|
|
@@ -411,7 +408,7 @@ export function flatten<T>(opt: Option<Option<T>>): Option<T> {
|
|
|
411
408
|
* contains(none, 42) // false
|
|
412
409
|
*/
|
|
413
410
|
export function contains<T>(opt: Option<T>, value: T): boolean {
|
|
414
|
-
return isSome(opt) && opt === value;
|
|
411
|
+
return isSome(opt) && (opt === value || (opt !== opt && value !== value));
|
|
415
412
|
}
|
|
416
413
|
|
|
417
414
|
/**
|
package/src/result.ts
CHANGED
|
@@ -33,6 +33,25 @@ export function of<T>(fn: () => T): Result<T, unknown> {
|
|
|
33
33
|
return tryCatch(fn);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Converts a Promise to a Result. Resolves to Ok if successful, Err on rejection.
|
|
38
|
+
* @param promise - The promise to convert
|
|
39
|
+
* @param onError - Optional error transformer
|
|
40
|
+
* @returns Promise resolving to Ok(value) or Err(error)
|
|
41
|
+
* @example
|
|
42
|
+
* await fromPromise(fetch('/api')) // Ok(Response) or Err(unknown)
|
|
43
|
+
* await fromPromise(fetch('/api'), e => String(e)) // Ok(Response) or Err(string)
|
|
44
|
+
*/
|
|
45
|
+
export async function fromPromise<T>(promise: Promise<T>): Promise<Result<T, unknown>>;
|
|
46
|
+
export async function fromPromise<T, E>(promise: Promise<T>, onError: (error: unknown) => E): Promise<Result<T, E>>;
|
|
47
|
+
export async function fromPromise<T, E = unknown>(promise: Promise<T>, onError?: (error: unknown) => E): Promise<Result<T, E>> {
|
|
48
|
+
try {
|
|
49
|
+
return (await promise) as Ok<T>;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return ERR(onError ? onError(error) : (error as E));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
/**
|
|
37
56
|
* Executes a function that may return sync or async, preserving sync execution when possible.
|
|
38
57
|
* @param fn - Function that may return T or Promise<T>
|
|
@@ -99,7 +118,7 @@ export function assertOk<T, E>(result: Result<T, E>, message?: string): asserts
|
|
|
99
118
|
*/
|
|
100
119
|
export function assertErr<T, E>(result: Result<T, E>, message?: string): asserts result is Err<E> {
|
|
101
120
|
if (isOk(result)) {
|
|
102
|
-
throw new Error(message ??
|
|
121
|
+
throw new Error(message ?? `Expected Err result. Received value: ${String(result)}`);
|
|
103
122
|
}
|
|
104
123
|
}
|
|
105
124
|
|
|
@@ -169,11 +188,7 @@ export function flatMap<T, U, E>(result: Result<T, E>, fn: (value: T) => Result<
|
|
|
169
188
|
* @param fn - Function returning a Result
|
|
170
189
|
* @returns The result of fn(value) if Ok, Err unchanged
|
|
171
190
|
*/
|
|
172
|
-
export
|
|
173
|
-
export function andThen<T, U, E>(result: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E>;
|
|
174
|
-
export function andThen<T, U, E>(result: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E> {
|
|
175
|
-
return isErr(result) ? result : fn(result);
|
|
176
|
-
}
|
|
191
|
+
export const andThen: typeof flatMap = flatMap;
|
|
177
192
|
|
|
178
193
|
/**
|
|
179
194
|
* Executes a side effect if Ok, then returns the original Result.
|
|
@@ -532,11 +547,13 @@ export function filterErr<T, E>(results: Iterable<Result<T, E>>): E[] {
|
|
|
532
547
|
* collect([ok(1), err('e')]) // Err('e')
|
|
533
548
|
*/
|
|
534
549
|
export function collect<T, E>(results: Result<T, E>[]): Result<T[], E> {
|
|
535
|
-
const
|
|
550
|
+
const len = results.length;
|
|
551
|
+
const values = new Array<T>(len);
|
|
536
552
|
|
|
537
|
-
for (
|
|
553
|
+
for (let i = 0; i < len; i++) {
|
|
554
|
+
const result = results[i];
|
|
538
555
|
if (isErr(result)) return result;
|
|
539
|
-
values
|
|
556
|
+
values[i] = result;
|
|
540
557
|
}
|
|
541
558
|
|
|
542
559
|
return values as Ok<T[]>;
|
|
@@ -551,7 +568,15 @@ export function collect<T, E>(results: Result<T, E>[]): Result<T[], E> {
|
|
|
551
568
|
* collectAll([ok(1), err('a'), err('b')]) // Err(['a', 'b'])
|
|
552
569
|
*/
|
|
553
570
|
export function collectAll<T, E>(results: Result<T, E>[]): Result<T[], E[]> {
|
|
554
|
-
const
|
|
571
|
+
const oks: T[] = [];
|
|
572
|
+
const errs: E[] = [];
|
|
573
|
+
for (const result of results) {
|
|
574
|
+
if (isOk(result)) {
|
|
575
|
+
oks.push(result);
|
|
576
|
+
} else {
|
|
577
|
+
errs.push(result.error);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
555
580
|
return errs.length > 0 ? ERR(errs) : (oks as Ok<T[]>);
|
|
556
581
|
}
|
|
557
582
|
|
|
@@ -688,25 +713,38 @@ export async function partitionMaybePromiseAsync<T, E>(
|
|
|
688
713
|
startIndex: number = 0,
|
|
689
714
|
): Promise<[Widen<T>[], WidenNever<E>[]]> {
|
|
690
715
|
const suffixLength = values.length - startIndex;
|
|
691
|
-
const
|
|
716
|
+
const pendingPromises: Promise<Result<T, E>>[] = [];
|
|
692
717
|
|
|
693
718
|
for (let i = 0; i < suffixLength; i++) {
|
|
694
719
|
const value = values[startIndex + i];
|
|
695
|
-
|
|
696
|
-
(
|
|
697
|
-
|
|
698
|
-
|
|
720
|
+
if (isThenable(value)) {
|
|
721
|
+
pendingPromises.push(
|
|
722
|
+
Promise.resolve(value).then(
|
|
723
|
+
(result) => result as Result<T, E>,
|
|
724
|
+
(error) => ERR(error as E),
|
|
725
|
+
),
|
|
726
|
+
);
|
|
727
|
+
} else {
|
|
728
|
+
if (isOk(value)) {
|
|
729
|
+
oks.push(value as Widen<T>);
|
|
730
|
+
} else {
|
|
731
|
+
errs.push((value as Err<WidenNever<E>>).error);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
699
734
|
}
|
|
700
735
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
736
|
+
if (pendingPromises.length > 0) {
|
|
737
|
+
const resolved = await Promise.all(pendingPromises);
|
|
738
|
+
for (let i = 0; i < resolved.length; i++) {
|
|
739
|
+
const result = resolved[i];
|
|
740
|
+
if (isOk(result)) {
|
|
741
|
+
oks.push(result as Widen<T>);
|
|
742
|
+
} else {
|
|
743
|
+
errs.push((result as Err<WidenNever<E>>).error);
|
|
744
|
+
}
|
|
708
745
|
}
|
|
709
746
|
}
|
|
747
|
+
|
|
710
748
|
return [oks, errs] as [Widen<T>[], WidenNever<E>[]];
|
|
711
749
|
}
|
|
712
750
|
|
package/src/types.ts
CHANGED
|
@@ -28,6 +28,8 @@ export type Some<T> = ValueType<T> & { readonly [SOME_BRAND]: true };
|
|
|
28
28
|
/** Represents the absence of a value (null or undefined). */
|
|
29
29
|
export type None = NoneValueType & { readonly [SOME_BRAND]: false };
|
|
30
30
|
|
|
31
|
+
export type UnOption<V, T extends Option<V>> = T extends Some<V> ? V : None;
|
|
32
|
+
|
|
31
33
|
/** Constant representing None. Use this instead of null/undefined for clarity. */
|
|
32
34
|
export const NONE = undefined as None;
|
|
33
35
|
|
|
@@ -167,7 +169,7 @@ export function isOk<E>(result: Err<E>): false;
|
|
|
167
169
|
export function isOk<T, E>(result: Result<T, E>): result is Ok<T>;
|
|
168
170
|
export function isOk(result: unknown): boolean;
|
|
169
171
|
export function isOk(result: unknown): boolean {
|
|
170
|
-
return !(result as Record<symbol,
|
|
172
|
+
return typeof result !== 'object' || result === null || !(result as Record<symbol, boolean>)[ERR_BRAND];
|
|
171
173
|
}
|
|
172
174
|
|
|
173
175
|
/**
|
|
@@ -183,7 +185,7 @@ export function isErr(result: Ok<unknown>): false;
|
|
|
183
185
|
export function isErr<T, E>(result: Result<T, E>): result is Err<E>;
|
|
184
186
|
export function isErr(result: unknown): result is Err<unknown>;
|
|
185
187
|
export function isErr(result: unknown): boolean {
|
|
186
|
-
return (result as Record<symbol,
|
|
188
|
+
return typeof result === 'object' && result !== null && (result as Record<symbol, boolean>)[ERR_BRAND] === true;
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
export function okUnchecked<T>(value: T): Ok<T> {
|
|
@@ -226,7 +228,7 @@ export type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;
|
|
|
226
228
|
* @returns true if value is a PromiseLike
|
|
227
229
|
*/
|
|
228
230
|
export function isThenable<T>(value: MaybePromise<T>): value is PromiseLike<T> {
|
|
229
|
-
return typeof (value as PromiseLike<T>)
|
|
231
|
+
return (typeof value === 'object' || typeof value === 'function') && value !== null && typeof (value as PromiseLike<T>).then === 'function';
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
/**
|
|
@@ -235,5 +237,5 @@ export function isThenable<T>(value: MaybePromise<T>): value is PromiseLike<T> {
|
|
|
235
237
|
* @returns true if value is not a PromiseLike
|
|
236
238
|
*/
|
|
237
239
|
export function isSync<T>(value: MaybePromise<T>): value is T {
|
|
238
|
-
return typeof (value as PromiseLike<T>)
|
|
240
|
+
return !(typeof value === 'object' || typeof value === 'function') || value === null || typeof (value as PromiseLike<T>).then !== 'function';
|
|
239
241
|
}
|