From 1681e2ae3743e5e98b86b039ad7d3a325ce4dfb1 Mon Sep 17 00:00:00 2001 From: John Nunemaker Date: Thu, 4 Jun 2026 12:29:10 -0400 Subject: [PATCH 1/4] feat: add FeatureEnabled expression and schema category metadata Add a FeatureEnabled expression (schema + valid/invalid examples) that returns true when the named feature is enabled for the current actor, and register it in schema.json. Add category metadata (Condition/Value) to several schemas and a reusable property definition. Bump to 1.1.0. Co-Authored-By: Claude Opus 4.8 --- examples/FeatureEnabled.json | 15 +++++++++++++++ package.json | 2 +- schemas/FeatureEnabled.schema.json | 11 +++++++++++ schemas/PercentageOfActors.schema.json | 3 ++- schemas/Property.schema.json | 1 + schemas/Random.schema.json | 1 + schemas/Time.schema.json | 1 + schemas/schema.json | 6 ++++++ 8 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 examples/FeatureEnabled.json create mode 100644 schemas/FeatureEnabled.schema.json diff --git a/examples/FeatureEnabled.json b/examples/FeatureEnabled.json new file mode 100644 index 0000000..816ce2a --- /dev/null +++ b/examples/FeatureEnabled.json @@ -0,0 +1,15 @@ +{ + "valid": [ + { + "expression": { "FeatureEnabled": ["other_feature"] } + }, + { + "expression": { "FeatureEnabled": [{ "Property": ["feature_name"] }] } + } + ], + "invalid": [ + { "FeatureEnabled": [] }, + { "FeatureEnabled": ["a", "b"] }, + { "FeatureEnabled": ["a"], "Any": [] } + ] +} diff --git a/package.json b/package.json index 45c2f6d..3d6d5d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@flippercloud.io/expressions", - "version": "1.0.0", + "version": "1.1.0", "description": "Library and Schema for evaluating Flipper Expressions", "type": "module", "files": [ diff --git a/schemas/FeatureEnabled.schema.json b/schemas/FeatureEnabled.schema.json new file mode 100644 index 0000000..f53a53b --- /dev/null +++ b/schemas/FeatureEnabled.schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://www.flippercloud.io/expressions/FeatureEnabled.schema.json", + "title": "FeatureEnabled", + "description": "Returns true if the named feature is enabled for the current actor.", + "category": "Value", + "type": "array", + "items": { "$ref": "schema.json#/definitions/string" }, + "minItems": 1, + "maxItems": 1 +} diff --git a/schemas/PercentageOfActors.schema.json b/schemas/PercentageOfActors.schema.json index e512104..92a60c4 100644 --- a/schemas/PercentageOfActors.schema.json +++ b/schemas/PercentageOfActors.schema.json @@ -2,7 +2,8 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://www.flippercloud.io/expressions/PercentageOfActors.schema.json", "title": "PercentageOfActors", - "description": "", + "description": "Returns true when the actor falls within the given percentage for the feature.", + "category": "Condition", "type": "array", "items": [ { "$ref": "schema.json#/definitions/string" }, diff --git a/schemas/Property.schema.json b/schemas/Property.schema.json index 0d564ad..f0c41ec 100644 --- a/schemas/Property.schema.json +++ b/schemas/Property.schema.json @@ -3,6 +3,7 @@ "$id": "https://www.flippercloud.io/expressions/Property.schema.json", "title": "Property", "description": "Extract a property from context[properties].", + "category": "Value", "type": "array", "items": { "$ref": "schema.json#/definitions/string" }, "minItems": 1, diff --git a/schemas/Random.schema.json b/schemas/Random.schema.json index dcdd89e..d719680 100644 --- a/schemas/Random.schema.json +++ b/schemas/Random.schema.json @@ -3,6 +3,7 @@ "$id": "https://www.flippercloud.io/expressions/Random.schema.json", "title": "Random", "description": "Return a random number", + "category": "Value", "type": "array", "items": { "title": "Maximum", diff --git a/schemas/Time.schema.json b/schemas/Time.schema.json index cc7bac7..4db6410 100644 --- a/schemas/Time.schema.json +++ b/schemas/Time.schema.json @@ -3,6 +3,7 @@ "$id": "https://www.flippercloud.io/expressions/Time.schema.json", "title": "Time", "description": "A time in ISO8601 format", + "category": "Value", "type": "array", "items": { "title": "Time", diff --git a/schemas/schema.json b/schemas/schema.json index f12d30a..ac8c7c7 100644 --- a/schemas/schema.json +++ b/schemas/schema.json @@ -44,6 +44,7 @@ "Divide": { "$ref": "Divide.schema.json" }, "Duration": { "$ref": "Duration.schema.json" }, "Equal": { "$ref": "Equal.schema.json" }, + "FeatureEnabled": { "$ref": "FeatureEnabled.schema.json" }, "GreaterThan": { "$ref": "GreaterThan.schema.json" }, "GreaterThanOrEqualTo": { "$ref": "GreaterThanOrEqualTo.schema.json" }, "LessThan": { "$ref": "LessThan.schema.json" }, @@ -64,6 +65,11 @@ }, "additionalProperties": false }, + "property": { + "title": "Property Value", + "description": "A value stored in context[properties]: a string, number, boolean, or null.", + "$ref": "#/definitions/constant" + }, "string": { "title": "String", "description": "A constant string value or a function that returns a string", From 18d714ada75294796abf9878a48230983b02390b Mon Sep 17 00:00:00 2001 From: John Nunemaker Date: Thu, 4 Jun 2026 12:29:10 -0400 Subject: [PATCH 2/4] docs: add CLAUDE.md noting flipper schema sync Document that changes to schemas/ or examples/ require re-running `rake expressions:vendor` and the schema spec in flippercloud/flipper. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5f3b90f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,21 @@ +# Flipper Expressions + +JSON Schema (`schemas/*.json`) and JS library for Flipper Expressions. The schemas are the canonical spec; the Ruby gem reimplements the expressions and vendors these schemas + examples for its tests. + +## Keep flipper in sync when schemas or examples change + +Any time `schemas/*.json` or `examples/*.json` change here, the vendored copies in the [flippercloud/flipper](https://github.com/flippercloud/flipper) gem must be re-synced and its specs re-run — otherwise Ruby and JS drift apart. + +In a flipper checkout: + +```sh +# Copies schemas/*.json and examples/*.json from this repo into flipper +# (lib/flipper/expression/schemas + spec/fixtures/expressions/examples). +# Defaults to a sibling ../expressions checkout; override with SOURCE. +rake expressions:vendor SOURCE=/path/to/expressions + +# Then run flipper's specs to confirm Ruby agrees with the schema/examples. +bundle exec rspec spec/flipper/expression/schema_spec.rb +``` + +See the README's "Adding a new expression" section for the full cross-repo workflow. From d4d1be56e097d6a99e7acb002539a84545fe57be Mon Sep 17 00:00:00 2001 From: John Nunemaker Date: Thu, 4 Jun 2026 15:20:46 -0400 Subject: [PATCH 3/4] chore: drop unused property definition from schema.json The property definition was an unreferenced alias of constant ("Property Value" -> constant). Reading a property is already handled by the Property function, and nothing in the schemas referenced this definition. Remove it until a consumer exists. Co-Authored-By: Claude Opus 4.8 --- schemas/schema.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/schemas/schema.json b/schemas/schema.json index ac8c7c7..682576a 100644 --- a/schemas/schema.json +++ b/schemas/schema.json @@ -65,11 +65,6 @@ }, "additionalProperties": false }, - "property": { - "title": "Property Value", - "description": "A value stored in context[properties]: a string, number, boolean, or null.", - "$ref": "#/definitions/constant" - }, "string": { "title": "String", "description": "A constant string value or a function that returns a string", From eb140e8e373d8e01c64620c74d0f9b26cbec3097 Mon Sep 17 00:00:00 2001 From: John Nunemaker Date: Thu, 4 Jun 2026 16:48:29 -0400 Subject: [PATCH 4/4] chore: publish under @flippercloud scope instead of @flippercloud.io MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the npm package from @flippercloud.io/expressions (a personal user scope) to @flippercloud/expressions, the org we control alongside @flippercloud/flipper and friends. Also fix the repository URL (typo'd https:: and pointing at the old monorepo). Schema $id URLs keep www.flippercloud.io — those are the canonical schema identifiers tied to the website domain and the Ruby gem's BaseURI, not the npm scope. Co-Authored-By: Claude Opus 4.8 --- package.json | 4 ++-- vite.config.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3d6d5d6..903041e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@flippercloud.io/expressions", + "name": "@flippercloud/expressions", "version": "1.1.0", "description": "Library and Schema for evaluating Flipper Expressions", "type": "module", @@ -16,7 +16,7 @@ "require": "./dist/expressions.umd.cjs" } }, - "repository": "https:://github.com/jnunemaker/flipper", + "repository": "https://github.com/flippercloud/expressions", "license": "MIT", "dependencies": { "ajv": "^8.12.0", diff --git a/vite.config.js b/vite.config.js index a4961e2..01820f0 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,7 +7,7 @@ export default defineConfig({ sourcemap: true, lib: { entry: resolve(__dirname, 'lib/index.js'), - name: '@flippercloud.io/expressions' + name: '@flippercloud/expressions' }, rollupOptions: { external: [