@filtron/js
Advanced tools
+2
-2
@@ -1,4 +0,4 @@ | ||
| function K(c,y={}){let T={allowedFields:y.allowedFields?new Set(y.allowedFields):void 0,fieldAccessor:y.fieldAccessor??((S,A)=>S[A]),caseInsensitive:y.caseInsensitive??!1,fieldMapping:y.fieldMapping};return j(c,T)}function k(c,y){return y.fieldMapping?.[c]??c}function q(c,y){if(y.allowedFields&&!y.allowedFields.has(c))throw Error(`Field "${c}" is not allowed. Allowed fields: ${[...y.allowedFields].join(", ")}`)}function j(c,y){switch(c.type){case"or":return L(c,y);case"and":return Q(c,y);case"not":return U(c,y);case"comparison":return W(c,y);case"oneOf":return X(c,y);case"notOneOf":return Y(c,y);case"exists":return Z(c,y);case"booleanField":return _(c,y);case"range":return $(c,y);default:let T=c;throw Error(`Unknown node type: ${c.type}`)}}function L(c,y){let T=j(c.left,y),S=j(c.right,y);return(A)=>T(A)||S(A)}function Q(c,y){let T=j(c.left,y),S=j(c.right,y);return(A)=>T(A)&&S(A)}function U(c,y){let T=j(c.expression,y);return(S)=>!T(S)}function W(c,y){q(c.field,y);let T=k(c.field,y),S=H(c.value),A=z(c.operator,S,y);return(J)=>{let D=y.fieldAccessor(J,T);return A(D,S)}}function X(c,y){q(c.field,y);let T=k(c.field,y),S=c.values.map((J)=>H(J));if(S.length===0)return()=>!1;if(y.caseInsensitive){let J=S.map((D)=>typeof D==="string"?D.toLowerCase():D);return(D)=>{let I=y.fieldAccessor(D,T),B=typeof I==="string"?I.toLowerCase():I;return J.some((G)=>G===B)}}if(S.length<=4)return(J)=>{let D=y.fieldAccessor(J,T);return S.includes(D)};let A=new Set(S);return(J)=>{let D=y.fieldAccessor(J,T);return A.has(D)}}function Y(c,y){q(c.field,y);let T=k(c.field,y),S=c.values.map((J)=>H(J));if(S.length===0)return()=>!0;if(y.caseInsensitive){let J=S.map((D)=>typeof D==="string"?D.toLowerCase():D);return(D)=>{let I=y.fieldAccessor(D,T),B=typeof I==="string"?I.toLowerCase():I;return!J.some((G)=>G===B)}}if(S.length<=4)return(J)=>{let D=y.fieldAccessor(J,T);return!S.includes(D)};let A=new Set(S);return(J)=>{let D=y.fieldAccessor(J,T);return!A.has(D)}}function Z(c,y){q(c.field,y);let T=k(c.field,y);return(S)=>{let A=y.fieldAccessor(S,T);return A!==null&&A!==void 0}}function _(c,y){q(c.field,y);let T=k(c.field,y);return(S)=>{return y.fieldAccessor(S,T)===!0}}function $(c,y){q(c.field,y);let T=k(c.field,y);return(S)=>{let A=y.fieldAccessor(S,T);if(typeof A!=="number")return!1;return A>=c.min&&A<=c.max}}function z(c,y,T){switch(c){case"=":case":":if(T.caseInsensitive)return(A,J)=>{if(typeof A==="string"&&typeof J==="string")return A.toLowerCase()===J.toLowerCase();return A===J};return(A,J)=>A===J;case"!=":if(T.caseInsensitive)return(A,J)=>{if(typeof A==="string"&&typeof J==="string")return A.toLowerCase()!==J.toLowerCase();return A!==J};return(A,J)=>A!==J;case"~":if(T.caseInsensitive)return(A,J)=>{if(typeof A!=="string"||typeof J!=="string")return!1;return A.toLowerCase().includes(J.toLowerCase())};return(A,J)=>{if(typeof A!=="string"||typeof J!=="string")return!1;return A.includes(J)};case">":return(A,J)=>{if(typeof A!=="number"||typeof J!=="number")return!1;return A>J};case">=":return(A,J)=>{if(typeof A!=="number"||typeof J!=="number")return!1;return A>=J};case"<":return(A,J)=>{if(typeof A!=="number"||typeof J!=="number")return!1;return A<J};case"<=":return(A,J)=>{if(typeof A!=="number"||typeof J!=="number")return!1;return A<=J};default:let S=c;throw Error(`Unknown operator: ${c}`)}}function H(c){switch(c.type){case"string":return c.value;case"number":return c.value;case"boolean":return c.value;case"identifier":return c.value;default:let y=c;throw Error(`Unknown value type: ${c.type}`)}}function E(c="."){let y=new Map;return(T,S)=>{let A=y.get(S);if(!A)A=S.split(c),y.set(S,A);let J=T;for(let D of A){if(J===null||J===void 0)return;if(typeof J!=="object")return;J=J[D]}return J}}export{K as toFilter,E as nestedAccessor}; | ||
| function U(y,A={}){let k={allowedFields:A.allowedFields?new Set(A.allowedFields):void 0,fieldAccessor:A.fieldAccessor??((J,j)=>J[j]),caseInsensitive:A.caseInsensitive??!1,fieldMapping:A.fieldMapping};return H(y,k)}function K(y,A){return A.fieldMapping?.[y]??y}function L(y,A){if(A.allowedFields&&!A.allowedFields.has(y))throw Error(`Field "${y}" is not allowed. Allowed fields: ${[...A.allowedFields].join(", ")}`)}function H(y,A){switch(y.type){case"or":return W(y,A);case"and":return X(y,A);case"not":return Y(y,A);case"comparison":return Z(y,A);case"oneOf":return $(y,A);case"notOneOf":return z(y,A);case"exists":return C(y,A);case"booleanField":return E(y,A);case"range":return M(y,A);default:let k=y;throw Error(`Unknown node type: ${y.type}`)}}function W(y,A){let k=H(y.left,A),J=H(y.right,A);return(j)=>k(j)||J(j)}function X(y,A){let k=H(y.left,A),J=H(y.right,A);return(j)=>k(j)&&J(j)}function Y(y,A){let k=H(y.expression,A);return(J)=>!k(J)}function Z(y,A){L(y.field,A);let k=K(y.field,A),J=S(y.value),j=A.fieldAccessor;switch(y.operator){case"=":case":":if(A.caseInsensitive&&typeof J==="string"){let D=J.toLowerCase();return(I)=>{let q=j(I,k);return typeof q==="string"?q.toLowerCase()===D:q===J}}return(D)=>j(D,k)===J;case"!=":if(A.caseInsensitive&&typeof J==="string"){let D=J.toLowerCase();return(I)=>{let q=j(I,k);return typeof q==="string"?q.toLowerCase()!==D:q!==J}}return(D)=>j(D,k)!==J;case"~":if(typeof J!=="string")return()=>!1;if(A.caseInsensitive){let D=J.toLowerCase();return(I)=>{let q=j(I,k);return typeof q==="string"&&q.toLowerCase().includes(D)}}return(D)=>{let I=j(D,k);return typeof I==="string"&&I.includes(J)};case">":if(typeof J!=="number")return()=>!1;return(D)=>{let I=j(D,k);return typeof I==="number"&&I>J};case">=":if(typeof J!=="number")return()=>!1;return(D)=>{let I=j(D,k);return typeof I==="number"&&I>=J};case"<":if(typeof J!=="number")return()=>!1;return(D)=>{let I=j(D,k);return typeof I==="number"&&I<J};case"<=":if(typeof J!=="number")return()=>!1;return(D)=>{let I=j(D,k);return typeof I==="number"&&I<=J};default:let B=y.operator;throw Error(`Unknown operator: ${B}`)}}function $(y,A){L(y.field,A);let k=K(y.field,A),J=y.values.map((D)=>S(D)),j=A.fieldAccessor;if(J.length===0)return()=>!1;if(A.caseInsensitive){let D=J.map((I)=>typeof I==="string"?I.toLowerCase():I);if(D.length>12){let I=new Set(D);return(q)=>{let G=j(q,k),Q=typeof G==="string"?G.toLowerCase():G;return I.has(Q)}}return(I)=>{let q=j(I,k),G=typeof q==="string"?q.toLowerCase():q;return D.includes(G)}}if(J.length<=12)return(D)=>J.includes(j(D,k));let B=new Set(J);return(D)=>B.has(j(D,k))}function z(y,A){L(y.field,A);let k=K(y.field,A),J=y.values.map((D)=>S(D)),j=A.fieldAccessor;if(J.length===0)return()=>!0;if(A.caseInsensitive){let D=J.map((I)=>typeof I==="string"?I.toLowerCase():I);if(D.length>12){let I=new Set(D);return(q)=>{let G=j(q,k),Q=typeof G==="string"?G.toLowerCase():G;return!I.has(Q)}}return(I)=>{let q=j(I,k),G=typeof q==="string"?q.toLowerCase():q;return!D.includes(G)}}if(J.length<=12)return(D)=>!J.includes(j(D,k));let B=new Set(J);return(D)=>!B.has(j(D,k))}function C(y,A){L(y.field,A);let k=K(y.field,A),J=A.fieldAccessor;return(j)=>{let B=J(j,k);return B!==null&&B!==void 0}}function E(y,A){L(y.field,A);let k=K(y.field,A),J=A.fieldAccessor;return(j)=>J(j,k)===!0}function M(y,A){L(y.field,A);let k=K(y.field,A),J=A.fieldAccessor,{min:j,max:B}=y;return(D)=>{let I=J(D,k);return typeof I==="number"&&I>=j&&I<=B}}function S(y){switch(y.type){case"string":return y.value;case"number":return y.value;case"boolean":return y.value;case"identifier":return y.value;default:let A=y;throw Error(`Unknown value type: ${y.type}`)}}function P(y="."){let A=new Map;return(k,J)=>{let j=A.get(J);if(!j)j=J.split(y),A.set(J,j);let B=k;for(let D of j){if(B===null||B===void 0)return;if(typeof B!=="object")return;B=B[D]}return B}}export{U as toFilter,P as nestedAccessor}; | ||
| //# debugId=986AD4A645D7DA5764756E2164756E21 | ||
| //# debugId=03072F786574590F64756E2164756E21 | ||
| //# sourceMappingURL=index.js.map |
@@ -5,7 +5,7 @@ { | ||
| "sourcesContent": [ | ||
| "/**\n * In-memory JavaScript filter for Filtron AST\n * Converts Filtron AST nodes to predicate functions for filtering arrays\n */\n\nimport type {\n\tASTNode,\n\tValue,\n\tComparisonOperator,\n\tOrExpression,\n\tAndExpression,\n\tNotExpression,\n\tComparisonExpression,\n\tOneOfExpression,\n\tNotOneOfExpression,\n\tExistsExpression,\n\tBooleanFieldExpression,\n\tRangeExpression,\n} from \"@filtron/core\";\n\n/**\n * Predicate function type that filters objects\n */\nexport type FilterPredicate<T = Record<string, unknown>> = (item: T) => boolean;\n\n/**\n * Filter generation options\n */\nexport interface FilterOptions {\n\t/**\n\t * Allowed field names for filtering\n\t * If provided, only these fields can be used in queries\n\t * Throws an error if a query references a field not in this list\n\t * @default undefined (all fields allowed)\n\t */\n\tallowedFields?: string[];\n\n\t/**\n\t * Custom field accessor function\n\t * Useful for accessing nested properties or transforming field names\n\t * @default (obj, field) => obj[field]\n\t *\n\t * @example\n\t * ```typescript\n\t * // Access nested properties using dot notation\n\t * toFilter(ast, {\n\t * fieldAccessor: (obj, field) => {\n\t * return field.split('.').reduce((o, k) => o?.[k], obj);\n\t * }\n\t * })\n\t * ```\n\t */\n\tfieldAccessor?: (obj: Record<string, unknown>, field: string) => unknown;\n\n\t/**\n\t * Case-insensitive string comparisons\n\t * Applies to equals (=, :) and contains (~) operators\n\t * @default false\n\t */\n\tcaseInsensitive?: boolean;\n\n\t/**\n\t * Maps query field names to object property names\n\t * Useful for exposing different field names in queries than in your data model\n\t * @default undefined (no mapping)\n\t *\n\t * @example\n\t * ```typescript\n\t * // Allow queries to use 'email' but map to 'emailAddress' in objects\n\t * toFilter(ast, {\n\t * fieldMapping: {\n\t * 'email': 'emailAddress',\n\t * 'name': 'fullName'\n\t * }\n\t * })\n\t * ```\n\t */\n\tfieldMapping?: Record<string, string>;\n}\n\n/**\n * Internal state for filter generation\n */\ninterface GeneratorState {\n\tallowedFields?: Set<string>;\n\tfieldAccessor: (obj: Record<string, unknown>, field: string) => unknown;\n\tcaseInsensitive: boolean;\n\tfieldMapping?: Record<string, string>;\n}\n\n/**\n * Converts a Filtron AST to a predicate function for filtering arrays\n *\n * @param ast - The Filtron AST node to convert\n * @param options - Filter generation options\n * @returns A predicate function that can be used with Array.filter()\n *\n * @example\n * ```typescript\n * import { parse } from '@filtron/core';\n * import { toFilter } from '@filtron/js';\n *\n * const result = parse('age > 18 AND status = \"active\"');\n * if (result.success) {\n * const filter = toFilter(result.ast);\n *\n * const users = [\n * { name: 'Alice', age: 25, status: 'active' },\n * { name: 'Bob', age: 16, status: 'active' },\n * { name: 'Charlie', age: 30, status: 'inactive' },\n * ];\n *\n * const filtered = users.filter(filter);\n * // [{ name: 'Alice', age: 25, status: 'active' }]\n * }\n * ```\n */\nexport function toFilter<T extends Record<string, unknown> = Record<string, unknown>>(\n\tast: ASTNode,\n\toptions: FilterOptions = {},\n): FilterPredicate<T> {\n\tconst state: GeneratorState = {\n\t\tallowedFields: options.allowedFields ? new Set(options.allowedFields) : undefined,\n\t\tfieldAccessor: options.fieldAccessor ?? ((obj, field) => obj[field]),\n\t\tcaseInsensitive: options.caseInsensitive ?? false,\n\t\tfieldMapping: options.fieldMapping,\n\t};\n\n\treturn generateFilter(ast, state) as FilterPredicate<T>;\n}\n\n/**\n * Resolves a field name using fieldMapping if provided\n */\nfunction resolveFieldName(field: string, state: GeneratorState): string {\n\treturn state.fieldMapping?.[field] ?? field;\n}\n\n/**\n * Validates that a field is allowed (if allowedFields is set)\n */\nfunction validateField(field: string, state: GeneratorState): void {\n\tif (state.allowedFields && !state.allowedFields.has(field)) {\n\t\tthrow new Error(\n\t\t\t`Field \"${field}\" is not allowed. Allowed fields: ${[...state.allowedFields].join(\", \")}`,\n\t\t);\n\t}\n}\n\n/**\n * Recursively generates filter predicates from AST nodes\n */\nfunction generateFilter(\n\tnode: ASTNode,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tswitch (node.type) {\n\t\tcase \"or\":\n\t\t\treturn generateOr(node, state);\n\t\tcase \"and\":\n\t\t\treturn generateAnd(node, state);\n\t\tcase \"not\":\n\t\t\treturn generateNot(node, state);\n\t\tcase \"comparison\":\n\t\t\treturn generateComparison(node, state);\n\t\tcase \"oneOf\":\n\t\t\treturn generateOneOf(node, state);\n\t\tcase \"notOneOf\":\n\t\t\treturn generateNotOneOf(node, state);\n\t\tcase \"exists\":\n\t\t\treturn generateExists(node, state);\n\t\tcase \"booleanField\":\n\t\t\treturn generateBooleanField(node, state);\n\t\tcase \"range\":\n\t\t\treturn generateRange(node, state);\n\t\tdefault:\n\t\t\t// TypeScript exhaustiveness check\n\t\t\tconst _exhaustive: never = node;\n\t\t\tthrow new Error(`Unknown node type: ${(node as ASTNode).type}`);\n\t}\n}\n\n/**\n * Generates predicate for OR expression\n */\nfunction generateOr(\n\tnode: OrExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tconst left = generateFilter(node.left, state);\n\tconst right = generateFilter(node.right, state);\n\treturn (item) => left(item) || right(item);\n}\n\n/**\n * Generates predicate for AND expression\n */\nfunction generateAnd(\n\tnode: AndExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tconst left = generateFilter(node.left, state);\n\tconst right = generateFilter(node.right, state);\n\treturn (item) => left(item) && right(item);\n}\n\n/**\n * Generates predicate for NOT expression\n */\nfunction generateNot(\n\tnode: NotExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tconst expr = generateFilter(node.expression, state);\n\treturn (item) => !expr(item);\n}\n\n/**\n * Generates predicate for comparison expression\n */\nfunction generateComparison(\n\tnode: ComparisonExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst value = extractValue(node.value);\n\tconst compareFn = getComparisonFunction(node.operator, value, state);\n\n\treturn (item) => {\n\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\treturn compareFn(fieldValue, value);\n\t};\n}\n\n/**\n * Generates predicate for one-of expression\n */\nfunction generateOneOf(\n\tnode: OneOfExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst values = node.values.map((v: Value) => extractValue(v));\n\n\tif (values.length === 0) {\n\t\t// Empty IN clause - always false\n\t\treturn () => false;\n\t}\n\n\tif (state.caseInsensitive) {\n\t\tconst lowerValues = values.map((v: string | number | boolean) =>\n\t\t\ttypeof v === \"string\" ? v.toLowerCase() : v,\n\t\t);\n\t\treturn (item) => {\n\t\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\t\tconst compareValue = typeof fieldValue === \"string\" ? fieldValue.toLowerCase() : fieldValue;\n\t\t\treturn lowerValues.some((v: string | number | boolean) => v === compareValue);\n\t\t};\n\t}\n\n\t// For small arrays (<=4 items), Array.includes is faster than Set lookup\n\tif (values.length <= 4) {\n\t\treturn (item) => {\n\t\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\t\treturn values.includes(fieldValue as string | number | boolean);\n\t\t};\n\t}\n\n\t// Use Set for O(1) lookup on larger lists\n\tconst valueSet = new Set(values);\n\treturn (item) => {\n\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\treturn valueSet.has(fieldValue as string | number | boolean);\n\t};\n}\n\n/**\n * Generates predicate for not-one-of expression\n */\nfunction generateNotOneOf(\n\tnode: NotOneOfExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst values = node.values.map((v: Value) => extractValue(v));\n\n\tif (values.length === 0) {\n\t\t// Empty NOT IN clause - always true\n\t\treturn () => true;\n\t}\n\n\tif (state.caseInsensitive) {\n\t\tconst lowerValues = values.map((v: string | number | boolean) =>\n\t\t\ttypeof v === \"string\" ? v.toLowerCase() : v,\n\t\t);\n\t\treturn (item) => {\n\t\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\t\tconst compareValue = typeof fieldValue === \"string\" ? fieldValue.toLowerCase() : fieldValue;\n\t\t\treturn !lowerValues.some((v: string | number | boolean) => v === compareValue);\n\t\t};\n\t}\n\n\t// For small arrays (<=4 items), Array.includes is faster than Set lookup\n\tif (values.length <= 4) {\n\t\treturn (item) => {\n\t\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\t\treturn !values.includes(fieldValue as string | number | boolean);\n\t\t};\n\t}\n\n\t// Use Set for O(1) lookup on larger lists\n\tconst valueSet = new Set(values);\n\treturn (item) => {\n\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\treturn !valueSet.has(fieldValue as string | number | boolean);\n\t};\n}\n\n/**\n * Generates predicate for exists expression\n */\nfunction generateExists(\n\tnode: ExistsExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\treturn (item) => {\n\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\treturn fieldValue !== null && fieldValue !== undefined;\n\t};\n}\n\n/**\n * Generates predicate for boolean field expression\n */\nfunction generateBooleanField(\n\tnode: BooleanFieldExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\treturn (item) => {\n\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\treturn fieldValue === true;\n\t};\n}\n\n/**\n * Generates predicate for range expression (BETWEEN)\n */\nfunction generateRange(\n\tnode: RangeExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\treturn (item) => {\n\t\tconst fieldValue = state.fieldAccessor(item, mappedField);\n\t\tif (typeof fieldValue !== \"number\") {\n\t\t\treturn false;\n\t\t}\n\t\treturn fieldValue >= node.min && fieldValue <= node.max;\n\t};\n}\n\n/**\n * Returns a comparison function for the given operator\n */\nfunction getComparisonFunction(\n\toperator: ComparisonOperator,\n\t_targetValue: string | number | boolean,\n\tstate: GeneratorState,\n): (fieldValue: unknown, targetValue: string | number | boolean) => boolean {\n\tswitch (operator) {\n\t\tcase \"=\":\n\t\tcase \":\":\n\t\t\tif (state.caseInsensitive) {\n\t\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\t\tif (typeof fieldValue === \"string\" && typeof targetValue === \"string\") {\n\t\t\t\t\t\treturn fieldValue.toLowerCase() === targetValue.toLowerCase();\n\t\t\t\t\t}\n\t\t\t\t\treturn fieldValue === targetValue;\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn (fieldValue, targetValue) => fieldValue === targetValue;\n\n\t\tcase \"!=\":\n\t\t\tif (state.caseInsensitive) {\n\t\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\t\tif (typeof fieldValue === \"string\" && typeof targetValue === \"string\") {\n\t\t\t\t\t\treturn fieldValue.toLowerCase() !== targetValue.toLowerCase();\n\t\t\t\t\t}\n\t\t\t\t\treturn fieldValue !== targetValue;\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn (fieldValue, targetValue) => fieldValue !== targetValue;\n\n\t\tcase \"~\":\n\t\t\t// Contains/LIKE - uses substring matching\n\t\t\tif (state.caseInsensitive) {\n\t\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\t\tif (typeof fieldValue !== \"string\" || typeof targetValue !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn fieldValue.toLowerCase().includes(targetValue.toLowerCase());\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\tif (typeof fieldValue !== \"string\" || typeof targetValue !== \"string\") {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn fieldValue.includes(targetValue);\n\t\t\t};\n\n\t\tcase \">\":\n\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\tif (typeof fieldValue !== \"number\" || typeof targetValue !== \"number\") {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn fieldValue > targetValue;\n\t\t\t};\n\n\t\tcase \">=\":\n\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\tif (typeof fieldValue !== \"number\" || typeof targetValue !== \"number\") {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn fieldValue >= targetValue;\n\t\t\t};\n\n\t\tcase \"<\":\n\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\tif (typeof fieldValue !== \"number\" || typeof targetValue !== \"number\") {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn fieldValue < targetValue;\n\t\t\t};\n\n\t\tcase \"<=\":\n\t\t\treturn (fieldValue, targetValue) => {\n\t\t\t\tif (typeof fieldValue !== \"number\" || typeof targetValue !== \"number\") {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn fieldValue <= targetValue;\n\t\t\t};\n\n\t\tdefault:\n\t\t\tconst _exhaustive: never = operator;\n\t\t\tthrow new Error(`Unknown operator: ${operator as string}`);\n\t}\n}\n\n/**\n * Extracts the primitive value from a Filtron Value node\n */\nfunction extractValue(value: Value): string | number | boolean {\n\tswitch (value.type) {\n\t\tcase \"string\":\n\t\t\treturn value.value;\n\t\tcase \"number\":\n\t\t\treturn value.value;\n\t\tcase \"boolean\":\n\t\t\treturn value.value;\n\t\tcase \"identifier\":\n\t\t\t// Identifiers are treated as strings\n\t\t\treturn value.value;\n\t\tdefault:\n\t\t\tconst _exhaustive: never = value;\n\t\t\tthrow new Error(`Unknown value type: ${(value as Value).type}`);\n\t}\n}\n\n/**\n * Creates a field accessor for nested properties using dot notation\n *\n * @param separator - The separator used in field names (default: \".\")\n * @returns A field accessor function\n *\n * @example\n * ```typescript\n * const filter = toFilter(ast, {\n * fieldAccessor: nestedAccessor()\n * });\n *\n * // Now you can query nested fields like \"user.address.city\"\n * const items = data.filter(filter);\n * ```\n */\nexport function nestedAccessor(\n\tseparator: string = \".\",\n): (obj: Record<string, unknown>, field: string) => unknown {\n\t// Memoize field splits to avoid repeated string splitting\n\tconst fieldPartsCache = new Map<string, string[]>();\n\n\treturn (obj: Record<string, unknown>, field: string): unknown => {\n\t\tlet parts = fieldPartsCache.get(field);\n\t\tif (!parts) {\n\t\t\tparts = field.split(separator);\n\t\t\tfieldPartsCache.set(field, parts);\n\t\t}\n\n\t\tlet current: unknown = obj;\n\n\t\tfor (const part of parts) {\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (typeof current !== \"object\") {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tcurrent = (current as Record<string, unknown>)[part];\n\t\t}\n\n\t\treturn current;\n\t};\n}\n" | ||
| "/**\n * In-memory JavaScript filter for Filtron AST\n * Converts Filtron AST nodes to predicate functions for filtering arrays\n */\n\nimport type {\n\tASTNode,\n\tValue,\n\tOrExpression,\n\tAndExpression,\n\tNotExpression,\n\tComparisonExpression,\n\tOneOfExpression,\n\tNotOneOfExpression,\n\tExistsExpression,\n\tBooleanFieldExpression,\n\tRangeExpression,\n} from \"@filtron/core\";\n\n/**\n * Predicate function type that filters objects\n */\nexport type FilterPredicate<T = Record<string, unknown>> = (item: T) => boolean;\n\n/**\n * Filter generation options\n */\nexport interface FilterOptions {\n\t/**\n\t * Allowed field names for filtering\n\t * If provided, only these fields can be used in queries\n\t * Throws an error if a query references a field not in this list\n\t * @default undefined (all fields allowed)\n\t */\n\tallowedFields?: string[];\n\n\t/**\n\t * Custom field accessor function\n\t * Useful for accessing nested properties or transforming field names\n\t * @default (obj, field) => obj[field]\n\t *\n\t * @example\n\t * ```typescript\n\t * // Access nested properties using dot notation\n\t * toFilter(ast, {\n\t * fieldAccessor: (obj, field) => {\n\t * return field.split('.').reduce((o, k) => o?.[k], obj);\n\t * }\n\t * })\n\t * ```\n\t */\n\tfieldAccessor?: (obj: Record<string, unknown>, field: string) => unknown;\n\n\t/**\n\t * Case-insensitive string comparisons\n\t * Applies to equals (=, :) and contains (~) operators\n\t * @default false\n\t */\n\tcaseInsensitive?: boolean;\n\n\t/**\n\t * Maps query field names to object property names\n\t * Useful for exposing different field names in queries than in your data model\n\t * @default undefined (no mapping)\n\t *\n\t * @example\n\t * ```typescript\n\t * // Allow queries to use 'email' but map to 'emailAddress' in objects\n\t * toFilter(ast, {\n\t * fieldMapping: {\n\t * 'email': 'emailAddress',\n\t * 'name': 'fullName'\n\t * }\n\t * })\n\t * ```\n\t */\n\tfieldMapping?: Record<string, string>;\n}\n\n/**\n * Internal state for filter generation\n */\ninterface GeneratorState {\n\tallowedFields?: Set<string>;\n\tfieldAccessor: (obj: Record<string, unknown>, field: string) => unknown;\n\tcaseInsensitive: boolean;\n\tfieldMapping?: Record<string, string>;\n}\n\n/**\n * Converts a Filtron AST to a predicate function for filtering arrays\n *\n * @param ast - The Filtron AST node to convert\n * @param options - Filter generation options\n * @returns A predicate function that can be used with Array.filter()\n *\n * @example\n * ```typescript\n * import { parse } from '@filtron/core';\n * import { toFilter } from '@filtron/js';\n *\n * const result = parse('age > 18 AND status = \"active\"');\n * if (result.success) {\n * const filter = toFilter(result.ast);\n *\n * const users = [\n * { name: 'Alice', age: 25, status: 'active' },\n * { name: 'Bob', age: 16, status: 'active' },\n * { name: 'Charlie', age: 30, status: 'inactive' },\n * ];\n *\n * const filtered = users.filter(filter);\n * // [{ name: 'Alice', age: 25, status: 'active' }]\n * }\n * ```\n */\nexport function toFilter<T extends Record<string, unknown> = Record<string, unknown>>(\n\tast: ASTNode,\n\toptions: FilterOptions = {},\n): FilterPredicate<T> {\n\tconst state: GeneratorState = {\n\t\tallowedFields: options.allowedFields ? new Set(options.allowedFields) : undefined,\n\t\tfieldAccessor: options.fieldAccessor ?? ((obj, field) => obj[field]),\n\t\tcaseInsensitive: options.caseInsensitive ?? false,\n\t\tfieldMapping: options.fieldMapping,\n\t};\n\n\treturn generateFilter(ast, state) as FilterPredicate<T>;\n}\n\n/**\n * Resolves a field name using fieldMapping if provided\n */\nfunction resolveFieldName(field: string, state: GeneratorState): string {\n\treturn state.fieldMapping?.[field] ?? field;\n}\n\n/**\n * Validates that a field is allowed (if allowedFields is set)\n */\nfunction validateField(field: string, state: GeneratorState): void {\n\tif (state.allowedFields && !state.allowedFields.has(field)) {\n\t\tthrow new Error(\n\t\t\t`Field \"${field}\" is not allowed. Allowed fields: ${[...state.allowedFields].join(\", \")}`,\n\t\t);\n\t}\n}\n\n/**\n * Recursively generates filter predicates from AST nodes\n */\nfunction generateFilter(\n\tnode: ASTNode,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tswitch (node.type) {\n\t\tcase \"or\":\n\t\t\treturn generateOr(node, state);\n\t\tcase \"and\":\n\t\t\treturn generateAnd(node, state);\n\t\tcase \"not\":\n\t\t\treturn generateNot(node, state);\n\t\tcase \"comparison\":\n\t\t\treturn generateComparison(node, state);\n\t\tcase \"oneOf\":\n\t\t\treturn generateOneOf(node, state);\n\t\tcase \"notOneOf\":\n\t\t\treturn generateNotOneOf(node, state);\n\t\tcase \"exists\":\n\t\t\treturn generateExists(node, state);\n\t\tcase \"booleanField\":\n\t\t\treturn generateBooleanField(node, state);\n\t\tcase \"range\":\n\t\t\treturn generateRange(node, state);\n\t\tdefault:\n\t\t\t// TypeScript exhaustiveness check\n\t\t\tconst _exhaustive: never = node;\n\t\t\tthrow new Error(`Unknown node type: ${(node as ASTNode).type}`);\n\t}\n}\n\n/**\n * Generates predicate for OR expression\n */\nfunction generateOr(\n\tnode: OrExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tconst left = generateFilter(node.left, state);\n\tconst right = generateFilter(node.right, state);\n\treturn (item) => left(item) || right(item);\n}\n\n/**\n * Generates predicate for AND expression\n */\nfunction generateAnd(\n\tnode: AndExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tconst left = generateFilter(node.left, state);\n\tconst right = generateFilter(node.right, state);\n\treturn (item) => left(item) && right(item);\n}\n\n/**\n * Generates predicate for NOT expression\n */\nfunction generateNot(\n\tnode: NotExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tconst expr = generateFilter(node.expression, state);\n\treturn (item) => !expr(item);\n}\n\n/**\n * Generates predicate for comparison expression\n */\nfunction generateComparison(\n\tnode: ComparisonExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst targetValue = extractValue(node.value);\n\tconst accessor = state.fieldAccessor;\n\n\t// Inline simple comparisons to avoid function call overhead\n\tswitch (node.operator) {\n\t\tcase \"=\":\n\t\tcase \":\":\n\t\t\tif (state.caseInsensitive && typeof targetValue === \"string\") {\n\t\t\t\tconst lowerTarget = targetValue.toLowerCase();\n\t\t\t\treturn (item) => {\n\t\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\t\treturn typeof fieldValue === \"string\"\n\t\t\t\t\t\t? fieldValue.toLowerCase() === lowerTarget\n\t\t\t\t\t\t: fieldValue === targetValue;\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn (item) => accessor(item, mappedField) === targetValue;\n\n\t\tcase \"!=\":\n\t\t\tif (state.caseInsensitive && typeof targetValue === \"string\") {\n\t\t\t\tconst lowerTarget = targetValue.toLowerCase();\n\t\t\t\treturn (item) => {\n\t\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\t\treturn typeof fieldValue === \"string\"\n\t\t\t\t\t\t? fieldValue.toLowerCase() !== lowerTarget\n\t\t\t\t\t\t: fieldValue !== targetValue;\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn (item) => accessor(item, mappedField) !== targetValue;\n\n\t\tcase \"~\":\n\t\t\tif (typeof targetValue !== \"string\") {\n\t\t\t\treturn () => false;\n\t\t\t}\n\t\t\tif (state.caseInsensitive) {\n\t\t\t\tconst lowerTarget = targetValue.toLowerCase();\n\t\t\t\treturn (item) => {\n\t\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\t\treturn typeof fieldValue === \"string\" && fieldValue.toLowerCase().includes(lowerTarget);\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\treturn typeof fieldValue === \"string\" && fieldValue.includes(targetValue);\n\t\t\t};\n\n\t\tcase \">\":\n\t\t\tif (typeof targetValue !== \"number\") return () => false;\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\treturn typeof fieldValue === \"number\" && fieldValue > targetValue;\n\t\t\t};\n\n\t\tcase \">=\":\n\t\t\tif (typeof targetValue !== \"number\") return () => false;\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\treturn typeof fieldValue === \"number\" && fieldValue >= targetValue;\n\t\t\t};\n\n\t\tcase \"<\":\n\t\t\tif (typeof targetValue !== \"number\") return () => false;\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\treturn typeof fieldValue === \"number\" && fieldValue < targetValue;\n\t\t\t};\n\n\t\tcase \"<=\":\n\t\t\tif (typeof targetValue !== \"number\") return () => false;\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\treturn typeof fieldValue === \"number\" && fieldValue <= targetValue;\n\t\t\t};\n\n\t\tdefault:\n\t\t\tconst _exhaustive: never = node.operator;\n\t\t\tthrow new Error(`Unknown operator: ${_exhaustive as string}`);\n\t}\n}\n\n/**\n * Generates predicate for one-of expression\n */\nfunction generateOneOf(\n\tnode: OneOfExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst values = node.values.map((v: Value) => extractValue(v));\n\tconst accessor = state.fieldAccessor;\n\n\tif (values.length === 0) {\n\t\treturn () => false;\n\t}\n\n\tif (state.caseInsensitive) {\n\t\tconst lowerValues = values.map((v: string | number | boolean) =>\n\t\t\ttypeof v === \"string\" ? v.toLowerCase() : v,\n\t\t);\n\t\t// Use Set for O(1) lookup on larger lists (threshold ~12 based on typical benchmarks)\n\t\tif (lowerValues.length > 12) {\n\t\t\tconst valueSet = new Set(lowerValues);\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\tconst compareValue = typeof fieldValue === \"string\" ? fieldValue.toLowerCase() : fieldValue;\n\t\t\t\treturn valueSet.has(compareValue as string | number | boolean);\n\t\t\t};\n\t\t}\n\t\treturn (item) => {\n\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\tconst compareValue = typeof fieldValue === \"string\" ? fieldValue.toLowerCase() : fieldValue;\n\t\t\treturn lowerValues.includes(compareValue as string | number | boolean);\n\t\t};\n\t}\n\n\t// For small arrays (<=12 items), Array.includes is faster than Set lookup\n\tif (values.length <= 12) {\n\t\treturn (item) => values.includes(accessor(item, mappedField) as string | number | boolean);\n\t}\n\n\t// Use Set for O(1) lookup on larger lists\n\tconst valueSet = new Set(values);\n\treturn (item) => valueSet.has(accessor(item, mappedField) as string | number | boolean);\n}\n\n/**\n * Generates predicate for not-one-of expression\n */\nfunction generateNotOneOf(\n\tnode: NotOneOfExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst values = node.values.map((v: Value) => extractValue(v));\n\tconst accessor = state.fieldAccessor;\n\n\tif (values.length === 0) {\n\t\treturn () => true;\n\t}\n\n\tif (state.caseInsensitive) {\n\t\tconst lowerValues = values.map((v: string | number | boolean) =>\n\t\t\ttypeof v === \"string\" ? v.toLowerCase() : v,\n\t\t);\n\t\t// Use Set for O(1) lookup on larger lists (threshold ~12 based on typical benchmarks)\n\t\tif (lowerValues.length > 12) {\n\t\t\tconst valueSet = new Set(lowerValues);\n\t\t\treturn (item) => {\n\t\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\t\tconst compareValue = typeof fieldValue === \"string\" ? fieldValue.toLowerCase() : fieldValue;\n\t\t\t\treturn !valueSet.has(compareValue as string | number | boolean);\n\t\t\t};\n\t\t}\n\t\treturn (item) => {\n\t\t\tconst fieldValue = accessor(item, mappedField);\n\t\t\tconst compareValue = typeof fieldValue === \"string\" ? fieldValue.toLowerCase() : fieldValue;\n\t\t\treturn !lowerValues.includes(compareValue as string | number | boolean);\n\t\t};\n\t}\n\n\t// For small arrays (<=12 items), Array.includes is faster than Set lookup\n\tif (values.length <= 12) {\n\t\treturn (item) => !values.includes(accessor(item, mappedField) as string | number | boolean);\n\t}\n\n\t// Use Set for O(1) lookup on larger lists\n\tconst valueSet = new Set(values);\n\treturn (item) => !valueSet.has(accessor(item, mappedField) as string | number | boolean);\n}\n\n/**\n * Generates predicate for exists expression\n */\nfunction generateExists(\n\tnode: ExistsExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst accessor = state.fieldAccessor;\n\treturn (item) => {\n\t\tconst fieldValue = accessor(item, mappedField);\n\t\treturn fieldValue !== null && fieldValue !== undefined;\n\t};\n}\n\n/**\n * Generates predicate for boolean field expression\n */\nfunction generateBooleanField(\n\tnode: BooleanFieldExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst accessor = state.fieldAccessor;\n\treturn (item) => accessor(item, mappedField) === true;\n}\n\n/**\n * Generates predicate for range expression (BETWEEN)\n */\nfunction generateRange(\n\tnode: RangeExpression,\n\tstate: GeneratorState,\n): FilterPredicate<Record<string, unknown>> {\n\tvalidateField(node.field, state);\n\tconst mappedField = resolveFieldName(node.field, state);\n\tconst accessor = state.fieldAccessor;\n\tconst { min, max } = node;\n\treturn (item) => {\n\t\tconst fieldValue = accessor(item, mappedField);\n\t\treturn typeof fieldValue === \"number\" && fieldValue >= min && fieldValue <= max;\n\t};\n}\n\n/**\n * Extracts the primitive value from a Filtron Value node\n */\nfunction extractValue(value: Value): string | number | boolean {\n\tswitch (value.type) {\n\t\tcase \"string\":\n\t\t\treturn value.value;\n\t\tcase \"number\":\n\t\t\treturn value.value;\n\t\tcase \"boolean\":\n\t\t\treturn value.value;\n\t\tcase \"identifier\":\n\t\t\t// Identifiers are treated as strings\n\t\t\treturn value.value;\n\t\tdefault:\n\t\t\tconst _exhaustive: never = value;\n\t\t\tthrow new Error(`Unknown value type: ${(value as Value).type}`);\n\t}\n}\n\n/**\n * Creates a field accessor for nested properties using dot notation\n *\n * @param separator - The separator used in field names (default: \".\")\n * @returns A field accessor function\n *\n * @example\n * ```typescript\n * const filter = toFilter(ast, {\n * fieldAccessor: nestedAccessor()\n * });\n *\n * // Now you can query nested fields like \"user.address.city\"\n * const items = data.filter(filter);\n * ```\n */\nexport function nestedAccessor(\n\tseparator: string = \".\",\n): (obj: Record<string, unknown>, field: string) => unknown {\n\t// Memoize field splits to avoid repeated string splitting\n\tconst fieldPartsCache = new Map<string, string[]>();\n\n\treturn (obj: Record<string, unknown>, field: string): unknown => {\n\t\tlet parts = fieldPartsCache.get(field);\n\t\tif (!parts) {\n\t\t\tparts = field.split(separator);\n\t\t\tfieldPartsCache.set(field, parts);\n\t\t}\n\n\t\tlet current: unknown = obj;\n\n\t\tfor (const part of parts) {\n\t\t\tif (current === null || current === undefined) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (typeof current !== \"object\") {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tcurrent = (current as Record<string, unknown>)[part];\n\t\t}\n\n\t\treturn current;\n\t};\n}\n" | ||
| ], | ||
| "mappings": "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", | ||
| "debugId": "986AD4A645D7DA5764756E2164756E21", | ||
| "mappings": "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", | ||
| "debugId": "03072F786574590F64756E2164756E21", | ||
| "names": [] | ||
| } |
+67
-67
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "name": "@filtron/js", | ||
| "version": "1.0.0", | ||
| "description": "In-memory JavaScript array filtering using Filtron AST", | ||
| "keywords": [ | ||
| "filtron", | ||
| "filter", | ||
| "javascript", | ||
| "array", | ||
| "predicate", | ||
| "ast", | ||
| "typescript" | ||
| ], | ||
| "homepage": "https://github.com/jbergstroem/filtron#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/jbergstroem/filtron/issues" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/jbergstroem/filtron.git", | ||
| "directory": "packages/js" | ||
| }, | ||
| "license": "MIT", | ||
| "author": "Johan Bergström <bugs@bergstroem.nu>", | ||
| "type": "module", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js", | ||
| "default": "./dist/index.js" | ||
| } | ||
| }, | ||
| "main": "./dist/index.js", | ||
| "module": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "files": [ | ||
| "dist", | ||
| "README.md", | ||
| "LICENSE" | ||
| ], | ||
| "scripts": { | ||
| "bench": "bun --expose-gc run benchmark.ts", | ||
| "build": "bun build --minify --splitting --sourcemap=linked --outdir=dist --external @filtron/core index.ts && tsc", | ||
| "prepublishOnly": "bun run build && bun test", | ||
| "test": "bun test", | ||
| "typecheck": "tsc --noemit" | ||
| }, | ||
| "dependencies": { | ||
| "@filtron/core": "1.0.0" | ||
| }, | ||
| "peerDependencies": { | ||
| "typescript": "5.9.3" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "typescript": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "devDependencies": { | ||
| "@types/bun": "1.3.3", | ||
| "mitata": "1.0.34" | ||
| }, | ||
| "engines": { | ||
| "node": ">=20.0.0", | ||
| "bun": ">=1.1.0" | ||
| }, | ||
| "packageManager": "bun@1.3.3" | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "name": "@filtron/js", | ||
| "version": "1.1.1", | ||
| "description": "Filtron helper: transform filter expressions into JavaScript predicates for Array.filter()", | ||
| "keywords": [ | ||
| "array", | ||
| "ast", | ||
| "filter", | ||
| "filtron", | ||
| "javascript", | ||
| "predicate", | ||
| "typescript" | ||
| ], | ||
| "homepage": "https://github.com/jbergstroem/filtron#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/jbergstroem/filtron/issues" | ||
| }, | ||
| "license": "MIT", | ||
| "author": "Johan Bergström <bugs@bergstroem.nu>", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/jbergstroem/filtron.git", | ||
| "directory": "packages/js" | ||
| }, | ||
| "files": [ | ||
| "dist", | ||
| "LICENSE", | ||
| "README.md" | ||
| ], | ||
| "type": "module", | ||
| "main": "./dist/index.js", | ||
| "module": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js", | ||
| "default": "./dist/index.js" | ||
| } | ||
| }, | ||
| "scripts": { | ||
| "bench": "bun --expose-gc run benchmark.ts", | ||
| "build": "bun build --production --splitting --sourcemap=linked --outdir=dist --external @filtron/core index.ts && tsc", | ||
| "prepublishOnly": "bun run build && bun test", | ||
| "test": "bun test", | ||
| "typecheck": "tsc --noemit" | ||
| }, | ||
| "dependencies": { | ||
| "@filtron/core": "workspace:*" | ||
| }, | ||
| "devDependencies": { | ||
| "@filtron/benchmark": "workspace:*", | ||
| "@types/bun": "catalog:" | ||
| }, | ||
| "peerDependencies": { | ||
| "typescript": "catalog:" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "typescript": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "engines": { | ||
| "bun": ">=1.1.0", | ||
| "node": ">=20.0.0" | ||
| }, | ||
| "packageManager": "bun@1.3.3" | ||
| } |
+62
-54
| # @filtron/js | ||
| In-memory JavaScript array filtering using [Filtron](https://github.com/jbergstroem/filtron) AST. | ||
| Convert Filtron AST to JavaScript filter predicates for use with `Array.filter()`. | ||
| [](https://www.npmjs.com/package/@filtron/js) | ||
| [](https://bundlephobia.com/package/@filtron/js) | ||
| [](https://codecov.io/gh/jbergstroem/filtron) | ||
| ## Installation | ||
| ```bash | ||
| bun add @filtron/js | ||
| # or | ||
| npm install @filtron/js | ||
| ``` | ||
| ## Quick Start | ||
| ## Usage | ||
@@ -29,4 +31,4 @@ ```typescript | ||
| const filtered = users.filter(filter); | ||
| // [{ name: "Alice", age: 25, status: "active" }] | ||
| users.filter(filter); | ||
| // => [{ name: "Alice", age: 25, status: "active" }] | ||
| } | ||
@@ -37,78 +39,79 @@ ``` | ||
| ### `toFilter(ast, options?)` | ||
| ### `toFilter<T>(ast, options?): (item: T) => boolean` | ||
| Converts a Filtron AST to a predicate function for use with `Array.filter()`. | ||
| Converts a Filtron AST to a predicate function. | ||
| **Options:** | ||
| #### Options | ||
| | Option | Type | Description | | ||
| |--------|------|-------------| | ||
| | `allowedFields` | `string[]` | Restrict queryable fields (throws if field not in list) | | ||
| | `fieldAccessor` | `(obj, field) => unknown` | Custom field value accessor | | ||
| | `caseInsensitive` | `boolean` | Case-insensitive string comparisons (default: `false`) | | ||
| | `fieldMapping` | `Record<string, string>` | Map query field names to object property names | | ||
| | Option | Type | Default | Description | | ||
| | ----------------- | ------------------------------------ | ----------- | ----------------------------------------------------------- | | ||
| | `allowedFields` | `string[]` | `undefined` | Whitelist of queryable fields (throws if field not in list) | | ||
| | `fieldAccessor` | `(obj: T, field: string) => unknown` | `undefined` | Custom function to retrieve field values | | ||
| | `caseInsensitive` | `boolean` | `false` | Case-insensitive string comparisons | | ||
| | `fieldMapping` | `Record<string, string>` | `undefined` | Map query field names to object property names | | ||
| #### Examples | ||
| **Restrict allowed fields:** | ||
| ```typescript | ||
| const filter = toFilter(ast, { | ||
| allowedFields: ["name", "email", "age"], | ||
| caseInsensitive: true, | ||
| }); | ||
| // Querying "password" will throw an error | ||
| ``` | ||
| ### `nestedAccessor(separator?)` | ||
| **Case-insensitive matching:** | ||
| Creates a field accessor for dot-notation nested properties. | ||
| ```typescript | ||
| import { toFilter, nestedAccessor } from "@filtron/js"; | ||
| const filter = toFilter(ast, { | ||
| fieldAccessor: nestedAccessor(), | ||
| caseInsensitive: true, | ||
| }); | ||
| // Query: "user.profile.age > 18" | ||
| // Matches: { user: { profile: { age: 25 } } } | ||
| // "status = 'ACTIVE'" matches { status: "active" } | ||
| ``` | ||
| ## Advanced Usage | ||
| **Field mapping:** | ||
| ### Field Mapping | ||
| ```typescript | ||
| const filter = toFilter(ast, { | ||
| fieldMapping: { | ||
| email: "emailAddress", | ||
| age: "userAge", | ||
| }, | ||
| }); | ||
| // Query "email" maps to object property "emailAddress" | ||
| ``` | ||
| Map query field names to different object property names. This is useful when you want to expose a different API in your queries than your internal data structure: | ||
| **Combined options:** | ||
| ```typescript | ||
| import { parse } from "@filtron/core"; | ||
| import { toFilter } from "@filtron/js"; | ||
| const filter = toFilter(ast, { | ||
| fieldMapping: { user_email: "email" }, | ||
| allowedFields: ["user_email"], // Validates against query field names | ||
| caseInsensitive: true, | ||
| }); | ||
| ``` | ||
| const result = parse('email = "user@example.com" AND age > 18'); | ||
| ### `nestedAccessor(separator?): FieldAccessor` | ||
| if (result.success) { | ||
| const filter = toFilter(result.ast, { | ||
| fieldMapping: { | ||
| email: "emailAddress", | ||
| age: "userAge", | ||
| }, | ||
| }); | ||
| Creates a field accessor for dot-notation nested properties: | ||
| const users = [ | ||
| { emailAddress: "user@example.com", userAge: 25 }, | ||
| { emailAddress: "other@example.com", userAge: 16 }, | ||
| ]; | ||
| ```typescript | ||
| import { toFilter, nestedAccessor } from "@filtron/js"; | ||
| const filtered = users.filter(filter); | ||
| // [{ emailAddress: "user@example.com", userAge: 25 }] | ||
| } | ||
| const filter = toFilter(ast, { | ||
| fieldAccessor: nestedAccessor(), | ||
| }); | ||
| // Query: "user.profile.age > 18" | ||
| // Matches: { user: { profile: { age: 25 } } } | ||
| ``` | ||
| Field mapping works with all expression types and can be combined with other options: | ||
| Custom separator: | ||
| ```typescript | ||
| const filter = toFilter(ast, { | ||
| fieldMapping: { | ||
| user_id: "id", | ||
| user_email: "email", | ||
| }, | ||
| allowedFields: ["user_id", "user_email"], // Validation uses query field names (before mapping) | ||
| caseInsensitive: true, | ||
| fieldAccessor: nestedAccessor("/"), | ||
| }); | ||
| // Query: "user/profile/age > 18" | ||
| ``` | ||
@@ -118,8 +121,13 @@ | ||
| When accepting user input, use `allowedFields` to prevent access to sensitive fields: | ||
| When accepting user input, use `allowedFields` to prevent access to sensitive properties: | ||
| ```typescript | ||
| const filter = toFilter(ast, { | ||
| allowedFields: ["name", "email", "status"], // "password" queries will throw | ||
| allowedFields: ["name", "email", "status"], | ||
| }); | ||
| // Queries against "password", "token", etc. will throw | ||
| ``` | ||
| ## License | ||
| MIT |
35567
3.87%131
6.5%- Removed
- Removed
- Removed
Updated