SCIM 2.0 filter parser with extended support for custom operators like in, nin, any, all, etc.
npm install scim2-astSCIM 2.0 filter parser and serializer for Node.js and the browser.
This package is an updated version of scim2-parse-filter (which itself was a fork of scim2-filter), modernized with new tooling, comprehensive tests, and extended with custom operators.
This library implements the filter syntax defined in RFC 7644 Section 3.4.2.2 (Filtering).
- Parse: Convert SCIM 2.0 filter strings into a typed Abstract Syntax Tree (AST).
- Filter: Execute the AST against JSON objects to filter results in memory.
- Stringify: Convert an AST back into a valid SCIM 2.0 filter string.
- Extended Operators: Support for in, nin, any, all, regexp, and nil (null) values.
- Modern Tooling: Built with TypeScript and tested with Vitest.
``bash`
npm install scim2-astor
pnpm add scim2-astor
yarn add scim2-ast
Convert a SCIM filter string into an AST object.
`typescript
import { parse } from 'scim2-ast';
const ast = parse('userName eq "bjensen"');
console.log(ast);
/*
Output:
{
op: "eq",
attrPath: "userName",
compValue: "bjensen"
}
*/
`
Compile a parsed AST into a predicate function to filter arrays of objects.
`typescript
import { parse, filter } from 'scim2-ast';
const users = [
{ userName: 'bjensen', active: true },
{ userName: 'jsmith', active: false }
];
const ast = parse('active eq true');
const predicate = filter(ast);
const activeUsers = users.filter(predicate);
// Result: [{ userName: 'bjensen', active: true }]
`
Convert an AST object back into a SCIM filter string.
`typescript
import { stringify } from 'scim2-ast';
const ast = {
op: "eq",
attrPath: "userName",
compValue: "bjensen"
};
const filterString = stringify(ast);
// Result: 'userName eq "bjensen"'
`
The following operators are defined in RFC 7644 Section 3.4.2.2.
| Operator | Description | Example String | Example AST |
|----------|-------------|----------------|-------------|
| eq | Equal | userName eq "bjensen" | { op: "eq", attrPath: "userName", compValue: "bjensen" } |ne
| | Not Equal | active ne false | { op: "ne", attrPath: "active", compValue: false } |co
| | Contains | title co "Manager" | { op: "co", attrPath: "title", compValue: "Manager" } |sw
| | Starts With | userName sw "b" | { op: "sw", attrPath: "userName", compValue: "b" } |ew
| | Ends With | userName ew "n" | { op: "ew", attrPath: "userName", compValue: "n" } |gt
| | Greater Than | meta.lastModified gt "2011-05-13" | { op: "gt", attrPath: "meta.lastModified", compValue: "2011-05-13" } |ge
| | Greater or Equal | age ge 18 | { op: "ge", attrPath: "age", compValue: 18 } |lt
| | Less Than | priority lt 5 | { op: "lt", attrPath: "priority", compValue: 5 } |le
| | Less or Equal | count le 10 | { op: "le", attrPath: "count", compValue: 10 } |pr
| | Present (Has value) | title pr | { op: "pr", attrPath: "title" } |and
| | Logical AND | title pr and active eq true | { op: "and", filters: [...] } |or
| | Logical OR | title pr or active eq true | { op: "or", filters: [...] } |not
| | Logical NOT | not (userType eq "Employee") | { op: "not", filter: { ... } } |
- eq: If the attribute is an array, eq checks if the comparison value exists within that array (standard SCIM behavior for multi-valued attributes).pr
- : Returns false if the attribute is an empty array [] (treats empty arrays as "not present").
These operators are not part of the standard SCIM 2.0 spec but are supported by this library for enhanced filtering capabilities.
#### in`
Checks if the attribute value exists in the provided array.typescript`
parse('userType in ["Employee", "Contractor"]');
// AST:
// {
// op: "in",
// attrPath: "userType",
// compValue: ["Employee", "Contractor"]
// }
#### nin (Not In)`
Checks if the attribute value does NOT exist in the provided array.typescript`
parse('userType nin ["Guest", "External"]');
// AST:
// {
// op: "nin",
// attrPath: "userType",
// compValue: ["Guest", "External"]
// }
#### any`
Checks if the array attribute has any intersection with the provided array (at least one match).typescript`
// Assuming 'roles' is an array like ["admin", "editor"]
parse('roles any ["admin", "root"]');
// AST:
// {
// op: "any",
// attrPath: "roles",
// compValue: ["admin", "root"]
// }
#### all`
Checks if the array attribute contains all of the values in the provided array.typescript`
// Assuming 'tags' is an array like ["red", "blue", "green"]
parse('tags all ["red", "blue"]');
// AST:
// {
// op: "all",
// attrPath: "tags",
// compValue: ["red", "blue"]
// }
#### regexp`
Checks if the attribute matches the regular expression pattern.typescript`
parse('userName regexp "^[a-z]+\\.[a-z]+$"');
// AST:
// {
// op: "regexp",
// attrPath: "userName",
// compValue: "^[a-z]+\\.[a-z]+$"
// }
(which parses to null).
`typescript
parse('name.middleName eq nil');
// AST:
// {
// op: "eq",
// attrPath: "name.middleName",
// compValue: null
// }
`Contributing
$3
`bash
pnpm build
`$3
`bash
pnpm test
``