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.

Library
npm
v0.3.0
23stars
Unlicense

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
56/100Fair
Development Activity76
Maintenance56
Community20
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
70/100Good
Architecture82
Code Quality78
Innovation74
Learning Curve45

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 Filter AST (Compare, Suffix, ValuePath, LogExp, NotFilter).
  • filter() - compiles a parsed AST into a (record) => boolean predicate usable with Array.prototype.filter.
  • stringify() - serializes an AST back into a valid SCIM filter query string.
  • expand() - rewrites an AST into disjunctive normal form by distributing and over or.
  • flatten() - collapses nested and/or groups of the same operator into a single flat list.
  • Configurable case sensitivity - an optional caseExact boolean or per-attribute resolver function for comparisons.

Common Use Cases

  • Implementing SCIM server-side filtering - parse the filter query parameter on /Users or /Groups endpoints 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 WHERE clauses, 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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search