@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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.0.2 - 2026-07-21
6
+
7
+ - Add node support (#2)
8
+
5
9
  ## 0.0.1 - 2026-07-20
6
10
 
7
11
  Initial public release of `@chenglou/freerange`.
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 code that can be refactored, especially code written or maintained by agents. It does not try to understand every TypeScript pattern. It accepts a small, predictable subset and gives concrete guidance for moving important calculations into that subset.
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 Limits
195
+ ## Analysis Scope
196
196
 
197
- If your production code actually needs support beyond these limits, please file an issue! We're open to relaxing the limits.
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 around arithmetic used in layouts and other everyday application code. For each TypeScript `number`, Freerange tracks one continuous range, whether the value is an integer, whether it may be `NaN` or infinite, and at most one exact number that a branch proved impossible. For example, after `value !== 0`, Freerange can remember that `value` cannot be `0`.
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 separate ranges 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 number may replace the number remembered from an earlier check.
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 one already-computed value, including aliases, stable property and array reads, lengths, and duplicate arguments passed to a supported function in the same file. For example, `const span = right - left; span - span` is exactly `0` unless `span` is infinite or `NaN`. Two separate evaluations are not assumed equal, even when their source code looks the same; store the result in a local when that identity matters.
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
- Earlier versions included an exact rational linear prover based on Farkas' lemma and other relational machinery. The current analyzer does not use that machinery or an SMT solver. In practice, those approaches made analysis unpredictable without proving much more real-world code. We also decided against analyzing numbers as real numbers, which would have been sweet, because doing so produces false proofs: floating-point arithmetic is not associative, rounds results, and can overflow or underflow.
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 follows calls to supported functions in the same file using the ranges known at each call. Imported functions are not followed. Imported constants are followed only when they resolve 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. Unknown calls, when callbacks run, caught exceptions, changes made through another reference to the same object, and most framework behavior remain outside the subset.
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 follow common UI calculations through named values: `Math.min` and `Math.max`, addition or subtraction by a nonnegative value, the same multiplication by a nonnegative value on both sides of a comparison, the fact that `index % columnCount < columnCount` when `columnCount` is positive, and fields read from a freshly constructed record. Freerange does not chain arbitrary comparisons: `left <= middle` and `middle <= right` do not by themselves prove `left <= right`.
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 analyzes a loop again until the ranges known at the start of an iteration stop changing. Freerange does not simulate every runtime iteration or try to produce a formula for the final value. Ordinary counting loops usually settle after two or three analysis passes. If the ranges still change after 16 passes, analysis stops for that path.
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 follows records, 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 its top-level input type cannot be represented.
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
- Property reads are assumed to be stable and side-effect-free during one analyzed function call. A getter or Proxy that changes its answer or performs work is outside the model. 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.
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 number 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 keep their more specific `assumes` lines instead. A supported literal default is used when an argument is omitted, so the default can satisfy the requirement automatically.
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, pass them outward to their own callers, or report a definitely invalid argument. A successful call also proves that the same stored argument is finite afterward. Writing `console.assert(Number.isFinite(value))` at the start of the function is allowed but normally redundant; `Number.isInteger(value)` is stronger and replaces the finite requirement.
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 one using the function's parameters so callers can be checked. A later operation on the same stored value and path may rely on that requirement; assigning a new value or repeating the calculation starts over. Freerange follows each intermediate calculation at most once. If one pass cannot reach the parameters, Freerange prints a local `assumes` condition instead.
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
- These limits may make a result less precise or stop analysis, but they cannot make a guarantee stronger than the code supports.
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/)