# JSON exact-value filter v1

Save `json-filter.mjs` beside `json-filter-example.mjs` containing this example.
Run `node json-filter-example.mjs` with Node 18+; no runtime dependencies or network.

```js
import assert from 'node:assert/strict';
import { filterJsonRecords } from './json-filter.mjs';
const input = JSON.stringify([
  { id: '001', status: 'ready', note: '=1+1' },
  { id: '002', status: 'draft', note: '' },
  { id: '003', status: 'ready', note: 'line\nnext' },
]);
const result = filterJsonRecords(input, 'status', 'ready');
assert.equal(result.records, 3);
assert.equal(result.matched, 2);
assert.equal(result.removed, 1);
assert.deepEqual(JSON.parse(result.output), [
  { id: '001', status: 'ready', note: '=1+1' },
  { id: '003', status: 'ready', note: 'line\nnext' },
]);
assert.equal(filterJsonRecords(input, 'note', '').output, '[{"id":"002","status":"draft","note":""}]');
console.log('JSON exact-value filter example passed.');
```

## Contract

`filterJsonRecords(input, field, value)` returns `{ output, records, matched, removed }`.
Supply a nonempty JSON array of flat objects with string values only, an exact
nonempty field name, and an exact match value. Empty match values are allowed.
The browser match boxes normalize line endings to LF; use this module for exact
CR or CRLF field names and values. In this module, spaces, case, tabs and newlines
are literal in both field and value; nothing is
trimmed or coerced. Every record must have the selected field as an own property.
The entire input is validated, including unmatched records and unselected fields.
Invalid JSON, non-array roots, empty input arrays, non-object records, and numbers,
booleans, null or nested values reject. No partial output is returned on error.
Errors identify positions without echoing pasted content.

Matching records keep their original order and all fields. Output is compact JSON
without a terminal newline. No matches is a successful `[]` output. Exact strings,
leading zeros, blanks, formula text, Unicode and controls are preserved. Formulas
are not evaluated. Prototype-like names remain inert own properties. Duplicate
JSON keys use the last parsed value; integer-like keys follow JSON.stringify order.

Limits: 100,000 characters in each input, field and value; 499 input records,
50 fields per record and 300,000 output characters, all measured as JavaScript
string length. Lone surrogates escape in output and count toward its limit.
No file writes, requests, Sheet writes or formula evaluation. A native export
plus a local Array.filter script is an alternative; no measured advantage claimed.

## Source parity

Generated from the exact browser `site/lib/json-filter.ts` core with
`node scripts/build-json-filter-source.mjs`; `--check` verifies bytes. Tests
compare results, errors and bounds and execute the example. Downloading is not
independent local execution. Input and output are not sent in analytics.

## License

MIT License

Copyright (c) 2026 QuasiLife

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
