@carlwr/fastcheck-utils 0.5.1 → 0.7.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 +57 -15
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -12
- package/dist/index.d.ts +57 -12
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +28 -26
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
# fastcheck-utils
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
_utilities and improved generators for [fast-check](https://github.com/dubzzz/fast-check)_
|
|
5
5
|
|
|
6
6
|
Links:
|
|
7
7
|
* github: [github.com/carlwr/fastcheck-utils](https://github.com/carlwr/fastcheck-utils)
|
|
@@ -15,15 +15,57 @@ npm install @carlwr/fastcheck-utils
|
|
|
15
15
|
|
|
16
16
|
`fast-check` is a peer dependency of this package. It should be picked up by the package manager also if you depend on e.g. `@fast-check/vitest` or `@fast-check/jest` rather than on `fast-check` directly.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
## Authoring
|
|
19
|
+
|
|
20
|
+
This `README.md` file, and any _JSDoc_ documentation, is written entirely by me, Carl, a human developer. [Agents are not allowed][AGENTS.md] to touch the prose of these.
|
|
21
|
+
|
|
22
|
+
I-the-human implemented everything up to and including _v0.5.2_. For later versions, agentic tools may be used as a development tool; with myself as the reviewer and ultimate decision-maker. I am and will remain the sole author of this `README.md` file and any _JSDoc_.
|
|
23
|
+
|
|
24
|
+
[AGENTS.md]: ./AGENTS.md
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
25
|
-
##
|
|
28
|
+
## Utilities and generators
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### `coverage`
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
function coverage<K>(requirements: Readonly<Record<K, number>>): Coverage<K>
|
|
35
|
+
```
|
|
36
|
+
Add minimum coverage checks to a property test.
|
|
37
|
+
|
|
38
|
+
In a test, the user first calls `coverage`, specifying the labels to use and, for each label, a minimum percentage of test cases that must "hit" the label for the coverage to be considered sufficient. The user then adds conditional calls to `hit()` to the test.
|
|
39
|
+
|
|
40
|
+
If the property test itself succeeds, the number of test cases that were run is compared to the number of hits for each label. If the specified minimum percentage was not met for at least one label, the property test still fails, with a useful message.
|
|
41
|
+
|
|
42
|
+
For an introduction to the coverage feature, the primary resource is the [example file](https://github.com/carlwr/fastcheck-utils/blob/main/test/coverage.example.test.ts).
|
|
43
|
+
|
|
44
|
+
Calls to `hit()` must specify one of the registered labels. This is enforced on the type level.
|
|
45
|
+
|
|
46
|
+
If the property fails, that error takes precedence and coverage is not asserted.
|
|
47
|
+
|
|
48
|
+
When counting the number of test cases (the denominator) and the number of hits,
|
|
49
|
+
- discarded cases are not included
|
|
50
|
+
- if a label is hit more than once, it is still only counted once
|
|
51
|
+
- _examples_ are included in the count of number of test cases (they contribute to the denominator)
|
|
52
|
+
- shrinking runs are not included (which does not matter, since in the case of the property failing, coverage is not checked anyways)
|
|
53
|
+
|
|
54
|
+
If there are no accepted test cases in a test, the coverage check will result in a coverage failure.
|
|
55
|
+
|
|
56
|
+
It is strongly recommended to use a fixed seed for tests that include coverage checks.
|
|
57
|
+
|
|
58
|
+
If a property is _replayed_, the coverage test will be ignored (since it isn't meaningful in replays).
|
|
59
|
+
|
|
60
|
+
`hit()` can only be called from a test - not from within an arbitrary or `fc.beforeEach`. If you want to use coverage to assert on the distribution of an arbitrary, it is suggested to write a dedicated property test.
|
|
61
|
+
|
|
62
|
+
If you use the `fc.ignoreEqualValues()` plugin: if used it must come before this coverage plugin, e.g. `[fc.ignoreEqualValues(), myCoverage.plugin]`.
|
|
63
|
+
|
|
64
|
+
parameters:
|
|
65
|
+
|
|
66
|
+
- `requirements`: A record where the user specifies labels as keys and required hit percentages as values
|
|
26
67
|
|
|
68
|
+
returns: An object with the `hit()` function for the user to call, and the `plugin` value to pass to something that accepts a `fast-check` plugin, e.g. `fast-check`'s `fc.assert`/`fc.check`, or `@fast-check/vitest`'s `it.prop`/`test.prop`. The plugin will be ignored if used with `fc.sample` or `fc.statistics`. Passing it to `fc.check` will result in coverage failures to throw, rather than report the failure. Passing it to `fc.installGlobalPlugin` does not make sense since that would mean the same requirements would be applied to all checks.
|
|
27
69
|
|
|
28
70
|
### `element`
|
|
29
71
|
|
|
@@ -34,12 +76,12 @@ Randomly choose one of the constant array values.
|
|
|
34
76
|
|
|
35
77
|
Shrinking is done towards the first element.
|
|
36
78
|
|
|
37
|
-
Similar to fc.constantFrom
|
|
79
|
+
Similar to `fc.constantFrom`, but _shrinks across all elements_ - `fc.constantFrom` only shrinks towards the first element.
|
|
38
80
|
|
|
39
81
|
example:
|
|
40
82
|
|
|
41
83
|
```ts
|
|
42
|
-
import * as fcu from 'fastcheck-utils'
|
|
84
|
+
import * as fcu from '@carlwr/fastcheck-utils'
|
|
43
85
|
import * as fc from 'fast-check'
|
|
44
86
|
|
|
45
87
|
const arb = fcu.element(['a', 'b', 'c'] as const)
|
|
@@ -63,16 +105,16 @@ function infiniteStream<T>(arb: Arbitrary<T>): Arbitrary<InfiniteStream<T>>
|
|
|
63
105
|
```
|
|
64
106
|
Generate an infinite stream of values.
|
|
65
107
|
|
|
66
|
-
This arbitrary is a minimal wrapper around fc.infiniteStream allowing access to the generated values in a type-safe way through the `getNext` helper.
|
|
108
|
+
This arbitrary is a minimal wrapper around `fc.infiniteStream` allowing access to the generated values in a type-safe way through the `getNext` helper.
|
|
67
109
|
|
|
68
110
|
features and non-features:
|
|
69
|
-
- does _not_ shrink at all unfortunately - since fc.infiniteStream doesn't
|
|
111
|
+
- does _not_ shrink at all unfortunately - since `fc.infiniteStream` doesn't
|
|
70
112
|
- _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream
|
|
71
113
|
|
|
72
114
|
example:
|
|
73
115
|
|
|
74
116
|
```ts
|
|
75
|
-
import * as fcu from 'fastcheck-utils'
|
|
117
|
+
import * as fcu from '@carlwr/fastcheck-utils'
|
|
76
118
|
import * as fc from 'fast-check'
|
|
77
119
|
|
|
78
120
|
const arb = fcu.infiniteStream(fc.nat({max:10}))
|
|
@@ -93,7 +135,7 @@ If a `constraints` parameter object is passed, it will be honored (function thro
|
|
|
93
135
|
example:
|
|
94
136
|
|
|
95
137
|
```ts
|
|
96
|
-
import * as fcu from 'fastcheck-utils'
|
|
138
|
+
import * as fcu from '@carlwr/fastcheck-utils'
|
|
97
139
|
import * as fc from 'fast-check'
|
|
98
140
|
|
|
99
141
|
const arb = fcu.nonEmptyArray(fc.nat({max:5}))
|
|
@@ -111,7 +153,7 @@ Generate a non-empty array of unique values.
|
|
|
111
153
|
example:
|
|
112
154
|
|
|
113
155
|
```ts
|
|
114
|
-
import * as fcu from 'fastcheck-utils'
|
|
156
|
+
import * as fcu from '@carlwr/fastcheck-utils'
|
|
115
157
|
import * as fc from 'fast-check'
|
|
116
158
|
|
|
117
159
|
const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))
|
|
@@ -128,14 +170,14 @@ function record<T>(model: Model<T>, constr: AllKeysRequired<T>): Arbitrary<Exact
|
|
|
128
170
|
|
|
129
171
|
function record<T, K>(model: Model<T>, constr: SomeKeysRequired<T, K>): Arbitrary<{ [K in string | number | symbol]: (Partial<T> & Pick<T, K & keyof T>)[K] }>
|
|
130
172
|
```
|
|
131
|
-
like fc.record
|
|
173
|
+
like `fc.record`, but with
|
|
132
174
|
- `noNullPrototype` _true_ by default
|
|
133
175
|
- stronger typing
|
|
134
176
|
|
|
135
177
|
example:
|
|
136
178
|
|
|
137
179
|
```ts
|
|
138
|
-
import * as fcu from 'fastcheck-utils'
|
|
180
|
+
import * as fcu from '@carlwr/fastcheck-utils'
|
|
139
181
|
import * as fc from 'fast-check'
|
|
140
182
|
|
|
141
183
|
const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var N=Object.create;var g=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var O=(e,r)=>{for(var t in r)g(e,t,{get:r[t],enumerable:!0})},v=(e,r,t,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of I(r))!D.call(e,n)&&n!==t&&g(e,n,{get:()=>r[n],enumerable:!(o=j(r,n))||o.enumerable});return e};var y=(e,r,t)=>(t=e!=null?N(F(e)):{},v(r||!e||!e.__esModule?g(t,"default",{value:e,enumerable:!0}):t,e)),_=e=>v(g({},"__esModule",{value:!0}),e);var ie={};O(ie,{coverage:()=>P,element:()=>z,getNext:()=>V,infiniteStream:()=>G,nonEmptyArray:()=>W,nonEmptyUniqueArray:()=>B,record:()=>H});module.exports=_(ie);function E(e,r){if(r<0||r>=e.length)throw new Error(`index ${r} out of bounds for array of length ${e.length}`);return e[r]}function K(e){return e.length>=1}var R=y(require("fast-check"),1);function z(e){return R.nat({max:e.length-1}).map(t=>E(e,t))}var S=y(require("fast-check"),1);function G(e){return S.infiniteStream(e)}function V(e){let{value:r,done:t}=e.next();if(t)throw new Error("infinite stream unexpectedly done");return r}var k=y(require("fast-check"),1);function W(e,r){if(r?.minLength===0)throw new Error("minLength cannot be 0 for non-empty array");let t=r??{minLength:1},o=t.minLength!==void 0?t:{...t,minLength:1};return k.array(e,o)}var h=y(require("fast-check"),1);function H(e,r){let t=r?.noNullPrototype??!0;if(!r?.requiredKeys)return h.record(e,{noNullPrototype:t});let o={noNullPrototype:t,requiredKeys:r.requiredKeys},n=h.record(e,o),i=Object.keys(e);return J(r.requiredKeys,i),n}function J(e,r){return e.length===r.length&&r.every(t=>e.includes(t))}var M=y(require("fast-check"),1);function B(e,r){let t=r??{},o={...t,minLength:Math.max(t.minLength??1,1)};return M.uniqueArray(e,o).filter(K)}function C(e,r){let t=$(e);return t?t.then(r):r(e)}function q(e,r){let t=a=>(r(),a),o=a=>{try{r()}catch(l){throw Q(l,a)}throw a},n;try{n=e()}catch(a){return o(a)}let i=$(n);return i?i.then(t,o):t(n)}function $(e){if(!(typeof e=="object"&&e!==null||typeof e=="function"))return;let r;try{r=e.then}catch(t){return Promise.reject(t)}if(typeof r=="function")return new Promise((t,o)=>r.call(e,t,o))}function Q(e,r){if(e instanceof Error&&e.cause===void 0&&e!==r)try{e.cause=r}catch{}return e}function f(e,r){let t=new Error(r);return Error.captureStackTrace?.(t,e),t}var c={noLabels:()=>"coverage requires at least one label",badPercent:e=>`coverage percentage for ${JSON.stringify(e)} must be between 0 and 100`,outsideCase:()=>"coverage.hit() called outside an active test case",inGenerator:()=>"coverage.hit() cannot be called from an arbitrary",noCheck:()=>"coverage.hit() called, but no property check with coverage.plugin is running",duplicatePlugin:()=>"coverage.plugin passed more than once to the same property check",unknownLabel:e=>`unknown coverage label ${JSON.stringify(e)}`,concurrentUse:()=>"coverage cannot be shared by concurrent property checks",runSummary:e=>`{ seed: ${e.seed}, skipped: ${e.numSkips} }`,noAccepted:()=>"coverage requires at least one accepted test case",unmetHeader:e=>`coverage failed after ${e} accepted test cases`,unmetLine:({label:e,minimum:r,count:t},o)=>{let n=`${t}/${o} (${te(t,o)}%)`,i=`required >= ${r}%`;return`- ${e}: ${n}, ${i}`},notRunning:(e,r)=>e===0?c.noCheck():r?c.inGenerator():c.outsideCase()};function P(e){let r=new Map(Object.entries(e)),t=ne(r);if(t.length>0)throw f(P,t.join(`
|
|
2
|
+
`));let o=Symbol("coverage"),n,i=0,a=!1;function l(p){if(!n)throw f(l,c.notRunning(i,a));r.has(p)?n.hits.add(p):n.tally.usageError??=f(l,c.unknownLabel(p))}let T=(p,b)=>{if(b.get(o))throw f(T,c.duplicatePlugin());b.set(o,!0),i+=1;let u={accepted:0,hits:new Map,usageError:void 0};return{decorateGenerate:d=>(...s)=>{a=!0;try{return d(...s)}finally{a=!1}},decorateRun:d=>function s(x){if(n&&n.tally!==u){let m=f(s,c.concurrentUse());return n.tally.usageError??=m,u.usageError??=m,d(x)}let A=new Set,w={tally:u,hits:A};n=w;let L=q(()=>d(x),()=>{n===w&&(n=void 0)});return C(L,m=>(m||re(u,A),m))},onAllRunsComplete:function d(s){if(!s.failed){if(u.usageError)throw u.usageError;if(!X(s)&&!Z(r,u))throw f(d,oe(r,u,s))}},afterAll:()=>{i-=1,n?.tally===u&&(n=void 0)}}};return{hit:l,plugin:T}}function X(e){return!!e.runConfiguration.path}function Y({minimum:e,count:r},t){return r*100/t>=e}function Z(e,r){let t=U(e,r),{accepted:o}=r;return o>0&&t.length===0}function ee(e){return Number.isFinite(e)&&e>=0&&e<=100}function re(e,r){e.accepted+=1;for(let t of r)e.hits.set(t,(e.hits.get(t)??0)+1)}function te(e,r){return(Math.floor(e*1e4/r)/100).toFixed(2)}function U(e,r){return[...e].map(([t,o])=>({label:t,minimum:o,count:r.hits.get(t)??0})).filter(t=>!Y(t,r.accepted))}function ne(e){return e.size===0?[c.noLabels()]:[...e].filter(([,r])=>!ee(r)).map(([r])=>c.badPercent(r))}function oe(e,r,t){let o=U(e,r),{accepted:n}=r,i=c.runSummary(t);return n===0?[c.noAccepted(),i].join(`
|
|
3
|
+
`):[c.unmetHeader(n),i,...o.map(a=>c.unmetLine(a,n))].join(`
|
|
4
|
+
`)}0&&(module.exports={coverage,element,getNext,infiniteStream,nonEmptyArray,nonEmptyUniqueArray,record});
|
|
2
5
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.8.2/node_modules/@carlwr/typescript-extra/src/extract.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.8.2/node_modules/@carlwr/typescript-extra/src/misc.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.8.2/node_modules/@carlwr/typescript-extra/src/node/node.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","/**\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 (rejections 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 * Cached, lazy evaluation.\n *\n * If {@link f} throws, the result is not cached: the next call will evaluate {@link f} again.\n *\n * This function can be thought of as the synchronous analogue to the promise-oriented {@link memoized}. Note though that the error-handling differs.\n *\n * @example\n * const getCfg = cached(() => readFileSync('config.json', 'utf8'))\n * let c0 = getCfg() // reads the file, returns result\n * let c1 = getCfg() // returns cached result\n */\nexport function cached<T>(f: () => T): () => T {\n let s: // state\n | { done: false }\n | { done: true, v: T }\n = { done: false }\n return () =>\n s.done\n ? s.v\n : ( s = { done: true, v: f() }).v\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\n/**\n * Map an async function over {@link xs} and keep only defined results.\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\n/**\n * Map an async function over {@link xs}.\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\n/**\n * Split items into those for which the async predicate resolves truthy and falsy.\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): 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\n/**\n * Marker for exhaustiveness checks in discriminated unions.\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 * 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 { rm } from 'node:fs/promises'\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","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,GEoHO,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,CA4DO,SAASC,EAAcC,EAAqC,CACjE,OAAOA,EAAG,QAAU,CACtB,CEtLA,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","l"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.13.0/node_modules/@carlwr/typescript-extra/src/extract.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.13.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","../node_modules/.pnpm/@carlwr+typescript-extra@0.13.0/node_modules/@carlwr/typescript-extra/src/maybe-async/maybe-async.ts","../src/utils.ts","../src/coverage.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'\nexport * from './coverage.js'\n","/**\n * walk `root` recursively while collecting all {@link T}s for which `pred` returns 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","/**\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 (rejections 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 * Cached, lazy, single-flight evaluation of a promise.\n *\n * A rejected promise is not cached: the next call will evaluate `f` again.\n *\n * The retrying analogue of {@link memoized}.\n */\nexport function memoizedRetry<T>(f: () => Promise<T>): () => Promise<T> {\n let p: Promise<T> | null = null\n const clear = (e: unknown): never => { p = null; throw e }\n return () => p ?? (p = f().catch(clear))\n}\n\n\n/**\n * Cached, lazy evaluation.\n *\n * If `f` throws, the result is not cached: the next call will evaluate `f` again.\n *\n * This function can be thought of as the synchronous analogue to the promise-oriented {@link memoized}. Note though that the error-handling differs.\n *\n * @example\n * const getCfg = cached(() => readFileSync('config.json', 'utf8'))\n * let c0 = getCfg() // reads the file, returns result\n * let c1 = getCfg() // returns cached result\n */\nexport function cached<T>(f: () => T): () => T {\n let s: // state\n | { done: false }\n | { done: true, v: T }\n = { done: false }\n return () =>\n s.done\n ? s.v\n : ( s = { done: true, v: f() }).v\n}\n\n\n/**\n * Cached, lazy evaluation of a single-argument function.\n *\n * The cache is keyed by argument identity (`SameValueZero`, i.e. `===` except for on `NaN`).\n *\n * If `f` throws, the result is not cached for that argument: the next call with the same argument will evaluate `f` again.\n *\n * @example\n * const getText = cachedUnary((path: string) => readFileSync(path, 'utf8'))\n * getText('a.txt') // reads a.txt, returns its content\n * getText('a.txt') // returns cached a.txt content\n * getText('B.txt') // reads B.txt, returns its content\n */\nexport function cachedUnary<K,V>(f: (k: K) => V): (k: K) => V {\n const cache = new Map<K, { v: V }>()\n return k => {\n let hit = cache.get(k)\n if (!hit) cache.set(k, hit = { v: f(k) })\n return hit.v\n }\n}\n\n\n/**\n * whether the elements of `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 * Construct a {@link NonEmpty} from a guaranteed head and zero or more tail elements.\n *\n * Useful for declaring or building a `[T,...T[]]` without an explicit type annotation or cast.\n *\n * @example\n * const xs = nonEmpty(1, 2, 3) // typed [number, ...number[]]\n */\nexport function nonEmpty<T>(head: T, ...rest: T[]): [T,...T[]] {\n return [head, ...rest]\n}\n\n/**\n * Map over a non-empty array while preserving the type as non-empty.\n *\n * The callback receives `(x, i, arr)` — same shape as `Array.prototype.map`, with `arr` narrowed to non-empty.\n */\nexport function mapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T, i: number, arr: readonly [T,...T[]]) => U\n): [U,...U[]] {\n const mapped = xs.map((x,i) => fn(x,i,xs))\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 *\n * The callback receives `(x, i, arr)` — same shape as `Array.prototype.flatMap`, with `arr` narrowed to non-empty.\n */\nexport function flatmapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T, i: number, arr: readonly [T,...T[]]) => [U,...U[]]\n): [U,...U[]] {\n const mapped = xs.flatMap((x,i) => fn(x,i,xs))\n return mapped as [U,...U[]]\n}\n\n\n/**\n * Stateful iterator yielding the elements of `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\n/**\n * Map an async function over `xs` and keep only defined results.\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\n/**\n * Map an async function over `xs`.\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\n/**\n * Split items into those for which the async predicate resolves truthy and falsy.\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 `str` does not start with `first`\n *\n * @example\n * withoutFirstSubstring('e', 'ego') // => 'go'\n */\nexport function withoutFirstSubstring(first: string, str: string): 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\n/**\n * Marker for exhaustiveness checks in discriminated unions.\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 *\n * if `K` is a known key of `T`, the narrowed property type also has `undefined` excluded (the `in` check has proved the key is set)\n */\nexport function hasKey<T,K extends PropertyKey>(\n value: T,\n key: K\n): value is T & { [P in K]: P extends keyof T ? Exclude<T[P], undefined> : unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && key in value\n )\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 * escape regex meta-characters in `s` so the result matches `s` literally when used as a `RegExp` pattern\n *\n * @example\n * new RegExp(escapeRegExp('a.b')) // matches \"a.b\" literally, not \"a<any>b\"\n */\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\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 *\n * Union types are compared as wholes, i.e. `Eq` does not distribute over them: `Eq<'a'|'b', 'a'|'b'>` is `true`, and `Eq<never, string>` is `false`.\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","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 {@linkcode fc.constantFrom}, but _shrinks across all elements_ - {@linkcode fc.constantFrom} only shrinks towards the first element.\n *\n * @example\n * import * as fcu from '@carlwr/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 {@linkcode fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@linkcode getNext} helper.\n *\n * features and non-features:\n * - does _not_ shrink at all unfortunately - since {@linkcode 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 '@carlwr/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 {@linkcode InfiniteStream} object yielded by {@linkcode 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 {@linkcode constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).\n *\n * @example\n * import * as fcu from '@carlwr/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 {@linkcode fc.record}, but with\n * - `noNullPrototype` _true_ by default\n * - stronger typing\n *\n * @example\n * import * as fcu from '@carlwr/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(k => requiredKeys.includes(k))\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 '@carlwr/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","/**\n * Helpers for values/functions that may or may not be asynchronous.\n *\n * The helpers can sometimes let code use a single implementation that handles both synchronous and asynchronous values/functions without branching at the call site. If the inputs are synchronous, the result stays synchronous, i.e. without introducing a promise.\n *\n * @module maybe-async\n */\n\n/**\n * A return value that is not a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables | thenable}.\n */\nexport type NotThenable<T> =\n unknown extends T ? unknown :\n [Extract<T, PromiseLike<unknown>>] extends [never] ? unknown :\n never\n\n/**\n * Apply a function (`f`) to a value (`value`). `value` may or may not be a promise, and `f` may or may not return a promise.\n *\n * If `value` is a promise, `f` receives the awaited `value`.\n *\n * What `andThen` returns, and how it calls `f`, is determined by what `value` is:\n *\n * | `value` | -> | `andThen` | calls `f` |\n * | :----------------- | :-: | :-------------------------- | :------------- |\n * | non-promise | -> | returns what `f` returned | synchronously |\n * | fulfilling promise | -> | returns a native promise | asynchronously |\n * | rejecting promise | -> | returns a rejecting promise | never |\n *\n * If `f` throws, then if `value` is\n * - a non-promise, the error propagates\n * - a promise, then the returned promise rejects\n *\n * If `value` is a promise, a _flattened_ (native) promise is returned.\n *\n * If a `.then` getter of `value` throws, the returned promise will be a rejection.\n *\n * In the documentation above, \"promise\" is used in the meaning of {@linkcode PromiseLike} on the type level, and an object with a {@linkcode PromiseLike.then} method at runtime. The two are typically the same but may disagree in rare cases.\n */\nexport function andThen<V, B>(\n value: V,\n f : (a: Awaited<V>) => B,\n):\n V extends PromiseLike<unknown> ? Promise<Awaited<B>> :\n PromiseLike<unknown> extends V ? B|Promise<Awaited<B>> :\n B\n\nexport function andThen<A, B>(\n value: A|PromiseLike<A>,\n f : (a: A) => B,\n): B|Promise<B> {\n const promise = toPromise(value)\n return promise\n ? promise.then(f)\n : f(value as A)\n}\n\n/**\n * Run the function `run`. Run the synchronous `cleanup` function once, regardless of whether `run` returned, threw or rejected.\n *\n * If `run` returns a promise, `cleanup` is run when that promise has settled.\n *\n * The return value of `cleanup` is always ignored.\n *\n * What `andFinally` returns, and how it calls `cleanup`, is determined by whether `run` threw, and if it didn't, what it returned:\n *\n * | `run` | -> | `andFinally` | calls `cleanup` |\n * | :--------------------- | :-: | :----------------------- | :-------------- |\n * | returned a non-promise | -> | returns or throws | synchronously |\n * | threw | -> | throws | synchronously |\n * | returned a promise | -> | returns a native promise | asynchronously |\n *\n * The outcome of `andFinally`, and what error is attached as `cause`, if any, is determined by whether `run` threw/rejected and whether `cleanup` threw:\n *\n * | `run` | `cleanup` | -> | outcome | `cause` set to |\n * | :-------- | :-------- | :-: | :---------------- | :---------------------- |\n * | succeeded | returned | -> | `run`'s value | – |\n * | failed | returned | -> | `run`'s error | – |\n * | succeeded | threw | -> | `cleanup`'s error | – |\n * | failed | threw | -> | `cleanup`'s error | `run`'s error, if unset |\n *\n * If `cleanup` throws an error and `run` failed, `cleanup`'s error object is mutated (can only matter if shared or reused).\n *\n * In the documentation above, \"promise\" is used in the meaning of {@linkcode PromiseLike} on the type level, and an object with a {@linkcode PromiseLike.then} method at runtime. The two are typically the same but may disagree in rare cases.\n */\nexport function andFinally<R, C = void>(\n run : () => R,\n cleanup: () => C & NotThenable<C>,\n): R extends PromiseLike<unknown> ? Promise<Awaited<R>> : R\n\nexport function andFinally<A>(\n run : () => A|PromiseLike<A>,\n cleanup: () => unknown,\n): A|Promise<A> {\n const onValue = (value: A): A => { cleanup(); return value }\n\n const onError = (error: unknown): never => {\n try { cleanup() }\n catch (fromCleanup) {\n throw withCause(fromCleanup, error)\n }\n throw error\n }\n\n let out: A|PromiseLike<A>\n try { out = run() }\n catch (error) {\n return onError(error)\n }\n\n const promise = toPromise(out)\n return promise\n ? promise.then(onValue, onError)\n : onValue(out as A)\n}\n\n/* Stance re. JSDoc of exported functions:\n\n- main goal: make them _comprehensible_\n - documenting all technical details would be overwhelming for a reader; instead prioritize conceptual clarity\n\n- tables + prose _as a whole_ should convey the information\n - the two are not intended to _independently_ convey everything\n\n- markdown tables should be max. 80 chars wide\n - too wide tables may make them hard to read or partially hidden in hovers; also, <80 chars keeps them readable as plaintext in an editor with soft-wrapping at 80 chars\n\n*/\n\n/* a native promise adopting `value` if it is a thenable, else undefined\n\nlike `Promise.resolve`, but `value.then` is read only once; as with `Promise.resolve`, a throwing `then` getter gives a rejecting promise\n*/\nfunction toPromise<A>(value: A|PromiseLike<A>): Promise<A>|undefined {\n const isObject =\n (typeof value === 'object' && value !== null) ||\n typeof value === 'function'\n if (!isObject) return undefined\n let then: unknown\n try { then = (value as {then?: unknown}).then }\n catch (error) {\n return Promise.reject(error)\n }\n if (typeof then !== 'function') return undefined\n return new Promise<A>((resolve, reject) => then.call(value, resolve, reject))\n}\n\nfunction withCause<E>(error: E, cause: unknown): E {\n if (\n error instanceof Error &&\n error.cause === undefined &&\n error !== cause\n )\n try { error.cause = cause }\n catch { /* e.g. a frozen error; leave it as it is */ }\n return error\n}\n","export type AnyFunction = (...args: never) => unknown\n\n/**\n * Return an error whose stack trace starts at the caller of `fromFn`.\n *\n * Using this function allows traces that point to the code that caused the error instead of pointing to library code. The `fromFn` should be the function that throws.\n *\n * This function might only trim the stack properly for the V8 runtime. For other runtimes, the stack is possibly left untouched.\n*/\nexport function callerError(fromFn: AnyFunction, message: string): Error {\n const error = new Error(message)\n Error.captureStackTrace?.(error, fromFn)\n return error\n}\n","import { andFinally, andThen } from '@carlwr/typescript-extra/maybe-async'\nimport type * as fc from 'fast-check'\nimport { callerError } from './utils.js'\n\ntype DetailsWithSkips = Pick<fc.RunDetails<unknown>, 'seed'|'numSkips'>\n\nconst MSG = {\n noLabels: () =>\n 'coverage requires at least one label',\n\n badPercent: (label: string) =>\n `coverage percentage for ${JSON.stringify(label)} must be between 0 and 100`,\n\n outsideCase: () =>\n 'coverage.hit() called outside an active test case',\n\n inGenerator: () =>\n 'coverage.hit() cannot be called from an arbitrary',\n\n noCheck: () =>\n 'coverage.hit() called, but no property check with coverage.plugin is running',\n\n duplicatePlugin: () =>\n 'coverage.plugin passed more than once to the same property check',\n\n unknownLabel: (label: string) =>\n `unknown coverage label ${JSON.stringify(label)}`,\n\n concurrentUse: () =>\n 'coverage cannot be shared by concurrent property checks',\n\n runSummary: (details: DetailsWithSkips) =>\n `{ seed: ${details.seed}, skipped: ${details.numSkips} }`,\n\n noAccepted: () =>\n 'coverage requires at least one accepted test case',\n\n unmetHeader: (accepted: number) =>\n `coverage failed after ${accepted} accepted test cases`,\n\n unmetLine: ({label, minimum, count}: Unmet, accepted: number) => {\n const haveStr = `${count}/${accepted} (${percent(count, accepted)}%)`\n const reqStr = `required >= ${minimum}%`\n return `- ${label}: ${haveStr}, ${reqStr}`\n },\n\n notRunning: (checks: number, isGenerating: boolean) => {\n if (checks === 0) return MSG.noCheck()\n if (isGenerating ) return MSG.inGenerator()\n return MSG.outsideCase()\n }\n}\n\n/* JSDoc style stance:\nDon't overwhelm the reader with details. Prefer clarity and conveying the concepts in a simple way over documenting every single truth about the behaviour.\n*/\n\n/**\n * Coverage for property tests.\n *\n * A single instance cannot be used for multiple concurrently running tests. Re-using the same instance for multiple sequential tests is fine.\n */\nexport interface Coverage<L extends string> {\n /** Register a hit for a label. */\n readonly hit: (label: L) => void\n\n /** A plugin value to pass to `fast-check`. */\n readonly plugin: <Ts>(pluginIndex: number, pluginStore: fc.PluginStore) => fc.PluginInstance<Ts>\n}\n\n/**\n * Add minimum coverage checks to a property test.\n *\n * In a test, the user first calls `coverage`, specifying the labels to use and, for each label, a minimum percentage of test cases that must \"hit\" the label for the coverage to be considered sufficient. The user then adds conditional calls to {@linkcode Coverage.hit | hit()} to the test.\n *\n * If the property test itself succeeds, the number of test cases that were run is compared to the number of hits for each label. If the specified minimum percentage was not met for at least one label, the property test still fails, with a useful message.\n *\n * For an introduction to the coverage feature, the primary resource is the {@link https://github.com/carlwr/fastcheck-utils/blob/main/test/coverage.example.test.ts | example file}.\n *\n * Calls to {@linkcode Coverage.hit | hit()} must specify one of the registered labels. This is enforced on the type level.\n *\n * If the property fails, that error takes precedence and coverage is not asserted.\n *\n * When counting the number of test cases (the denominator) and the number of hits,\n * - discarded cases are not included\n * - if a label is hit more than once, it is still only counted once\n * - _examples_ are included in the count of number of test cases (they contribute to the denominator)\n * - shrinking runs are not included (which does not matter, since in the case of the property failing, coverage is not checked anyways)\n *\n * If there are no accepted test cases in a test, the coverage check will result in a coverage failure.\n *\n * It is strongly recommended to use a fixed seed for tests that include coverage checks.\n *\n * If a property is _replayed_, the coverage test will be ignored (since it isn't meaningful in replays).\n *\n * {@linkcode Coverage.hit | hit()} can only be called from a test - not from within an arbitrary or {@linkcode fc.beforeEach}. If you want to use coverage to assert on the distribution of an arbitrary, it is suggested to write a dedicated property test.\n *\n * If you use the `fc.ignoreEqualValues()` plugin: if used it must come before this coverage plugin, e.g. `[fc.ignoreEqualValues(), myCoverage.plugin]`.\n *\n * @param requirements A record where the user specifies labels as keys and required hit percentages as values\n * @returns An object with the {@linkcode Coverage.hit | hit()} function for the user to call, and the {@linkcode Coverage.plugin | plugin} value to pass to something that accepts a `fast-check` plugin, e.g. `fast-check`'s {@linkcode fc.assert}/{@linkcode fc.check}, or `@fast-check/vitest`'s `it.prop`/`test.prop`. The plugin will be ignored if used with {@linkcode fc.sample} or {@linkcode fc.statistics}. Passing it to {@linkcode fc.check} will result in coverage failures to throw, rather than report the failure. Passing it to {@linkcode fc.installGlobalPlugin} does not make sense since that would mean the same requirements would be applied to all checks.\n */\nexport function coverage<K extends string>(\n requirements: Readonly<Record<K, number>>\n): Coverage<K> {\n const minimums = new Map(Object.entries<number>(requirements))\n\n const bad = badRequirements(minimums)\n if (bad.length > 0)\n throw callerError(coverage, bad.join('\\n'))\n\n const storeKey = Symbol('coverage')\n let running: RunningCase|undefined\n let checks = 0\n let generating = false\n\n function hit(label: string): void {\n if (!running)\n throw callerError(hit, MSG.notRunning(checks, generating))\n if (minimums.has(label))\n running.hits.add(label)\n else\n running.tally.usageError ??= callerError(hit, MSG.unknownLabel(label))\n }\n\n const plugin: Coverage<K>['plugin'] = (_, store) => {\n if (store.get(storeKey))\n throw callerError(plugin, MSG.duplicatePlugin())\n store.set(storeKey, true)\n checks += 1\n const tally: Tally = {\n accepted : 0,\n hits : new Map(),\n usageError: undefined\n }\n\n return {\n decorateGenerate: nestedGenerate => (...args) => {\n generating = true\n try { return nestedGenerate(...args) }\n finally {\n generating = false\n }\n },\n\n decorateRun: nestedRun => function coveredRun(value) {\n /* same tally: a previous run was abandoned by an outer plugin (e.g. fc.timeout) */\n if (running && running.tally !== tally) {\n const error = callerError(coveredRun, MSG.concurrentUse())\n running.tally.usageError ??= error\n tally.usageError ??= error\n return nestedRun(value)\n }\n const hits = new Set<string>()\n const current = {tally, hits}\n running = current\n const outcome = andFinally(\n () => nestedRun(value),\n () => { if (running === current) running = undefined },\n )\n const f = <T>(result: T) => {\n if (!result)\n countCase(tally, hits)\n return result\n }\n return andThen(outcome, f)\n },\n\n /* throws also for fc.check: plugins cannot mark a run as failed */\n onAllRunsComplete: function checkCoverage(details) {\n if (details.failed ) return\n if (tally.usageError ) throw tally.usageError\n if (isReplay(details)) return // coverage not meaningful\n\n if (!isCovered(minimums, tally))\n throw callerError(checkCoverage, report_(minimums, tally, details))\n },\n\n /* a run abandoned by an outer plugin (e.g. fc.timeout) may still be pending */\n afterAll: () => {\n checks -= 1\n if (running?.tally === tally)\n running = undefined\n },\n }\n }\n\n return {hit, plugin}\n}\n\nfunction isReplay<Ts>(details: fc.RunDetails<Ts>): boolean {\n return !!details.runConfiguration.path\n}\n\ninterface Tally {\n accepted : number\n hits : Map<string, number>\n usageError: Error|undefined\n}\n\ninterface RunningCase {\n tally: Tally\n hits : Set<string>\n}\n\ninterface Unmet {\n label : string\n minimum: number\n count : number\n}\n\ntype Minimums = ReadonlyMap<string, number>\n\nfunction isMet({minimum, count}: Unmet, accepted: number) {\n return count * 100 / accepted >= minimum\n}\n\nfunction isCovered(minimums: Minimums, tally: Tally) {\n const unmet = unmetLabels(minimums, tally)\n const {accepted} = tally\n return accepted > 0 && unmet.length === 0\n}\n\nfunction isPercentage(minimum: number) {\n return Number.isFinite(minimum) && minimum >= 0 && minimum <= 100\n}\n\nfunction countCase(tally: Tally, hits: ReadonlySet<string>): void {\n tally.accepted += 1\n for (const label of hits)\n tally.hits.set(label, (tally.hits.get(label) ?? 0) + 1)\n}\n\nfunction percent(part: number, whole: number): string {\n return (Math.floor(part * 10000 / whole) / 100).toFixed(2)\n}\n\nfunction unmetLabels(minimums: Minimums, tally: Tally): Unmet[] {\n return [...minimums]\n .map(([label, minimum]) => ({\n label,\n minimum,\n count: tally.hits.get(label) ?? 0,\n }))\n .filter(u => !isMet(u, tally.accepted))\n}\n\nfunction badRequirements(minimums: Minimums): string[] {\n if (minimums.size === 0)\n return [MSG.noLabels()]\n return [...minimums]\n .filter(([, minimum]) => !isPercentage(minimum))\n .map(([label]) => MSG.badPercent(label))\n}\n\nfunction report_(\n minimums: Minimums,\n tally : Tally,\n details: DetailsWithSkips,\n): string {\n const unmet = unmetLabels(minimums, tally)\n const {accepted} = tally\n const summary = MSG.runSummary(details)\n if (accepted === 0)\n return [MSG.noAccepted(), summary].join('\\n')\n\n return [\n MSG.unmetHeader(accepted),\n summary,\n ...unmet.map(u => MSG.unmetLine(u, accepted)),\n ].join('\\n')\n}\n"],"mappings":"0jBAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,cAAAE,EAAA,YAAAC,EAAA,YAAAC,EAAA,mBAAAC,EAAA,kBAAAC,EAAA,wBAAAC,EAAA,WAAAC,IAAA,eAAAC,EAAAT,IEwKO,SAASU,EAAaC,EAA0BC,EAAc,CACnE,GAAIA,EAAE,GAAKA,GAAGD,EAAG,OACf,MAAM,IAAI,MAAM,SAASC,CAAC,sCAAsCD,EAAG,MAAM,EAAE,EAE7E,OAAOA,EAAGC,CAAC,CACb,CA4DO,SAASC,EAAcC,EAAqC,CACjE,OAAOA,EAAG,QAAU,CACtB,CC1OA,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,MAAMG,GAAKD,EAAa,SAASC,CAAC,CAAC,CAE/C,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,CCkBO,SAASC,EACdC,EACAC,EACc,CACd,IAAMC,EAAUC,EAAUH,CAAK,EAC/B,OAAOE,EACHA,EAAQ,KAAKD,CAAC,EACdA,EAAED,CAAU,CAClB,CAmCO,SAASI,EACdC,EACAC,EACc,CACd,IAAMC,EAAWP,IAAkBM,EAAQ,EAAUN,GAE/CQ,EAAWC,GAA0B,CACzC,GAAI,CAAEH,EAAQ,CAAE,OACTI,EAAa,CAClB,MAAMC,EAAUD,EAAaD,CAAK,CACpC,CACA,MAAMA,CACR,EAEIG,EACJ,GAAI,CAAEA,EAAMP,EAAI,CAAE,OACXI,EAAO,CACZ,OAAOD,EAAQC,CAAK,CACtB,CAEA,IAAMP,EAAUC,EAAUS,CAAG,EAC7B,OAAOV,EACHA,EAAQ,KAAKK,EAASC,CAAO,EAC7BD,EAAQK,CAAQ,CACtB,CAmBA,SAAST,EAAaH,EAA+C,CAInE,GAAI,EAFD,OAAOA,GAAU,UAAYA,IAAU,MACvC,OAAOA,GAAU,YACL,OACf,IAAIa,EACJ,GAAI,CAAEA,EAAQb,EAA2B,IAAK,OACvCS,EAAO,CACZ,OAAO,QAAQ,OAAOA,CAAK,CAC7B,CACA,GAAI,OAAOI,GAAS,WACpB,OAAO,IAAI,QAAW,CAACC,EAASC,IAAWF,EAAK,KAAKb,EAAOc,EAASC,CAAM,CAAC,CAC9E,CAEA,SAASJ,EAAaF,EAAUO,EAAmB,CACjD,GACEP,aAAiB,OACjBA,EAAM,QAAU,QAChBA,IAAUO,EAEV,GAAI,CAAEP,EAAM,MAAQO,CAAM,MACpB,CAA+C,CACvD,OAAOP,CACT,CCnJO,SAASQ,EAAYC,EAAqBC,EAAwB,CACvE,IAAMC,EAAQ,IAAI,MAAMD,CAAO,EAC/B,aAAM,oBAAoBC,EAAOF,CAAM,EAChCE,CACT,CCPA,IAAMC,EAAM,CACV,SAAU,IACR,uCAEF,WAAaC,GACX,2BAA2B,KAAK,UAAUA,CAAK,CAAC,6BAElD,YAAa,IACX,oDAEF,YAAa,IACX,oDAEF,QAAS,IACP,+EAEF,gBAAiB,IACf,mEAEF,aAAeA,GACb,0BAA0B,KAAK,UAAUA,CAAK,CAAC,GAEjD,cAAe,IACb,0DAEF,WAAaC,GACX,WAAWA,EAAQ,IAAI,cAAcA,EAAQ,QAAQ,KAEvD,WAAY,IACV,oDAEF,YAAcC,GACZ,yBAAyBA,CAAQ,uBAEnC,UAAW,CAAC,CAAC,MAAAF,EAAO,QAAAG,EAAS,MAAAC,CAAK,EAAUF,IAAqB,CAC/D,IAAMG,EAAU,GAAGD,CAAK,IAAIF,CAAQ,KAAKI,GAAQF,EAAOF,CAAQ,CAAC,KAC3DK,EAAS,eAAeJ,CAAO,IACrC,MAAO,KAAKH,CAAK,KAAKK,CAAO,KAAKE,CAAM,EAC1C,EAEA,WAAY,CAACC,EAAgBC,IACvBD,IAAW,EAAUT,EAAI,QAAQ,EACjCU,EAAuBV,EAAI,YAAY,EACpCA,EAAI,YAAY,CAE3B,EAmDO,SAASW,EACdC,EACa,CACb,IAAMC,EAAW,IAAI,IAAI,OAAO,QAAgBD,CAAY,CAAC,EAEvDE,EAAMC,GAAgBF,CAAQ,EACpC,GAAIC,EAAI,OAAS,EACf,MAAME,EAAYL,EAAUG,EAAI,KAAK;AAAA,CAAI,CAAC,EAE5C,IAAMG,EAAW,OAAO,UAAU,EAC9BC,EACAT,EAAS,EACTU,EAAa,GAEjB,SAASC,EAAInB,EAAqB,CAChC,GAAI,CAACiB,EACH,MAAMF,EAAYI,EAAKpB,EAAI,WAAWS,EAAQU,CAAU,CAAC,EACvDN,EAAS,IAAIZ,CAAK,EACpBiB,EAAQ,KAAK,IAAIjB,CAAK,EAEtBiB,EAAQ,MAAM,aAAeF,EAAYI,EAAKpB,EAAI,aAAaC,CAAK,CAAC,CACzE,CAEA,IAAMoB,EAAgC,CAACC,EAAGC,IAAU,CAClD,GAAIA,EAAM,IAAIN,CAAQ,EACpB,MAAMD,EAAYK,EAAQrB,EAAI,gBAAgB,CAAC,EACjDuB,EAAM,IAAIN,EAAU,EAAI,EACxBR,GAAU,EACV,IAAMe,EAAe,CACnB,SAAY,EACZ,KAAY,IAAI,IAChB,WAAY,MACd,EAEA,MAAO,CACL,iBAAkBC,GAAkB,IAAIC,IAAS,CAC/CP,EAAa,GACb,GAAI,CAAE,OAAOM,EAAe,GAAGC,CAAI,CAAE,QACrC,CACEP,EAAa,EACf,CACF,EAEA,YAAaQ,GAAa,SAASC,EAAWC,EAAO,CAEnD,GAAIX,GAAWA,EAAQ,QAAUM,EAAO,CACtC,IAAMM,EAAQd,EAAYY,EAAY5B,EAAI,cAAc,CAAC,EACzD,OAAAkB,EAAQ,MAAM,aAAeY,EAC7BN,EAAM,aAAuBM,EACtBH,EAAUE,CAAK,CACxB,CACA,IAAME,EAAO,IAAI,IACXC,EAAU,CAAC,MAAAR,EAAO,KAAAO,CAAI,EAC5Bb,EAAUc,EACV,IAAMC,EAAUC,EACd,IAAMP,EAAUE,CAAK,EACrB,IAAM,CAAMX,IAAYc,IAASd,EAAU,OAAU,CACvD,EAMA,OAAOiB,EAAQF,EALDG,IACPA,GACHC,GAAUb,EAAOO,CAAI,EAChBK,EAEgB,CAC3B,EAGA,kBAAmB,SAASE,EAAcpC,EAAS,CACjD,GAAI,CAAAA,EAAQ,OACZ,IAAIsB,EAAM,WAAa,MAAMA,EAAM,WACnC,GAAI,CAAAe,EAASrC,CAAO,GAEhB,CAACsC,EAAU3B,EAAUW,CAAK,EAC5B,MAAMR,EAAYsB,EAAeG,GAAQ5B,EAAUW,EAAOtB,CAAO,CAAC,EACtE,EAGA,SAAU,IAAM,CACdO,GAAU,EACNS,GAAS,QAAUM,IACrBN,EAAU,OACd,CACF,CACF,EAEA,MAAO,CAAC,IAAAE,EAAK,OAAAC,CAAM,CACrB,CAEA,SAASkB,EAAarC,EAAqC,CACzD,MAAO,CAAC,CAACA,EAAQ,iBAAiB,IACpC,CAqBA,SAASwC,EAAM,CAAC,QAAAtC,EAAS,MAAAC,CAAK,EAAUF,EAAkB,CACxD,OAAOE,EAAQ,IAAMF,GAAYC,CACnC,CAEA,SAASoC,EAAU3B,EAAoBW,EAAc,CACnD,IAAMmB,EAAQC,EAAY/B,EAAUW,CAAK,EACnC,CAAC,SAAArB,CAAQ,EAAIqB,EACnB,OAAOrB,EAAW,GAAKwC,EAAM,SAAW,CAC1C,CAEA,SAASE,GAAazC,EAAiB,CACrC,OAAO,OAAO,SAASA,CAAO,GAAKA,GAAW,GAAKA,GAAW,GAChE,CAEA,SAASiC,GAAUb,EAAcO,EAAiC,CAChEP,EAAM,UAAY,EAClB,QAAWvB,KAAS8B,EAClBP,EAAM,KAAK,IAAIvB,GAAQuB,EAAM,KAAK,IAAIvB,CAAK,GAAK,GAAK,CAAC,CAC1D,CAEA,SAASM,GAAQuC,EAAcC,EAAuB,CACpD,OAAQ,KAAK,MAAMD,EAAO,IAAQC,CAAK,EAAI,KAAK,QAAQ,CAAC,CAC3D,CAEA,SAASH,EAAY/B,EAAoBW,EAAuB,CAC9D,MAAO,CAAC,GAAGX,CAAQ,EAChB,IAAI,CAAC,CAACZ,EAAOG,CAAO,KAAO,CAC1B,MAAAH,EACA,QAAAG,EACA,MAAOoB,EAAM,KAAK,IAAIvB,CAAK,GAAK,CAClC,EAAE,EACD,OAAO+C,GAAK,CAACN,EAAMM,EAAGxB,EAAM,QAAQ,CAAC,CAC1C,CAEA,SAAST,GAAgBF,EAA8B,CACrD,OAAIA,EAAS,OAAS,EACb,CAACb,EAAI,SAAS,CAAC,EACjB,CAAC,GAAGa,CAAQ,EAChB,OAAO,CAAC,CAAC,CAAET,CAAO,IAAM,CAACyC,GAAazC,CAAO,CAAC,EAC9C,IAAI,CAAC,CAACH,CAAK,IAAMD,EAAI,WAAWC,CAAK,CAAC,CAC3C,CAEA,SAASwC,GACP5B,EACAW,EACAtB,EACQ,CACR,IAAMyC,EAAQC,EAAY/B,EAAUW,CAAK,EACnC,CAAC,SAAArB,CAAQ,EAAIqB,EACbyB,EAAUjD,EAAI,WAAWE,CAAO,EACtC,OAAIC,IAAa,EACR,CAACH,EAAI,WAAW,EAAGiD,CAAO,EAAE,KAAK;AAAA,CAAI,EAEvC,CACLjD,EAAI,YAAYG,CAAQ,EACxB8C,EACA,GAAGN,EAAM,IAAIK,GAAKhD,EAAI,UAAUgD,EAAG7C,CAAQ,CAAC,CAC9C,EAAE,KAAK;AAAA,CAAI,CACb","names":["src_exports","__export","coverage","element","getNext","infiniteStream","nonEmptyArray","nonEmptyUniqueArray","record","__toCommonJS","safeIndex","xs","i","isNonEmpty","xs","fc","element","xs","i","P","fc","infiniteStream","arb","getNext","stream","value","done","fc","nonEmptyArray","arb","constraints","base","constr","fc","record","model","constr","nnp","constr_","arb","allKeys","isAllKeysRequired","requiredKeys","k","fc","nonEmptyUniqueArray","arb","constraints","base","constr","l","andThen","value","f","promise","toPromise","andFinally","run","cleanup","onValue","onError","error","fromCleanup","withCause","out","then","resolve","reject","cause","callerError","fromFn","message","error","MSG","label","details","accepted","minimum","count","haveStr","percent","reqStr","checks","isGenerating","coverage","requirements","minimums","bad","badRequirements","callerError","storeKey","running","generating","hit","plugin","_","store","tally","nestedGenerate","args","nestedRun","coveredRun","value","error","hits","current","outcome","d","A","result","countCase","checkCoverage","isReplay","isCovered","report_","isMet","unmet","unmetLabels","isPercentage","part","whole","u","summary"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -5,10 +5,10 @@ import * as fc from 'fast-check';
|
|
|
5
5
|
*
|
|
6
6
|
* Shrinking is done towards the first element.
|
|
7
7
|
*
|
|
8
|
-
* Similar to {@
|
|
8
|
+
* Similar to {@linkcode fc.constantFrom}, but _shrinks across all elements_ - {@linkcode fc.constantFrom} only shrinks towards the first element.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
|
-
* import * as fcu from 'fastcheck-utils'
|
|
11
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
12
12
|
* import * as fc from 'fast-check'
|
|
13
13
|
*
|
|
14
14
|
* const arb = fcu.element(['a', 'b', 'c'] as const)
|
|
@@ -22,14 +22,14 @@ interface InfiniteStream<T> extends fc.Stream<T> {
|
|
|
22
22
|
/**
|
|
23
23
|
* Generate an infinite stream of values.
|
|
24
24
|
*
|
|
25
|
-
* This arbitrary is a minimal wrapper around {@
|
|
25
|
+
* This arbitrary is a minimal wrapper around {@linkcode fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@linkcode getNext} helper.
|
|
26
26
|
*
|
|
27
27
|
* features and non-features:
|
|
28
|
-
* - does _not_ shrink at all unfortunately - since {@
|
|
28
|
+
* - does _not_ shrink at all unfortunately - since {@linkcode fc.infiniteStream} doesn't
|
|
29
29
|
* - _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
|
-
* import * as fcu from 'fastcheck-utils'
|
|
32
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
33
33
|
* import * as fc from 'fast-check'
|
|
34
34
|
*
|
|
35
35
|
* const arb = fcu.infiniteStream(fc.nat({max:10}))
|
|
@@ -39,7 +39,7 @@ interface InfiniteStream<T> extends fc.Stream<T> {
|
|
|
39
39
|
*/
|
|
40
40
|
declare function infiniteStream<T>(arb: fc.Arbitrary<T>): fc.Arbitrary<InfiniteStream<T>>;
|
|
41
41
|
/**
|
|
42
|
-
* Get next value from an {@
|
|
42
|
+
* Get next value from an {@linkcode InfiniteStream} object yielded by {@linkcode infiniteStream}.
|
|
43
43
|
*
|
|
44
44
|
* Throws if the stream is unexpectedly done (it is my understanding that this should never happen).
|
|
45
45
|
*/
|
|
@@ -48,10 +48,10 @@ declare function getNext<T>(stream: InfiniteStream<T>): T;
|
|
|
48
48
|
/**
|
|
49
49
|
* Generate a non-empty array.
|
|
50
50
|
*
|
|
51
|
-
* If a {@
|
|
51
|
+
* If a {@linkcode constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).
|
|
52
52
|
*
|
|
53
53
|
* @example
|
|
54
|
-
* import * as fcu from 'fastcheck-utils'
|
|
54
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
55
55
|
* import * as fc from 'fast-check'
|
|
56
56
|
*
|
|
57
57
|
* const arb = fcu.nonEmptyArray(fc.nat({max:5}))
|
|
@@ -61,12 +61,12 @@ declare function getNext<T>(stream: InfiniteStream<T>): T;
|
|
|
61
61
|
declare function nonEmptyArray<T>(arb: fc.Arbitrary<T>, constraints?: fc.ArrayConstraints): fc.Arbitrary<[T, ...T[]]>;
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* like {@
|
|
64
|
+
* like {@linkcode fc.record}, but with
|
|
65
65
|
* - `noNullPrototype` _true_ by default
|
|
66
66
|
* - stronger typing
|
|
67
67
|
*
|
|
68
68
|
* @example
|
|
69
|
-
* import * as fcu from 'fastcheck-utils'
|
|
69
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
70
70
|
* import * as fc from 'fast-check'
|
|
71
71
|
*
|
|
72
72
|
* const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})
|
|
@@ -93,7 +93,7 @@ type SomeKeysRequired<T, K extends keyof T> = fc.RecordConstraints<K> & {
|
|
|
93
93
|
* Generate a non-empty array of unique values.
|
|
94
94
|
*
|
|
95
95
|
* @example
|
|
96
|
-
* import * as fcu from 'fastcheck-utils'
|
|
96
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
97
97
|
* import * as fc from 'fast-check'
|
|
98
98
|
*
|
|
99
99
|
* const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))
|
|
@@ -102,4 +102,49 @@ type SomeKeysRequired<T, K extends keyof T> = fc.RecordConstraints<K> & {
|
|
|
102
102
|
*/
|
|
103
103
|
declare function nonEmptyUniqueArray<T, U = T>(arb: fc.Arbitrary<T>, constraints?: fc.UniqueArrayConstraints<T, U>): fc.Arbitrary<[T, ...T[]]>;
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Coverage for property tests.
|
|
107
|
+
*
|
|
108
|
+
* A single instance cannot be used for multiple concurrently running tests. Re-using the same instance for multiple sequential tests is fine.
|
|
109
|
+
*/
|
|
110
|
+
interface Coverage<L extends string> {
|
|
111
|
+
/** Register a hit for a label. */
|
|
112
|
+
readonly hit: (label: L) => void;
|
|
113
|
+
/** A plugin value to pass to `fast-check`. */
|
|
114
|
+
readonly plugin: <Ts>(pluginIndex: number, pluginStore: fc.PluginStore) => fc.PluginInstance<Ts>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Add minimum coverage checks to a property test.
|
|
118
|
+
*
|
|
119
|
+
* In a test, the user first calls `coverage`, specifying the labels to use and, for each label, a minimum percentage of test cases that must "hit" the label for the coverage to be considered sufficient. The user then adds conditional calls to {@linkcode Coverage.hit | hit()} to the test.
|
|
120
|
+
*
|
|
121
|
+
* If the property test itself succeeds, the number of test cases that were run is compared to the number of hits for each label. If the specified minimum percentage was not met for at least one label, the property test still fails, with a useful message.
|
|
122
|
+
*
|
|
123
|
+
* For an introduction to the coverage feature, the primary resource is the {@link https://github.com/carlwr/fastcheck-utils/blob/main/test/coverage.example.test.ts | example file}.
|
|
124
|
+
*
|
|
125
|
+
* Calls to {@linkcode Coverage.hit | hit()} must specify one of the registered labels. This is enforced on the type level.
|
|
126
|
+
*
|
|
127
|
+
* If the property fails, that error takes precedence and coverage is not asserted.
|
|
128
|
+
*
|
|
129
|
+
* When counting the number of test cases (the denominator) and the number of hits,
|
|
130
|
+
* - discarded cases are not included
|
|
131
|
+
* - if a label is hit more than once, it is still only counted once
|
|
132
|
+
* - _examples_ are included in the count of number of test cases (they contribute to the denominator)
|
|
133
|
+
* - shrinking runs are not included (which does not matter, since in the case of the property failing, coverage is not checked anyways)
|
|
134
|
+
*
|
|
135
|
+
* If there are no accepted test cases in a test, the coverage check will result in a coverage failure.
|
|
136
|
+
*
|
|
137
|
+
* It is strongly recommended to use a fixed seed for tests that include coverage checks.
|
|
138
|
+
*
|
|
139
|
+
* If a property is _replayed_, the coverage test will be ignored (since it isn't meaningful in replays).
|
|
140
|
+
*
|
|
141
|
+
* {@linkcode Coverage.hit | hit()} can only be called from a test - not from within an arbitrary or {@linkcode fc.beforeEach}. If you want to use coverage to assert on the distribution of an arbitrary, it is suggested to write a dedicated property test.
|
|
142
|
+
*
|
|
143
|
+
* If you use the `fc.ignoreEqualValues()` plugin: if used it must come before this coverage plugin, e.g. `[fc.ignoreEqualValues(), myCoverage.plugin]`.
|
|
144
|
+
*
|
|
145
|
+
* @param requirements A record where the user specifies labels as keys and required hit percentages as values
|
|
146
|
+
* @returns An object with the {@linkcode Coverage.hit | hit()} function for the user to call, and the {@linkcode Coverage.plugin | plugin} value to pass to something that accepts a `fast-check` plugin, e.g. `fast-check`'s {@linkcode fc.assert}/{@linkcode fc.check}, or `@fast-check/vitest`'s `it.prop`/`test.prop`. The plugin will be ignored if used with {@linkcode fc.sample} or {@linkcode fc.statistics}. Passing it to {@linkcode fc.check} will result in coverage failures to throw, rather than report the failure. Passing it to {@linkcode fc.installGlobalPlugin} does not make sense since that would mean the same requirements would be applied to all checks.
|
|
147
|
+
*/
|
|
148
|
+
declare function coverage<K extends string>(requirements: Readonly<Record<K, number>>): Coverage<K>;
|
|
149
|
+
|
|
150
|
+
export { type Coverage, type InfiniteStream, coverage, element, getNext, infiniteStream, nonEmptyArray, nonEmptyUniqueArray, record };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ import * as fc from 'fast-check';
|
|
|
5
5
|
*
|
|
6
6
|
* Shrinking is done towards the first element.
|
|
7
7
|
*
|
|
8
|
-
* Similar to {@
|
|
8
|
+
* Similar to {@linkcode fc.constantFrom}, but _shrinks across all elements_ - {@linkcode fc.constantFrom} only shrinks towards the first element.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
|
-
* import * as fcu from 'fastcheck-utils'
|
|
11
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
12
12
|
* import * as fc from 'fast-check'
|
|
13
13
|
*
|
|
14
14
|
* const arb = fcu.element(['a', 'b', 'c'] as const)
|
|
@@ -22,14 +22,14 @@ interface InfiniteStream<T> extends fc.Stream<T> {
|
|
|
22
22
|
/**
|
|
23
23
|
* Generate an infinite stream of values.
|
|
24
24
|
*
|
|
25
|
-
* This arbitrary is a minimal wrapper around {@
|
|
25
|
+
* This arbitrary is a minimal wrapper around {@linkcode fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@linkcode getNext} helper.
|
|
26
26
|
*
|
|
27
27
|
* features and non-features:
|
|
28
|
-
* - does _not_ shrink at all unfortunately - since {@
|
|
28
|
+
* - does _not_ shrink at all unfortunately - since {@linkcode fc.infiniteStream} doesn't
|
|
29
29
|
* - _does_ print a meaningful counterexample and execution summary on failure, that includes some of the previously tried values in the stream
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
|
-
* import * as fcu from 'fastcheck-utils'
|
|
32
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
33
33
|
* import * as fc from 'fast-check'
|
|
34
34
|
*
|
|
35
35
|
* const arb = fcu.infiniteStream(fc.nat({max:10}))
|
|
@@ -39,7 +39,7 @@ interface InfiniteStream<T> extends fc.Stream<T> {
|
|
|
39
39
|
*/
|
|
40
40
|
declare function infiniteStream<T>(arb: fc.Arbitrary<T>): fc.Arbitrary<InfiniteStream<T>>;
|
|
41
41
|
/**
|
|
42
|
-
* Get next value from an {@
|
|
42
|
+
* Get next value from an {@linkcode InfiniteStream} object yielded by {@linkcode infiniteStream}.
|
|
43
43
|
*
|
|
44
44
|
* Throws if the stream is unexpectedly done (it is my understanding that this should never happen).
|
|
45
45
|
*/
|
|
@@ -48,10 +48,10 @@ declare function getNext<T>(stream: InfiniteStream<T>): T;
|
|
|
48
48
|
/**
|
|
49
49
|
* Generate a non-empty array.
|
|
50
50
|
*
|
|
51
|
-
* If a {@
|
|
51
|
+
* If a {@linkcode constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).
|
|
52
52
|
*
|
|
53
53
|
* @example
|
|
54
|
-
* import * as fcu from 'fastcheck-utils'
|
|
54
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
55
55
|
* import * as fc from 'fast-check'
|
|
56
56
|
*
|
|
57
57
|
* const arb = fcu.nonEmptyArray(fc.nat({max:5}))
|
|
@@ -61,12 +61,12 @@ declare function getNext<T>(stream: InfiniteStream<T>): T;
|
|
|
61
61
|
declare function nonEmptyArray<T>(arb: fc.Arbitrary<T>, constraints?: fc.ArrayConstraints): fc.Arbitrary<[T, ...T[]]>;
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* like {@
|
|
64
|
+
* like {@linkcode fc.record}, but with
|
|
65
65
|
* - `noNullPrototype` _true_ by default
|
|
66
66
|
* - stronger typing
|
|
67
67
|
*
|
|
68
68
|
* @example
|
|
69
|
-
* import * as fcu from 'fastcheck-utils'
|
|
69
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
70
70
|
* import * as fc from 'fast-check'
|
|
71
71
|
*
|
|
72
72
|
* const arb = fcu.record({name: fc.string(), age: fc.nat({max: 100})})
|
|
@@ -93,7 +93,7 @@ type SomeKeysRequired<T, K extends keyof T> = fc.RecordConstraints<K> & {
|
|
|
93
93
|
* Generate a non-empty array of unique values.
|
|
94
94
|
*
|
|
95
95
|
* @example
|
|
96
|
-
* import * as fcu from 'fastcheck-utils'
|
|
96
|
+
* import * as fcu from '@carlwr/fastcheck-utils'
|
|
97
97
|
* import * as fc from 'fast-check'
|
|
98
98
|
*
|
|
99
99
|
* const arb = fcu.nonEmptyUniqueArray(fc.nat({max:10}))
|
|
@@ -102,4 +102,49 @@ type SomeKeysRequired<T, K extends keyof T> = fc.RecordConstraints<K> & {
|
|
|
102
102
|
*/
|
|
103
103
|
declare function nonEmptyUniqueArray<T, U = T>(arb: fc.Arbitrary<T>, constraints?: fc.UniqueArrayConstraints<T, U>): fc.Arbitrary<[T, ...T[]]>;
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Coverage for property tests.
|
|
107
|
+
*
|
|
108
|
+
* A single instance cannot be used for multiple concurrently running tests. Re-using the same instance for multiple sequential tests is fine.
|
|
109
|
+
*/
|
|
110
|
+
interface Coverage<L extends string> {
|
|
111
|
+
/** Register a hit for a label. */
|
|
112
|
+
readonly hit: (label: L) => void;
|
|
113
|
+
/** A plugin value to pass to `fast-check`. */
|
|
114
|
+
readonly plugin: <Ts>(pluginIndex: number, pluginStore: fc.PluginStore) => fc.PluginInstance<Ts>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Add minimum coverage checks to a property test.
|
|
118
|
+
*
|
|
119
|
+
* In a test, the user first calls `coverage`, specifying the labels to use and, for each label, a minimum percentage of test cases that must "hit" the label for the coverage to be considered sufficient. The user then adds conditional calls to {@linkcode Coverage.hit | hit()} to the test.
|
|
120
|
+
*
|
|
121
|
+
* If the property test itself succeeds, the number of test cases that were run is compared to the number of hits for each label. If the specified minimum percentage was not met for at least one label, the property test still fails, with a useful message.
|
|
122
|
+
*
|
|
123
|
+
* For an introduction to the coverage feature, the primary resource is the {@link https://github.com/carlwr/fastcheck-utils/blob/main/test/coverage.example.test.ts | example file}.
|
|
124
|
+
*
|
|
125
|
+
* Calls to {@linkcode Coverage.hit | hit()} must specify one of the registered labels. This is enforced on the type level.
|
|
126
|
+
*
|
|
127
|
+
* If the property fails, that error takes precedence and coverage is not asserted.
|
|
128
|
+
*
|
|
129
|
+
* When counting the number of test cases (the denominator) and the number of hits,
|
|
130
|
+
* - discarded cases are not included
|
|
131
|
+
* - if a label is hit more than once, it is still only counted once
|
|
132
|
+
* - _examples_ are included in the count of number of test cases (they contribute to the denominator)
|
|
133
|
+
* - shrinking runs are not included (which does not matter, since in the case of the property failing, coverage is not checked anyways)
|
|
134
|
+
*
|
|
135
|
+
* If there are no accepted test cases in a test, the coverage check will result in a coverage failure.
|
|
136
|
+
*
|
|
137
|
+
* It is strongly recommended to use a fixed seed for tests that include coverage checks.
|
|
138
|
+
*
|
|
139
|
+
* If a property is _replayed_, the coverage test will be ignored (since it isn't meaningful in replays).
|
|
140
|
+
*
|
|
141
|
+
* {@linkcode Coverage.hit | hit()} can only be called from a test - not from within an arbitrary or {@linkcode fc.beforeEach}. If you want to use coverage to assert on the distribution of an arbitrary, it is suggested to write a dedicated property test.
|
|
142
|
+
*
|
|
143
|
+
* If you use the `fc.ignoreEqualValues()` plugin: if used it must come before this coverage plugin, e.g. `[fc.ignoreEqualValues(), myCoverage.plugin]`.
|
|
144
|
+
*
|
|
145
|
+
* @param requirements A record where the user specifies labels as keys and required hit percentages as values
|
|
146
|
+
* @returns An object with the {@linkcode Coverage.hit | hit()} function for the user to call, and the {@linkcode Coverage.plugin | plugin} value to pass to something that accepts a `fast-check` plugin, e.g. `fast-check`'s {@linkcode fc.assert}/{@linkcode fc.check}, or `@fast-check/vitest`'s `it.prop`/`test.prop`. The plugin will be ignored if used with {@linkcode fc.sample} or {@linkcode fc.statistics}. Passing it to {@linkcode fc.check} will result in coverage failures to throw, rather than report the failure. Passing it to {@linkcode fc.installGlobalPlugin} does not make sense since that would mean the same requirements would be applied to all checks.
|
|
147
|
+
*/
|
|
148
|
+
declare function coverage<K extends string>(requirements: Readonly<Record<K, number>>): Coverage<K>;
|
|
149
|
+
|
|
150
|
+
export { type Coverage, type InfiniteStream, coverage, element, getNext, infiniteStream, nonEmptyArray, nonEmptyUniqueArray, record };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
function
|
|
1
|
+
function A(e,r){if(r<0||r>=e.length)throw new Error(`index ${r} out of bounds for array of length ${e.length}`);return e[r]}function w(e){return e.length>=1}import*as v from"fast-check";function W(e){return v.nat({max:e.length-1}).map(t=>A(e,t))}import*as E from"fast-check";function J(e){return E.infiniteStream(e)}function B(e){let{value:r,done:t}=e.next();if(t)throw new Error("infinite stream unexpectedly done");return r}import*as K from"fast-check";function X(e,r){if(r?.minLength===0)throw new Error("minLength cannot be 0 for non-empty array");let t=r??{minLength:1},o=t.minLength!==void 0?t:{...t,minLength:1};return K.array(e,o)}import*as p from"fast-check";function Z(e,r){let t=r?.noNullPrototype??!0;if(!r?.requiredKeys)return p.record(e,{noNullPrototype:t});let o={noNullPrototype:t,requiredKeys:r.requiredKeys},n=p.record(e,o),i=Object.keys(e);return $(r.requiredKeys,i),n}function $(e,r){return e.length===r.length&&r.every(t=>e.includes(t))}import*as R from"fast-check";function te(e,r){let t=r??{},o={...t,minLength:Math.max(t.minLength??1,1)};return R.uniqueArray(e,o).filter(w)}function S(e,r){let t=M(e);return t?t.then(r):r(e)}function k(e,r){let t=a=>(r(),a),o=a=>{try{r()}catch(l){throw P(l,a)}throw a},n;try{n=e()}catch(a){return o(a)}let i=M(n);return i?i.then(t,o):t(n)}function M(e){if(!(typeof e=="object"&&e!==null||typeof e=="function"))return;let r;try{r=e.then}catch(t){return Promise.reject(t)}if(typeof r=="function")return new Promise((t,o)=>r.call(e,t,o))}function P(e,r){if(e instanceof Error&&e.cause===void 0&&e!==r)try{e.cause=r}catch{}return e}function f(e,r){let t=new Error(r);return Error.captureStackTrace?.(t,e),t}var c={noLabels:()=>"coverage requires at least one label",badPercent:e=>`coverage percentage for ${JSON.stringify(e)} must be between 0 and 100`,outsideCase:()=>"coverage.hit() called outside an active test case",inGenerator:()=>"coverage.hit() cannot be called from an arbitrary",noCheck:()=>"coverage.hit() called, but no property check with coverage.plugin is running",duplicatePlugin:()=>"coverage.plugin passed more than once to the same property check",unknownLabel:e=>`unknown coverage label ${JSON.stringify(e)}`,concurrentUse:()=>"coverage cannot be shared by concurrent property checks",runSummary:e=>`{ seed: ${e.seed}, skipped: ${e.numSkips} }`,noAccepted:()=>"coverage requires at least one accepted test case",unmetHeader:e=>`coverage failed after ${e} accepted test cases`,unmetLine:({label:e,minimum:r,count:t},o)=>{let n=`${t}/${o} (${D(t,o)}%)`,i=`required >= ${r}%`;return`- ${e}: ${n}, ${i}`},notRunning:(e,r)=>e===0?c.noCheck():r?c.inGenerator():c.outsideCase()};function U(e){let r=new Map(Object.entries(e)),t=O(r);if(t.length>0)throw f(U,t.join(`
|
|
2
|
+
`));let o=Symbol("coverage"),n,i=0,a=!1;function l(y){if(!n)throw f(l,c.notRunning(i,a));r.has(y)?n.hits.add(y):n.tally.usageError??=f(l,c.unknownLabel(y))}let g=(y,h)=>{if(h.get(o))throw f(g,c.duplicatePlugin());h.set(o,!0),i+=1;let u={accepted:0,hits:new Map,usageError:void 0};return{decorateGenerate:d=>(...s)=>{a=!0;try{return d(...s)}finally{a=!1}},decorateRun:d=>function s(T){if(n&&n.tally!==u){let m=f(s,c.concurrentUse());return n.tally.usageError??=m,u.usageError??=m,d(T)}let b=new Set,x={tally:u,hits:b};n=x;let q=k(()=>d(T),()=>{n===x&&(n=void 0)});return S(q,m=>(m||F(u,b),m))},onAllRunsComplete:function d(s){if(!s.failed){if(u.usageError)throw u.usageError;if(!L(s)&&!j(r,u))throw f(d,_(r,u,s))}},afterAll:()=>{i-=1,n?.tally===u&&(n=void 0)}}};return{hit:l,plugin:g}}function L(e){return!!e.runConfiguration.path}function N({minimum:e,count:r},t){return r*100/t>=e}function j(e,r){let t=C(e,r),{accepted:o}=r;return o>0&&t.length===0}function I(e){return Number.isFinite(e)&&e>=0&&e<=100}function F(e,r){e.accepted+=1;for(let t of r)e.hits.set(t,(e.hits.get(t)??0)+1)}function D(e,r){return(Math.floor(e*1e4/r)/100).toFixed(2)}function C(e,r){return[...e].map(([t,o])=>({label:t,minimum:o,count:r.hits.get(t)??0})).filter(t=>!N(t,r.accepted))}function O(e){return e.size===0?[c.noLabels()]:[...e].filter(([,r])=>!I(r)).map(([r])=>c.badPercent(r))}function _(e,r,t){let o=C(e,r),{accepted:n}=r,i=c.runSummary(t);return n===0?[c.noAccepted(),i].join(`
|
|
3
|
+
`):[c.unmetHeader(n),i,...o.map(a=>c.unmetLine(a,n))].join(`
|
|
4
|
+
`)}export{U as coverage,W as element,B as getNext,J as infiniteStream,X as nonEmptyArray,te as nonEmptyUniqueArray,Z as record};
|
|
2
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/.pnpm/@carlwr+typescript-extra@0.8.2/node_modules/@carlwr/typescript-extra/src/extract.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.8.2/node_modules/@carlwr/typescript-extra/src/misc.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.8.2/node_modules/@carlwr/typescript-extra/src/node/node.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","/**\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 (rejections 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 * Cached, lazy evaluation.\n *\n * If {@link f} throws, the result is not cached: the next call will evaluate {@link f} again.\n *\n * This function can be thought of as the synchronous analogue to the promise-oriented {@link memoized}. Note though that the error-handling differs.\n *\n * @example\n * const getCfg = cached(() => readFileSync('config.json', 'utf8'))\n * let c0 = getCfg() // reads the file, returns result\n * let c1 = getCfg() // returns cached result\n */\nexport function cached<T>(f: () => T): () => T {\n let s: // state\n | { done: false }\n | { done: true, v: T }\n = { done: false }\n return () =>\n s.done\n ? s.v\n : ( s = { done: true, v: f() }).v\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\n/**\n * Map an async function over {@link xs} and keep only defined results.\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\n/**\n * Map an async function over {@link xs}.\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\n/**\n * Split items into those for which the async predicate resolves truthy and falsy.\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): 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\n/**\n * Marker for exhaustiveness checks in discriminated unions.\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 * 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 { rm } from 'node:fs/promises'\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","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":"ACoHO,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,CA4DO,SAASC,EAAcC,EAAqC,CACjE,OAAOA,EAAG,QAAU,CACtB,CEtLA,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","l"]}
|
|
1
|
+
{"version":3,"sources":["../node_modules/.pnpm/@carlwr+typescript-extra@0.13.0/node_modules/@carlwr/typescript-extra/src/extract.ts","../node_modules/.pnpm/@carlwr+typescript-extra@0.13.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","../node_modules/.pnpm/@carlwr+typescript-extra@0.13.0/node_modules/@carlwr/typescript-extra/src/maybe-async/maybe-async.ts","../src/utils.ts","../src/coverage.ts"],"sourcesContent":["/**\n * walk `root` recursively while collecting all {@link T}s for which `pred` returns 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","/**\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 (rejections 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 * Cached, lazy, single-flight evaluation of a promise.\n *\n * A rejected promise is not cached: the next call will evaluate `f` again.\n *\n * The retrying analogue of {@link memoized}.\n */\nexport function memoizedRetry<T>(f: () => Promise<T>): () => Promise<T> {\n let p: Promise<T> | null = null\n const clear = (e: unknown): never => { p = null; throw e }\n return () => p ?? (p = f().catch(clear))\n}\n\n\n/**\n * Cached, lazy evaluation.\n *\n * If `f` throws, the result is not cached: the next call will evaluate `f` again.\n *\n * This function can be thought of as the synchronous analogue to the promise-oriented {@link memoized}. Note though that the error-handling differs.\n *\n * @example\n * const getCfg = cached(() => readFileSync('config.json', 'utf8'))\n * let c0 = getCfg() // reads the file, returns result\n * let c1 = getCfg() // returns cached result\n */\nexport function cached<T>(f: () => T): () => T {\n let s: // state\n | { done: false }\n | { done: true, v: T }\n = { done: false }\n return () =>\n s.done\n ? s.v\n : ( s = { done: true, v: f() }).v\n}\n\n\n/**\n * Cached, lazy evaluation of a single-argument function.\n *\n * The cache is keyed by argument identity (`SameValueZero`, i.e. `===` except for on `NaN`).\n *\n * If `f` throws, the result is not cached for that argument: the next call with the same argument will evaluate `f` again.\n *\n * @example\n * const getText = cachedUnary((path: string) => readFileSync(path, 'utf8'))\n * getText('a.txt') // reads a.txt, returns its content\n * getText('a.txt') // returns cached a.txt content\n * getText('B.txt') // reads B.txt, returns its content\n */\nexport function cachedUnary<K,V>(f: (k: K) => V): (k: K) => V {\n const cache = new Map<K, { v: V }>()\n return k => {\n let hit = cache.get(k)\n if (!hit) cache.set(k, hit = { v: f(k) })\n return hit.v\n }\n}\n\n\n/**\n * whether the elements of `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 * Construct a {@link NonEmpty} from a guaranteed head and zero or more tail elements.\n *\n * Useful for declaring or building a `[T,...T[]]` without an explicit type annotation or cast.\n *\n * @example\n * const xs = nonEmpty(1, 2, 3) // typed [number, ...number[]]\n */\nexport function nonEmpty<T>(head: T, ...rest: T[]): [T,...T[]] {\n return [head, ...rest]\n}\n\n/**\n * Map over a non-empty array while preserving the type as non-empty.\n *\n * The callback receives `(x, i, arr)` — same shape as `Array.prototype.map`, with `arr` narrowed to non-empty.\n */\nexport function mapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T, i: number, arr: readonly [T,...T[]]) => U\n): [U,...U[]] {\n const mapped = xs.map((x,i) => fn(x,i,xs))\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 *\n * The callback receives `(x, i, arr)` — same shape as `Array.prototype.flatMap`, with `arr` narrowed to non-empty.\n */\nexport function flatmapNonEmpty<T,U>(\n xs: readonly [T,...T[]],\n fn: (x: T, i: number, arr: readonly [T,...T[]]) => [U,...U[]]\n): [U,...U[]] {\n const mapped = xs.flatMap((x,i) => fn(x,i,xs))\n return mapped as [U,...U[]]\n}\n\n\n/**\n * Stateful iterator yielding the elements of `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\n/**\n * Map an async function over `xs` and keep only defined results.\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\n/**\n * Map an async function over `xs`.\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\n/**\n * Split items into those for which the async predicate resolves truthy and falsy.\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 `str` does not start with `first`\n *\n * @example\n * withoutFirstSubstring('e', 'ego') // => 'go'\n */\nexport function withoutFirstSubstring(first: string, str: string): 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\n/**\n * Marker for exhaustiveness checks in discriminated unions.\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 *\n * if `K` is a known key of `T`, the narrowed property type also has `undefined` excluded (the `in` check has proved the key is set)\n */\nexport function hasKey<T,K extends PropertyKey>(\n value: T,\n key: K\n): value is T & { [P in K]: P extends keyof T ? Exclude<T[P], undefined> : unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && key in value\n )\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 * escape regex meta-characters in `s` so the result matches `s` literally when used as a `RegExp` pattern\n *\n * @example\n * new RegExp(escapeRegExp('a.b')) // matches \"a.b\" literally, not \"a<any>b\"\n */\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\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 *\n * Union types are compared as wholes, i.e. `Eq` does not distribute over them: `Eq<'a'|'b', 'a'|'b'>` is `true`, and `Eq<never, string>` is `false`.\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","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 {@linkcode fc.constantFrom}, but _shrinks across all elements_ - {@linkcode fc.constantFrom} only shrinks towards the first element.\n *\n * @example\n * import * as fcu from '@carlwr/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 {@linkcode fc.infiniteStream} allowing access to the generated values in a type-safe way through the {@linkcode getNext} helper.\n *\n * features and non-features:\n * - does _not_ shrink at all unfortunately - since {@linkcode 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 '@carlwr/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 {@linkcode InfiniteStream} object yielded by {@linkcode 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 {@linkcode constraints} parameter object is passed, it will be honored (function throws if `{minLength: 0}` is specified).\n *\n * @example\n * import * as fcu from '@carlwr/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 {@linkcode fc.record}, but with\n * - `noNullPrototype` _true_ by default\n * - stronger typing\n *\n * @example\n * import * as fcu from '@carlwr/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(k => requiredKeys.includes(k))\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 '@carlwr/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","/**\n * Helpers for values/functions that may or may not be asynchronous.\n *\n * The helpers can sometimes let code use a single implementation that handles both synchronous and asynchronous values/functions without branching at the call site. If the inputs are synchronous, the result stays synchronous, i.e. without introducing a promise.\n *\n * @module maybe-async\n */\n\n/**\n * A return value that is not a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables | thenable}.\n */\nexport type NotThenable<T> =\n unknown extends T ? unknown :\n [Extract<T, PromiseLike<unknown>>] extends [never] ? unknown :\n never\n\n/**\n * Apply a function (`f`) to a value (`value`). `value` may or may not be a promise, and `f` may or may not return a promise.\n *\n * If `value` is a promise, `f` receives the awaited `value`.\n *\n * What `andThen` returns, and how it calls `f`, is determined by what `value` is:\n *\n * | `value` | -> | `andThen` | calls `f` |\n * | :----------------- | :-: | :-------------------------- | :------------- |\n * | non-promise | -> | returns what `f` returned | synchronously |\n * | fulfilling promise | -> | returns a native promise | asynchronously |\n * | rejecting promise | -> | returns a rejecting promise | never |\n *\n * If `f` throws, then if `value` is\n * - a non-promise, the error propagates\n * - a promise, then the returned promise rejects\n *\n * If `value` is a promise, a _flattened_ (native) promise is returned.\n *\n * If a `.then` getter of `value` throws, the returned promise will be a rejection.\n *\n * In the documentation above, \"promise\" is used in the meaning of {@linkcode PromiseLike} on the type level, and an object with a {@linkcode PromiseLike.then} method at runtime. The two are typically the same but may disagree in rare cases.\n */\nexport function andThen<V, B>(\n value: V,\n f : (a: Awaited<V>) => B,\n):\n V extends PromiseLike<unknown> ? Promise<Awaited<B>> :\n PromiseLike<unknown> extends V ? B|Promise<Awaited<B>> :\n B\n\nexport function andThen<A, B>(\n value: A|PromiseLike<A>,\n f : (a: A) => B,\n): B|Promise<B> {\n const promise = toPromise(value)\n return promise\n ? promise.then(f)\n : f(value as A)\n}\n\n/**\n * Run the function `run`. Run the synchronous `cleanup` function once, regardless of whether `run` returned, threw or rejected.\n *\n * If `run` returns a promise, `cleanup` is run when that promise has settled.\n *\n * The return value of `cleanup` is always ignored.\n *\n * What `andFinally` returns, and how it calls `cleanup`, is determined by whether `run` threw, and if it didn't, what it returned:\n *\n * | `run` | -> | `andFinally` | calls `cleanup` |\n * | :--------------------- | :-: | :----------------------- | :-------------- |\n * | returned a non-promise | -> | returns or throws | synchronously |\n * | threw | -> | throws | synchronously |\n * | returned a promise | -> | returns a native promise | asynchronously |\n *\n * The outcome of `andFinally`, and what error is attached as `cause`, if any, is determined by whether `run` threw/rejected and whether `cleanup` threw:\n *\n * | `run` | `cleanup` | -> | outcome | `cause` set to |\n * | :-------- | :-------- | :-: | :---------------- | :---------------------- |\n * | succeeded | returned | -> | `run`'s value | – |\n * | failed | returned | -> | `run`'s error | – |\n * | succeeded | threw | -> | `cleanup`'s error | – |\n * | failed | threw | -> | `cleanup`'s error | `run`'s error, if unset |\n *\n * If `cleanup` throws an error and `run` failed, `cleanup`'s error object is mutated (can only matter if shared or reused).\n *\n * In the documentation above, \"promise\" is used in the meaning of {@linkcode PromiseLike} on the type level, and an object with a {@linkcode PromiseLike.then} method at runtime. The two are typically the same but may disagree in rare cases.\n */\nexport function andFinally<R, C = void>(\n run : () => R,\n cleanup: () => C & NotThenable<C>,\n): R extends PromiseLike<unknown> ? Promise<Awaited<R>> : R\n\nexport function andFinally<A>(\n run : () => A|PromiseLike<A>,\n cleanup: () => unknown,\n): A|Promise<A> {\n const onValue = (value: A): A => { cleanup(); return value }\n\n const onError = (error: unknown): never => {\n try { cleanup() }\n catch (fromCleanup) {\n throw withCause(fromCleanup, error)\n }\n throw error\n }\n\n let out: A|PromiseLike<A>\n try { out = run() }\n catch (error) {\n return onError(error)\n }\n\n const promise = toPromise(out)\n return promise\n ? promise.then(onValue, onError)\n : onValue(out as A)\n}\n\n/* Stance re. JSDoc of exported functions:\n\n- main goal: make them _comprehensible_\n - documenting all technical details would be overwhelming for a reader; instead prioritize conceptual clarity\n\n- tables + prose _as a whole_ should convey the information\n - the two are not intended to _independently_ convey everything\n\n- markdown tables should be max. 80 chars wide\n - too wide tables may make them hard to read or partially hidden in hovers; also, <80 chars keeps them readable as plaintext in an editor with soft-wrapping at 80 chars\n\n*/\n\n/* a native promise adopting `value` if it is a thenable, else undefined\n\nlike `Promise.resolve`, but `value.then` is read only once; as with `Promise.resolve`, a throwing `then` getter gives a rejecting promise\n*/\nfunction toPromise<A>(value: A|PromiseLike<A>): Promise<A>|undefined {\n const isObject =\n (typeof value === 'object' && value !== null) ||\n typeof value === 'function'\n if (!isObject) return undefined\n let then: unknown\n try { then = (value as {then?: unknown}).then }\n catch (error) {\n return Promise.reject(error)\n }\n if (typeof then !== 'function') return undefined\n return new Promise<A>((resolve, reject) => then.call(value, resolve, reject))\n}\n\nfunction withCause<E>(error: E, cause: unknown): E {\n if (\n error instanceof Error &&\n error.cause === undefined &&\n error !== cause\n )\n try { error.cause = cause }\n catch { /* e.g. a frozen error; leave it as it is */ }\n return error\n}\n","export type AnyFunction = (...args: never) => unknown\n\n/**\n * Return an error whose stack trace starts at the caller of `fromFn`.\n *\n * Using this function allows traces that point to the code that caused the error instead of pointing to library code. The `fromFn` should be the function that throws.\n *\n * This function might only trim the stack properly for the V8 runtime. For other runtimes, the stack is possibly left untouched.\n*/\nexport function callerError(fromFn: AnyFunction, message: string): Error {\n const error = new Error(message)\n Error.captureStackTrace?.(error, fromFn)\n return error\n}\n","import { andFinally, andThen } from '@carlwr/typescript-extra/maybe-async'\nimport type * as fc from 'fast-check'\nimport { callerError } from './utils.js'\n\ntype DetailsWithSkips = Pick<fc.RunDetails<unknown>, 'seed'|'numSkips'>\n\nconst MSG = {\n noLabels: () =>\n 'coverage requires at least one label',\n\n badPercent: (label: string) =>\n `coverage percentage for ${JSON.stringify(label)} must be between 0 and 100`,\n\n outsideCase: () =>\n 'coverage.hit() called outside an active test case',\n\n inGenerator: () =>\n 'coverage.hit() cannot be called from an arbitrary',\n\n noCheck: () =>\n 'coverage.hit() called, but no property check with coverage.plugin is running',\n\n duplicatePlugin: () =>\n 'coverage.plugin passed more than once to the same property check',\n\n unknownLabel: (label: string) =>\n `unknown coverage label ${JSON.stringify(label)}`,\n\n concurrentUse: () =>\n 'coverage cannot be shared by concurrent property checks',\n\n runSummary: (details: DetailsWithSkips) =>\n `{ seed: ${details.seed}, skipped: ${details.numSkips} }`,\n\n noAccepted: () =>\n 'coverage requires at least one accepted test case',\n\n unmetHeader: (accepted: number) =>\n `coverage failed after ${accepted} accepted test cases`,\n\n unmetLine: ({label, minimum, count}: Unmet, accepted: number) => {\n const haveStr = `${count}/${accepted} (${percent(count, accepted)}%)`\n const reqStr = `required >= ${minimum}%`\n return `- ${label}: ${haveStr}, ${reqStr}`\n },\n\n notRunning: (checks: number, isGenerating: boolean) => {\n if (checks === 0) return MSG.noCheck()\n if (isGenerating ) return MSG.inGenerator()\n return MSG.outsideCase()\n }\n}\n\n/* JSDoc style stance:\nDon't overwhelm the reader with details. Prefer clarity and conveying the concepts in a simple way over documenting every single truth about the behaviour.\n*/\n\n/**\n * Coverage for property tests.\n *\n * A single instance cannot be used for multiple concurrently running tests. Re-using the same instance for multiple sequential tests is fine.\n */\nexport interface Coverage<L extends string> {\n /** Register a hit for a label. */\n readonly hit: (label: L) => void\n\n /** A plugin value to pass to `fast-check`. */\n readonly plugin: <Ts>(pluginIndex: number, pluginStore: fc.PluginStore) => fc.PluginInstance<Ts>\n}\n\n/**\n * Add minimum coverage checks to a property test.\n *\n * In a test, the user first calls `coverage`, specifying the labels to use and, for each label, a minimum percentage of test cases that must \"hit\" the label for the coverage to be considered sufficient. The user then adds conditional calls to {@linkcode Coverage.hit | hit()} to the test.\n *\n * If the property test itself succeeds, the number of test cases that were run is compared to the number of hits for each label. If the specified minimum percentage was not met for at least one label, the property test still fails, with a useful message.\n *\n * For an introduction to the coverage feature, the primary resource is the {@link https://github.com/carlwr/fastcheck-utils/blob/main/test/coverage.example.test.ts | example file}.\n *\n * Calls to {@linkcode Coverage.hit | hit()} must specify one of the registered labels. This is enforced on the type level.\n *\n * If the property fails, that error takes precedence and coverage is not asserted.\n *\n * When counting the number of test cases (the denominator) and the number of hits,\n * - discarded cases are not included\n * - if a label is hit more than once, it is still only counted once\n * - _examples_ are included in the count of number of test cases (they contribute to the denominator)\n * - shrinking runs are not included (which does not matter, since in the case of the property failing, coverage is not checked anyways)\n *\n * If there are no accepted test cases in a test, the coverage check will result in a coverage failure.\n *\n * It is strongly recommended to use a fixed seed for tests that include coverage checks.\n *\n * If a property is _replayed_, the coverage test will be ignored (since it isn't meaningful in replays).\n *\n * {@linkcode Coverage.hit | hit()} can only be called from a test - not from within an arbitrary or {@linkcode fc.beforeEach}. If you want to use coverage to assert on the distribution of an arbitrary, it is suggested to write a dedicated property test.\n *\n * If you use the `fc.ignoreEqualValues()` plugin: if used it must come before this coverage plugin, e.g. `[fc.ignoreEqualValues(), myCoverage.plugin]`.\n *\n * @param requirements A record where the user specifies labels as keys and required hit percentages as values\n * @returns An object with the {@linkcode Coverage.hit | hit()} function for the user to call, and the {@linkcode Coverage.plugin | plugin} value to pass to something that accepts a `fast-check` plugin, e.g. `fast-check`'s {@linkcode fc.assert}/{@linkcode fc.check}, or `@fast-check/vitest`'s `it.prop`/`test.prop`. The plugin will be ignored if used with {@linkcode fc.sample} or {@linkcode fc.statistics}. Passing it to {@linkcode fc.check} will result in coverage failures to throw, rather than report the failure. Passing it to {@linkcode fc.installGlobalPlugin} does not make sense since that would mean the same requirements would be applied to all checks.\n */\nexport function coverage<K extends string>(\n requirements: Readonly<Record<K, number>>\n): Coverage<K> {\n const minimums = new Map(Object.entries<number>(requirements))\n\n const bad = badRequirements(minimums)\n if (bad.length > 0)\n throw callerError(coverage, bad.join('\\n'))\n\n const storeKey = Symbol('coverage')\n let running: RunningCase|undefined\n let checks = 0\n let generating = false\n\n function hit(label: string): void {\n if (!running)\n throw callerError(hit, MSG.notRunning(checks, generating))\n if (minimums.has(label))\n running.hits.add(label)\n else\n running.tally.usageError ??= callerError(hit, MSG.unknownLabel(label))\n }\n\n const plugin: Coverage<K>['plugin'] = (_, store) => {\n if (store.get(storeKey))\n throw callerError(plugin, MSG.duplicatePlugin())\n store.set(storeKey, true)\n checks += 1\n const tally: Tally = {\n accepted : 0,\n hits : new Map(),\n usageError: undefined\n }\n\n return {\n decorateGenerate: nestedGenerate => (...args) => {\n generating = true\n try { return nestedGenerate(...args) }\n finally {\n generating = false\n }\n },\n\n decorateRun: nestedRun => function coveredRun(value) {\n /* same tally: a previous run was abandoned by an outer plugin (e.g. fc.timeout) */\n if (running && running.tally !== tally) {\n const error = callerError(coveredRun, MSG.concurrentUse())\n running.tally.usageError ??= error\n tally.usageError ??= error\n return nestedRun(value)\n }\n const hits = new Set<string>()\n const current = {tally, hits}\n running = current\n const outcome = andFinally(\n () => nestedRun(value),\n () => { if (running === current) running = undefined },\n )\n const f = <T>(result: T) => {\n if (!result)\n countCase(tally, hits)\n return result\n }\n return andThen(outcome, f)\n },\n\n /* throws also for fc.check: plugins cannot mark a run as failed */\n onAllRunsComplete: function checkCoverage(details) {\n if (details.failed ) return\n if (tally.usageError ) throw tally.usageError\n if (isReplay(details)) return // coverage not meaningful\n\n if (!isCovered(minimums, tally))\n throw callerError(checkCoverage, report_(minimums, tally, details))\n },\n\n /* a run abandoned by an outer plugin (e.g. fc.timeout) may still be pending */\n afterAll: () => {\n checks -= 1\n if (running?.tally === tally)\n running = undefined\n },\n }\n }\n\n return {hit, plugin}\n}\n\nfunction isReplay<Ts>(details: fc.RunDetails<Ts>): boolean {\n return !!details.runConfiguration.path\n}\n\ninterface Tally {\n accepted : number\n hits : Map<string, number>\n usageError: Error|undefined\n}\n\ninterface RunningCase {\n tally: Tally\n hits : Set<string>\n}\n\ninterface Unmet {\n label : string\n minimum: number\n count : number\n}\n\ntype Minimums = ReadonlyMap<string, number>\n\nfunction isMet({minimum, count}: Unmet, accepted: number) {\n return count * 100 / accepted >= minimum\n}\n\nfunction isCovered(minimums: Minimums, tally: Tally) {\n const unmet = unmetLabels(minimums, tally)\n const {accepted} = tally\n return accepted > 0 && unmet.length === 0\n}\n\nfunction isPercentage(minimum: number) {\n return Number.isFinite(minimum) && minimum >= 0 && minimum <= 100\n}\n\nfunction countCase(tally: Tally, hits: ReadonlySet<string>): void {\n tally.accepted += 1\n for (const label of hits)\n tally.hits.set(label, (tally.hits.get(label) ?? 0) + 1)\n}\n\nfunction percent(part: number, whole: number): string {\n return (Math.floor(part * 10000 / whole) / 100).toFixed(2)\n}\n\nfunction unmetLabels(minimums: Minimums, tally: Tally): Unmet[] {\n return [...minimums]\n .map(([label, minimum]) => ({\n label,\n minimum,\n count: tally.hits.get(label) ?? 0,\n }))\n .filter(u => !isMet(u, tally.accepted))\n}\n\nfunction badRequirements(minimums: Minimums): string[] {\n if (minimums.size === 0)\n return [MSG.noLabels()]\n return [...minimums]\n .filter(([, minimum]) => !isPercentage(minimum))\n .map(([label]) => MSG.badPercent(label))\n}\n\nfunction report_(\n minimums: Minimums,\n tally : Tally,\n details: DetailsWithSkips,\n): string {\n const unmet = unmetLabels(minimums, tally)\n const {accepted} = tally\n const summary = MSG.runSummary(details)\n if (accepted === 0)\n return [MSG.noAccepted(), summary].join('\\n')\n\n return [\n MSG.unmetHeader(accepted),\n summary,\n ...unmet.map(u => MSG.unmetLine(u, accepted)),\n ].join('\\n')\n}\n"],"mappings":"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","names":["safeIndex","xs","i","isNonEmpty","xs","fc","element","xs","i","P","fc","infiniteStream","arb","getNext","stream","value","done","fc","nonEmptyArray","arb","constraints","base","constr","fc","record","model","constr","nnp","constr_","arb","allKeys","isAllKeysRequired","requiredKeys","k","fc","nonEmptyUniqueArray","arb","constraints","base","constr","l","andThen","value","f","promise","toPromise","andFinally","run","cleanup","onValue","onError","error","fromCleanup","withCause","out","then","resolve","reject","cause","callerError","fromFn","message","error","MSG","label","details","accepted","minimum","count","haveStr","percent","reqStr","checks","isGenerating","coverage","requirements","minimums","bad","badRequirements","callerError","storeKey","running","generating","hit","plugin","_","store","tally","nestedGenerate","args","nestedRun","coveredRun","value","error","hits","current","outcome","d","A","result","countCase","checkCoverage","isReplay","isCovered","report_","isMet","unmet","unmetLabels","isPercentage","part","whole","u","summary"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carlwr/fastcheck-utils",
|
|
3
|
-
"description": "improved generators for fast-check",
|
|
3
|
+
"description": "utilities and improved generators for fast-check",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"fast-check",
|
|
6
6
|
"arbitraries",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
|
-
"version": "0.
|
|
25
|
+
"version": "0.7.0",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"type": "module",
|
|
28
28
|
"main": "./dist/index.cjs",
|
|
@@ -45,31 +45,9 @@
|
|
|
45
45
|
"LICENSE",
|
|
46
46
|
"README.md"
|
|
47
47
|
],
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"fast-check": "^4.6.0"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@biomejs/biome": "^2.4.12",
|
|
53
|
-
"@carlwr/typescript-extra": "^0.8.2",
|
|
54
|
-
"@fast-check/vitest": "^0.4.0",
|
|
55
|
-
"@types/node": "^25.6.0",
|
|
56
|
-
"arg": "^5.0.2",
|
|
57
|
-
"fast-check": "^4.7.0",
|
|
58
|
-
"read-pkg": "^10.1.0",
|
|
59
|
-
"tsup": "^8.5.1",
|
|
60
|
-
"tsx": "^4.21.0",
|
|
61
|
-
"typedoc": "^0.28.19",
|
|
62
|
-
"typedoc-plugin-markdown": "^4.11.0",
|
|
63
|
-
"typescript": "^6.0.3",
|
|
64
|
-
"vitest": "^4.1.4",
|
|
65
|
-
"zod": "^4.3.6",
|
|
66
|
-
"zzz_LAST_dummy": "npm:empty-npm-package@1.0.0"
|
|
67
|
-
},
|
|
68
|
-
"engines": {
|
|
69
|
-
"node": ">=20.0.0"
|
|
70
|
-
},
|
|
71
48
|
"scripts": {
|
|
72
49
|
"build": "tsx build.ts",
|
|
50
|
+
"prepack": "pnpm run qa >/dev/null && pnpm run build && pnpm run readme",
|
|
73
51
|
"publish:dry": "pnpm pack --dry-run",
|
|
74
52
|
"publish:release": "pnpm publish",
|
|
75
53
|
"version:patch": "pnpm version patch",
|
|
@@ -83,5 +61,29 @@
|
|
|
83
61
|
"qa": "pnpm typecheck && pnpm lint && pnpm test",
|
|
84
62
|
"readme": "tsx scripts/makeReadme.ts",
|
|
85
63
|
"LAST_dummy": "false"
|
|
64
|
+
},
|
|
65
|
+
"packageManager": "pnpm@12.6.0",
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"fast-check": "^4.10.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@biomejs/biome": "^2.4.15",
|
|
71
|
+
"@carlwr/typescript-extra": "^0.13.0",
|
|
72
|
+
"@fast-check/vitest": "^0.5.0",
|
|
73
|
+
"@types/node": "^25.9.0",
|
|
74
|
+
"arg": "^5.0.2",
|
|
75
|
+
"fast-check": "^4.10.0",
|
|
76
|
+
"read-pkg": "^10.1.0",
|
|
77
|
+
"tsup": "^8.5.1",
|
|
78
|
+
"tsx": "^4.22.2",
|
|
79
|
+
"typedoc": "^0.28.19",
|
|
80
|
+
"typedoc-plugin-markdown": "^4.11.0",
|
|
81
|
+
"typescript": "^6.0.3",
|
|
82
|
+
"vitest": "^4.1.6",
|
|
83
|
+
"zod": "^4.4.3",
|
|
84
|
+
"zzz_LAST_dummy": "npm:empty-npm-package@1.0.0"
|
|
85
|
+
},
|
|
86
|
+
"engines": {
|
|
87
|
+
"node": ">=20.0.0"
|
|
86
88
|
}
|
|
87
|
-
}
|
|
89
|
+
}
|