nalloc 0.2.2 → 0.5.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 +326 -178
- package/build/codemod-cli.cjs +153 -0
- package/build/codemod-cli.cjs.map +1 -0
- package/build/codemod-cli.d.ts +2 -0
- package/build/codemod-cli.js +103 -0
- package/build/codemod-cli.js.map +1 -0
- package/build/codemod.cjs +652 -0
- package/build/codemod.cjs.map +1 -0
- package/build/codemod.d.ts +29 -0
- package/build/codemod.js +634 -0
- package/build/codemod.js.map +1 -0
- package/build/eslint.cjs +221 -0
- package/build/eslint.cjs.map +1 -0
- package/build/eslint.d.ts +36 -0
- package/build/eslint.js +198 -0
- package/build/eslint.js.map +1 -0
- package/build/http.cjs +31 -0
- package/build/http.cjs.map +1 -0
- package/build/http.d.ts +31 -0
- package/build/http.js +13 -0
- package/build/http.js.map +1 -0
- package/build/nonempty.cjs +35 -0
- package/build/nonempty.cjs.map +1 -0
- package/build/nonempty.d.ts +34 -0
- package/build/nonempty.js +14 -0
- package/build/nonempty.js.map +1 -0
- package/build/option.cjs +1 -1
- package/build/option.cjs.map +1 -1
- package/build/option.d.ts +2 -2
- package/build/option.js +1 -1
- package/build/option.js.map +1 -1
- package/build/result.cjs +10 -18
- package/build/result.cjs.map +1 -1
- package/build/result.d.ts +3 -34
- package/build/result.js +10 -15
- package/build/result.js.map +1 -1
- package/build/safe.cjs +8 -0
- package/build/safe.cjs.map +1 -1
- package/build/safe.d.ts +3 -0
- package/build/safe.js +2 -0
- package/build/safe.js.map +1 -1
- package/build/schema.cjs +32 -0
- package/build/schema.cjs.map +1 -0
- package/build/schema.d.ts +44 -0
- package/build/schema.js +14 -0
- package/build/schema.js.map +1 -0
- package/package.json +63 -10
- package/src/__tests__/codemod.ts +211 -0
- package/src/__tests__/eslint.ts +99 -0
- package/src/__tests__/fixtures/tsconfig.json +10 -0
- package/src/__tests__/http.ts +64 -0
- package/src/__tests__/iter.ts +18 -0
- package/src/__tests__/nonempty.ts +46 -0
- package/src/__tests__/nonempty.types.ts +38 -0
- package/src/__tests__/option.ts +4 -0
- package/src/__tests__/result.ts +104 -129
- package/src/__tests__/result.types.ts +2 -2
- package/src/__tests__/schema.ts +58 -0
- package/src/codemod-cli.ts +108 -0
- package/src/codemod.ts +623 -0
- package/src/eslint.ts +145 -0
- package/src/http.ts +42 -0
- package/src/nonempty.ts +48 -0
- package/src/option.ts +3 -4
- package/src/result.ts +18 -49
- package/src/safe.ts +3 -0
- package/src/schema.ts +52 -0
package/build/safe.cjs
CHANGED
|
@@ -12,12 +12,18 @@ _export(exports, {
|
|
|
12
12
|
get Iter () {
|
|
13
13
|
return _itercjs;
|
|
14
14
|
},
|
|
15
|
+
get NonEmpty () {
|
|
16
|
+
return _nonemptycjs;
|
|
17
|
+
},
|
|
15
18
|
get Option () {
|
|
16
19
|
return _optioncjs;
|
|
17
20
|
},
|
|
18
21
|
get Result () {
|
|
19
22
|
return _resultcjs;
|
|
20
23
|
},
|
|
24
|
+
get Schema () {
|
|
25
|
+
return _schemacjs;
|
|
26
|
+
},
|
|
21
27
|
get err () {
|
|
22
28
|
return _typescjs.err;
|
|
23
29
|
},
|
|
@@ -71,6 +77,8 @@ const _typescjs = require("./types.cjs");
|
|
|
71
77
|
const _resultcjs = /*#__PURE__*/ _interop_require_wildcard(require("./result.cjs"));
|
|
72
78
|
const _optioncjs = /*#__PURE__*/ _interop_require_wildcard(require("./option.cjs"));
|
|
73
79
|
const _itercjs = /*#__PURE__*/ _interop_require_wildcard(require("./iter.cjs"));
|
|
80
|
+
const _nonemptycjs = /*#__PURE__*/ _interop_require_wildcard(require("./nonempty.cjs"));
|
|
81
|
+
const _schemacjs = /*#__PURE__*/ _interop_require_wildcard(require("./schema.cjs"));
|
|
74
82
|
function _getRequireWildcardCache(nodeInterop) {
|
|
75
83
|
if (typeof WeakMap !== "function") return null;
|
|
76
84
|
var cacheBabelInterop = new WeakMap();
|
package/build/safe.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/safe.ts"],"sourcesContent":["export { some, none, ok, err, isOk, isErr, isSome, isNone, isThenable, isSync } from './types.js';\nexport type {\n Some,\n None,\n Ok,\n Err,\n Option as OptionType,\n OptionValue,\n IsOption,\n InferSome,\n Result as ResultType,\n ResultValue,\n ResultErrorType,\n IsResult,\n InferErr,\n MaybePromise,\n} from './types.js';\nexport { safeTry, safeTryAsync, unwrap, gen, genAsync } from './result.js';\nexport * as Option from './option.js';\nexport * as Result from './result.js';\nexport * as Iter from './iter.js';\n\n/**\n * Threads a value through a sequence of unary functions, left to right.\n * @param value - The initial value\n * @param fns - Functions to apply in order\n * @returns The result of applying all functions\n * @example\n * pipe(\n * Result.tryCatch(() => JSON.parse(input)),\n * r => Result.flatMap(r, validate),\n * r => Result.map(r, transform),\n * )\n */\nexport function pipe<A>(a: A): A;\nexport function pipe<A, B>(a: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E;\nexport function pipe<A, B, C, D, E, F>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F;\nexport function pipe<A, B, C, D, E, F, G>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(a: unknown, ...fns: ((v: unknown) => unknown)[]): unknown {\n let result = a;\n for (let i = 0; i < fns.length; i++) {\n result = fns[i](result);\n }\n return result;\n}\n"],"names":["Iter","Option","Result","err","gen","genAsync","isErr","isNone","isOk","isSome","isSync","isThenable","none","ok","pipe","safeTry","safeTryAsync","some","unwrap","a","fns","result","i","length"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/safe.ts"],"sourcesContent":["export { some, none, ok, err, isOk, isErr, isSome, isNone, isThenable, isSync } from './types.js';\nexport type {\n Some,\n None,\n Ok,\n Err,\n Option as OptionType,\n OptionValue,\n IsOption,\n InferSome,\n Result as ResultType,\n ResultValue,\n ResultErrorType,\n IsResult,\n InferErr,\n MaybePromise,\n} from './types.js';\nexport { safeTry, safeTryAsync, unwrap, gen, genAsync } from './result.js';\nexport type { NonEmptyArray, ReadonlyNonEmptyArray } from './nonempty.js';\nexport * as Option from './option.js';\nexport * as Result from './result.js';\nexport * as Iter from './iter.js';\nexport * as NonEmpty from './nonempty.js';\nexport * as Schema from './schema.js';\n\n/**\n * Threads a value through a sequence of unary functions, left to right.\n * @param value - The initial value\n * @param fns - Functions to apply in order\n * @returns The result of applying all functions\n * @example\n * pipe(\n * Result.tryCatch(() => JSON.parse(input)),\n * r => Result.flatMap(r, validate),\n * r => Result.map(r, transform),\n * )\n */\nexport function pipe<A>(a: A): A;\nexport function pipe<A, B>(a: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E;\nexport function pipe<A, B, C, D, E, F>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F;\nexport function pipe<A, B, C, D, E, F, G>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(a: unknown, ...fns: ((v: unknown) => unknown)[]): unknown {\n let result = a;\n for (let i = 0; i < fns.length; i++) {\n result = fns[i](result);\n }\n return result;\n}\n"],"names":["Iter","NonEmpty","Option","Result","Schema","err","gen","genAsync","isErr","isNone","isOk","isSome","isSync","isThenable","none","ok","pipe","safeTry","safeTryAsync","some","unwrap","a","fns","result","i","length"],"mappings":";;;;;;;;;;;QAqBYA;;;QACAC;;;QAHAC;;;QACAC;;;QAGAC;;;QAvBaC;eAAAA,aAAG;;QAiBYC;eAAAA,cAAG;;QAAEC;eAAAA,mBAAQ;;QAjBjBC;eAAAA,eAAK;;QAAUC;eAAAA,gBAAM;;QAA3BC;eAAAA,cAAI;;QAASC;eAAAA,gBAAM;;QAAsBC;eAAAA,gBAAM;;QAAlBC;eAAAA,oBAAU;;QAAtDC;eAAAA,cAAI;;QAAEC;eAAAA,YAAE;;QAiEPC;eAAAA;;QAhDPC;eAAAA,kBAAO;;QAAEC;eAAAA,uBAAY;;QAjBrBC;eAAAA,cAAI;;QAiBmBC;eAAAA,iBAAM;;;0BAjB+C;mEAiBxB;mEAErC;iEAEF;qEACI;mEACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CjB,SAASJ,KAAKK,CAAU,EAAE,GAAGC,GAAgC;IAClE,IAAIC,SAASF;IACb,IAAK,IAAIG,IAAI,GAAGA,IAAIF,IAAIG,MAAM,EAAED,IAAK;QACnCD,SAASD,GAAG,CAACE,EAAE,CAACD;IAClB;IACA,OAAOA;AACT"}
|
package/build/safe.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export { some, none, ok, err, isOk, isErr, isSome, isNone, isThenable, isSync } from './types.js';
|
|
2
2
|
export type { Some, None, Ok, Err, Option as OptionType, OptionValue, IsOption, InferSome, Result as ResultType, ResultValue, ResultErrorType, IsResult, InferErr, MaybePromise, } from './types.js';
|
|
3
3
|
export { safeTry, safeTryAsync, unwrap, gen, genAsync } from './result.js';
|
|
4
|
+
export type { NonEmptyArray, ReadonlyNonEmptyArray } from './nonempty.js';
|
|
4
5
|
export * as Option from './option.js';
|
|
5
6
|
export * as Result from './result.js';
|
|
6
7
|
export * as Iter from './iter.js';
|
|
8
|
+
export * as NonEmpty from './nonempty.js';
|
|
9
|
+
export * as Schema from './schema.js';
|
|
7
10
|
/**
|
|
8
11
|
* Threads a value through a sequence of unary functions, left to right.
|
|
9
12
|
* @param value - The initial value
|
package/build/safe.js
CHANGED
|
@@ -3,6 +3,8 @@ export { safeTry, safeTryAsync, unwrap, gen, genAsync } from "./result.js";
|
|
|
3
3
|
export * as Option from "./option.js";
|
|
4
4
|
export * as Result from "./result.js";
|
|
5
5
|
export * as Iter from "./iter.js";
|
|
6
|
+
export * as NonEmpty from "./nonempty.js";
|
|
7
|
+
export * as Schema from "./schema.js";
|
|
6
8
|
export function pipe(a, ...fns) {
|
|
7
9
|
let result = a;
|
|
8
10
|
for(let i = 0; i < fns.length; i++){
|
package/build/safe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/safe.ts"],"sourcesContent":["export { some, none, ok, err, isOk, isErr, isSome, isNone, isThenable, isSync } from './types.js';\nexport type {\n Some,\n None,\n Ok,\n Err,\n Option as OptionType,\n OptionValue,\n IsOption,\n InferSome,\n Result as ResultType,\n ResultValue,\n ResultErrorType,\n IsResult,\n InferErr,\n MaybePromise,\n} from './types.js';\nexport { safeTry, safeTryAsync, unwrap, gen, genAsync } from './result.js';\nexport * as Option from './option.js';\nexport * as Result from './result.js';\nexport * as Iter from './iter.js';\n\n/**\n * Threads a value through a sequence of unary functions, left to right.\n * @param value - The initial value\n * @param fns - Functions to apply in order\n * @returns The result of applying all functions\n * @example\n * pipe(\n * Result.tryCatch(() => JSON.parse(input)),\n * r => Result.flatMap(r, validate),\n * r => Result.map(r, transform),\n * )\n */\nexport function pipe<A>(a: A): A;\nexport function pipe<A, B>(a: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E;\nexport function pipe<A, B, C, D, E, F>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F;\nexport function pipe<A, B, C, D, E, F, G>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(a: unknown, ...fns: ((v: unknown) => unknown)[]): unknown {\n let result = a;\n for (let i = 0; i < fns.length; i++) {\n result = fns[i](result);\n }\n return result;\n}\n"],"names":["some","none","ok","err","isOk","isErr","isSome","isNone","isThenable","isSync","safeTry","safeTryAsync","unwrap","gen","genAsync","Option","Result","Iter","pipe","a","fns","result","i","length"],"mappings":"AAAA,SAASA,IAAI,EAAEC,IAAI,EAAEC,EAAE,EAAEC,GAAG,EAAEC,IAAI,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAEC,MAAM,QAAQ,aAAa;AAiBlG,SAASC,OAAO,EAAEC,YAAY,EAAEC,MAAM,EAAEC,GAAG,EAAEC,QAAQ,QAAQ,cAAc;
|
|
1
|
+
{"version":3,"sources":["../src/safe.ts"],"sourcesContent":["export { some, none, ok, err, isOk, isErr, isSome, isNone, isThenable, isSync } from './types.js';\nexport type {\n Some,\n None,\n Ok,\n Err,\n Option as OptionType,\n OptionValue,\n IsOption,\n InferSome,\n Result as ResultType,\n ResultValue,\n ResultErrorType,\n IsResult,\n InferErr,\n MaybePromise,\n} from './types.js';\nexport { safeTry, safeTryAsync, unwrap, gen, genAsync } from './result.js';\nexport type { NonEmptyArray, ReadonlyNonEmptyArray } from './nonempty.js';\nexport * as Option from './option.js';\nexport * as Result from './result.js';\nexport * as Iter from './iter.js';\nexport * as NonEmpty from './nonempty.js';\nexport * as Schema from './schema.js';\n\n/**\n * Threads a value through a sequence of unary functions, left to right.\n * @param value - The initial value\n * @param fns - Functions to apply in order\n * @returns The result of applying all functions\n * @example\n * pipe(\n * Result.tryCatch(() => JSON.parse(input)),\n * r => Result.flatMap(r, validate),\n * r => Result.map(r, transform),\n * )\n */\nexport function pipe<A>(a: A): A;\nexport function pipe<A, B>(a: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E;\nexport function pipe<A, B, C, D, E, F>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F;\nexport function pipe<A, B, C, D, E, F, G>(a: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n a: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(a: unknown, ...fns: ((v: unknown) => unknown)[]): unknown {\n let result = a;\n for (let i = 0; i < fns.length; i++) {\n result = fns[i](result);\n }\n return result;\n}\n"],"names":["some","none","ok","err","isOk","isErr","isSome","isNone","isThenable","isSync","safeTry","safeTryAsync","unwrap","gen","genAsync","Option","Result","Iter","NonEmpty","Schema","pipe","a","fns","result","i","length"],"mappings":"AAAA,SAASA,IAAI,EAAEC,IAAI,EAAEC,EAAE,EAAEC,GAAG,EAAEC,IAAI,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAEC,MAAM,QAAQ,aAAa;AAiBlG,SAASC,OAAO,EAAEC,YAAY,EAAEC,MAAM,EAAEC,GAAG,EAAEC,QAAQ,QAAQ,cAAc;AAE3E,OAAO,KAAKC,MAAM,MAAM,cAAc;AACtC,OAAO,KAAKC,MAAM,MAAM,cAAc;AACtC,OAAO,KAAKC,IAAI,MAAM,YAAY;AAClC,OAAO,KAAKC,QAAQ,MAAM,gBAAgB;AAC1C,OAAO,KAAKC,MAAM,MAAM,cAAc;AA0CtC,OAAO,SAASC,KAAKC,CAAU,EAAE,GAAGC,GAAgC;IAClE,IAAIC,SAASF;IACb,IAAK,IAAIG,IAAI,GAAGA,IAAIF,IAAIG,MAAM,EAAED,IAAK;QACnCD,SAASD,GAAG,CAACE,EAAE,CAACD;IAClB;IACA,OAAOA;AACT"}
|
package/build/schema.cjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get fromSchema () {
|
|
13
|
+
return fromSchema;
|
|
14
|
+
},
|
|
15
|
+
get wrapSchema () {
|
|
16
|
+
return wrapSchema;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const _typescjs = require("./types.cjs");
|
|
20
|
+
function schemaResultToResult(sr) {
|
|
21
|
+
return sr.issues ? (0, _typescjs.err)(sr.issues) : sr.value;
|
|
22
|
+
}
|
|
23
|
+
function fromSchema(schema, value) {
|
|
24
|
+
const sr = schema['~standard'].validate(value);
|
|
25
|
+
if ((0, _typescjs.isThenable)(sr)) return sr.then(schemaResultToResult);
|
|
26
|
+
return schemaResultToResult(sr);
|
|
27
|
+
}
|
|
28
|
+
function wrapSchema(schema) {
|
|
29
|
+
return (value)=>fromSchema(schema, value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=schema.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["import { err as ERR, isThenable } from './types.js';\nimport type { Ok, Result } from './types.js';\n\n/** A validation issue from a Standard Schema validator. */\nexport interface SchemaIssue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>;\n}\n\n/** Minimal Standard Schema v1 interface (duck-typed, no external dependency). */\nexport interface StandardSchema<O = unknown> {\n readonly '~standard': {\n readonly validate: (value: unknown) => StandardSchemaResult<O> | Promise<StandardSchemaResult<O>>;\n };\n}\n\ntype StandardSchemaResult<O> = { readonly value: O; readonly issues?: undefined } | { readonly issues: ReadonlyArray<SchemaIssue> };\n\nfunction schemaResultToResult<O>(sr: StandardSchemaResult<O>): Result<O, readonly SchemaIssue[]> {\n return sr.issues ? ERR(sr.issues) : (sr.value as Ok<O>);\n}\n\n/**\n * Validates a value against a Standard Schema and returns a Result.\n * Works with any Standard Schema v1 compliant library (Zod, Valibot, ArkType, etc.).\n * Returns synchronously if the schema validates synchronously.\n * @param schema - A Standard Schema v1 compliant schema\n * @param value - The value to validate\n * @returns Ok(parsed) if valid, Err(issues) if invalid\n * @example\n * import { z } from 'zod';\n * const result = fromSchema(z.string().email(), input);\n * // Result<string, readonly SchemaIssue[]>\n */\nexport function fromSchema<O>(schema: StandardSchema<O>, value: unknown): Result<O, readonly SchemaIssue[]> | Promise<Result<O, readonly SchemaIssue[]>> {\n const sr = schema['~standard'].validate(value);\n if (isThenable(sr)) return sr.then(schemaResultToResult);\n return schemaResultToResult(sr);\n}\n\n/**\n * Binds a Standard Schema once and returns a reusable validator function.\n * The partial-application form of fromSchema - validate many values against one schema.\n * @param schema - A Standard Schema v1 compliant schema\n * @returns A function that validates a value and returns a Result\n * @example\n * const parseUser = wrapSchema(userSchema);\n * const result = parseUser(input); // Result<User, readonly SchemaIssue[]>\n */\nexport function wrapSchema<O>(schema: StandardSchema<O>): (value: unknown) => Result<O, readonly SchemaIssue[]> | Promise<Result<O, readonly SchemaIssue[]>> {\n return (value) => fromSchema(schema, value);\n}\n"],"names":["fromSchema","wrapSchema","schemaResultToResult","sr","issues","ERR","value","schema","validate","isThenable","then"],"mappings":";;;;;;;;;;;QAkCgBA;eAAAA;;QAeAC;eAAAA;;;0BAjDuB;AAkBvC,SAASC,qBAAwBC,EAA2B;IAC1D,OAAOA,GAAGC,MAAM,GAAGC,IAAAA,aAAG,EAACF,GAAGC,MAAM,IAAKD,GAAGG,KAAK;AAC/C;AAcO,SAASN,WAAcO,MAAyB,EAAED,KAAc;IACrE,MAAMH,KAAKI,MAAM,CAAC,YAAY,CAACC,QAAQ,CAACF;IACxC,IAAIG,IAAAA,oBAAU,EAACN,KAAK,OAAOA,GAAGO,IAAI,CAACR;IACnC,OAAOA,qBAAqBC;AAC9B;AAWO,SAASF,WAAcM,MAAyB;IACrD,OAAO,CAACD,QAAUN,WAAWO,QAAQD;AACvC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Result } from './types.js';
|
|
2
|
+
/** A validation issue from a Standard Schema validator. */
|
|
3
|
+
export interface SchemaIssue {
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly path?: ReadonlyArray<PropertyKey | {
|
|
6
|
+
readonly key: PropertyKey;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
/** Minimal Standard Schema v1 interface (duck-typed, no external dependency). */
|
|
10
|
+
export interface StandardSchema<O = unknown> {
|
|
11
|
+
readonly '~standard': {
|
|
12
|
+
readonly validate: (value: unknown) => StandardSchemaResult<O> | Promise<StandardSchemaResult<O>>;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
type StandardSchemaResult<O> = {
|
|
16
|
+
readonly value: O;
|
|
17
|
+
readonly issues?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
readonly issues: ReadonlyArray<SchemaIssue>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Validates a value against a Standard Schema and returns a Result.
|
|
23
|
+
* Works with any Standard Schema v1 compliant library (Zod, Valibot, ArkType, etc.).
|
|
24
|
+
* Returns synchronously if the schema validates synchronously.
|
|
25
|
+
* @param schema - A Standard Schema v1 compliant schema
|
|
26
|
+
* @param value - The value to validate
|
|
27
|
+
* @returns Ok(parsed) if valid, Err(issues) if invalid
|
|
28
|
+
* @example
|
|
29
|
+
* import { z } from 'zod';
|
|
30
|
+
* const result = fromSchema(z.string().email(), input);
|
|
31
|
+
* // Result<string, readonly SchemaIssue[]>
|
|
32
|
+
*/
|
|
33
|
+
export declare function fromSchema<O>(schema: StandardSchema<O>, value: unknown): Result<O, readonly SchemaIssue[]> | Promise<Result<O, readonly SchemaIssue[]>>;
|
|
34
|
+
/**
|
|
35
|
+
* Binds a Standard Schema once and returns a reusable validator function.
|
|
36
|
+
* The partial-application form of fromSchema - validate many values against one schema.
|
|
37
|
+
* @param schema - A Standard Schema v1 compliant schema
|
|
38
|
+
* @returns A function that validates a value and returns a Result
|
|
39
|
+
* @example
|
|
40
|
+
* const parseUser = wrapSchema(userSchema);
|
|
41
|
+
* const result = parseUser(input); // Result<User, readonly SchemaIssue[]>
|
|
42
|
+
*/
|
|
43
|
+
export declare function wrapSchema<O>(schema: StandardSchema<O>): (value: unknown) => Result<O, readonly SchemaIssue[]> | Promise<Result<O, readonly SchemaIssue[]>>;
|
|
44
|
+
export {};
|
package/build/schema.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { err as ERR, isThenable } from "./types.js";
|
|
2
|
+
function schemaResultToResult(sr) {
|
|
3
|
+
return sr.issues ? ERR(sr.issues) : sr.value;
|
|
4
|
+
}
|
|
5
|
+
export function fromSchema(schema, value) {
|
|
6
|
+
const sr = schema['~standard'].validate(value);
|
|
7
|
+
if (isThenable(sr)) return sr.then(schemaResultToResult);
|
|
8
|
+
return schemaResultToResult(sr);
|
|
9
|
+
}
|
|
10
|
+
export function wrapSchema(schema) {
|
|
11
|
+
return (value)=>fromSchema(schema, value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["import { err as ERR, isThenable } from './types.js';\nimport type { Ok, Result } from './types.js';\n\n/** A validation issue from a Standard Schema validator. */\nexport interface SchemaIssue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>;\n}\n\n/** Minimal Standard Schema v1 interface (duck-typed, no external dependency). */\nexport interface StandardSchema<O = unknown> {\n readonly '~standard': {\n readonly validate: (value: unknown) => StandardSchemaResult<O> | Promise<StandardSchemaResult<O>>;\n };\n}\n\ntype StandardSchemaResult<O> = { readonly value: O; readonly issues?: undefined } | { readonly issues: ReadonlyArray<SchemaIssue> };\n\nfunction schemaResultToResult<O>(sr: StandardSchemaResult<O>): Result<O, readonly SchemaIssue[]> {\n return sr.issues ? ERR(sr.issues) : (sr.value as Ok<O>);\n}\n\n/**\n * Validates a value against a Standard Schema and returns a Result.\n * Works with any Standard Schema v1 compliant library (Zod, Valibot, ArkType, etc.).\n * Returns synchronously if the schema validates synchronously.\n * @param schema - A Standard Schema v1 compliant schema\n * @param value - The value to validate\n * @returns Ok(parsed) if valid, Err(issues) if invalid\n * @example\n * import { z } from 'zod';\n * const result = fromSchema(z.string().email(), input);\n * // Result<string, readonly SchemaIssue[]>\n */\nexport function fromSchema<O>(schema: StandardSchema<O>, value: unknown): Result<O, readonly SchemaIssue[]> | Promise<Result<O, readonly SchemaIssue[]>> {\n const sr = schema['~standard'].validate(value);\n if (isThenable(sr)) return sr.then(schemaResultToResult);\n return schemaResultToResult(sr);\n}\n\n/**\n * Binds a Standard Schema once and returns a reusable validator function.\n * The partial-application form of fromSchema - validate many values against one schema.\n * @param schema - A Standard Schema v1 compliant schema\n * @returns A function that validates a value and returns a Result\n * @example\n * const parseUser = wrapSchema(userSchema);\n * const result = parseUser(input); // Result<User, readonly SchemaIssue[]>\n */\nexport function wrapSchema<O>(schema: StandardSchema<O>): (value: unknown) => Result<O, readonly SchemaIssue[]> | Promise<Result<O, readonly SchemaIssue[]>> {\n return (value) => fromSchema(schema, value);\n}\n"],"names":["err","ERR","isThenable","schemaResultToResult","sr","issues","value","fromSchema","schema","validate","then","wrapSchema"],"mappings":"AAAA,SAASA,OAAOC,GAAG,EAAEC,UAAU,QAAQ,aAAa;AAkBpD,SAASC,qBAAwBC,EAA2B;IAC1D,OAAOA,GAAGC,MAAM,GAAGJ,IAAIG,GAAGC,MAAM,IAAKD,GAAGE,KAAK;AAC/C;AAcA,OAAO,SAASC,WAAcC,MAAyB,EAAEF,KAAc;IACrE,MAAMF,KAAKI,MAAM,CAAC,YAAY,CAACC,QAAQ,CAACH;IACxC,IAAIJ,WAAWE,KAAK,OAAOA,GAAGM,IAAI,CAACP;IACnC,OAAOA,qBAAqBC;AAC9B;AAWA,OAAO,SAASO,WAAcH,MAAyB;IACrD,OAAO,CAACF,QAAUC,WAAWC,QAAQF;AACvC"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
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.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"main": "build/index.cjs",
|
|
8
8
|
"module": "build/index.js",
|
|
9
|
+
"sideEffects": false,
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
12
|
"require": "./build/index.cjs",
|
|
@@ -23,6 +24,10 @@
|
|
|
23
24
|
"require": "./build/result.cjs",
|
|
24
25
|
"import": "./build/result.js"
|
|
25
26
|
},
|
|
27
|
+
"./schema": {
|
|
28
|
+
"require": "./build/schema.cjs",
|
|
29
|
+
"import": "./build/schema.js"
|
|
30
|
+
},
|
|
26
31
|
"./safe": {
|
|
27
32
|
"require": "./build/safe.cjs",
|
|
28
33
|
"import": "./build/safe.js"
|
|
@@ -35,8 +40,27 @@
|
|
|
35
40
|
"require": "./build/iter.cjs",
|
|
36
41
|
"import": "./build/iter.js"
|
|
37
42
|
},
|
|
43
|
+
"./nonempty": {
|
|
44
|
+
"require": "./build/nonempty.cjs",
|
|
45
|
+
"import": "./build/nonempty.js"
|
|
46
|
+
},
|
|
47
|
+
"./http": {
|
|
48
|
+
"require": "./build/http.cjs",
|
|
49
|
+
"import": "./build/http.js"
|
|
50
|
+
},
|
|
51
|
+
"./eslint": {
|
|
52
|
+
"require": "./build/eslint.cjs",
|
|
53
|
+
"import": "./build/eslint.js"
|
|
54
|
+
},
|
|
55
|
+
"./codemod": {
|
|
56
|
+
"require": "./build/codemod.cjs",
|
|
57
|
+
"import": "./build/codemod.js"
|
|
58
|
+
},
|
|
38
59
|
"./package.json": "./package.json"
|
|
39
60
|
},
|
|
61
|
+
"bin": {
|
|
62
|
+
"nalloc-codemod": "./build/codemod-cli.js"
|
|
63
|
+
},
|
|
40
64
|
"files": [
|
|
41
65
|
"build",
|
|
42
66
|
"src"
|
|
@@ -54,6 +78,8 @@
|
|
|
54
78
|
"err",
|
|
55
79
|
"some",
|
|
56
80
|
"none",
|
|
81
|
+
"nonempty",
|
|
82
|
+
"non-empty-array",
|
|
57
83
|
"unwrap",
|
|
58
84
|
"rust",
|
|
59
85
|
"typescript",
|
|
@@ -91,24 +117,51 @@
|
|
|
91
117
|
"url": "https://github.com/3axap4eHko/nalloc/issues"
|
|
92
118
|
},
|
|
93
119
|
"homepage": "https://github.com/3axap4eHko/nalloc#readme",
|
|
120
|
+
"peerDependencies": {
|
|
121
|
+
"@typescript-eslint/parser": ">=8",
|
|
122
|
+
"@typescript-eslint/utils": ">=8",
|
|
123
|
+
"eslint": ">=9",
|
|
124
|
+
"oxc-parser": ">=0.135.0",
|
|
125
|
+
"typescript": ">=4.8.4"
|
|
126
|
+
},
|
|
127
|
+
"peerDependenciesMeta": {
|
|
128
|
+
"@typescript-eslint/parser": {
|
|
129
|
+
"optional": true
|
|
130
|
+
},
|
|
131
|
+
"@typescript-eslint/utils": {
|
|
132
|
+
"optional": true
|
|
133
|
+
},
|
|
134
|
+
"eslint": {
|
|
135
|
+
"optional": true
|
|
136
|
+
},
|
|
137
|
+
"oxc-parser": {
|
|
138
|
+
"optional": true
|
|
139
|
+
},
|
|
140
|
+
"typescript": {
|
|
141
|
+
"optional": true
|
|
142
|
+
}
|
|
143
|
+
},
|
|
94
144
|
"devDependencies": {
|
|
95
|
-
"@
|
|
96
|
-
"@
|
|
97
|
-
"eslint": "^
|
|
98
|
-
"eslint
|
|
145
|
+
"@types/node": "^25.9.3",
|
|
146
|
+
"@typescript-eslint/parser": "^8.61.1",
|
|
147
|
+
"@typescript-eslint/rule-tester": "^8.61.1",
|
|
148
|
+
"@typescript-eslint/utils": "^8.61.1",
|
|
149
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
99
150
|
"inop": "^0.9.0",
|
|
100
151
|
"overtake": "^2.1.1",
|
|
101
|
-
"
|
|
152
|
+
"oxc-parser": "^0.136.0",
|
|
153
|
+
"oxlint": "^1.70.0",
|
|
154
|
+
"prettier": "^3.8.4",
|
|
102
155
|
"tsafe": "^1.8.12",
|
|
103
156
|
"tslib": "^2.8.1",
|
|
104
|
-
"typescript": "^6.0.
|
|
105
|
-
"
|
|
106
|
-
"vitest": "^4.1.2"
|
|
157
|
+
"typescript": "^6.0.3",
|
|
158
|
+
"vitest": "^4.1.9"
|
|
107
159
|
},
|
|
108
160
|
"scripts": {
|
|
109
161
|
"check": "tsc --noEmit -p tsconfig.check.json",
|
|
110
162
|
"build": "rm -rf build && inop src build -i __tests__ -i *.tmp.ts && tsc --declaration --emitDeclarationOnly",
|
|
111
|
-
"lint": "
|
|
163
|
+
"lint": "oxlint",
|
|
164
|
+
"format": "prettier --check .",
|
|
112
165
|
"test": "vitest run",
|
|
113
166
|
"bench": "overtake benchmarks/* --pin-cores -w 1 --progress",
|
|
114
167
|
"bench:save": "pnpm bench --save-baseline .bench-baseline.json",
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { migrateSource, renderReport } from '../codemod.js';
|
|
3
|
+
|
|
4
|
+
function migrate(source: string): ReturnType<typeof migrateSource> {
|
|
5
|
+
return migrateSource(source, 'fixture.ts');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe('codemod', () => {
|
|
9
|
+
it('leaves files without neverthrow imports untouched', () => {
|
|
10
|
+
const source = `const xs = [1, 2].map(x => x * 2);\n`;
|
|
11
|
+
const result = migrate(source);
|
|
12
|
+
expect(result.changed).toBe(false);
|
|
13
|
+
expect(result.output).toBe(source);
|
|
14
|
+
expect(result.skipped).toEqual([]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('rewrites the import and converts provenant single-method calls', () => {
|
|
18
|
+
const source = [
|
|
19
|
+
`import { ok, err } from 'neverthrow';`,
|
|
20
|
+
`const r = ok(1);`,
|
|
21
|
+
`const d = r.map(x => x + 1);`,
|
|
22
|
+
`const e = d.andThen(x => x > 0 ? ok(x) : err('neg'));`,
|
|
23
|
+
].join('\n');
|
|
24
|
+
const result = migrate(source);
|
|
25
|
+
expect(result.changed).toBe(true);
|
|
26
|
+
expect(result.output).toContain(`import { ok, err, Result } from 'nalloc';`);
|
|
27
|
+
expect(result.output).toContain(`const d = Result.map(r, x => x + 1);`);
|
|
28
|
+
expect(result.output).toContain(`const e = Result.flatMap(d, x => x > 0 ? ok(x) : err('neg'));`);
|
|
29
|
+
expect(result.output).not.toContain('neverthrow');
|
|
30
|
+
expect(result.skipped).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('preserves import aliases', () => {
|
|
34
|
+
const source = [`import { ok as okay } from 'neverthrow';`, `const r = okay(1);`, `const d = r.map(x => x + 1);`].join('\n');
|
|
35
|
+
const result = migrate(source);
|
|
36
|
+
expect(result.output).toContain(`import { ok as okay, Result } from 'nalloc';`);
|
|
37
|
+
expect(result.output).toContain(`Result.map(r, x => x + 1)`);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('folds chains of two or more methods into pipe', () => {
|
|
41
|
+
const source = [`import { ok } from 'neverthrow';`, `const out = ok(1).map(a => a + 1).mapErr(String).unwrapOr(0);`].join('\n');
|
|
42
|
+
const result = migrate(source);
|
|
43
|
+
expect(result.output).toContain(`import { ok, Result, pipe } from 'nalloc';`);
|
|
44
|
+
expect(result.output).toContain(
|
|
45
|
+
`const out = pipe(ok(1), ($r) => Result.map($r, a => a + 1), ($r) => Result.mapErr($r, String), ($r) => Result.unwrapOr($r, 0));`,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('does not touch bare .map calls without provenance', () => {
|
|
50
|
+
const source = [
|
|
51
|
+
`import { ok } from 'neverthrow';`,
|
|
52
|
+
`const xs = [1, 2].map(x => x * 2);`,
|
|
53
|
+
`declare const unknownValue: { map(fn: (x: number) => number): number[] };`,
|
|
54
|
+
`unknownValue.map(x => x);`,
|
|
55
|
+
].join('\n');
|
|
56
|
+
const result = migrate(source);
|
|
57
|
+
expect(result.output).toContain(`[1, 2].map(x => x * 2)`);
|
|
58
|
+
expect(result.output).toContain(`unknownValue.map(x => x)`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('converts neverthrow-exclusive methods without provenance', () => {
|
|
62
|
+
const source = [`import { ok } from 'neverthrow';`, `declare const foreign: any;`, `const r = foreign.andThen((x: number) => ok(x));`].join('\n');
|
|
63
|
+
const result = migrate(source);
|
|
64
|
+
expect(result.output).toContain(`Result.flatMap(foreign, (x: number) => ok(x))`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('uses type annotations from neverthrow imports as provenance and renames the Result type', () => {
|
|
68
|
+
const source = [
|
|
69
|
+
`import { ok, Result } from 'neverthrow';`,
|
|
70
|
+
`const t: Result<number, string> = ok(2);`,
|
|
71
|
+
`const u = t.map(x => x * 2);`,
|
|
72
|
+
`function f(input: Result<number, string>): Result<number, string> { return input.mapErr(e => e); }`,
|
|
73
|
+
].join('\n');
|
|
74
|
+
const result = migrate(source);
|
|
75
|
+
expect(result.output).toContain(`import { ok, Result, type ResultType } from 'nalloc';`);
|
|
76
|
+
expect(result.output).toContain(`const t: ResultType<number, string> = ok(2);`);
|
|
77
|
+
expect(result.output).toContain(`Result.map(t, x => x * 2)`);
|
|
78
|
+
expect(result.output).toContain(`function f(input: ResultType<number, string>): ResultType<number, string> { return Result.mapErr(input, e => e); }`);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('converts fromThrowable to Result.wrap and tracks wrapped functions as provenance', () => {
|
|
82
|
+
const source = [
|
|
83
|
+
`import { fromThrowable } from 'neverthrow';`,
|
|
84
|
+
`const safeParse = fromThrowable(JSON.parse);`,
|
|
85
|
+
`const p = safeParse('{}');`,
|
|
86
|
+
`const q = p.map(v => v);`,
|
|
87
|
+
].join('\n');
|
|
88
|
+
const result = migrate(source);
|
|
89
|
+
expect(result.output).toContain(`const safeParse = Result.wrap(JSON.parse);`);
|
|
90
|
+
expect(result.output).toContain(`Result.map(p, v => v)`);
|
|
91
|
+
expect(result.output).not.toContain('neverthrow');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('converts combine and combineWithAllErrors including Result statics', () => {
|
|
95
|
+
const source = [
|
|
96
|
+
`import { ok, combine, Result } from 'neverthrow';`,
|
|
97
|
+
`const a = combine([ok(1), ok(2)]);`,
|
|
98
|
+
`const b = Result.combineWithAllErrors([ok(1)]);`,
|
|
99
|
+
`const c = Result.fromThrowable(JSON.parse);`,
|
|
100
|
+
].join('\n');
|
|
101
|
+
const result = migrate(source);
|
|
102
|
+
expect(result.output).toContain(`const a = Result.all([ok(1), ok(2)]);`);
|
|
103
|
+
expect(result.output).toContain(`const b = Result.collectAll([ok(1)]);`);
|
|
104
|
+
expect(result.output).toContain(`const c = Result.wrap(JSON.parse);`);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('converts directly-awaited fromPromise and treats its result as migrated', () => {
|
|
108
|
+
const source = [
|
|
109
|
+
`import { fromPromise } from 'neverthrow';`,
|
|
110
|
+
`async function run(p: Promise<number>) {`,
|
|
111
|
+
` const r = await fromPromise(p, e => String(e));`,
|
|
112
|
+
` return r.map(x => x + 1);`,
|
|
113
|
+
`}`,
|
|
114
|
+
].join('\n');
|
|
115
|
+
const result = migrate(source);
|
|
116
|
+
expect(result.output).toContain(`await Result.fromPromise(p, e => String(e))`);
|
|
117
|
+
expect(result.output).toContain(`Result.map(r, x => x + 1)`);
|
|
118
|
+
expect(result.skipped).toEqual([]);
|
|
119
|
+
expect(result.output).not.toContain('neverthrow');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('reports chained fromPromise as result-async and keeps the neverthrow import', () => {
|
|
123
|
+
const source = [`import { fromPromise } from 'neverthrow';`, `const ra = fromPromise(Promise.resolve(1), e => e).map(x => x);`].join('\n');
|
|
124
|
+
const result = migrate(source);
|
|
125
|
+
expect(result.skipped.map((s) => s.reason)).toContain('result-async');
|
|
126
|
+
expect(result.output).toContain(`import { fromPromise } from 'neverthrow';`);
|
|
127
|
+
expect(result.output).toContain(`fromPromise(Promise.resolve(1), e => e).map(x => x)`);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('reports ResultAsync values and never converts methods on them', () => {
|
|
131
|
+
const source = [
|
|
132
|
+
`import { okAsync } from 'neverthrow';`,
|
|
133
|
+
`const ra = okAsync(1);`,
|
|
134
|
+
`const rb = ra.map(x => x + 1);`,
|
|
135
|
+
`async function f() { const r = await ra; return r.mapErr(e => e); }`,
|
|
136
|
+
].join('\n');
|
|
137
|
+
const result = migrate(source);
|
|
138
|
+
expect(result.output).toContain(`const rb = ra.map(x => x + 1);`);
|
|
139
|
+
expect(result.output).toContain(`return r.mapErr(e => e);`);
|
|
140
|
+
expect(result.output).toContain(`import { okAsync } from 'neverthrow';`);
|
|
141
|
+
expect(result.skipped.some((s) => s.reason === 'result-async')).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('reports safeTry for manual gen migration', () => {
|
|
145
|
+
const source = [`import { ok, safeTry } from 'neverthrow';`, `const r = safeTry(function* () { return ok(1); });`].join('\n');
|
|
146
|
+
const result = migrate(source);
|
|
147
|
+
expect(result.skipped.map((s) => s.reason)).toContain('safe-try');
|
|
148
|
+
expect(result.output).toContain(`import { safeTry } from 'neverthrow';`);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('refuses whole chains containing unsupported methods', () => {
|
|
152
|
+
const source = [`import { ok } from 'neverthrow';`, `const r = ok(1).andThrough(x => ok(x)).map(x => x + 1);`].join('\n');
|
|
153
|
+
const result = migrate(source);
|
|
154
|
+
expect(result.skipped.map((s) => s.reason)).toContain('unsupported');
|
|
155
|
+
expect(result.output).toContain(`ok(1).andThrough(x => ok(x)).map(x => x + 1)`);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('skips namespace imports entirely', () => {
|
|
159
|
+
const source = [`import * as nt from 'neverthrow';`, `const r = nt.ok(1);`].join('\n');
|
|
160
|
+
const result = migrate(source);
|
|
161
|
+
expect(result.changed).toBe(false);
|
|
162
|
+
expect(result.skipped.map((s) => s.reason)).toEqual(['namespace-import']);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('converts nested chains inside chain arguments', () => {
|
|
166
|
+
const source = [`import { ok, err } from 'neverthrow';`, `const inner = ok(2);`, `const r = ok(1).andThen(x => inner.mapErr(e => e)).map(x => x);`].join(
|
|
167
|
+
'\n',
|
|
168
|
+
);
|
|
169
|
+
const result = migrate(source);
|
|
170
|
+
expect(result.output).toContain(`pipe(ok(1), ($r) => Result.flatMap($r, x => Result.mapErr(inner, e => e)), ($r) => Result.map($r, x => x))`);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('converts match and unwrap accessors', () => {
|
|
174
|
+
const source = [
|
|
175
|
+
`import { ok } from 'neverthrow';`,
|
|
176
|
+
`const r = ok(1);`,
|
|
177
|
+
`const m = r.match(x => x, e => 0);`,
|
|
178
|
+
`const v = r._unsafeUnwrap();`,
|
|
179
|
+
`const o = r.isOk();`,
|
|
180
|
+
].join('\n');
|
|
181
|
+
const result = migrate(source);
|
|
182
|
+
expect(result.output).toContain(`const m = Result.match(r, x => x, e => 0);`);
|
|
183
|
+
expect(result.output).toContain(`const v = Result.unwrap(r);`);
|
|
184
|
+
expect(result.output).toContain(`const o = Result.isOk(r);`);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('drops provenance for conflicting bindings', () => {
|
|
188
|
+
const source = [
|
|
189
|
+
`import { ok } from 'neverthrow';`,
|
|
190
|
+
`function a() { const v = ok(1); return v; }`,
|
|
191
|
+
`function b() { const v = [1, 2]; return v.map(x => x); }`,
|
|
192
|
+
].join('\n');
|
|
193
|
+
const result = migrate(source);
|
|
194
|
+
expect(result.output).toContain(`return v.map(x => x);`);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('renders a grouped markdown report', () => {
|
|
198
|
+
const report = renderReport({
|
|
199
|
+
filesChanged: 1,
|
|
200
|
+
converted: 3,
|
|
201
|
+
skipped: [
|
|
202
|
+
{ file: 'a.ts', line: 2, reason: 'result-async', text: 'okAsync(1)' },
|
|
203
|
+
{ file: 'a.ts', line: 5, reason: 'safe-try', text: 'safeTry(fn)' },
|
|
204
|
+
],
|
|
205
|
+
});
|
|
206
|
+
expect(report).toContain('# nalloc migration report');
|
|
207
|
+
expect(report).toContain('Files changed: 1. Sites converted: 3. Sites needing manual review: 2.');
|
|
208
|
+
expect(report).toContain('## a.ts');
|
|
209
|
+
expect(report).toContain('- line 2 [result-async]: `okAsync(1)`');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
4
|
+
import plugin, { rules, mustUse, noUnwrap } from '../eslint.js';
|
|
5
|
+
|
|
6
|
+
describe('eslint plugin', () => {
|
|
7
|
+
it('exposes both rules with create + meta', () => {
|
|
8
|
+
for (const name of ['must-use', 'no-unwrap'] as const) {
|
|
9
|
+
expect(typeof rules[name].create).toBe('function');
|
|
10
|
+
expect(rules[name].meta.messages).toBeTypeOf('object');
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('wires the recommended config', () => {
|
|
15
|
+
expect(plugin.meta.name).toBe('nalloc');
|
|
16
|
+
const recommended = plugin.configs.recommended as { plugins: Record<string, unknown>; rules: Record<string, string> };
|
|
17
|
+
expect(recommended.plugins.nalloc).toBe(plugin);
|
|
18
|
+
expect(recommended.rules['nalloc/must-use']).toBe('error');
|
|
19
|
+
expect(recommended.rules['nalloc/no-unwrap']).toBe('error');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const typedTester = new RuleTester({
|
|
24
|
+
languageOptions: {
|
|
25
|
+
parserOptions: {
|
|
26
|
+
projectService: { allowDefaultProject: ['*.ts*'] },
|
|
27
|
+
tsconfigRootDir: path.join(import.meta.dirname, 'fixtures'),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
typedTester.run('must-use', mustUse, {
|
|
33
|
+
valid: [
|
|
34
|
+
`type Result<T, E> = readonly [T] | { error: E }; declare function f(): Result<number, string>; const r = f();`,
|
|
35
|
+
`declare function g(): number; g();`,
|
|
36
|
+
`type Foo<T> = readonly [T]; declare function f(): Foo<number>; f();`,
|
|
37
|
+
{
|
|
38
|
+
code: `type Result<T, E> = readonly [T] | { error: E }; declare function f(): Result<number, string>; f();`,
|
|
39
|
+
options: [{ typeNames: ['Option'] }],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
invalid: [
|
|
43
|
+
{
|
|
44
|
+
code: `type Result<T, E> = readonly [T] | { error: E }; declare function f(): Result<number, string>; f();`,
|
|
45
|
+
errors: [{ messageId: 'mustUse', data: { name: 'Result' } }],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
code: `type Option<T> = T | undefined; declare function f(): Option<number>; f();`,
|
|
49
|
+
errors: [{ messageId: 'mustUse', data: { name: 'Option' } }],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
code: `type Result<T, E> = readonly [T] | { error: E }; declare function f(): Promise<Result<number, string>>; async function run() { await f(); }`,
|
|
53
|
+
errors: [{ messageId: 'mustUse', data: { name: 'Result' } }],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
code: `type Mine<T> = readonly [T]; declare function f(): Mine<number>; f();`,
|
|
57
|
+
options: [{ typeNames: ['Mine'] }],
|
|
58
|
+
errors: [{ messageId: 'mustUse', data: { name: 'Mine' } }],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const tester = new RuleTester({ languageOptions: { parserOptions: { sourceType: 'module' } } });
|
|
64
|
+
|
|
65
|
+
tester.run('no-unwrap', noUnwrap, {
|
|
66
|
+
valid: [
|
|
67
|
+
`import { unwrap } from 'other-lib'; declare const r: unknown; unwrap(r);`,
|
|
68
|
+
`import { Result } from 'nalloc'; declare const r: unknown; Result.unwrapOr(r, 0);`,
|
|
69
|
+
`const Result = { unwrap(x: unknown) { return x; } }; Result.unwrap(1);`,
|
|
70
|
+
`function unwrap(x: unknown) { return x; } unwrap(1);`,
|
|
71
|
+
],
|
|
72
|
+
invalid: [
|
|
73
|
+
{
|
|
74
|
+
code: `import { unwrap } from 'nalloc'; declare const r: unknown; unwrap(r);`,
|
|
75
|
+
errors: [{ messageId: 'noUnwrap', data: { name: 'unwrap' } }],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
code: `import { Result } from 'nalloc'; declare const r: unknown; Result.unwrap(r);`,
|
|
79
|
+
errors: [{ messageId: 'noUnwrap', data: { name: 'unwrap' } }],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
code: `import { Option } from 'nalloc'; declare const o: unknown; Option.expect(o, 'm');`,
|
|
83
|
+
errors: [{ messageId: 'noUnwrap', data: { name: 'expect' } }],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
code: `import { Result } from 'nalloc'; declare const r: unknown; Result.unwrapErr(r);`,
|
|
87
|
+
errors: [{ messageId: 'noUnwrap', data: { name: 'unwrapErr' } }],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
code: `import { unwrap as uw } from 'nalloc'; declare const r: unknown; uw(r);`,
|
|
91
|
+
errors: [{ messageId: 'noUnwrap', data: { name: 'unwrap' } }],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
code: `import { unwrap } from '@me/x'; declare const r: unknown; unwrap(r);`,
|
|
95
|
+
options: [{ modules: ['@me/x'] }],
|
|
96
|
+
errors: [{ messageId: 'noUnwrap', data: { name: 'unwrap' } }],
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|