@flagwire/evaluate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +1 -0
- package/package.json +42 -0
- package/vectors/.gitkeep +1 -0
- package/vectors/bucketing.json +86 -0
- package/vectors/eval/fail-safe-01.json +42 -0
- package/vectors/eval/fail-safe-02.json +42 -0
- package/vectors/eval/fail-safe-03.json +20 -0
- package/vectors/eval/fail-safe-04.json +42 -0
- package/vectors/eval/fail-safe-05.json +42 -0
- package/vectors/eval/fail-safe-06.json +60 -0
- package/vectors/eval/fail-safe-07.json +42 -0
- package/vectors/eval/fail-safe-08.json +57 -0
- package/vectors/eval/operators-01.json +60 -0
- package/vectors/eval/operators-02.json +60 -0
- package/vectors/eval/operators-03.json +60 -0
- package/vectors/eval/operators-04.json +60 -0
- package/vectors/eval/operators-05.json +60 -0
- package/vectors/eval/operators-06.json +60 -0
- package/vectors/eval/operators-07.json +60 -0
- package/vectors/eval/operators-08.json +60 -0
- package/vectors/eval/operators-09.json +60 -0
- package/vectors/eval/operators-10.json +60 -0
- package/vectors/eval/operators-11.json +60 -0
- package/vectors/eval/operators-12.json +60 -0
- package/vectors/eval/operators-13.json +60 -0
- package/vectors/eval/operators-14.json +60 -0
- package/vectors/eval/operators-15.json +60 -0
- package/vectors/eval/operators-16.json +60 -0
- package/vectors/eval/operators-17.json +60 -0
- package/vectors/eval/operators-18.json +60 -0
- package/vectors/eval/operators-19.json +58 -0
- package/vectors/eval/operators-20.json +60 -0
- package/vectors/eval/operators-21.json +67 -0
- package/vectors/eval/operators-22.json +74 -0
- package/vectors/eval/operators-23.json +60 -0
- package/vectors/eval/operators-24.json +60 -0
- package/vectors/eval/rollouts-01.json +53 -0
- package/vectors/eval/rollouts-02.json +53 -0
- package/vectors/eval/rollouts-03.json +53 -0
- package/vectors/eval/rollouts-04.json +53 -0
- package/vectors/eval/rollouts-05.json +53 -0
- package/vectors/eval/rollouts-06.json +71 -0
- package/vectors/eval/rollouts-07.json +71 -0
- package/vectors/eval/rollouts-08.json +49 -0
- package/vectors/eval/segments-01.json +73 -0
- package/vectors/eval/segments-02.json +91 -0
- package/vectors/eval/segments-03.json +91 -0
- package/vectors/eval/segments-04.json +86 -0
- package/vectors/eval/segments-05.json +58 -0
- package/vectors/eval/segments-06.json +83 -0
- package/vectors/eval/segments-07.json +73 -0
- package/vectors/eval/segments-08.json +85 -0
- package/vectors/eval/segments-09.json +83 -0
- package/vectors/eval/segments-10.json +73 -0
- package/vectors/murmur3.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FlagWire contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# `@flagwire/evaluate`
|
|
2
|
+
|
|
3
|
+
Deterministic local evaluation engine shared by FlagWire's server SDKs. The package includes the
|
|
4
|
+
append-only compatibility vectors used to keep language implementations behaviorally identical.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { evaluateFlag } from "@flagwire/evaluate";
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Existing vector files are immutable compatibility history. Add a new vector file for every new
|
|
11
|
+
edge case.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type EvaluationContext, type JsonValue } from "@flagwire/schema";
|
|
2
|
+
export type { EvaluationContext } from "@flagwire/schema";
|
|
3
|
+
export type EvaluationReason = "ERROR" | "ERROR_MISSING_KEY" | "FALLTHROUGH" | "FLAG_NOT_FOUND" | "OFF" | "REVOKED" | `RULE_MATCH:${string}`;
|
|
4
|
+
export interface EvaluationDetail {
|
|
5
|
+
variantKey: string | null;
|
|
6
|
+
value: JsonValue;
|
|
7
|
+
reason: EvaluationReason;
|
|
8
|
+
}
|
|
9
|
+
export interface EvaluationSnapshot {
|
|
10
|
+
version: number;
|
|
11
|
+
flags: Record<string, EvaluationDetail>;
|
|
12
|
+
}
|
|
13
|
+
/** MurmurHash3 x86 32-bit over UTF-8 bytes. */
|
|
14
|
+
export declare function murmur3_32(input: string | Uint8Array, seed?: number): number;
|
|
15
|
+
export declare function bucketFor(contextKey: string, flagKey: string, salt: string): {
|
|
16
|
+
uint32: number;
|
|
17
|
+
value: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function evaluateFlag(bundleInput: unknown, flagKey: string, context: EvaluationContext, codeDefault: JsonValue): EvaluationDetail;
|
|
20
|
+
export declare function evaluateBundle(bundleInput: unknown, context: EvaluationContext): EvaluationSnapshot;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{bundleSchema as y}from"@flagwire/schema";var A=new TextEncoder,b=4294967296;function c(n,r){return n<<r|n>>>32-r}function S(n,r=0){let t=typeof n=="string"?A.encode(n):n,o=t.length-t.length%4,e=r>>>0;for(let i=0;i<o;i+=4){let s=t[i]|t[i+1]<<8|t[i+2]<<16|t[i+3]<<24;s=Math.imul(s,3432918353),s=c(s,15),s=Math.imul(s,461845907),e^=s,e=c(e,13),e=Math.imul(e,5)+3864292196|0}let a=t.length&3;if(a>0){let i=t[o];a>=2&&(i^=t[o+1]<<8),a===3&&(i^=t[o+2]<<16),i=Math.imul(i,3432918353),i=c(i,15),i=Math.imul(i,461845907),e^=i}return e^=t.length,e^=e>>>16,e=Math.imul(e,2246822507),e^=e>>>13,e=Math.imul(e,3266489909),e^=e>>>16,e>>>0}function C(n,r,t){let o=S(`${n}:${r}:${t}`,0);return{uint32:o,value:o/b}}function g(n){let r=/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(n);if(!r)return;let t=Number(r[1]),o=Number(r[2]),e=Number(r[3]);if(![t,o,e].every(Number.isSafeInteger))return;let a=r[4]?.split(".")??[];if(!a.some(i=>/^\d+$/.test(i)&&i.length>1&&i.startsWith("0")))return{major:t,minor:o,patch:e,prerelease:a}}function F(n,r){for(let o of["major","minor","patch"])if(n[o]!==r[o])return n[o]<r[o]?-1:1;if(n.prerelease.length===0||r.prerelease.length===0)return n.prerelease.length===r.prerelease.length?0:n.prerelease.length===0?1:-1;let t=Math.max(n.prerelease.length,r.prerelease.length);for(let o=0;o<t;o+=1){let e=n.prerelease[o],a=r.prerelease[o];if(e===void 0||a===void 0)return e===a?0:e===void 0?-1:1;if(e===a)continue;let i=/^\d+$/.test(e),s=/^\d+$/.test(a);return i&&s?e.length!==a.length?e.length<a.length?-1:1:e<a?-1:1:i!==s?i?-1:1:e<a?-1:1}return 0}function v(n){if(typeof n=="number")return Number.isFinite(n)?n:void 0;if(typeof n!="string"||n.trim()==="")return;let r=Number(n);return Number.isFinite(r)?r:void 0}function N(n,r,t){try{switch(n){case"eq":return Array.isArray(r)||typeof r!=typeof t?"error":r===t?"match":"no-match";case"neq":return Array.isArray(r)||typeof r!=typeof t?"error":r!==t?"match":"no-match";case"in":return Array.isArray(r)||typeof r!=typeof t?"error":r===t?"match":"no-match";case"contains":return typeof r=="string"&&typeof t=="string"||Array.isArray(r)&&typeof t=="string"?r.includes(t)?"match":"no-match":"error";case"startsWith":return typeof r=="string"&&typeof t=="string"?r.startsWith(t)?"match":"no-match":"error";case"endsWith":return typeof r=="string"&&typeof t=="string"?r.endsWith(t)?"match":"no-match":"error";case"gt":case"gte":case"lt":case"lte":{if(Array.isArray(r))return"error";let o=v(r),e=v(t);return o===void 0||e===void 0?"error":(n==="gt"?o>e:n==="gte"?o>=e:n==="lt"?o<e:o<=e)?"match":"no-match"}case"semverEq":case"semverGt":case"semverLt":{if(typeof r!="string"||typeof t!="string")return"error";let o=g(r),e=g(t);if(!o||!e)return"error";let a=F(o,e);return(n==="semverEq"?a===0:n==="semverGt"?a>0:a<0)?"match":"no-match"}case"regex":return typeof r!="string"||typeof t!="string"||t.length>256?"error":new RegExp(t).test(r)?"match":"no-match"}return"error"}catch{return"error"}}function k(n,r,t,o){let e=r[n];if(!e||o.has(n))return"error";let a=new Set(o);a.add(n);let i=!1;for(let s of e){let u=!0;for(let R of s.clauses){let h=p(R,r,t,a);if(h==="error"){i=!0,u=!1;break}if(h==="no-match"){u=!1;break}}if(u)return"match"}return i?"error":"no-match"}function p(n,r,t,o){let e=!1,a=!1;if(n.op==="segment")for(let i of n.values){if(typeof i!="string"||!r[i]){a=!0;continue}let s=k(i,r,t,o);if(s==="error"){a=!0;continue}if(s==="match"){e=!0;break}}else{let i=t.attributes?.[n.attr];if(i===void 0)return"error";for(let s of n.values){let u=N(n.op,i,s);if(u==="error"){a=!0;continue}if(u==="match"){e=!0;break}}}return!e&&a?"error":(n.negate&&(e=!e),e?"match":"no-match")}function l(n,r){return{variantKey:null,value:n,reason:r}}function m(n,r,t,o){let e=n.variants[r];return e?{variantKey:e.key,value:e.value,reason:t}:l(o,"ERROR")}function f(n,r,t){return m(n,n.offVariant,r,t)}function d(n,r,t,o,e,a){if("variant"in o)return m(n,o.variant,e,a);if(!t.key)return f(n,"ERROR_MISSING_KEY",a);let i=C(t.key,r,n.salt).uint32,s=0;for(let u of o.rollout.variations)if(s+=u.weight,i*1e5<s*b)return m(n,u.variant,e,a);return f(n,"ERROR",a)}function E(n,r,t,o,e){try{if(!t.on)return f(t,"OFF",e);for(let a of t.rules){let i=!0;for(let s of a.clauses)if(p(s,n.segments,o,new Set)!=="match"){i=!1;break}if(i)return d(t,r,o,a.serve,`RULE_MATCH:${a.id}`,e)}return d(t,r,o,t.fallthrough,"FALLTHROUGH",e)}catch{return f(t,"ERROR",e)}}function M(n,r,t,o){let e=y.safeParse(n);if(!e.success)return l(o,"ERROR");if(e.data.revoked)return l(o,"REVOKED");let a=e.data.flags[r];return a?E(e.data,r,a,t,o):l(o,"FLAG_NOT_FOUND")}function _(n,r){let t=y.safeParse(n);if(!t.success)return{version:0,flags:{}};let o=t.data;if(o.revoked)return{version:o.version,flags:{}};let e={};for(let[a,i]of Object.entries(o.flags)){let s=i.variants[i.offVariant]?.value??null;e[a]=E(o,a,i,r,s)}return{version:o.version,flags:e}}export{C as bucketFor,_ as evaluateBundle,M as evaluateFlag,S as murmur3_32};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flagwire/evaluate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Deterministic local evaluation engine for FlagWire",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"vectors"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./vectors/*": "./vectors/*"
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/flagwire/flagwire-js.git",
|
|
24
|
+
"directory": "packages/evaluate"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"provenance": true
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@flagwire/schema": "0.1.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"esbuild": "0.28.2"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "node ../../scripts/clean-package-dist.mjs && tsc -p tsconfig.build.json && esbuild src/index.ts --bundle --minify --format=esm --platform=neutral --target=es2022 --external:@flagwire/schema --outfile=dist/index.js",
|
|
38
|
+
"lint": "eslint src",
|
|
39
|
+
"test": "vitest run --passWithNoTests",
|
|
40
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/vectors/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"contextKey": "user-1",
|
|
4
|
+
"flagKey": "new-checkout",
|
|
5
|
+
"salt": "0123456789abcdef",
|
|
6
|
+
"expectedBucketUint32": 1270273690,
|
|
7
|
+
"expectedFloat12dp": 0.295758640859
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"contextKey": "user-2",
|
|
11
|
+
"flagKey": "new-checkout",
|
|
12
|
+
"salt": "0123456789abcdef",
|
|
13
|
+
"expectedBucketUint32": 2494462029,
|
|
14
|
+
"expectedFloat12dp": 0.580787199782
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"contextKey": "",
|
|
18
|
+
"flagKey": "new-checkout",
|
|
19
|
+
"salt": "0123456789abcdef",
|
|
20
|
+
"expectedBucketUint32": 2569104276,
|
|
21
|
+
"expectedFloat12dp": 0.598166202195
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"contextKey": "🚀",
|
|
25
|
+
"flagKey": "launch",
|
|
26
|
+
"salt": "fedcba9876543210",
|
|
27
|
+
"expectedBucketUint32": 2390768489,
|
|
28
|
+
"expectedFloat12dp": 0.556644166121
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"contextKey": "café",
|
|
32
|
+
"flagKey": "pricing.test",
|
|
33
|
+
"salt": "a1b2c3d4e5f60718",
|
|
34
|
+
"expectedBucketUint32": 953215369,
|
|
35
|
+
"expectedFloat12dp": 0.221937747905
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"contextKey": "same",
|
|
39
|
+
"flagKey": "flag-a",
|
|
40
|
+
"salt": "0000000000000000",
|
|
41
|
+
"expectedBucketUint32": 1685671660,
|
|
42
|
+
"expectedFloat12dp": 0.392476017587
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"contextKey": "same",
|
|
46
|
+
"flagKey": "flag-b",
|
|
47
|
+
"salt": "0000000000000000",
|
|
48
|
+
"expectedBucketUint32": 4153084138,
|
|
49
|
+
"expectedFloat12dp": 0.966965252999
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"contextKey": "same",
|
|
53
|
+
"flagKey": "flag-a",
|
|
54
|
+
"salt": "ffffffffffffffff",
|
|
55
|
+
"expectedBucketUint32": 1774884951,
|
|
56
|
+
"expectedFloat12dp": 0.413247605553
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"contextKey": "tenant:user:123",
|
|
60
|
+
"flagKey": "payments-v2",
|
|
61
|
+
"salt": "1234567890abcdef",
|
|
62
|
+
"expectedBucketUint32": 587700822,
|
|
63
|
+
"expectedFloat12dp": 0.136834760662
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"contextKey": "A",
|
|
67
|
+
"flagKey": "a",
|
|
68
|
+
"salt": "1111111111111111",
|
|
69
|
+
"expectedBucketUint32": 1610235386,
|
|
70
|
+
"expectedFloat12dp": 0.374912141357
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"contextKey": "very-long-context-key-0123456789-abcdefghijklmnopqrstuvwxyz",
|
|
74
|
+
"flagKey": "long-flag-key.test",
|
|
75
|
+
"salt": "abcdefabcdefabcd",
|
|
76
|
+
"expectedBucketUint32": 2704953973,
|
|
77
|
+
"expectedFloat12dp": 0.629796174588
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"contextKey": "你好",
|
|
81
|
+
"flagKey": "i18n-rollout",
|
|
82
|
+
"salt": "9999999999999999",
|
|
83
|
+
"expectedBucketUint32": 3734192613,
|
|
84
|
+
"expectedFloat12dp": 0.869434469612
|
|
85
|
+
}
|
|
86
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "disabled flag serves off variant",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": false,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 1
|
|
29
|
+
},
|
|
30
|
+
"rules": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"context": {
|
|
35
|
+
"key": "u"
|
|
36
|
+
},
|
|
37
|
+
"expected": {
|
|
38
|
+
"variantKey": "off",
|
|
39
|
+
"value": false,
|
|
40
|
+
"reason": "OFF"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "missing flag uses code default",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"another-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 0
|
|
29
|
+
},
|
|
30
|
+
"rules": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"context": {
|
|
35
|
+
"key": "u"
|
|
36
|
+
},
|
|
37
|
+
"expected": {
|
|
38
|
+
"variantKey": null,
|
|
39
|
+
"value": "CODE_DEFAULT",
|
|
40
|
+
"reason": "FLAG_NOT_FOUND"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "revoked bundle uses code default",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 8,
|
|
7
|
+
"publishedAt": 1700000000001,
|
|
8
|
+
"revoked": true,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {}
|
|
11
|
+
},
|
|
12
|
+
"context": {
|
|
13
|
+
"key": "u"
|
|
14
|
+
},
|
|
15
|
+
"expected": {
|
|
16
|
+
"variantKey": null,
|
|
17
|
+
"value": "CODE_DEFAULT",
|
|
18
|
+
"reason": "REVOKED"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "unknown bundle format uses code default",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 2,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 0
|
|
29
|
+
},
|
|
30
|
+
"rules": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"context": {
|
|
35
|
+
"key": "u"
|
|
36
|
+
},
|
|
37
|
+
"expected": {
|
|
38
|
+
"variantKey": null,
|
|
39
|
+
"value": "CODE_DEFAULT",
|
|
40
|
+
"reason": "ERROR"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "invalid variant reference uses code default",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 99
|
|
29
|
+
},
|
|
30
|
+
"rules": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"context": {
|
|
35
|
+
"key": "u"
|
|
36
|
+
},
|
|
37
|
+
"expected": {
|
|
38
|
+
"variantKey": null,
|
|
39
|
+
"value": "CODE_DEFAULT",
|
|
40
|
+
"reason": "ERROR"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nonnumeric comparison is a false clause",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 0
|
|
29
|
+
},
|
|
30
|
+
"rules": [
|
|
31
|
+
{
|
|
32
|
+
"id": "rule-1",
|
|
33
|
+
"clauses": [
|
|
34
|
+
{
|
|
35
|
+
"attr": "score",
|
|
36
|
+
"op": "gt",
|
|
37
|
+
"values": [5],
|
|
38
|
+
"negate": false
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"serve": {
|
|
42
|
+
"variant": 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"context": {
|
|
50
|
+
"key": "u",
|
|
51
|
+
"attributes": {
|
|
52
|
+
"score": "five"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"expected": {
|
|
56
|
+
"variantKey": "off",
|
|
57
|
+
"value": false,
|
|
58
|
+
"reason": "FALLTHROUGH"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "normal fallthrough serves configured variant",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 1
|
|
29
|
+
},
|
|
30
|
+
"rules": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"context": {
|
|
35
|
+
"key": "u"
|
|
36
|
+
},
|
|
37
|
+
"expected": {
|
|
38
|
+
"variantKey": "on",
|
|
39
|
+
"value": true,
|
|
40
|
+
"reason": "FALLTHROUGH"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "empty attributes do not throw",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 0
|
|
29
|
+
},
|
|
30
|
+
"rules": [
|
|
31
|
+
{
|
|
32
|
+
"id": "rule-1",
|
|
33
|
+
"clauses": [
|
|
34
|
+
{
|
|
35
|
+
"attr": "plan",
|
|
36
|
+
"op": "eq",
|
|
37
|
+
"values": ["pro"],
|
|
38
|
+
"negate": false
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"serve": {
|
|
42
|
+
"variant": 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"context": {
|
|
50
|
+
"key": "u"
|
|
51
|
+
},
|
|
52
|
+
"expected": {
|
|
53
|
+
"variantKey": "off",
|
|
54
|
+
"value": false,
|
|
55
|
+
"reason": "FALLTHROUGH"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eq matches equal strings",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 0
|
|
29
|
+
},
|
|
30
|
+
"rules": [
|
|
31
|
+
{
|
|
32
|
+
"id": "rule-1",
|
|
33
|
+
"clauses": [
|
|
34
|
+
{
|
|
35
|
+
"attr": "value",
|
|
36
|
+
"op": "eq",
|
|
37
|
+
"values": ["pro"],
|
|
38
|
+
"negate": false
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"serve": {
|
|
42
|
+
"variant": 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"context": {
|
|
50
|
+
"key": "u",
|
|
51
|
+
"attributes": {
|
|
52
|
+
"value": "pro"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"expected": {
|
|
56
|
+
"variantKey": "on",
|
|
57
|
+
"value": true,
|
|
58
|
+
"reason": "RULE_MATCH:rule-1"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eq does not match unequal strings",
|
|
3
|
+
"bundle": {
|
|
4
|
+
"fmt": 1,
|
|
5
|
+
"envId": "env_test",
|
|
6
|
+
"version": 7,
|
|
7
|
+
"publishedAt": 1700000000000,
|
|
8
|
+
"revoked": false,
|
|
9
|
+
"segments": {},
|
|
10
|
+
"flags": {
|
|
11
|
+
"test-flag": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"version": 3,
|
|
14
|
+
"salt": "0123456789abcdef",
|
|
15
|
+
"on": true,
|
|
16
|
+
"variants": [
|
|
17
|
+
{
|
|
18
|
+
"key": "off",
|
|
19
|
+
"value": false
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"key": "on",
|
|
23
|
+
"value": true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"offVariant": 0,
|
|
27
|
+
"fallthrough": {
|
|
28
|
+
"variant": 0
|
|
29
|
+
},
|
|
30
|
+
"rules": [
|
|
31
|
+
{
|
|
32
|
+
"id": "rule-1",
|
|
33
|
+
"clauses": [
|
|
34
|
+
{
|
|
35
|
+
"attr": "value",
|
|
36
|
+
"op": "eq",
|
|
37
|
+
"values": ["pro"],
|
|
38
|
+
"negate": false
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"serve": {
|
|
42
|
+
"variant": 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"context": {
|
|
50
|
+
"key": "u",
|
|
51
|
+
"attributes": {
|
|
52
|
+
"value": "free"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"expected": {
|
|
56
|
+
"variantKey": "off",
|
|
57
|
+
"value": false,
|
|
58
|
+
"reason": "FALLTHROUGH"
|
|
59
|
+
}
|
|
60
|
+
}
|