Add JSON Logic iteration operators (none, map, filter, reduce)#6834
Merged
ajpallares merged 52 commits intoJun 9, 2026
Conversation
426aee0 to
4aa50c4
Compare
d2ca6d6 to
e73eabe
Compare
2a0a33c to
46d6925
Compare
e73eabe to
d2ca6d6
Compare
46d6925 to
2a0a33c
Compare
…tures
Convert the hand-written operator unit tests for operators available at the
string+array level into declarative JSON predicate fixtures sharing khepri's
conformance format, run by a shared runner that auto-discovers every file in
PredicateFixtures/. `expected` is a khepri-compatible superset (Bool or
{ "error": ... }) plus optional `description` and `expectedWarnings`.
Adds a test-only Value: Decodable conformance so predicates and variables
decode straight into the engine's value model; production Value is unchanged.
Tests that can't be expressed as predicate->Bool stay in Swift; ValueTests
untouched. Test-only change.
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 40901e5)
d2ca6d6 to
03295d9
Compare
2a0a33c to
df4b140
Compare
1 task
Load the in-repo fixtures once and feed the decoded cases straight into the parameterized test, instead of collecting IDs and re-reading every file to find the matching case per ID. PredicateConformanceFixtureCase gains Identifiable + Sendable (for the arguments) and a CustomTestStringConvertible extension so each case still displays by id. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Asserting an exact warning count coupled the fixtures to engine internals. Match warnings by substring only (count-agnostic); an empty `contains` now asserts that no warning is emitted, preserving the "does not warn" cases. The three count-only fixtures gain the "missing variable" substring they actually emit. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the khepri single-file plumbing (defaultFixtureURL, fixtureURL, the no-arg loadCases(), the env var, and the unused fixtureNotFound error). This PR only runs the in-repo PredicateFixtures/ directory; the khepri conformance loader lands with its test in the conformance PR. Co-authored-by: Cursor <cursoragent@cursor.com>
Implements the json-logic-js some/all iteration predicates. Operator coverage is expressed as JSON predicate fixtures (some.json / all.json) run by the shared fixture runner introduced downstack, replacing hand-written unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit d11529c)
Implements the json-logic-js min/max operators (variadic flat list, ±∞ for empty input, NaN propagation for non-numeric operands). Coverage is expressed as JSON predicate fixtures (min.json / max.json) run by the shared fixture runner, replacing hand-written unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit e73eabe)
03295d9 to
8a445e2
Compare
Completes the json-logic-js iteration family (none/map/filter/reduce) on top of some/all. Coverage is expressed as JSON predicate fixtures (none.json / map.json / filter.json / reduce.json) run by the shared fixture runner, replacing hand-written unit tests. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 46d6925)
df4b140 to
6d0efcb
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
ajpallares
added a commit
that referenced
this pull request
Jun 3, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
…on-logic-iteration-mapping-operators
📸 Snapshot Test274 unchanged
🛸 Powered by Emerge Tools |
- Rename the object-array some/all fixtures to describe behavior instead of referencing the khepri oracle - Add fixtures for literal predicates and non-boolean truthy predicate results - Drop the unused opName parameter from parseIterationArgs and return an optional items list so a non-array source is distinguishable from an empty one - Bump pinned fixture count to 271 Co-authored-by: Cursor <cursoragent@cursor.com>
Place operator docs on each op instead of duplicating at the type level; drop "not vacuous truth" wording. Co-authored-by: Cursor <cursoragent@cursor.com>
…-logic-iteration-operators
Add var-fed cases (including the single-array-arg NaN gotcha), min fail-closed and empty-list fail-closed coverage. Collapse the two-pass NaN scan + extremum into a single reducing fold. Pinned fixture count 294 -> 301. Co-authored-by: Cursor <cursoragent@cursor.com>
…erators' into pallares/json-logic-min-max-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
…ators' into pallares/json-logic-iteration-mapping-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # RulesEngineInternal/Operators/IterationOperators.swift # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
Strengthens iteration-operator coverage for none/map/filter/reduce: - Add object-item named-property fixtures (none/map/filter) — the most common real-world shape, previously untested (only scalar/empty items). - Assert map/filter return real arrays by folding through reduce (count/sum) instead of relying solely on string coercion. - Add a reduce fixture pinning that the initial accumulator is evaluated even for a non-array source (matches initial = apply(values[2], data)). - Reword the none non-array rationale: current json-logic-js implements none directly with !Array.isArray(x) || !x.length, not via filter. Expected fixture count 341 -> 345. Co-authored-by: Cursor <cursoragent@cursor.com>
9082909 to
10cb6d1
Compare
…teration-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
Label each operator grouping in Operators.dispatch per PR review feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
Rename the all-match fixture and add cases where only the first or only the middle item matches, addressing PR review feedback that the existing some coverage was order dependent. Co-authored-by: Cursor <cursoragent@cursor.com>
khepri guards all with notEmpty, so it returns false for an empty array like json-logic-js; drop the inaccurate claim that khepri returns true. Co-authored-by: Cursor <cursoragent@cursor.com>
…teration-operators
…erators' into pallares/json-logic-min-max-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
Remove the duplicate WorkflowsCacheTests.swift PBXFileReference introduced during a merge, and add a dedicated "Min and max" grouping comment so the min/max cases are no longer lumped under arithmetic. Co-authored-by: Cursor <cursoragent@cursor.com>
…ators' into pallares/json-logic-iteration-mapping-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
…teration-mapping-operators Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # Tests/RulesEngineInternalTests/PredicateFixtureTests.swift # Tests/RulesEngineInternalTests/PredicateFixtures/max.json # Tests/RulesEngineInternalTests/PredicateFixtures/min.json
Co-authored-by: Cursor <cursoragent@cursor.com>
tonidero
approved these changes
Jun 9, 2026
tonidero
left a comment
Contributor
There was a problem hiding this comment.
I think it makes sense! Just a question for tests.
| } | ||
| ] | ||
| }, | ||
| "" |
Contributor
There was a problem hiding this comment.
I guess this isn't actually an empty array but an empty string, which is a bit confusing... I guess we're relying on it being equivalent... Probably NABD though.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Completes the iteration-operator family alongside #6817, mirroring json-logic-js. Resolves SDK-4349.
Android counterpart: RevenueCat/purchases-android/pull/3553
Description
none/map/filter/reduce, sharing the per-item scope helper;reducerebindsvarsto{"current", "accumulator"}(the only asymmetric case).none→true(JS guards with!Array.isArray(x) || !x.length);map/filter→[];reduce→ seed unchanged.Note
Medium Risk
Expands predicate evaluation semantics (scoping and non-array edge cases) in the rules engine; risk is mitigated by alignment with json-logic-js and broad fixture coverage rather than touching auth or I/O.
Overview
Adds
none,map,filter, andreduceto the JSON Logic rules engine so iteration behavior matchesjson-logic-jsalongside existingsome/all.none,map, andfilterreuse the same[arrayExpr, predicate]shape andparseIterationArgs: the array is evaluated in the outer scope; the predicate runs per element withvarsset to the item only (no parent scope).noneis the inverse ofsome(short-circuits on the first truthy item).mapreturns unevaluated predicate results;filterkeeps original items when the predicate is truthy. Non-array sources yieldtruefornone,[]formap/filter.reducetakes[arrayExpr, predicate, initialAccumulator], evaluates source and seed in the outer scope, and runs the predicate with{"current", "accumulator"}only. Non-array sources return the evaluated seed unchanged.Operators.dispatchregisters all four operators. Predicate conformance fixtures grow by 40 cases (307 → 347) infilter.json,map.json,none.json, andreduce.json(edge cases, scope isolation, composition withreduce).Reviewed by Cursor Bugbot for commit 6bffc32. Bugbot is set up for automated code reviews on this repo. Configure here.