@carlwr/fastcheck-utils 0.3.1 → 0.4.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/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +105 -0
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var T=Object.create;var a=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var x=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty;var K=(r,t)=>{for(var e in t)a(r,e,{get:t[e],enumerable:!0})},f=(r,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of A(t))!b.call(r,n)&&n!==e&&a(r,n,{get:()=>t[n],enumerable:!(o=h(t,n))||o.enumerable});return r};var i=(r,t,e)=>(e=r!=null?T(x(r)):{},f(t||!r||!r.__esModule?a(e,"default",{value:r,enumerable:!0}):e,r)),g=r=>f(a({},"__esModule",{value:!0}),r);var U={};K(U,{element:()=>E,getNext:()=>R,infiniteStream:()=>w,nonEmptyArray:()=>q,nonEmptyUniqueArray:()=>N,record:()=>S});module.exports=g(U);function s(r,t){if(t<0||t>=r.length)throw new Error(`index ${t} out of bounds for array of length ${r.length}`);return r[t]}function u(r){return r.length>=1}var y=i(require("fast-check"),1);function E(r){return y.nat({max:r.length-1}).map(e=>s(r,e))}var l=i(require("fast-check"),1);function w(r){return l.infiniteStream(r)}function R(r){let{value:t,done:e}=r.next();if(e)throw new Error("infinite stream unexpectedly done");return t}var m=i(require("fast-check"),1);function q(r,t){if(t?.minLength===0)throw new Error("minLength cannot be 0 for non-empty array");let e=t??{minLength:1},o=e.minLength!==void 0?e:{...e,minLength:1};return m.array(r,o)}var c=i(require("fast-check"),1);function S(r,t){let e=t?.noNullPrototype??!0;if(!t?.requiredKeys)return c.record(r,{noNullPrototype:e});let o={noNullPrototype:e,requiredKeys:t.requiredKeys},n=c.record(r,o),p=Object.keys(r);return k(t.requiredKeys,p),n}function k(r,t){return r.length===t.length&&t.every(r.includes)}var d=i(require("fast-check"),1);function N(r,t){let e=t??{},o={...e,minLength:Math.max(e.minLength??1,1)};return d.uniqueArray(r,o).filter(u)}0&&(module.exports={element,getNext,infiniteStream,nonEmptyArray,nonEmptyUniqueArray,record});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../node_modules/.pnpm/@carlwr+typescript-extra@0.5.0/node_modules/@carlwr/typescript-extra/src/extract.ts","../../node_modules/.pnpm/@carlwr+typescript-extra@0.5.0/node_modules/@carlwr/typescript-extra/src/misc.ts","../src/generators/element.ts","../src/generators/infiniteStream.ts","../src/generators/nonEmptyArray.ts","../src/generators/record.ts","../src/generators/nonEmptyUniqueArray.ts"],"sourcesContent":["export * from './generators/element.js'\nexport * from './generators/infiniteStream.js'\nexport * from './generators/nonEmptyArray.js'\nexport * from './generators/record.js'\nexport * from './generators/nonEmptyUniqueArray.js'\n","/**\n * walk {@link root} recursively while collecting all {@link T}s for which {@link pred} returnes a defined result\n *\n * @example\n * const root = {\n * A: 1,\n * b: 0,\n * c: {\n * 99: [{A:2}, {e:\"zero\"}, null],\n * A: 3\n * }\n * }\n * function pred(k: string, v: any): number|undefined {\n * if (k === 'A' && typeof v === 'number') { return v }\n * return undefined\n * }\n * const result = extract<number>(root, pred)\n * // result === [1, 2, 3]\n */\nexport function extract<T>(\n root: unknown,\n pred: (k: string, v: unknown) => T|undefined\n): T[] {\n\n function handleEntry([k,v]: [string,unknown]): T[] {\n const r = pred(k,v)\n return (r !== undefined) ? [r] : go(v)\n }\n\n function go(node: unknown): T[] {\n\n const ret =\n Array.isArray(node) ? node.flatMap(go)\n : node===null ? []\n : typeof node === 'object' ? Object.entries(node).flatMap(handleEntry)\n : []\n return ret\n }\n\n return go(root)\n}\n","import { rm } from 'node:fs/promises'\n\n/**\n * A non-empty array\n */\nexport type NonEmpty<T> = [T, ...T[]]\n\nexport type Primitive =\n | string\n | number\n | boolean\n | bigint\n | symbol\n | null\n | undefined\n\n\n/**\n * Cached, lazy, single-flight evaluation of a promise.\n *\n * A rejected promise is cached as well, i.e. no re-attempts (failures are assumed to be permanent).\n */\nexport function memoized<T>(f: () => Promise<T>): () => Promise<T> {\n let p: Promise<T> | null = null\n return () => p ?? (p = f())\n}\n\n\n/**\n * whether the elements of {@link xs} are unique, in the `===` sense\n *\n * O(n)\n * (if the hash tables behave, and they should for primitives)\n * (constants likely not too great)\n */\nexport function allUnique<T extends Primitive>(xs: T[]): boolean {\n return new Set(xs).size === xs.length\n}\n\n\n/**\n * Map over a non-empty array while preserving the type as non-empty\n */\nexport function mapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T) => U\n): [U,...U[]] {\n const mapped = xs.map(fn)\n return mapped as [U,...U[]] // if the world were perfect, it wouldn't be\n}\n\n/**\n * Flatmap over a non-empty array with a function returning a non-empty arrays. Return the flattened result as a non-empty array.\n */\nexport function flatmapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T) => [U,...U[]]\n): [U,...U[]] {\n const mapped = xs.map(fn)\n const flattened = mapped.flat()\n return flattened as [U,...U[]]\n}\n\n\n/**\n * Stateful iterator yielding the elements of {@link xs}.\n *\n * When the last element is reached, calls will continue to return that element indefinitely.\n *\n * @example\n * const next = drain([1, 2, 3])\n * next() // => 1\n * next() // => 2\n * next() // => 3\n * next() // => 3\n * next() // => 3 (returns 3 forever)\n */\n// a.k.a. imperative sometimes has its merits\n// edit: no it hasn't. But it occasionally has _its uses_.\nexport function drain<T>(xs: [T,...T[]]): () => T {\n let state: [T,...T[]] = xs\n return () => {\n if (hasAtleastTwo(state)) {\n const [x0,...xs_rest] = state\n state = xs_rest\n return x0\n }\n return state[0]\n }\n}\n\n/**\n * return the `i`th element of `xs`, or throw if `i` is out of bounds\n */\nexport function safeIndex<T>(xs: readonly [T, ...T[]], i: number): T {\n if (i<0 || i>=xs.length) {\n throw new Error(`index ${i} out of bounds for array of length ${xs.length}`)\n }\n return xs[i] as T\n}\n\nexport function mapFilterAsync<T, U>(\n xs: readonly T[],\n f: (x: T) => Promise<U|null|undefined>\n): Promise<U[]> {\n return Promise.all(xs.map(f)).then(results => results.filter(isDefined))\n}\n\nexport function mapAsync<T, U>(\n xs: readonly T[],\n f: (x: T) => Promise<U>\n): Promise<U[]> {\n return Promise.all(xs.map(f))\n}\n\nexport async function partitionAsync<A, B extends boolean>(\n items: readonly A[],\n pred: (a: A) => Promise<B>\n): Promise<readonly [A[], A[]]> {\n const results = await Promise.all(items.map(pred))\n\n const yes: A[] = []\n const no : A[] = []\n\n for (let i = 0; i < items.length; i++) {\n (results[i] ? yes : no).push(items[i] as A)\n }\n return [yes, no] as const\n}\n\n/**\n * remove a substring from the beginning of a string; throw if {@link str} does not start with {@link first}\n *\n * @example\n * withoutFirstSubstring('e', 'ego') // => 'go'\n */\nexport function withoutFirstSubstring(first: string, str: string) {\n if (!str.startsWith(first))\n throw new Error(`expected \"${str}\" to start with \"${first}\"`)\n return str.slice(first.length)\n}\n\nexport function isDefined<T>(x: T|null|undefined): x is NonNullable<T> {\n return x != null\n}\n\nexport function isEmpty<T>(xs: readonly T[]): xs is [] {\n return xs.length === 0\n}\n\nexport function isNonEmpty<T>(xs: readonly T[]): xs is [T, ...T[]] {\n return xs.length >= 1\n}\n\nexport function isSingle<T>(xs: readonly T[]): xs is [T] {\n return xs.length === 1\n}\n\nexport function hasAtleastTwo<T>(xs: readonly T[]): xs is [T, T, ...T[]] {\n return xs.length >= 2\n}\n\nexport function assertNever(_: never): never {\n throw new Error(\"unhandled variant\")\n}\n\n/**\n * whether the value is an object that has the key\n *\n * in the `true` branch, the type of the passed argument is narrowed to include the knowledge that the key is present, without destroying any other knowledge about the type prior to the call\n */\nexport function hasKey<T,K extends PropertyKey>(\n value: T,\n key: K\n): value is T & { [P in K]: unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && key in value\n )\n}\n\n/**\n * remove a file or directory recursively; ignore errors\n */\nexport function rm_rf(path: string): Promise<void> {\n return rm(path, {recursive: true}).catch(() => {})\n}\n\n/**\n * return the match of the regex, or throw if no match\n */\nexport function getMatch(re: RegExp, str: string): string {\n const matches = str.match(re) || []\n if (matches===null || !isNonEmpty(matches))\n throw new Error(`regex /${re.source}/ did not match '${str}' exactly once`)\n return matches[0]\n}\n\n/**\n * the `.trim()` method as a function\n *\n * `trim(str) <=> str.trim()`\n */\nexport function trim(str: string): string {\n return str.trim()\n}\n\n/**\n * Evaluate to type `true` if each type extends the other.\n */\nexport type Eq<T,Expected> =\n T extends Expected\n ? Expected extends T\n ? true\n : false\n : false\n\n/**\n * A generic that only type-checks if its parameter is `true`.\n *\n * Intended to be used with {@link Eq} as a compile-time assertion that two types are equal.\n *\n * @example\n *\n * type T = Assert<Eq<number, number>>\n * type U = Assert<Eq<number, string>> // type error\n *\n * // test a generic that is type function-like:\n * type WithoutNumber<T> = Exclude<T, number>\n * type _Test = Assert<Eq<WithoutNumber<number|string>, string>>\n *\n */\nexport type Assert<_T extends true> = null\n\n// test for Eq and Assert - should type-check:\ntype _WithoutNumber<T> = Exclude<T, number>\ntype _TestEqAssert = Assert<Eq<_WithoutNumber<number|string>, string>>\n","import { safeIndex } from '@carlwr/typescript-extra';\nimport * as fc from 'fast-check';\n\n/**\n * Randomly choose one of the constant array values.\n *\n * Shrinking is done towards the first element.\n *\n * Similar to {@link fc.constantFrom}, but _shrinks across all elements_ - {@link fc.constantFrom} only shrinks towards the first element.\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.element(['a', 'b', 'c'] as const)\n * const samples = fc.sample(arb, {seed:1, numRuns:3})\n * console.log(samples) // ['b', 'c', 'b']\n */\nexport function element<T>(xs: readonly [T, ...T[]]): fc.Arbitrary<T> {\n const arb = fc.nat({max: xs.length - 1})\n return arb.map(i => safeIndex(xs, i))\n}\n","import * as fc from 'fast-check'\n\nexport { infiniteStream, getNext }\nexport type { InfiniteStream }\n\ninterface InfiniteStream<T> extends fc.Stream<T> {}\n\n/**\n * Generate an infinite stream of values.\n *\n * This arbitrary is a minimal wrapper around {@link fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@link getNext} helper.\n *\n * features and non-features:\n * - does _not_ shrink at all unfortunately - since {@link fc.infiniteStream} doesn't\n * - _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.infiniteStream(fc.nat({max:10}))\n * const stream = fc.sample(arb, {seed:1})[0] ?? fail()\n * console.log(fcu.getNext(stream)) // 8\n * console.log(fcu.getNext(stream)) // 2\n */\nfunction infiniteStream<T>(arb: fc.Arbitrary<T>): fc.Arbitrary<InfiniteStream<T>> {\n return fc.infiniteStream(arb) as fc.Arbitrary<InfiniteStream<T>>\n}\n\n/**\n * Get next value from an {@link InfiniteStream} object yielded by {@link infiniteStream}.\n *\n * Throws if the stream is unexpectedly done (it is my understanding that this should never happen).\n */\nfunction getNext<T>(stream: InfiniteStream<T>): T {\n const {value, done} = stream.next()\n if (done) {\n throw new Error('infinite stream unexpectedly done')\n }\n return value\n}\n","import * as fc from 'fast-check';\n\nexport { nonEmptyArray }\n\n/**\n * Generate a non-empty array.\n *\n * If a {@link constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.nonEmptyArray(fc.nat({max:5}))\n * const sample = fc.sample(arb, {seed:1})[0]\n * console.log(sample) // [5, 4, 2, 2, 5, 0, 3, 1, 5, 3, 1]\n */\nfunction nonEmptyArray<T>(\n arb : fc.Arbitrary<T>,\n constraints?: fc.ArrayConstraints\n): fc.Arbitrary<[T,...T[]]> {\n\n if (constraints?.minLength === 0) {\n throw new Error(\"minLength cannot be 0 for non-empty array\");\n }\n\n const base = constraints ?? { minLength: 1 };\n const constr: fc.ArrayConstraints = base.minLength !== undefined\n ? base\n : { ...base, minLength: 1 };\n\n return fc.array(arb, constr) as fc.Arbitrary<[T,...T[]]>\n}\n","import * as fc from 'fast-check';\n\n\n/**\n * like {@link fc.record}, but with\n * - `noNullPrototype` _true_ by default\n * - stronger typing\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})\n * const sample = fc.sample(arb, {seed:1})[0] ?? fail()\n * console.log(sample) // {name: 'TVb~o\"nP', age: 36}\n */\nexport function record<T>(\n model: Model<T>\n): fc.Arbitrary<ExactRecord<T>>\n\nexport function record<T>(\n model : Model<T>,\n constr: AllKeysRequired<T>\n): fc.Arbitrary<ExactRecord<T>>\n\nexport function record<T, K extends keyof T>(\n model : Model<T>,\n constr : SomeKeysRequired<T,K>\n): fc.Arbitrary<fc.RecordValue<T,K>>\n\nexport function record<T, K extends keyof T = keyof T>(\n model : Model<T>,\n constr?: fc.RecordConstraints<K>\n):| fc.Arbitrary<ExactRecord<T>>\n | fc.Arbitrary<fc.RecordValue<T,K>>\n {\n\n const nnp = constr?.noNullPrototype ?? true\n\n if (!constr?.requiredKeys) {\n // No constraints or no requiredKeys specified - default to all keys required\n const constr_ = { noNullPrototype: nnp }\n return fc.record(model, constr_) as fc.Arbitrary<ExactRecord<T>>\n }\n\n const constr_ = {\n noNullPrototype: nnp,\n requiredKeys : constr.requiredKeys\n }\n\n const arb = fc.record(model, constr_)\n\n const allKeys = Object.keys(model)\n const typedArb = isAllKeysRequired(constr.requiredKeys, allKeys)\n ? arb as fc.Arbitrary<ExactRecord<T>>\n : arb as fc.Arbitrary<fc.RecordValue<T,K>>\n\n return typedArb\n}\n\nfunction isAllKeysRequired(\n requiredKeys: PropertyKey[],\n allKeys : PropertyKey[]\n): boolean {\n return (\n requiredKeys.length === allKeys.length &&\n allKeys.every(requiredKeys.includes)\n )\n}\n\ntype Model<T> = { [K in keyof T]: fc.Arbitrary<T[K]> }\ntype ExactRecord<T> = { [K in keyof T]: T[K] }\n\ntype AllKeysRequired<T> =\n fc.RecordConstraints<keyof T> &\n { requiredKeys: readonly (keyof T)[] }\n\ntype SomeKeysRequired<T, K extends keyof T> =\n fc.RecordConstraints<K> &\n { requiredKeys: readonly K[] }\n","import { isNonEmpty } from \"@carlwr/typescript-extra\";\nimport * as fc from 'fast-check';\n\nexport { nonEmptyUniqueArray }\n\n/**\n * Generate a non-empty array of unique values.\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))\n * const sample = fc.sample(arb, {seed:1})[0]\n * console.log(sample) // [2, 6, 5, 9, 4, 7, 10, 3, 1, 0, 8]\n */\nfunction nonEmptyUniqueArray<T, U = T>(\n arb : fc.Arbitrary<T>,\n constraints?: fc.UniqueArrayConstraints<T, U>\n): fc.Arbitrary<[T,...T[]]> {\n\n const base = constraints ?? {} as fc.UniqueArrayConstraints<T, U>;\n const constr: fc.UniqueArrayConstraints<T, U> = {\n ...base,\n minLength: Math.max(base.minLength ?? 1, 1)\n };\n\n const ret = fc.uniqueArray(arb, constr).filter(isNonEmpty)\n return ret as fc.Arbitrary<[T,...T[]]>\n}\n"],"mappings":"0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,YAAAC,EAAA,mBAAAC,EAAA,kBAAAC,EAAA,wBAAAC,EAAA,WAAAC,IAAA,eAAAC,EAAAR,GE8FO,SAASS,EAAaC,EAA0BC,EAAc,CACnE,GAAIA,EAAE,GAAKA,GAAGD,EAAG,OACf,MAAM,IAAI,MAAM,SAASC,CAAC,sCAAsCD,EAAG,MAAM,EAAE,EAE7E,OAAOA,EAAGC,CAAC,CACb,CAmDO,SAASC,EAAcC,EAAqC,CACjE,OAAOA,EAAG,QAAU,CACtB,CCvJA,IAAAC,EAAoB,2BAiBb,SAASC,EAAWC,EAA2C,CAEpE,OADe,MAAI,CAAC,IAAKA,EAAG,OAAS,CAAC,CAAC,EAC5B,IAAIC,GAAKC,EAAUF,EAAIC,CAAC,CAAC,CACtC,CCrBA,IAAAE,EAAoB,2BAyBpB,SAASC,EAAkBC,EAAuD,CAChF,OAAU,iBAAeA,CAAG,CAC9B,CAOA,SAASC,EAAWC,EAA8B,CAChD,GAAM,CAAC,MAAAC,EAAO,KAAAC,CAAI,EAAIF,EAAO,KAAK,EAClC,GAAIE,EACF,MAAM,IAAI,MAAM,mCAAmC,EAErD,OAAOD,CACT,CCxCA,IAAAE,EAAoB,2BAiBpB,SAASC,EACPC,EACAC,EAC0B,CAE1B,GAAIA,GAAa,YAAc,EAC7B,MAAM,IAAI,MAAM,2CAA2C,EAG7D,IAAMC,EAAOD,GAAe,CAAE,UAAW,CAAE,EACrCE,EAA8BD,EAAK,YAAc,OACnDA,EACA,CAAE,GAAGA,EAAM,UAAW,CAAE,EAE5B,OAAU,QAAMF,EAAKG,CAAM,CAC7B,CChCA,IAAAC,EAAoB,2BA8Bb,SAASC,EACdC,EACAC,EAGA,CAEA,IAAMC,EAAMD,GAAQ,iBAAmB,GAEvC,GAAI,CAACA,GAAQ,aAGX,OAAU,SAAOD,EADD,CAAE,gBAAiBE,CAAI,CACR,EAGjC,IAAMC,EAAU,CACd,gBAAiBD,EACjB,aAAiBD,EAAO,YAC1B,EAEMG,EAAS,SAAOJ,EAAOG,CAAO,EAE9BE,EAAU,OAAO,KAAKL,CAAK,EAKjC,OAJiBM,EAAkBL,EAAO,aAAcI,CAAO,EAC3DD,CAIN,CAEA,SAASE,EACPC,EACAF,EACS,CACT,OACEE,EAAa,SAAWF,EAAQ,QAChCA,EAAQ,MAAME,EAAa,QAAQ,CAEvC,CCnEA,IAAAC,EAAoB,2BAepB,SAASC,EACPC,EACAC,EAC0B,CAE1B,IAAMC,EAAOD,GAAe,CAAC,EACvBE,EAA0C,CAC9C,GAAGD,EACH,UAAW,KAAK,IAAIA,EAAK,WAAa,EAAG,CAAC,CAC5C,EAGA,OADe,cAAYF,EAAKG,CAAM,EAAE,OAAOC,CAAU,CAE3D","names":["src_exports","__export","element","getNext","infiniteStream","nonEmptyArray","nonEmptyUniqueArray","record","__toCommonJS","safeIndex","xs","i","isNonEmpty","xs","fc","element","xs","i","g","fc","infiniteStream","arb","getNext","stream","value","done","fc","nonEmptyArray","arb","constraints","base","constr","fc","record","model","constr","nnp","constr_","arb","allKeys","isAllKeysRequired","requiredKeys","fc","nonEmptyUniqueArray","arb","constraints","base","constr","a"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as fc from 'fast-check';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Randomly choose one of the constant array values.
|
|
5
|
+
*
|
|
6
|
+
* Shrinking is done towards the first element.
|
|
7
|
+
*
|
|
8
|
+
* Similar to {@link fc.constantFrom}, but _shrinks across all elements_ - {@link fc.constantFrom} only shrinks towards the first element.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import * as fcu from 'fastcheck-utils'
|
|
12
|
+
* import * as fc from 'fast-check'
|
|
13
|
+
*
|
|
14
|
+
* const arb = fcu.element(['a', 'b', 'c'] as const)
|
|
15
|
+
* const samples = fc.sample(arb, {seed:1, numRuns:3})
|
|
16
|
+
* console.log(samples) // ['b', 'c', 'b']
|
|
17
|
+
*/
|
|
18
|
+
declare function element<T>(xs: readonly [T, ...T[]]): fc.Arbitrary<T>;
|
|
19
|
+
|
|
20
|
+
interface InfiniteStream<T> extends fc.Stream<T> {
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate an infinite stream of values.
|
|
24
|
+
*
|
|
25
|
+
* This arbitrary is a minimal wrapper around {@link fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@link getNext} helper.
|
|
26
|
+
*
|
|
27
|
+
* features and non-features:
|
|
28
|
+
* - does _not_ shrink at all unfortunately - since {@link fc.infiniteStream} doesn't
|
|
29
|
+
* - _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* import * as fcu from 'fastcheck-utils'
|
|
33
|
+
* import * as fc from 'fast-check'
|
|
34
|
+
*
|
|
35
|
+
* const arb = fcu.infiniteStream(fc.nat({max:10}))
|
|
36
|
+
* const stream = fc.sample(arb, {seed:1})[0] ?? fail()
|
|
37
|
+
* console.log(fcu.getNext(stream)) // 8
|
|
38
|
+
* console.log(fcu.getNext(stream)) // 2
|
|
39
|
+
*/
|
|
40
|
+
declare function infiniteStream<T>(arb: fc.Arbitrary<T>): fc.Arbitrary<InfiniteStream<T>>;
|
|
41
|
+
/**
|
|
42
|
+
* Get next value from an {@link InfiniteStream} object yielded by {@link infiniteStream}.
|
|
43
|
+
*
|
|
44
|
+
* Throws if the stream is unexpectedly done (it is my understanding that this should never happen).
|
|
45
|
+
*/
|
|
46
|
+
declare function getNext<T>(stream: InfiniteStream<T>): T;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate a non-empty array.
|
|
50
|
+
*
|
|
51
|
+
* If a {@link constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* import * as fcu from 'fastcheck-utils'
|
|
55
|
+
* import * as fc from 'fast-check'
|
|
56
|
+
*
|
|
57
|
+
* const arb = fcu.nonEmptyArray(fc.nat({max:5}))
|
|
58
|
+
* const sample = fc.sample(arb, {seed:1})[0]
|
|
59
|
+
* console.log(sample) // [5, 4, 2, 2, 5, 0, 3, 1, 5, 3, 1]
|
|
60
|
+
*/
|
|
61
|
+
declare function nonEmptyArray<T>(arb: fc.Arbitrary<T>, constraints?: fc.ArrayConstraints): fc.Arbitrary<[T, ...T[]]>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* like {@link fc.record}, but with
|
|
65
|
+
* - `noNullPrototype` _true_ by default
|
|
66
|
+
* - stronger typing
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* import * as fcu from 'fastcheck-utils'
|
|
70
|
+
* import * as fc from 'fast-check'
|
|
71
|
+
*
|
|
72
|
+
* const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})
|
|
73
|
+
* const sample = fc.sample(arb, {seed:1})[0] ?? fail()
|
|
74
|
+
* console.log(sample) // {name: 'TVb~o"nP', age: 36}
|
|
75
|
+
*/
|
|
76
|
+
declare function record<T>(model: Model<T>): fc.Arbitrary<ExactRecord<T>>;
|
|
77
|
+
declare function record<T>(model: Model<T>, constr: AllKeysRequired<T>): fc.Arbitrary<ExactRecord<T>>;
|
|
78
|
+
declare function record<T, K extends keyof T>(model: Model<T>, constr: SomeKeysRequired<T, K>): fc.Arbitrary<fc.RecordValue<T, K>>;
|
|
79
|
+
type Model<T> = {
|
|
80
|
+
[K in keyof T]: fc.Arbitrary<T[K]>;
|
|
81
|
+
};
|
|
82
|
+
type ExactRecord<T> = {
|
|
83
|
+
[K in keyof T]: T[K];
|
|
84
|
+
};
|
|
85
|
+
type AllKeysRequired<T> = fc.RecordConstraints<keyof T> & {
|
|
86
|
+
requiredKeys: readonly (keyof T)[];
|
|
87
|
+
};
|
|
88
|
+
type SomeKeysRequired<T, K extends keyof T> = fc.RecordConstraints<K> & {
|
|
89
|
+
requiredKeys: readonly K[];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Generate a non-empty array of unique values.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* import * as fcu from 'fastcheck-utils'
|
|
97
|
+
* import * as fc from 'fast-check'
|
|
98
|
+
*
|
|
99
|
+
* const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))
|
|
100
|
+
* const sample = fc.sample(arb, {seed:1})[0]
|
|
101
|
+
* console.log(sample) // [2, 6, 5, 9, 4, 7, 10, 3, 1, 0, 8]
|
|
102
|
+
*/
|
|
103
|
+
declare function nonEmptyUniqueArray<T, U = T>(arb: fc.Arbitrary<T>, constraints?: fc.UniqueArrayConstraints<T, U>): fc.Arbitrary<[T, ...T[]]>;
|
|
104
|
+
|
|
105
|
+
export { type InfiniteStream, element, getNext, infiniteStream, nonEmptyArray, nonEmptyUniqueArray, record };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/.pnpm/@carlwr+typescript-extra@0.5.0/node_modules/@carlwr/typescript-extra/src/extract.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.5.0/node_modules/@carlwr/typescript-extra/src/misc.ts","../src/generators/element.ts","../src/generators/infiniteStream.ts","../src/generators/nonEmptyArray.ts","../src/generators/record.ts","../src/generators/nonEmptyUniqueArray.ts"],"sourcesContent":["/**\n * walk {@link root} recursively while collecting all {@link T}s for which {@link pred} returnes a defined result\n *\n * @example\n * const root = {\n * A: 1,\n * b: 0,\n * c: {\n * 99: [{A:2}, {e:\"zero\"}, null],\n * A: 3\n * }\n * }\n * function pred(k: string, v: any): number|undefined {\n * if (k === 'A' && typeof v === 'number') { return v }\n * return undefined\n * }\n * const result = extract<number>(root, pred)\n * // result === [1, 2, 3]\n */\nexport function extract<T>(\n root: unknown,\n pred: (k: string, v: unknown) => T|undefined\n): T[] {\n\n function handleEntry([k,v]: [string,unknown]): T[] {\n const r = pred(k,v)\n return (r !== undefined) ? [r] : go(v)\n }\n\n function go(node: unknown): T[] {\n\n const ret =\n Array.isArray(node) ? node.flatMap(go)\n : node===null ? []\n : typeof node === 'object' ? Object.entries(node).flatMap(handleEntry)\n : []\n return ret\n }\n\n return go(root)\n}\n","import { rm } from 'node:fs/promises'\n\n/**\n * A non-empty array\n */\nexport type NonEmpty<T> = [T, ...T[]]\n\nexport type Primitive =\n | string\n | number\n | boolean\n | bigint\n | symbol\n | null\n | undefined\n\n\n/**\n * Cached, lazy, single-flight evaluation of a promise.\n *\n * A rejected promise is cached as well, i.e. no re-attempts (failures are assumed to be permanent).\n */\nexport function memoized<T>(f: () => Promise<T>): () => Promise<T> {\n let p: Promise<T> | null = null\n return () => p ?? (p = f())\n}\n\n\n/**\n * whether the elements of {@link xs} are unique, in the `===` sense\n *\n * O(n)\n * (if the hash tables behave, and they should for primitives)\n * (constants likely not too great)\n */\nexport function allUnique<T extends Primitive>(xs: T[]): boolean {\n return new Set(xs).size === xs.length\n}\n\n\n/**\n * Map over a non-empty array while preserving the type as non-empty\n */\nexport function mapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T) => U\n): [U,...U[]] {\n const mapped = xs.map(fn)\n return mapped as [U,...U[]] // if the world were perfect, it wouldn't be\n}\n\n/**\n * Flatmap over a non-empty array with a function returning a non-empty arrays. Return the flattened result as a non-empty array.\n */\nexport function flatmapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T) => [U,...U[]]\n): [U,...U[]] {\n const mapped = xs.map(fn)\n const flattened = mapped.flat()\n return flattened as [U,...U[]]\n}\n\n\n/**\n * Stateful iterator yielding the elements of {@link xs}.\n *\n * When the last element is reached, calls will continue to return that element indefinitely.\n *\n * @example\n * const next = drain([1, 2, 3])\n * next() // => 1\n * next() // => 2\n * next() // => 3\n * next() // => 3\n * next() // => 3 (returns 3 forever)\n */\n// a.k.a. imperative sometimes has its merits\n// edit: no it hasn't. But it occasionally has _its uses_.\nexport function drain<T>(xs: [T,...T[]]): () => T {\n let state: [T,...T[]] = xs\n return () => {\n if (hasAtleastTwo(state)) {\n const [x0,...xs_rest] = state\n state = xs_rest\n return x0\n }\n return state[0]\n }\n}\n\n/**\n * return the `i`th element of `xs`, or throw if `i` is out of bounds\n */\nexport function safeIndex<T>(xs: readonly [T, ...T[]], i: number): T {\n if (i<0 || i>=xs.length) {\n throw new Error(`index ${i} out of bounds for array of length ${xs.length}`)\n }\n return xs[i] as T\n}\n\nexport function mapFilterAsync<T, U>(\n xs: readonly T[],\n f: (x: T) => Promise<U|null|undefined>\n): Promise<U[]> {\n return Promise.all(xs.map(f)).then(results => results.filter(isDefined))\n}\n\nexport function mapAsync<T, U>(\n xs: readonly T[],\n f: (x: T) => Promise<U>\n): Promise<U[]> {\n return Promise.all(xs.map(f))\n}\n\nexport async function partitionAsync<A, B extends boolean>(\n items: readonly A[],\n pred: (a: A) => Promise<B>\n): Promise<readonly [A[], A[]]> {\n const results = await Promise.all(items.map(pred))\n\n const yes: A[] = []\n const no : A[] = []\n\n for (let i = 0; i < items.length; i++) {\n (results[i] ? yes : no).push(items[i] as A)\n }\n return [yes, no] as const\n}\n\n/**\n * remove a substring from the beginning of a string; throw if {@link str} does not start with {@link first}\n *\n * @example\n * withoutFirstSubstring('e', 'ego') // => 'go'\n */\nexport function withoutFirstSubstring(first: string, str: string) {\n if (!str.startsWith(first))\n throw new Error(`expected \"${str}\" to start with \"${first}\"`)\n return str.slice(first.length)\n}\n\nexport function isDefined<T>(x: T|null|undefined): x is NonNullable<T> {\n return x != null\n}\n\nexport function isEmpty<T>(xs: readonly T[]): xs is [] {\n return xs.length === 0\n}\n\nexport function isNonEmpty<T>(xs: readonly T[]): xs is [T, ...T[]] {\n return xs.length >= 1\n}\n\nexport function isSingle<T>(xs: readonly T[]): xs is [T] {\n return xs.length === 1\n}\n\nexport function hasAtleastTwo<T>(xs: readonly T[]): xs is [T, T, ...T[]] {\n return xs.length >= 2\n}\n\nexport function assertNever(_: never): never {\n throw new Error(\"unhandled variant\")\n}\n\n/**\n * whether the value is an object that has the key\n *\n * in the `true` branch, the type of the passed argument is narrowed to include the knowledge that the key is present, without destroying any other knowledge about the type prior to the call\n */\nexport function hasKey<T,K extends PropertyKey>(\n value: T,\n key: K\n): value is T & { [P in K]: unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && key in value\n )\n}\n\n/**\n * remove a file or directory recursively; ignore errors\n */\nexport function rm_rf(path: string): Promise<void> {\n return rm(path, {recursive: true}).catch(() => {})\n}\n\n/**\n * return the match of the regex, or throw if no match\n */\nexport function getMatch(re: RegExp, str: string): string {\n const matches = str.match(re) || []\n if (matches===null || !isNonEmpty(matches))\n throw new Error(`regex /${re.source}/ did not match '${str}' exactly once`)\n return matches[0]\n}\n\n/**\n * the `.trim()` method as a function\n *\n * `trim(str) <=> str.trim()`\n */\nexport function trim(str: string): string {\n return str.trim()\n}\n\n/**\n * Evaluate to type `true` if each type extends the other.\n */\nexport type Eq<T,Expected> =\n T extends Expected\n ? Expected extends T\n ? true\n : false\n : false\n\n/**\n * A generic that only type-checks if its parameter is `true`.\n *\n * Intended to be used with {@link Eq} as a compile-time assertion that two types are equal.\n *\n * @example\n *\n * type T = Assert<Eq<number, number>>\n * type U = Assert<Eq<number, string>> // type error\n *\n * // test a generic that is type function-like:\n * type WithoutNumber<T> = Exclude<T, number>\n * type _Test = Assert<Eq<WithoutNumber<number|string>, string>>\n *\n */\nexport type Assert<_T extends true> = null\n\n// test for Eq and Assert - should type-check:\ntype _WithoutNumber<T> = Exclude<T, number>\ntype _TestEqAssert = Assert<Eq<_WithoutNumber<number|string>, string>>\n","import { safeIndex } from '@carlwr/typescript-extra';\nimport * as fc from 'fast-check';\n\n/**\n * Randomly choose one of the constant array values.\n *\n * Shrinking is done towards the first element.\n *\n * Similar to {@link fc.constantFrom}, but _shrinks across all elements_ - {@link fc.constantFrom} only shrinks towards the first element.\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.element(['a', 'b', 'c'] as const)\n * const samples = fc.sample(arb, {seed:1, numRuns:3})\n * console.log(samples) // ['b', 'c', 'b']\n */\nexport function element<T>(xs: readonly [T, ...T[]]): fc.Arbitrary<T> {\n const arb = fc.nat({max: xs.length - 1})\n return arb.map(i => safeIndex(xs, i))\n}\n","import * as fc from 'fast-check'\n\nexport { infiniteStream, getNext }\nexport type { InfiniteStream }\n\ninterface InfiniteStream<T> extends fc.Stream<T> {}\n\n/**\n * Generate an infinite stream of values.\n *\n * This arbitrary is a minimal wrapper around {@link fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@link getNext} helper.\n *\n * features and non-features:\n * - does _not_ shrink at all unfortunately - since {@link fc.infiniteStream} doesn't\n * - _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.infiniteStream(fc.nat({max:10}))\n * const stream = fc.sample(arb, {seed:1})[0] ?? fail()\n * console.log(fcu.getNext(stream)) // 8\n * console.log(fcu.getNext(stream)) // 2\n */\nfunction infiniteStream<T>(arb: fc.Arbitrary<T>): fc.Arbitrary<InfiniteStream<T>> {\n return fc.infiniteStream(arb) as fc.Arbitrary<InfiniteStream<T>>\n}\n\n/**\n * Get next value from an {@link InfiniteStream} object yielded by {@link infiniteStream}.\n *\n * Throws if the stream is unexpectedly done (it is my understanding that this should never happen).\n */\nfunction getNext<T>(stream: InfiniteStream<T>): T {\n const {value, done} = stream.next()\n if (done) {\n throw new Error('infinite stream unexpectedly done')\n }\n return value\n}\n","import * as fc from 'fast-check';\n\nexport { nonEmptyArray }\n\n/**\n * Generate a non-empty array.\n *\n * If a {@link constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.nonEmptyArray(fc.nat({max:5}))\n * const sample = fc.sample(arb, {seed:1})[0]\n * console.log(sample) // [5, 4, 2, 2, 5, 0, 3, 1, 5, 3, 1]\n */\nfunction nonEmptyArray<T>(\n arb : fc.Arbitrary<T>,\n constraints?: fc.ArrayConstraints\n): fc.Arbitrary<[T,...T[]]> {\n\n if (constraints?.minLength === 0) {\n throw new Error(\"minLength cannot be 0 for non-empty array\");\n }\n\n const base = constraints ?? { minLength: 1 };\n const constr: fc.ArrayConstraints = base.minLength !== undefined\n ? base\n : { ...base, minLength: 1 };\n\n return fc.array(arb, constr) as fc.Arbitrary<[T,...T[]]>\n}\n","import * as fc from 'fast-check';\n\n\n/**\n * like {@link fc.record}, but with\n * - `noNullPrototype` _true_ by default\n * - stronger typing\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})\n * const sample = fc.sample(arb, {seed:1})[0] ?? fail()\n * console.log(sample) // {name: 'TVb~o\"nP', age: 36}\n */\nexport function record<T>(\n model: Model<T>\n): fc.Arbitrary<ExactRecord<T>>\n\nexport function record<T>(\n model : Model<T>,\n constr: AllKeysRequired<T>\n): fc.Arbitrary<ExactRecord<T>>\n\nexport function record<T, K extends keyof T>(\n model : Model<T>,\n constr : SomeKeysRequired<T,K>\n): fc.Arbitrary<fc.RecordValue<T,K>>\n\nexport function record<T, K extends keyof T = keyof T>(\n model : Model<T>,\n constr?: fc.RecordConstraints<K>\n):| fc.Arbitrary<ExactRecord<T>>\n | fc.Arbitrary<fc.RecordValue<T,K>>\n {\n\n const nnp = constr?.noNullPrototype ?? true\n\n if (!constr?.requiredKeys) {\n // No constraints or no requiredKeys specified - default to all keys required\n const constr_ = { noNullPrototype: nnp }\n return fc.record(model, constr_) as fc.Arbitrary<ExactRecord<T>>\n }\n\n const constr_ = {\n noNullPrototype: nnp,\n requiredKeys : constr.requiredKeys\n }\n\n const arb = fc.record(model, constr_)\n\n const allKeys = Object.keys(model)\n const typedArb = isAllKeysRequired(constr.requiredKeys, allKeys)\n ? arb as fc.Arbitrary<ExactRecord<T>>\n : arb as fc.Arbitrary<fc.RecordValue<T,K>>\n\n return typedArb\n}\n\nfunction isAllKeysRequired(\n requiredKeys: PropertyKey[],\n allKeys : PropertyKey[]\n): boolean {\n return (\n requiredKeys.length === allKeys.length &&\n allKeys.every(requiredKeys.includes)\n )\n}\n\ntype Model<T> = { [K in keyof T]: fc.Arbitrary<T[K]> }\ntype ExactRecord<T> = { [K in keyof T]: T[K] }\n\ntype AllKeysRequired<T> =\n fc.RecordConstraints<keyof T> &\n { requiredKeys: readonly (keyof T)[] }\n\ntype SomeKeysRequired<T, K extends keyof T> =\n fc.RecordConstraints<K> &\n { requiredKeys: readonly K[] }\n","import { isNonEmpty } from \"@carlwr/typescript-extra\";\nimport * as fc from 'fast-check';\n\nexport { nonEmptyUniqueArray }\n\n/**\n * Generate a non-empty array of unique values.\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))\n * const sample = fc.sample(arb, {seed:1})[0]\n * console.log(sample) // [2, 6, 5, 9, 4, 7, 10, 3, 1, 0, 8]\n */\nfunction nonEmptyUniqueArray<T, U = T>(\n arb : fc.Arbitrary<T>,\n constraints?: fc.UniqueArrayConstraints<T, U>\n): fc.Arbitrary<[T,...T[]]> {\n\n const base = constraints ?? {} as fc.UniqueArrayConstraints<T, U>;\n const constr: fc.UniqueArrayConstraints<T, U> = {\n ...base,\n minLength: Math.max(base.minLength ?? 1, 1)\n };\n\n const ret = fc.uniqueArray(arb, constr).filter(isNonEmpty)\n return ret as fc.Arbitrary<[T,...T[]]>\n}\n"],"mappings":"AC8FO,SAASA,EAAaC,EAA0BC,EAAc,CACnE,GAAIA,EAAE,GAAKA,GAAGD,EAAG,OACf,MAAM,IAAI,MAAM,SAASC,CAAC,sCAAsCD,EAAG,MAAM,EAAE,EAE7E,OAAOA,EAAGC,CAAC,CACb,CAmDO,SAASC,EAAcC,EAAqC,CACjE,OAAOA,EAAG,QAAU,CACtB,CCvJA,UAAYC,MAAQ,aAiBb,SAASC,EAAWC,EAA2C,CAEpE,OADe,MAAI,CAAC,IAAKA,EAAG,OAAS,CAAC,CAAC,EAC5B,IAAIC,GAAKC,EAAUF,EAAIC,CAAC,CAAC,CACtC,CCrBA,UAAYE,MAAQ,aAyBpB,SAASC,EAAkBC,EAAuD,CAChF,OAAU,iBAAeA,CAAG,CAC9B,CAOA,SAASC,EAAWC,EAA8B,CAChD,GAAM,CAAC,MAAAC,EAAO,KAAAC,CAAI,EAAIF,EAAO,KAAK,EAClC,GAAIE,EACF,MAAM,IAAI,MAAM,mCAAmC,EAErD,OAAOD,CACT,CCxCA,UAAYE,MAAQ,aAiBpB,SAASC,EACPC,EACAC,EAC0B,CAE1B,GAAIA,GAAa,YAAc,EAC7B,MAAM,IAAI,MAAM,2CAA2C,EAG7D,IAAMC,EAAOD,GAAe,CAAE,UAAW,CAAE,EACrCE,EAA8BD,EAAK,YAAc,OACnDA,EACA,CAAE,GAAGA,EAAM,UAAW,CAAE,EAE5B,OAAU,QAAMF,EAAKG,CAAM,CAC7B,CChCA,UAAYC,MAAQ,aA8Bb,SAASC,EACdC,EACAC,EAGA,CAEA,IAAMC,EAAMD,GAAQ,iBAAmB,GAEvC,GAAI,CAACA,GAAQ,aAGX,OAAU,SAAOD,EADD,CAAE,gBAAiBE,CAAI,CACR,EAGjC,IAAMC,EAAU,CACd,gBAAiBD,EACjB,aAAiBD,EAAO,YAC1B,EAEMG,EAAS,SAAOJ,EAAOG,CAAO,EAE9BE,EAAU,OAAO,KAAKL,CAAK,EAKjC,OAJiBM,EAAkBL,EAAO,aAAcI,CAAO,EAC3DD,CAIN,CAEA,SAASE,EACPC,EACAF,EACS,CACT,OACEE,EAAa,SAAWF,EAAQ,QAChCA,EAAQ,MAAME,EAAa,QAAQ,CAEvC,CCnEA,UAAYC,MAAQ,aAepB,SAASC,EACPC,EACAC,EAC0B,CAE1B,IAAMC,EAAOD,GAAe,CAAC,EACvBE,EAA0C,CAC9C,GAAGD,EACH,UAAW,KAAK,IAAIA,EAAK,WAAa,EAAG,CAAC,CAC5C,EAGA,OADe,cAAYF,EAAKG,CAAM,EAAE,OAAOC,CAAU,CAE3D","names":["safeIndex","xs","i","isNonEmpty","xs","fc","element","xs","i","g","fc","infiniteStream","arb","getNext","stream","value","done","fc","nonEmptyArray","arb","constraints","base","constr","fc","record","model","constr","nnp","constr_","arb","allKeys","isAllKeysRequired","requiredKeys","fc","nonEmptyUniqueArray","arb","constraints","base","constr","a"]}
|
|
1
|
+
{"version":3,"sources":["../../node_modules/.pnpm/@carlwr+typescript-extra@0.5.0/node_modules/@carlwr/typescript-extra/src/extract.ts","../../node_modules/.pnpm/@carlwr+typescript-extra@0.5.0/node_modules/@carlwr/typescript-extra/src/misc.ts","../src/generators/element.ts","../src/generators/infiniteStream.ts","../src/generators/nonEmptyArray.ts","../src/generators/record.ts","../src/generators/nonEmptyUniqueArray.ts"],"sourcesContent":["/**\n * walk {@link root} recursively while collecting all {@link T}s for which {@link pred} returnes a defined result\n *\n * @example\n * const root = {\n * A: 1,\n * b: 0,\n * c: {\n * 99: [{A:2}, {e:\"zero\"}, null],\n * A: 3\n * }\n * }\n * function pred(k: string, v: any): number|undefined {\n * if (k === 'A' && typeof v === 'number') { return v }\n * return undefined\n * }\n * const result = extract<number>(root, pred)\n * // result === [1, 2, 3]\n */\nexport function extract<T>(\n root: unknown,\n pred: (k: string, v: unknown) => T|undefined\n): T[] {\n\n function handleEntry([k,v]: [string,unknown]): T[] {\n const r = pred(k,v)\n return (r !== undefined) ? [r] : go(v)\n }\n\n function go(node: unknown): T[] {\n\n const ret =\n Array.isArray(node) ? node.flatMap(go)\n : node===null ? []\n : typeof node === 'object' ? Object.entries(node).flatMap(handleEntry)\n : []\n return ret\n }\n\n return go(root)\n}\n","import { rm } from 'node:fs/promises'\n\n/**\n * A non-empty array\n */\nexport type NonEmpty<T> = [T, ...T[]]\n\nexport type Primitive =\n | string\n | number\n | boolean\n | bigint\n | symbol\n | null\n | undefined\n\n\n/**\n * Cached, lazy, single-flight evaluation of a promise.\n *\n * A rejected promise is cached as well, i.e. no re-attempts (failures are assumed to be permanent).\n */\nexport function memoized<T>(f: () => Promise<T>): () => Promise<T> {\n let p: Promise<T> | null = null\n return () => p ?? (p = f())\n}\n\n\n/**\n * whether the elements of {@link xs} are unique, in the `===` sense\n *\n * O(n)\n * (if the hash tables behave, and they should for primitives)\n * (constants likely not too great)\n */\nexport function allUnique<T extends Primitive>(xs: T[]): boolean {\n return new Set(xs).size === xs.length\n}\n\n\n/**\n * Map over a non-empty array while preserving the type as non-empty\n */\nexport function mapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T) => U\n): [U,...U[]] {\n const mapped = xs.map(fn)\n return mapped as [U,...U[]] // if the world were perfect, it wouldn't be\n}\n\n/**\n * Flatmap over a non-empty array with a function returning a non-empty arrays. Return the flattened result as a non-empty array.\n */\nexport function flatmapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T) => [U,...U[]]\n): [U,...U[]] {\n const mapped = xs.map(fn)\n const flattened = mapped.flat()\n return flattened as [U,...U[]]\n}\n\n\n/**\n * Stateful iterator yielding the elements of {@link xs}.\n *\n * When the last element is reached, calls will continue to return that element indefinitely.\n *\n * @example\n * const next = drain([1, 2, 3])\n * next() // => 1\n * next() // => 2\n * next() // => 3\n * next() // => 3\n * next() // => 3 (returns 3 forever)\n */\n// a.k.a. imperative sometimes has its merits\n// edit: no it hasn't. But it occasionally has _its uses_.\nexport function drain<T>(xs: [T,...T[]]): () => T {\n let state: [T,...T[]] = xs\n return () => {\n if (hasAtleastTwo(state)) {\n const [x0,...xs_rest] = state\n state = xs_rest\n return x0\n }\n return state[0]\n }\n}\n\n/**\n * return the `i`th element of `xs`, or throw if `i` is out of bounds\n */\nexport function safeIndex<T>(xs: readonly [T, ...T[]], i: number): T {\n if (i<0 || i>=xs.length) {\n throw new Error(`index ${i} out of bounds for array of length ${xs.length}`)\n }\n return xs[i] as T\n}\n\nexport function mapFilterAsync<T, U>(\n xs: readonly T[],\n f: (x: T) => Promise<U|null|undefined>\n): Promise<U[]> {\n return Promise.all(xs.map(f)).then(results => results.filter(isDefined))\n}\n\nexport function mapAsync<T, U>(\n xs: readonly T[],\n f: (x: T) => Promise<U>\n): Promise<U[]> {\n return Promise.all(xs.map(f))\n}\n\nexport async function partitionAsync<A, B extends boolean>(\n items: readonly A[],\n pred: (a: A) => Promise<B>\n): Promise<readonly [A[], A[]]> {\n const results = await Promise.all(items.map(pred))\n\n const yes: A[] = []\n const no : A[] = []\n\n for (let i = 0; i < items.length; i++) {\n (results[i] ? yes : no).push(items[i] as A)\n }\n return [yes, no] as const\n}\n\n/**\n * remove a substring from the beginning of a string; throw if {@link str} does not start with {@link first}\n *\n * @example\n * withoutFirstSubstring('e', 'ego') // => 'go'\n */\nexport function withoutFirstSubstring(first: string, str: string) {\n if (!str.startsWith(first))\n throw new Error(`expected \"${str}\" to start with \"${first}\"`)\n return str.slice(first.length)\n}\n\nexport function isDefined<T>(x: T|null|undefined): x is NonNullable<T> {\n return x != null\n}\n\nexport function isEmpty<T>(xs: readonly T[]): xs is [] {\n return xs.length === 0\n}\n\nexport function isNonEmpty<T>(xs: readonly T[]): xs is [T, ...T[]] {\n return xs.length >= 1\n}\n\nexport function isSingle<T>(xs: readonly T[]): xs is [T] {\n return xs.length === 1\n}\n\nexport function hasAtleastTwo<T>(xs: readonly T[]): xs is [T, T, ...T[]] {\n return xs.length >= 2\n}\n\nexport function assertNever(_: never): never {\n throw new Error(\"unhandled variant\")\n}\n\n/**\n * whether the value is an object that has the key\n *\n * in the `true` branch, the type of the passed argument is narrowed to include the knowledge that the key is present, without destroying any other knowledge about the type prior to the call\n */\nexport function hasKey<T,K extends PropertyKey>(\n value: T,\n key: K\n): value is T & { [P in K]: unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && key in value\n )\n}\n\n/**\n * remove a file or directory recursively; ignore errors\n */\nexport function rm_rf(path: string): Promise<void> {\n return rm(path, {recursive: true}).catch(() => {})\n}\n\n/**\n * return the match of the regex, or throw if no match\n */\nexport function getMatch(re: RegExp, str: string): string {\n const matches = str.match(re) || []\n if (matches===null || !isNonEmpty(matches))\n throw new Error(`regex /${re.source}/ did not match '${str}' exactly once`)\n return matches[0]\n}\n\n/**\n * the `.trim()` method as a function\n *\n * `trim(str) <=> str.trim()`\n */\nexport function trim(str: string): string {\n return str.trim()\n}\n\n/**\n * Evaluate to type `true` if each type extends the other.\n */\nexport type Eq<T,Expected> =\n T extends Expected\n ? Expected extends T\n ? true\n : false\n : false\n\n/**\n * A generic that only type-checks if its parameter is `true`.\n *\n * Intended to be used with {@link Eq} as a compile-time assertion that two types are equal.\n *\n * @example\n *\n * type T = Assert<Eq<number, number>>\n * type U = Assert<Eq<number, string>> // type error\n *\n * // test a generic that is type function-like:\n * type WithoutNumber<T> = Exclude<T, number>\n * type _Test = Assert<Eq<WithoutNumber<number|string>, string>>\n *\n */\nexport type Assert<_T extends true> = null\n\n// test for Eq and Assert - should type-check:\ntype _WithoutNumber<T> = Exclude<T, number>\ntype _TestEqAssert = Assert<Eq<_WithoutNumber<number|string>, string>>\n","import { safeIndex } from '@carlwr/typescript-extra';\nimport * as fc from 'fast-check';\n\n/**\n * Randomly choose one of the constant array values.\n *\n * Shrinking is done towards the first element.\n *\n * Similar to {@link fc.constantFrom}, but _shrinks across all elements_ - {@link fc.constantFrom} only shrinks towards the first element.\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.element(['a', 'b', 'c'] as const)\n * const samples = fc.sample(arb, {seed:1, numRuns:3})\n * console.log(samples) // ['b', 'c', 'b']\n */\nexport function element<T>(xs: readonly [T, ...T[]]): fc.Arbitrary<T> {\n const arb = fc.nat({max: xs.length - 1})\n return arb.map(i => safeIndex(xs, i))\n}\n","import * as fc from 'fast-check'\n\nexport { infiniteStream, getNext }\nexport type { InfiniteStream }\n\ninterface InfiniteStream<T> extends fc.Stream<T> {}\n\n/**\n * Generate an infinite stream of values.\n *\n * This arbitrary is a minimal wrapper around {@link fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@link getNext} helper.\n *\n * features and non-features:\n * - does _not_ shrink at all unfortunately - since {@link fc.infiniteStream} doesn't\n * - _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.infiniteStream(fc.nat({max:10}))\n * const stream = fc.sample(arb, {seed:1})[0] ?? fail()\n * console.log(fcu.getNext(stream)) // 8\n * console.log(fcu.getNext(stream)) // 2\n */\nfunction infiniteStream<T>(arb: fc.Arbitrary<T>): fc.Arbitrary<InfiniteStream<T>> {\n return fc.infiniteStream(arb) as fc.Arbitrary<InfiniteStream<T>>\n}\n\n/**\n * Get next value from an {@link InfiniteStream} object yielded by {@link infiniteStream}.\n *\n * Throws if the stream is unexpectedly done (it is my understanding that this should never happen).\n */\nfunction getNext<T>(stream: InfiniteStream<T>): T {\n const {value, done} = stream.next()\n if (done) {\n throw new Error('infinite stream unexpectedly done')\n }\n return value\n}\n","import * as fc from 'fast-check';\n\nexport { nonEmptyArray }\n\n/**\n * Generate a non-empty array.\n *\n * If a {@link constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.nonEmptyArray(fc.nat({max:5}))\n * const sample = fc.sample(arb, {seed:1})[0]\n * console.log(sample) // [5, 4, 2, 2, 5, 0, 3, 1, 5, 3, 1]\n */\nfunction nonEmptyArray<T>(\n arb : fc.Arbitrary<T>,\n constraints?: fc.ArrayConstraints\n): fc.Arbitrary<[T,...T[]]> {\n\n if (constraints?.minLength === 0) {\n throw new Error(\"minLength cannot be 0 for non-empty array\");\n }\n\n const base = constraints ?? { minLength: 1 };\n const constr: fc.ArrayConstraints = base.minLength !== undefined\n ? base\n : { ...base, minLength: 1 };\n\n return fc.array(arb, constr) as fc.Arbitrary<[T,...T[]]>\n}\n","import * as fc from 'fast-check';\n\n\n/**\n * like {@link fc.record}, but with\n * - `noNullPrototype` _true_ by default\n * - stronger typing\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})\n * const sample = fc.sample(arb, {seed:1})[0] ?? fail()\n * console.log(sample) // {name: 'TVb~o\"nP', age: 36}\n */\nexport function record<T>(\n model: Model<T>\n): fc.Arbitrary<ExactRecord<T>>\n\nexport function record<T>(\n model : Model<T>,\n constr: AllKeysRequired<T>\n): fc.Arbitrary<ExactRecord<T>>\n\nexport function record<T, K extends keyof T>(\n model : Model<T>,\n constr : SomeKeysRequired<T,K>\n): fc.Arbitrary<fc.RecordValue<T,K>>\n\nexport function record<T, K extends keyof T = keyof T>(\n model : Model<T>,\n constr?: fc.RecordConstraints<K>\n):| fc.Arbitrary<ExactRecord<T>>\n | fc.Arbitrary<fc.RecordValue<T,K>>\n {\n\n const nnp = constr?.noNullPrototype ?? true\n\n if (!constr?.requiredKeys) {\n // No constraints or no requiredKeys specified - default to all keys required\n const constr_ = { noNullPrototype: nnp }\n return fc.record(model, constr_) as fc.Arbitrary<ExactRecord<T>>\n }\n\n const constr_ = {\n noNullPrototype: nnp,\n requiredKeys : constr.requiredKeys\n }\n\n const arb = fc.record(model, constr_)\n\n const allKeys = Object.keys(model)\n const typedArb = isAllKeysRequired(constr.requiredKeys, allKeys)\n ? arb as fc.Arbitrary<ExactRecord<T>>\n : arb as fc.Arbitrary<fc.RecordValue<T,K>>\n\n return typedArb\n}\n\nfunction isAllKeysRequired(\n requiredKeys: PropertyKey[],\n allKeys : PropertyKey[]\n): boolean {\n return (\n requiredKeys.length === allKeys.length &&\n allKeys.every(requiredKeys.includes)\n )\n}\n\ntype Model<T> = { [K in keyof T]: fc.Arbitrary<T[K]> }\ntype ExactRecord<T> = { [K in keyof T]: T[K] }\n\ntype AllKeysRequired<T> =\n fc.RecordConstraints<keyof T> &\n { requiredKeys: readonly (keyof T)[] }\n\ntype SomeKeysRequired<T, K extends keyof T> =\n fc.RecordConstraints<K> &\n { requiredKeys: readonly K[] }\n","import { isNonEmpty } from \"@carlwr/typescript-extra\";\nimport * as fc from 'fast-check';\n\nexport { nonEmptyUniqueArray }\n\n/**\n * Generate a non-empty array of unique values.\n *\n * @example\n * import * as fcu from 'fastcheck-utils'\n * import * as fc from 'fast-check'\n *\n * const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))\n * const sample = fc.sample(arb, {seed:1})[0]\n * console.log(sample) // [2, 6, 5, 9, 4, 7, 10, 3, 1, 0, 8]\n */\nfunction nonEmptyUniqueArray<T, U = T>(\n arb : fc.Arbitrary<T>,\n constraints?: fc.UniqueArrayConstraints<T, U>\n): fc.Arbitrary<[T,...T[]]> {\n\n const base = constraints ?? {} as fc.UniqueArrayConstraints<T, U>;\n const constr: fc.UniqueArrayConstraints<T, U> = {\n ...base,\n minLength: Math.max(base.minLength ?? 1, 1)\n };\n\n const ret = fc.uniqueArray(arb, constr).filter(isNonEmpty)\n return ret as fc.Arbitrary<[T,...T[]]>\n}\n"],"mappings":"AC8FO,SAASA,EAAaC,EAA0BC,EAAc,CACnE,GAAIA,EAAE,GAAKA,GAAGD,EAAG,OACf,MAAM,IAAI,MAAM,SAASC,CAAC,sCAAsCD,EAAG,MAAM,EAAE,EAE7E,OAAOA,EAAGC,CAAC,CACb,CAmDO,SAASC,EAAcC,EAAqC,CACjE,OAAOA,EAAG,QAAU,CACtB,CCvJA,UAAYC,MAAQ,aAiBb,SAASC,EAAWC,EAA2C,CAEpE,OADe,MAAI,CAAC,IAAKA,EAAG,OAAS,CAAC,CAAC,EAC5B,IAAIC,GAAKC,EAAUF,EAAIC,CAAC,CAAC,CACtC,CCrBA,UAAYE,MAAQ,aAyBpB,SAASC,EAAkBC,EAAuD,CAChF,OAAU,iBAAeA,CAAG,CAC9B,CAOA,SAASC,EAAWC,EAA8B,CAChD,GAAM,CAAC,MAAAC,EAAO,KAAAC,CAAI,EAAIF,EAAO,KAAK,EAClC,GAAIE,EACF,MAAM,IAAI,MAAM,mCAAmC,EAErD,OAAOD,CACT,CCxCA,UAAYE,MAAQ,aAiBpB,SAASC,EACPC,EACAC,EAC0B,CAE1B,GAAIA,GAAa,YAAc,EAC7B,MAAM,IAAI,MAAM,2CAA2C,EAG7D,IAAMC,EAAOD,GAAe,CAAE,UAAW,CAAE,EACrCE,EAA8BD,EAAK,YAAc,OACnDA,EACA,CAAE,GAAGA,EAAM,UAAW,CAAE,EAE5B,OAAU,QAAMF,EAAKG,CAAM,CAC7B,CChCA,UAAYC,MAAQ,aA8Bb,SAASC,EACdC,EACAC,EAGA,CAEA,IAAMC,EAAMD,GAAQ,iBAAmB,GAEvC,GAAI,CAACA,GAAQ,aAGX,OAAU,SAAOD,EADD,CAAE,gBAAiBE,CAAI,CACR,EAGjC,IAAMC,EAAU,CACd,gBAAiBD,EACjB,aAAiBD,EAAO,YAC1B,EAEMG,EAAS,SAAOJ,EAAOG,CAAO,EAE9BE,EAAU,OAAO,KAAKL,CAAK,EAKjC,OAJiBM,EAAkBL,EAAO,aAAcI,CAAO,EAC3DD,CAIN,CAEA,SAASE,EACPC,EACAF,EACS,CACT,OACEE,EAAa,SAAWF,EAAQ,QAChCA,EAAQ,MAAME,EAAa,QAAQ,CAEvC,CCnEA,UAAYC,MAAQ,aAepB,SAASC,EACPC,EACAC,EAC0B,CAE1B,IAAMC,EAAOD,GAAe,CAAC,EACvBE,EAA0C,CAC9C,GAAGD,EACH,UAAW,KAAK,IAAIA,EAAK,WAAa,EAAG,CAAC,CAC5C,EAGA,OADe,cAAYF,EAAKG,CAAM,EAAE,OAAOC,CAAU,CAE3D","names":["safeIndex","xs","i","isNonEmpty","xs","fc","element","xs","i","g","fc","infiniteStream","arb","getNext","stream","value","done","fc","nonEmptyArray","arb","constraints","base","constr","fc","record","model","constr","nnp","constr_","arb","allKeys","isAllKeysRequired","requiredKeys","fc","nonEmptyUniqueArray","arb","constraints","base","constr","a"]}
|
package/package.json
CHANGED
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
|
-
"version": "0.
|
|
25
|
+
"version": "0.4.0",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"type": "module",
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
28
29
|
"module": "./dist/index.js",
|
|
29
30
|
"types": "./dist/index.d.ts",
|
|
30
31
|
"exports": {
|
|
@@ -32,6 +33,10 @@
|
|
|
32
33
|
"import": {
|
|
33
34
|
"types": "./dist/index.d.ts",
|
|
34
35
|
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/index.d.cts",
|
|
39
|
+
"default": "./dist/index.cjs"
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
},
|
|
@@ -44,19 +49,19 @@
|
|
|
44
49
|
"fast-check": ">=3.0.0"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
47
|
-
"@biomejs/biome": "^2.
|
|
52
|
+
"@biomejs/biome": "^2.4.10",
|
|
48
53
|
"@carlwr/typescript-extra": "^0.5.0",
|
|
49
|
-
"@fast-check/vitest": "^0.2.
|
|
50
|
-
"@types/node": "^24.0
|
|
54
|
+
"@fast-check/vitest": "^0.2.4",
|
|
55
|
+
"@types/node": "^24.12.0",
|
|
51
56
|
"arg": "^5.0.2",
|
|
52
57
|
"read-pkg": "^9.0.1",
|
|
53
|
-
"tsup": "^8.5.
|
|
54
|
-
"tsx": "^4.
|
|
55
|
-
"typedoc": "^0.28.
|
|
56
|
-
"typedoc-plugin-markdown": "^4.
|
|
57
|
-
"typescript": "^5.
|
|
58
|
+
"tsup": "^8.5.1",
|
|
59
|
+
"tsx": "^4.21.0",
|
|
60
|
+
"typedoc": "^0.28.18",
|
|
61
|
+
"typedoc-plugin-markdown": "^4.11.0",
|
|
62
|
+
"typescript": "^5.9.3",
|
|
58
63
|
"vitest": "^3.2.4",
|
|
59
|
-
"zod": "^3.25.
|
|
64
|
+
"zod": "^3.25.76",
|
|
60
65
|
"zzz_LAST_dummy": "npm:empty-npm-package@1.0.0"
|
|
61
66
|
},
|
|
62
67
|
"engines": {
|
|
@@ -72,7 +77,6 @@
|
|
|
72
77
|
"version:minor": "pnpm version minor",
|
|
73
78
|
"version:major": "pnpm version major",
|
|
74
79
|
"lint": "biome check",
|
|
75
|
-
"imports:sort": "biome check --linter-enabled=false --organize-imports-enabled=true --write",
|
|
76
80
|
"typecheck": "tsc --noEmit",
|
|
77
81
|
"test": "vitest run",
|
|
78
82
|
"test:watch": "vitest",
|