adjointly 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +345 -2
- package/dist/index.d.ts +868 -0
- package/dist/index.js +1390 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +20 -0
- package/docs/api.md +165 -0
- package/docs/concepts.md +143 -0
- package/docs/pitfalls.md +133 -0
- package/docs/recipes.md +151 -0
- package/llms.txt +36 -0
- package/package.json +91 -4
package/docs/pitfalls.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Pitfalls
|
|
2
|
+
|
|
3
|
+
Each of these gives a wrong derivative without an error. Most are invisible to the type checker.
|
|
4
|
+
When a gradient looks wrong, run once with `new Tape({ debug: true })` (or `new Forward({ debug:
|
|
5
|
+
true })`): it checks every operand and throws at the first misuse it can see.
|
|
6
|
+
|
|
7
|
+
## Telling variables from numbers with `typeof`
|
|
8
|
+
|
|
9
|
+
**Never tell a variable from a plain number by `typeof`, or by its value.** A `Var` (or `Dual`) is a
|
|
10
|
+
number at run time, so `typeof x === 'number'` is true for every variable. Code that uses such a test
|
|
11
|
+
to turn "numbers" into constants turns every variable into a constant too, and every gradient comes
|
|
12
|
+
out exactly 0, silently:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { type Arithmetic, gradient, Tape, type Var } from 'adjointly';
|
|
16
|
+
|
|
17
|
+
// Wrong: meant to lift plain numbers into constants and pass variables through.
|
|
18
|
+
function lift<T>(A: Arithmetic<T>, v: T | number): T {
|
|
19
|
+
return typeof v === 'number' ? A.constant(v) : v;
|
|
20
|
+
}
|
|
21
|
+
gradient((A, [x]) => A.mul(lift(A, x), lift(A, 2)), [3]).gradient; //=> [0]
|
|
22
|
+
|
|
23
|
+
// Debug mode refuses a variable where a number belongs.
|
|
24
|
+
const t = new Tape({ debug: true });
|
|
25
|
+
const x: Var = t.input(3);
|
|
26
|
+
let message = '';
|
|
27
|
+
try {
|
|
28
|
+
lift(t, x);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
message = (e as Error).message.split(',')[0]!;
|
|
31
|
+
}
|
|
32
|
+
message; //=> 'Tape.constant: argument 1 is a variable'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Keep variables and numbers in separate places (separate parameters, separate arrays, a field that
|
|
36
|
+
says which), and pass numbers to the plain-number operations, which take them as such:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { gradient } from 'adjointly';
|
|
40
|
+
|
|
41
|
+
gradient((A, [x]) => A.mulc(x, 2), [3]).gradient; //=> [2]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Arithmetic operators on a `Var`
|
|
45
|
+
|
|
46
|
+
`Var` and `Dual` are numbers at run time, and TypeScript allows `+`, `*`, `Math.exp` on number
|
|
47
|
+
subtypes. The result is arithmetic on _indices_: meaningless, and unchecked.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { Tape } from 'adjointly';
|
|
51
|
+
|
|
52
|
+
const t = new Tape();
|
|
53
|
+
const x = t.input(10);
|
|
54
|
+
const y = t.input(20);
|
|
55
|
+
// Wrong: `x + y` adds the nodes' indices (1 and 2: slot 0 is reserved). Right:
|
|
56
|
+
t.value(t.add(x, y)); //=> 30
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Route every operation through the arithmetic object, `Math.*` included.
|
|
60
|
+
|
|
61
|
+
## Reading a value out and feeding it back
|
|
62
|
+
|
|
63
|
+
`A.value(x)` is a plain number. Anything computed from it and fed back through `A.constant` is a
|
|
64
|
+
constant: the derivative through it is lost.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { gradient } from 'adjointly';
|
|
68
|
+
|
|
69
|
+
// Intends x^2, differentiates as if the second x were a constant.
|
|
70
|
+
gradient((A, [x]) => A.mul(x, A.constant(A.value(x))), [3]).gradient; //=> [3]
|
|
71
|
+
gradient((A, [x]) => A.square(x), [3]).gradient; //=> [6]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Use `A.value` for branch conditions, logging, step sizes and results; never for a quantity a
|
|
75
|
+
derivative should flow through.
|
|
76
|
+
|
|
77
|
+
## Branches at their boundary, and kinks written by hand
|
|
78
|
+
|
|
79
|
+
The derivative is that of the branch taken. A hand-written `abs` is `+1` at zero; `A.abs` is `0`
|
|
80
|
+
by convention. Prefer the primitives for kinks.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { gradient } from 'adjointly';
|
|
84
|
+
|
|
85
|
+
gradient((A, [x]) => (A.value(x) >= 0 ? x : A.neg(x)), [0]).gradient; //=> [1]
|
|
86
|
+
gradient((A, [x]) => A.abs(x), [0]).gradient; //=> [0]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A piecewise function that is discontinuous at a boundary has no derivative there, and the one you
|
|
90
|
+
get describes only the side taken. Finite differences across the boundary are meaningless too, so
|
|
91
|
+
test gradients away from boundaries.
|
|
92
|
+
|
|
93
|
+
## Unstable compositions
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { plain } from 'adjointly';
|
|
97
|
+
|
|
98
|
+
// Built by hand from exp and log, softplus overflows; the primitive does not.
|
|
99
|
+
plain.log(plain.add(plain.constant(1), plain.exp(800))); //=> Infinity
|
|
100
|
+
plain.softplus(800); //=> 800
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `sigmoid`, `softplus`, `log1p`, `expm1`, `logsumexp` and `softmax` rather than assembling them.
|
|
104
|
+
|
|
105
|
+
## Leaving a function's domain
|
|
106
|
+
|
|
107
|
+
`log`, `sqrt`, `div` by zero and `pow` with a non-positive base produce `NaN` or `Infinity` in value
|
|
108
|
+
and derivative. A parameter that must stay positive should be reparametrised (`softplus(raw)`)
|
|
109
|
+
rather than clamped after the fact.
|
|
110
|
+
|
|
111
|
+
## Using a `Var` after `clear()` or `rewind()`
|
|
112
|
+
|
|
113
|
+
A `Var` is an index. After `clear()`, or `rewind(m)` to before it was made, it names nothing, or
|
|
114
|
+
whichever newer node took its slot. Nothing checks. Keep only `Var`s made before a `mark()` you
|
|
115
|
+
rewind to.
|
|
116
|
+
|
|
117
|
+
## A `Var` from another tape
|
|
118
|
+
|
|
119
|
+
The brand cannot tell two tapes apart. Passing a `Var` from tape A to tape B reads B's node with the
|
|
120
|
+
same index. Keep one tape per computation, and do not mix them.
|
|
121
|
+
|
|
122
|
+
## `noUncheckedIndexedAccess` and generic accumulators
|
|
123
|
+
|
|
124
|
+
Under that compiler option `xs[i]` is `T | undefined`. For a fixed number of inputs, type them as a
|
|
125
|
+
tuple (`[x, y]: readonly [T, T]`, or `ScalarFunction<[number, number]>`) and they are plain `T`s;
|
|
126
|
+
`tape.inputs([1, 2])` likewise returns a tuple of `Var`s. For a list of any length, index with `!`;
|
|
127
|
+
and note that `let s = xs[0]!` infers `NonNullable<T>`, which a later `s = A.add(s, ...)` (returning
|
|
128
|
+
`T`) cannot be assigned to. Annotate it: `let s: T = xs[0]!`.
|
|
129
|
+
|
|
130
|
+
## Adjoints after the output
|
|
131
|
+
|
|
132
|
+
`backward(output)` only zeroes and sweeps nodes up to `output`. Adjoints of nodes recorded after it
|
|
133
|
+
are left over from earlier sweeps and mean nothing.
|
package/docs/recipes.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Recipes
|
|
2
|
+
|
|
3
|
+
## Gradient descent in an inner loop
|
|
4
|
+
|
|
5
|
+
Keep one tape for the whole loop and clear it per step; pass a buffer for the gradient. After the
|
|
6
|
+
first step the tape has grown to the size of the computation and nothing more is allocated.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { type Arithmetic, Tape } from 'adjointly';
|
|
10
|
+
|
|
11
|
+
// Least squares: fit y = a x + b to three points.
|
|
12
|
+
const xs = [0, 1, 2];
|
|
13
|
+
const ys = [1, 3, 5];
|
|
14
|
+
function loss<T>(A: Arithmetic<T>, a: T, b: T): T {
|
|
15
|
+
let s = A.constant(0);
|
|
16
|
+
for (let i = 0; i < xs.length; i++) {
|
|
17
|
+
const r = A.subc(A.add(A.mulc(a, xs[i]!), b), ys[i]!);
|
|
18
|
+
s = A.add(s, A.square(r));
|
|
19
|
+
}
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const tape = new Tape();
|
|
24
|
+
const grad = new Float64Array(2);
|
|
25
|
+
let a = 0;
|
|
26
|
+
let b = 0;
|
|
27
|
+
for (let step = 0; step < 2000; step++) {
|
|
28
|
+
tape.clear();
|
|
29
|
+
const av = tape.input(a);
|
|
30
|
+
const bv = tape.input(b);
|
|
31
|
+
tape.gradient(loss(tape, av, bv), [av, bv], grad);
|
|
32
|
+
a -= 0.05 * grad[0]!;
|
|
33
|
+
b -= 0.05 * grad[1]!;
|
|
34
|
+
}
|
|
35
|
+
[a, b]; //=> ~[2, 1]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## A shared prefix with `mark` and `rewind`
|
|
39
|
+
|
|
40
|
+
When many evaluations share their first part (parameters, features computed from them), record it
|
|
41
|
+
once, mark the tape, and rewind to the mark for each evaluation. Vars recorded before the mark stay
|
|
42
|
+
valid.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { Tape } from 'adjointly';
|
|
46
|
+
|
|
47
|
+
const t = new Tape();
|
|
48
|
+
const w = t.input(2);
|
|
49
|
+
const w2 = t.square(w); // shared: recorded once
|
|
50
|
+
const m = t.mark();
|
|
51
|
+
let total = 0;
|
|
52
|
+
for (const x of [1, 2, 3]) {
|
|
53
|
+
t.rewind(m);
|
|
54
|
+
// d/dw of w^2 * x = 2 w x
|
|
55
|
+
t.backward(t.mulc(w2, x));
|
|
56
|
+
total += t.adjoint(w);
|
|
57
|
+
}
|
|
58
|
+
total; //=> 2 * 2 * (1 + 2 + 3)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Several outputs: one sweep each, or one weighted sweep
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { Tape } from 'adjointly';
|
|
65
|
+
|
|
66
|
+
const t = new Tape();
|
|
67
|
+
const [x, y] = t.inputs([1, 2]);
|
|
68
|
+
const f = t.mul(x, y);
|
|
69
|
+
const g = t.add(x, t.square(y));
|
|
70
|
+
|
|
71
|
+
t.backward(f);
|
|
72
|
+
t.adjoints([x, y]); //=> [2, 1]
|
|
73
|
+
t.backward(g);
|
|
74
|
+
t.adjoints([x, y]); //=> [1, 4]
|
|
75
|
+
t.vjp([f, g], [10, 1]); // gradient of 10 f + g
|
|
76
|
+
t.adjoints([x, y]); //=> [21, 14]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## A full Jacobian
|
|
80
|
+
|
|
81
|
+
Reverse mode gives a row per sweep; forward mode a column per pass. Use whichever needs fewer.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { type Arithmetic, Forward, Tape } from 'adjointly';
|
|
85
|
+
|
|
86
|
+
// F(x, y) = (x y, x + y, sin x)
|
|
87
|
+
function F<T>(A: Arithmetic<T>, x: T, y: T): T[] {
|
|
88
|
+
return [A.mul(x, y), A.add(x, y), A.sin(x)];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Rows: one recording, one sweep per output.
|
|
92
|
+
const t = new Tape();
|
|
93
|
+
const x = t.input(2);
|
|
94
|
+
const y = t.input(3);
|
|
95
|
+
const outs = F(t, x, y);
|
|
96
|
+
const rows = outs.map((o) => Array.from(t.gradient(o, [x, y])));
|
|
97
|
+
rows; //=> [[3, 2], [1, 1], [cos(2), 0]]
|
|
98
|
+
|
|
99
|
+
// Columns: one forward pass per input.
|
|
100
|
+
const cols = [0, 1].map((j) => {
|
|
101
|
+
const fw = new Forward();
|
|
102
|
+
const ys = F(fw, fw.input(2, j === 0 ? 1 : 0), fw.input(3, j === 1 ? 1 : 0));
|
|
103
|
+
return ys.map((v) => fw.tangent(v));
|
|
104
|
+
});
|
|
105
|
+
cols; //=> [[3, 1, cos(2)], [2, 1, 0]]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Constrained parameters
|
|
109
|
+
|
|
110
|
+
Optimise an unconstrained number and map it into the domain with a smooth primitive: `softplus`
|
|
111
|
+
for positive, `sigmoid` for a probability, `exp` for a scale.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { type Arithmetic, gradient } from 'adjointly';
|
|
115
|
+
|
|
116
|
+
// A rate that must stay positive, parametrised by an unconstrained raw value.
|
|
117
|
+
function negLogLikelihood<T>(A: Arithmetic<T>, [raw]: readonly [T]): T {
|
|
118
|
+
const rate = A.softplus(raw);
|
|
119
|
+
const waits = [0.5, 1.5, 1.0];
|
|
120
|
+
// exponential distribution: -sum(log(rate) - rate * w)
|
|
121
|
+
let s = A.constant(0);
|
|
122
|
+
for (const w of waits) s = A.sub(s, A.sub(A.log(rate), A.mulc(rate, w)));
|
|
123
|
+
return s;
|
|
124
|
+
}
|
|
125
|
+
// At the optimum rate = 1 / mean(waits) = 1, i.e. raw = log(e - 1), the gradient vanishes.
|
|
126
|
+
gradient(negLogLikelihood, [Math.log(Math.E - 1)]).gradient; //=> ~[0]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Test your own gradient
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { checkGradient, logsumexp, type ScalarFunction } from 'adjointly';
|
|
133
|
+
|
|
134
|
+
const f: ScalarFunction = (A, xs) =>
|
|
135
|
+
A.add(logsumexp(A, xs), A.mul(A.tanh(xs[0]!), A.sigmoid(xs[1]!)));
|
|
136
|
+
|
|
137
|
+
for (const at of [
|
|
138
|
+
[0.1, -0.7, 2],
|
|
139
|
+
[-3, 0.4, 0.5],
|
|
140
|
+
]) {
|
|
141
|
+
const r = checkGradient(f, at);
|
|
142
|
+
if (!r.ok) throw new Error(`component ${String(r.worst.index)} is off`);
|
|
143
|
+
}
|
|
144
|
+
checkGradient(f, [1, 2, 3]).ok; //=> true
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## The worked example
|
|
148
|
+
|
|
149
|
+
[examples/production-line/](https://github.com/feldgendler/adjointly/tree/main/examples/production-line) (in the repository, not the npm package) puts these together: a
|
|
150
|
+
minute-by-minute simulation written once, its gradient with respect to 1 440 decisions from one
|
|
151
|
+
tape pass, first-order estimates ranking discrete moves, and forward mode for a parameter.
|
package/llms.txt
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# adjointly
|
|
2
|
+
|
|
3
|
+
> Dependency-free automatic differentiation for scalar float64 programs in TypeScript. Write a function once against `Arithmetic<T>`; evaluate it with `plain`, get its gradient by reverse mode with `Tape`, or a directional derivative by forward mode with `Forward`. ESM only, Node 22+.
|
|
4
|
+
|
|
5
|
+
Rules for code that this library differentiates:
|
|
6
|
+
|
|
7
|
+
- Write the function generic in `T`: `function f<T>(A: Arithmetic<T>, xs: readonly T[]): T`.
|
|
8
|
+
- Never tell a variable from a number with `typeof x === 'number'` or by value: a `Var`/`Dual` is a number at run time and passes, so every variable would become a constant and every gradient 0. Keep them in separate places. To find misuse, run once with `new Tape({ debug: true })`, which throws at any operand that is not a live variable of that tape, and at a variable where a number belongs.
|
|
9
|
+
- Every operation on a differentiable value goes through `A`: `A.add`, `A.mul`, `A.exp`, never `+`, `*` or `Math.*`. `Var`/`Dual` are numbers at run time, so the mistake type-checks and is silent.
|
|
10
|
+
- Numbers enter through `A.constant(c)`, or better as the number operand of `addc`/`subc`/`csub`/`mulc`/`divc`/`cdiv`/`powc` (one tape node instead of two), and leave through `A.value(x)`. A number taken out with `A.value` and fed back with `A.constant` carries no derivative.
|
|
11
|
+
- Branching on `A.value(x)` is fine; the derivative is that of the branch taken.
|
|
12
|
+
- Use the stable primitives `sigmoid`, `softplus`, `log1p`, `expm1`, `square`, and the composites `logsumexp`, `softmax`.
|
|
13
|
+
- In loops, reuse one `Tape`: `clear()` (or `rewind(mark)`) per evaluation, and pass an output buffer to `tape.gradient`.
|
|
14
|
+
- Verify a new function with `checkGradient(f, at)` at a few points away from kinks.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { type Arithmetic, checkGradient, gradient, jvp } from 'adjointly';
|
|
18
|
+
|
|
19
|
+
function f<T>(A: Arithmetic<T>, [x, y]: readonly [T, T]): T {
|
|
20
|
+
return A.add(A.mul(x, y), A.softplus(x));
|
|
21
|
+
}
|
|
22
|
+
gradient(f, [1, 2]); //=> { value: 2 + log(1 + E), gradient: [2 + 1 / (1 + exp(-1)), 1] }
|
|
23
|
+
jvp(f, [1, 2], [0, 1]); //=> { value: 2 + log(1 + E), derivative: 1 }
|
|
24
|
+
checkGradient(f, [1, 2]).ok; //=> true
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Primitives (all on `Arithmetic`): neg, add, sub, mul, div, recip, square, sqrt, exp, expm1, log, log1p, pow, abs, min, max, sin, cos, tanh, sigmoid, softplus. With a plain-number operand c, in one tape node instead of constant(c) plus the operation: addc(a, c), subc(a, c), csub(c, a), mulc(a, c), divc(a, c), cdiv(c, a), powc(a, c). Subgradients: abs'(0) = 0; max/min ties go to the first argument; pow's exponent derivative is 0 for a base <= 0, its base derivative 0 for exponent 0; sqrt'(0) = sqrt'(-0) = Infinity; max/min with a NaN argument have NaN partials. Zero rule: a chain-rule term with an exactly zero partial, adjoint or tangent is 0, even against Infinity or NaN.
|
|
28
|
+
|
|
29
|
+
## Docs
|
|
30
|
+
|
|
31
|
+
- [README](README.md): overview, quick start, API overview, conventions, design notes, rules for agents
|
|
32
|
+
- [docs/concepts.md](docs/concepts.md): the tape, adjoints, forward vs reverse mode, control flow
|
|
33
|
+
- [docs/api.md](docs/api.md): every export by topic, with its contract
|
|
34
|
+
- [docs/recipes.md](docs/recipes.md): inner loops, mark/rewind, several outputs, Jacobians, constrained parameters, testing gradients
|
|
35
|
+
- [docs/pitfalls.md](docs/pitfalls.md): mistakes that silently give wrong derivatives
|
|
36
|
+
- [examples/production-line](https://github.com/feldgendler/adjointly/tree/main/examples/production-line): a worked optimisation example (in the repository, not the package)
|
package/package.json
CHANGED
|
@@ -1,6 +1,93 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adjointly",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Dependency-free automatic differentiation for scalar float64 programs in TypeScript: reverse mode on a typed-array tape and forward mode, from one function written once.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"autodiff",
|
|
7
|
+
"automatic-differentiation",
|
|
8
|
+
"gradient",
|
|
9
|
+
"reverse-mode",
|
|
10
|
+
"forward-mode",
|
|
11
|
+
"backpropagation",
|
|
12
|
+
"optimization",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Alexey Feldgendler",
|
|
17
|
+
"homepage": "https://github.com/feldgendler/adjointly#readme",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/feldgendler/adjointly.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/feldgendler/adjointly/issues"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"docs",
|
|
38
|
+
"llms.txt",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22"
|
|
44
|
+
},
|
|
45
|
+
"packageManager": "npm@11.19.1",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"provenance": true
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsdown",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
53
|
+
"lint": "eslint .",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"format:check": "prettier --check .",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"api:check": "api-extractor run",
|
|
59
|
+
"api:update": "npm run build && api-extractor run --local",
|
|
60
|
+
"publint": "publint --strict",
|
|
61
|
+
"attw": "attw --pack . --profile esm-only",
|
|
62
|
+
"check": "npm run format:check && npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run publint && npm run attw && npm run api:check",
|
|
63
|
+
"mutate": "stryker run",
|
|
64
|
+
"bench": "node bench/measure.ts",
|
|
65
|
+
"changeset": "changeset",
|
|
66
|
+
"release": "changeset publish",
|
|
67
|
+
"prepublishOnly": "npm run check",
|
|
68
|
+
"example": "node examples/production-line/run.ts",
|
|
69
|
+
"version-packages": "changeset version",
|
|
70
|
+
"bench:ab": "node bench/ab.ts"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
74
|
+
"@changesets/cli": "^3.0.3",
|
|
75
|
+
"@eslint/js": "^10.0.1",
|
|
76
|
+
"@microsoft/api-extractor": "^7.60.0",
|
|
77
|
+
"@stryker-mutator/core": "^10.1.1",
|
|
78
|
+
"@stryker-mutator/typescript-checker": "^10.1.1",
|
|
79
|
+
"@stryker-mutator/vitest-runner": "^10.1.1",
|
|
80
|
+
"@types/node": "^26.6.5",
|
|
81
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
82
|
+
"eslint": "^10.13.0",
|
|
83
|
+
"eslint-plugin-tsdoc": "^0.5.4",
|
|
84
|
+
"fast-check": "^4.10.2",
|
|
85
|
+
"prettier": "^3.9.10",
|
|
86
|
+
"publint": "^0.3.25",
|
|
87
|
+
"tinybench": "^6.2.2",
|
|
88
|
+
"tsdown": "^0.23.3",
|
|
89
|
+
"typescript": "~6.0.3",
|
|
90
|
+
"typescript-eslint": "^8.72.1",
|
|
91
|
+
"vitest": "^5.0.5"
|
|
92
|
+
}
|
|
93
|
+
}
|