@chenglou/freerange 0.0.1 → 0.0.2
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 +4 -0
- package/README.md +24 -18
- package/dist/fr.js +7671 -0
- package/fr.ts +0 -2
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -7,12 +7,12 @@ Freerange shows you the range of every `number` in your TypeScript codebase, let
|
|
|
7
7
|
- **Fast**. Uses a negligible fraction of TypeScript's analysis time.
|
|
8
8
|
- **Robust**. Adversarially tested by agents against thousands of edge cases.
|
|
9
9
|
|
|
10
|
-
Freerange is deliberately designed for
|
|
10
|
+
Freerange is deliberately designed to cater to a useful (and growing) subset of TypeScript, and gives concrete guidance for moving important calculations into that subset, so that your code and math can meet in the middle to unlock the most proof power without much ergonomics drawbacks. AI agents are especially well-suited to refactor such code, and we highly recommend you asking them to do so. However, if you/they do find an unsupported TS feature truly valuable, please file an issue!
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
14
|
```sh
|
|
15
|
-
bun install --dev @chenglou/freerange
|
|
15
|
+
bun install --dev @chenglou/freerange # npm install works too of course
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## API
|
|
@@ -44,7 +44,7 @@ function gridItemWidth(containerWidth: number) {
|
|
|
44
44
|
gridItemWidth(200)
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`bun fr` outputs:
|
|
47
|
+
`bun fr` (or `npx fr`) outputs:
|
|
48
48
|
|
|
49
49
|
```zsh
|
|
50
50
|
index.ts:9:1 - error [inferred-requirement]: call to gridItemWidth violates its nonzero divisor requirement (division at index.ts:6:10)
|
|
@@ -192,47 +192,49 @@ An `ensures` line assumes its `requires` and `assumes`. A requirement may be a r
|
|
|
192
192
|
|
|
193
193
|
Always read the coverage line. No findings does not mean an unsupported file is safe. A derived guarantee becoming weaker, for example `at least 54` becoming `at least 0`, appears in the audit rather than the shorter findings output.
|
|
194
194
|
|
|
195
|
-
## Analysis
|
|
195
|
+
## Analysis Scope
|
|
196
196
|
|
|
197
|
-
If
|
|
197
|
+
Freerange deliberately analyzes a restricted part of TypeScript. When code leaves that scope, `fr --audit` says so instead of guessing what the code does. If an unsupported pattern is important in production code and cannot be reasonably refactored, please file an issue. We're open to expanding the scope when the added complexity proves worthwhile.
|
|
198
198
|
|
|
199
199
|
### Numbers
|
|
200
200
|
|
|
201
|
-
Freerange's numeric analysis is designed
|
|
201
|
+
Freerange's numeric analysis is designed for layouts and other everyday application code. For each TypeScript `number`, Freerange remembers its lowest and highest possible values, whether it is an integer, whether it may be `NaN` or infinite, and at most one exact value that has been ruled out. For example, after `value !== 0`, Freerange remembers that `value` cannot be `0`.
|
|
202
202
|
|
|
203
|
-
Freerange does not keep
|
|
203
|
+
Freerange does not keep gaps or arbitrary sets of possible numbers. If one branch produces `1..2` and another produces `10..11`, Freerange keeps the combined range `1..11`. A later check that rules out a different exact value may replace the value remembered from an earlier check.
|
|
204
204
|
|
|
205
|
-
Freerange recognizes repeated uses of
|
|
205
|
+
Freerange recognizes repeated uses of the same stored value, including local aliases, stable property and array reads, lengths, and the same argument passed to multiple parameters of a function in the same file. For example, `const span = right - left; span - span` is exactly `0` unless `span` is infinite or `NaN`. Two separately evaluated expressions are not assumed to produce the same value just because their source code looks alike. Store the result in a local when the equality matters.
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
Freerange does not search for arbitrary relationships between different values or use a general-purpose theorem prover. Those approaches made earlier versions less predictable without proving much more real code. Freerange also reasons about JavaScript floating-point numbers rather than ideal real numbers. Real-number algebra would produce false guarantees because JavaScript arithmetic rounds and can overflow or underflow.
|
|
208
208
|
|
|
209
209
|
### Function calls
|
|
210
210
|
|
|
211
|
-
Freerange
|
|
211
|
+
Freerange analyzes supported function calls in the same file using what it knows at each call. It does not analyze imported functions. It reads an imported constant only when the constant resolves to a numeric literal such as `export const GAP = 24`. Literal default parameters work in supported calls; object and calculated defaults do not. Passing more arguments than the implementation declares is also unsupported.
|
|
212
|
+
|
|
213
|
+
Freerange does not guess what an unknown function does, when a callback runs, which exceptions are caught, how another reference might change an object, or how a framework schedules work.
|
|
212
214
|
|
|
213
215
|
### Static assertions
|
|
214
216
|
|
|
215
|
-
Inside a `console.assert`, Freerange can
|
|
217
|
+
Inside a `console.assert`, Freerange can prove several common UI calculations through named values: `Math.min` and `Math.max`, adding or subtracting a nonnegative value, multiplying both sides of a comparison by the same nonnegative value, `index % columnCount < columnCount` when `columnCount` is positive, and fields read from a newly created object. Freerange does not chain arbitrary comparisons: `left <= middle` and `middle <= right` do not by themselves prove `left <= right`.
|
|
216
218
|
|
|
217
219
|
### Loops
|
|
218
220
|
|
|
219
|
-
Freerange
|
|
221
|
+
Freerange checks a loop repeatedly until the possible values at the start of an iteration stop changing. It does not simulate every runtime iteration or try to derive a formula for the final value. Ordinary counting loops usually settle after two or three checks. If the possible values still change after 16 checks, Freerange stops analyzing that path.
|
|
220
222
|
|
|
221
223
|
### Objects and arrays
|
|
222
224
|
|
|
223
|
-
Freerange
|
|
225
|
+
Freerange reads plain objects, tuples, arrays, and tagged unions declared in your project through at most eight nested levels. A deeper property becomes unknown. A function is unsupported when one of its parameter types cannot be represented within this scope.
|
|
224
226
|
|
|
225
|
-
|
|
227
|
+
Freerange assumes that reading a property returns the same value and performs no work during one analyzed function call. A getter or Proxy that changes its answer or performs work is outside the scope. Property writes, including assignments that invoke setters, are unsupported. Object spread is also unsupported because JavaScript copies only an object's own enumerable properties, which may not match the fields declared by its TypeScript type.
|
|
226
228
|
|
|
227
229
|
### Caller requirements
|
|
228
230
|
|
|
229
|
-
Every plain `number` parameter must be finite and not `NaN`. The same rule applies to
|
|
231
|
+
Every plain `number` parameter must be finite and not `NaN`. The same rule applies to numeric fields in fixed-shape object parameters, even when the function does not read them. Numeric literal types such as `1 | 2` already satisfy the rule. Nullable numbers, arrays, tuples, and tagged unions use more specific `assumes` lines instead. When a caller omits an argument with a supported literal default, the default can satisfy the requirement automatically.
|
|
230
232
|
|
|
231
|
-
Calls to supported functions in the same file either prove these requirements,
|
|
233
|
+
Calls to supported functions in the same file either prove these requirements, require their own callers to satisfy them, or report a definitely invalid argument. After a call proves that an argument is finite, later uses of that same stored value can reuse the result. Writing `console.assert(Number.isFinite(value))` at the start of a function is allowed but normally redundant. `Number.isInteger(value)` is stronger and replaces the finite requirement.
|
|
232
234
|
|
|
233
|
-
Division and array reads can create additional requirements. Freerange tries to express each
|
|
235
|
+
Division and array reads can create additional requirements. Freerange tries to express each requirement using the function's parameters so that callers can be checked. Later operations can reuse the requirement only when they use the same stored value in the same branch. Assigning a new value or repeating the calculation starts over. Freerange traces each intermediate value at most once; if it cannot express the condition using the parameters, `fr --audit` prints a local `assumes` condition instead.
|
|
234
236
|
|
|
235
|
-
|
|
237
|
+
Code outside this scope may make a result less precise or stop analysis. Freerange does not publish a stronger guarantee by pretending that unsupported code was understood.
|
|
236
238
|
|
|
237
239
|
## Development
|
|
238
240
|
|
|
@@ -240,3 +242,7 @@ These limits may make a result less precise or stop analysis, but they cannot ma
|
|
|
240
242
|
bun install
|
|
241
243
|
bun run check
|
|
242
244
|
```
|
|
245
|
+
|
|
246
|
+
## Credits
|
|
247
|
+
|
|
248
|
+
[Infer](https://github.com/facebook/infer), [AlphaProof](https://deepmind.google/blog/ai-solves-imo-problems-at-silver-medal-level/)
|