scim2-parse-filter
Parses and evaluates RFC 7644 SCIM 2.0 filter expressions into a typed AST you can test, stringify, expand, or flatten in TypeScript.
Repository Health
Technical Analysis
scim2-parse-filter implements the SCIM 2.0 filter grammar defined in RFC 7644 section 3.4.2.2, turning filter query strings like userName eq "bob" into a typed abstract syntax tree. Beyond parsing, it ships a filter() function that compiles an AST into a JavaScript predicate for filtering arrays of records, stringify() to convert an AST back into a SCIM query string, expand() to distribute logical operators into disjunctive normal form, and flatten() to collapse nested boolean groups of the same operator.
The library targets SCIM server and client implementations — identity providers and service providers that need to support the ?filter= query parameter on endpoints like /Users and /Groups — without hand-rolling a filter grammar parser. It is a maintained fork of the abandoned scim2-filter package, with bug fixes and continued releases.
What You Get
- parse() - turns a SCIM filter query string into a typed
FilterAST (Compare,Suffix,ValuePath,LogExp,NotFilter). - filter() - compiles a parsed AST into a
(record) => booleanpredicate usable withArray.prototype.filter. - stringify() - serializes an AST back into a valid SCIM filter query string.
- expand() - rewrites an AST into disjunctive normal form by distributing
andoveror. - flatten() - collapses nested
and/orgroups of the same operator into a single flat list. - Configurable case sensitivity - an optional
caseExactboolean or per-attribute resolver function for comparisons.
Common Use Cases
- Implementing SCIM server-side filtering - parse the
filterquery parameter on/Usersor/Groupsendpoints and apply it against in-memory or database-backed records. - Validating SCIM client queries - check that a filter string a client sends is syntactically valid SCIM 2.0 before forwarding it to a datastore.
- Translating filters to another query language - walk the parsed AST to generate SQL
WHEREclauses, LDAP filters, or other backend-specific query syntax. - Normalizing complex filters - use
expand()/flatten()to simplify deeply nested boolean filter expressions before further processing.
Under The Hood
Architecture
The library is organized as a small set of single-purpose modules that share one central contract: the discriminated-union Filter AST type defined in index.ts (AttrExp, LogExp, ValuePath, NotFilter). parser.ts implements a hand-written tokenizer (a single regex-driven scanner) plus a Pratt-style operator-precedence parser (parseFilter/parseExpression/parseInxif) that turns a filter string into that AST. tester.ts defines a separate Tester class that walks the same AST recursively (a switch on f.op) to evaluate it against an arbitrary JS record, including array-aware attribute-path traversal (attrPath/attrTest) that handles dotted and colon-namespaced SCIM attribute paths. expand.ts and flatten.ts are independent AST-to-AST transforms — expand distributes and over or via a cartesian-product helper to produce DNF, flatten collapses nested same-operator groups — and stringify.ts performs the inverse of parse. Because every module pattern-matches on the same Filter union, any change to that type ripples predictably through parser, tester, expand, flatten, and stringify alike.
Tech Stack
A pure TypeScript library with zero runtime dependencies. Dev tooling: TypeScript (strict mode, ES6 target, CommonJS output via tsc to lib/), mocha with chai assertions and ts-node for running tests directly against .ts sources, plus @types/node/@types/chai/@types/mocha. npm overrides pin transitive diff and serialize-javascript versions, Travis CI builds, tests, and auto-publishes to npm on tagged releases, and SonarCloud runs static analysis on pushes.
Code Quality
Nine dedicated test files (over a thousand lines total) cover the tokenizer, parser, stringify, filter/tester semantics, attribute-path resolution, and the expand/flatten transforms — including a stringify.test.ts whose own comments note it mirrors parse.test.ts to keep parse/stringify inverses in sync, and a dedicated readme.test.ts that runs every README example verbatim so documentation can’t silently drift from behavior. tsconfig.json enables strict, noUnusedLocals, and noImplicitReturns. Malformed filter input throws descriptive Errors rather than failing silently. No linter or formatter configuration (ESLint/Prettier) is present in the repo.
API Design
The public surface is deliberately small and composable: five top-level functions (parse, filter, stringify, expand, flatten) plus a Tester class, each doing one job on the same plain-object AST, so callers can mix and match — parse then stringify, or parse then walk the AST to target an entirely different backend. The caseExact option (a boolean or a per-attribute resolver function) mirrors SCIM’s actual per-attribute case-sensitivity semantics rather than flattening it into one global flag. Because the README’s examples double as the test suite, the documented API and the tested API can’t drift apart, which is an above-average discipline for a low-level parser library — though the library intentionally stops at filter parsing/evaluation and leaves resource storage and SCIM error handling to the integrator.
Used by 4 apps in this directory
Budibase
Low Code Platforms · No Code Platforms
Build AI agents, automations, and internal apps on a single open-source platform with full self-hosting control.
GrowthBook
Developer Tools · Analytics · Monitoring
Open source feature flags, A/B testing, and warehouse-native experimentation that queries your existing data infrastructure—no data movement required.
Infisical
Security · Devops
The open-source platform for secrets, certificates, privileged access, and AI agent security — all in one self-hostable system.
Lightdash
Analytics · Data Engineering
The open-source Looker alternative that turns your dbt project's metrics and dimensions into governed, self-serve charts and dashboards — no license key required.