prompt-flags 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/README.md +154 -0
- package/dist/__tests__/client.test.d.ts +2 -0
- package/dist/__tests__/client.test.d.ts.map +1 -0
- package/dist/__tests__/client.test.js +313 -0
- package/dist/__tests__/client.test.js.map +1 -0
- package/dist/bucket.d.ts +6 -0
- package/dist/bucket.d.ts.map +1 -0
- package/dist/bucket.js +23 -0
- package/dist/bucket.js.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +84 -0
- package/dist/client.js.map +1 -0
- package/dist/conditions.d.ts +5 -0
- package/dist/conditions.d.ts.map +1 -0
- package/dist/conditions.js +77 -0
- package/dist/conditions.js.map +1 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/evaluate.d.ts +4 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +72 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# prompt-flags
|
|
2
|
+
|
|
3
|
+
AI-native feature flags for prompt variants and model selection. Deterministic bucketing via murmurhash3, targeting rules with 13 operators, and 4 flag types (`prompt`, `model`, `config`, `boolean`).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install prompt-flags
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createClient, FlagConfiguration } from 'prompt-flags'
|
|
15
|
+
|
|
16
|
+
const config: FlagConfiguration = {
|
|
17
|
+
flags: [
|
|
18
|
+
{
|
|
19
|
+
key: 'system-prompt',
|
|
20
|
+
type: 'prompt',
|
|
21
|
+
enabled: true,
|
|
22
|
+
variants: [
|
|
23
|
+
{ key: 'v1', value: 'You are a helpful assistant.' },
|
|
24
|
+
{ key: 'v2', value: 'You are a concise assistant.' },
|
|
25
|
+
],
|
|
26
|
+
defaultVariant: 'v1',
|
|
27
|
+
rules: [
|
|
28
|
+
{
|
|
29
|
+
conditions: [{ attribute: 'plan', operator: 'equals', value: 'pro' }],
|
|
30
|
+
serve: { variant: 'v2' },
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'model-select',
|
|
36
|
+
type: 'model',
|
|
37
|
+
enabled: true,
|
|
38
|
+
variants: [
|
|
39
|
+
{ key: 'fast', value: { model: 'gpt-3.5-turbo', temperature: 0.7 } },
|
|
40
|
+
{ key: 'smart', value: { model: 'gpt-4o', temperature: 0.3 } },
|
|
41
|
+
],
|
|
42
|
+
defaultVariant: 'fast',
|
|
43
|
+
rules: [
|
|
44
|
+
{
|
|
45
|
+
serve: { rollout: [{ variant: 'smart', weight: 20 }, { variant: 'fast', weight: 80 }] },
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'new-ui',
|
|
51
|
+
type: 'boolean',
|
|
52
|
+
enabled: true,
|
|
53
|
+
variants: [
|
|
54
|
+
{ key: 'on', value: true },
|
|
55
|
+
{ key: 'off', value: false },
|
|
56
|
+
],
|
|
57
|
+
defaultVariant: 'off',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const client = createClient({ config })
|
|
63
|
+
|
|
64
|
+
const ctx = { key: 'user-123', plan: 'pro' }
|
|
65
|
+
|
|
66
|
+
// Prompt flag
|
|
67
|
+
const prompt = client.getPrompt('system-prompt', ctx) // 'You are a concise assistant.'
|
|
68
|
+
|
|
69
|
+
// Model flag (deterministic rollout)
|
|
70
|
+
const model = client.getModel('model-select', ctx) // { model: 'gpt-3.5-turbo', temperature: 0.7 }
|
|
71
|
+
|
|
72
|
+
// Boolean flag
|
|
73
|
+
const enabled = client.isEnabled('new-ui', ctx) // false
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Flag Types
|
|
77
|
+
|
|
78
|
+
| Type | Getter | Value type |
|
|
79
|
+
|-----------|----------------|--------------|
|
|
80
|
+
| `prompt` | `getPrompt` | `string` |
|
|
81
|
+
| `model` | `getModel` | `ModelConfig`|
|
|
82
|
+
| `config` | `getConfig<T>` | `T` |
|
|
83
|
+
| `boolean` | `isEnabled` | `boolean` |
|
|
84
|
+
|
|
85
|
+
## Targeting Rules
|
|
86
|
+
|
|
87
|
+
Rules are evaluated in order. The first matching rule wins. All conditions within a rule use AND logic.
|
|
88
|
+
|
|
89
|
+
### Supported operators
|
|
90
|
+
|
|
91
|
+
| Operator | Description |
|
|
92
|
+
|------------------------|--------------------------------------|
|
|
93
|
+
| `equals` | Strict string equality |
|
|
94
|
+
| `notEquals` | Strict string inequality |
|
|
95
|
+
| `in` | Value is in `values` array |
|
|
96
|
+
| `notIn` | Value is not in `values` array |
|
|
97
|
+
| `contains` | String contains substring |
|
|
98
|
+
| `startsWith` | String starts with value |
|
|
99
|
+
| `endsWith` | String ends with value |
|
|
100
|
+
| `greaterThan` | Numeric `>` |
|
|
101
|
+
| `lessThan` | Numeric `<` |
|
|
102
|
+
| `greaterThanOrEqual` | Numeric `>=` |
|
|
103
|
+
| `lessThanOrEqual` | Numeric `<=` |
|
|
104
|
+
| `matches` | Regex test (value is pattern string) |
|
|
105
|
+
| `exists` | Attribute is not null/undefined |
|
|
106
|
+
| `notExists` | Attribute is null/undefined |
|
|
107
|
+
|
|
108
|
+
Set `negate: true` on any condition to invert it.
|
|
109
|
+
|
|
110
|
+
### Context attributes
|
|
111
|
+
|
|
112
|
+
Built-in: `key`, `plan`, `region`, `email`, `role`.
|
|
113
|
+
Custom attributes: prefix with `custom.` (e.g., `custom.betaTester`).
|
|
114
|
+
|
|
115
|
+
## Percentage Rollouts
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
rules: [
|
|
119
|
+
{
|
|
120
|
+
serve: {
|
|
121
|
+
rollout: [
|
|
122
|
+
{ variant: 'treatment', weight: 20 },
|
|
123
|
+
{ variant: 'control', weight: 80 },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Weights are percentages (sum to 100). Bucketing is deterministic: the same `ctx.key` always resolves to the same variant for a given flag.
|
|
131
|
+
|
|
132
|
+
## Test Overrides
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
client.overrideForTest('new-ui', 'on')
|
|
136
|
+
client.isEnabled('new-ui', ctx) // true, reason: 'override'
|
|
137
|
+
|
|
138
|
+
client.clearOverride('new-ui')
|
|
139
|
+
client.clearAllOverrides()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Callbacks
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const client = createClient({
|
|
146
|
+
config,
|
|
147
|
+
onEvaluation: (result) => console.log(result),
|
|
148
|
+
onError: (err) => console.error(err),
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const client_1 = require("../client");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const baseCtx = { key: 'user-abc' };
|
|
7
|
+
const config = {
|
|
8
|
+
flags: [
|
|
9
|
+
// Boolean flag
|
|
10
|
+
{
|
|
11
|
+
key: 'feature-x',
|
|
12
|
+
type: 'boolean',
|
|
13
|
+
enabled: true,
|
|
14
|
+
variants: [
|
|
15
|
+
{ key: 'on', value: true },
|
|
16
|
+
{ key: 'off', value: false },
|
|
17
|
+
],
|
|
18
|
+
defaultVariant: 'off',
|
|
19
|
+
},
|
|
20
|
+
// Boolean flag that is disabled at the flag level
|
|
21
|
+
{
|
|
22
|
+
key: 'feature-disabled',
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
enabled: false,
|
|
25
|
+
variants: [
|
|
26
|
+
{ key: 'on', value: true },
|
|
27
|
+
{ key: 'off', value: false },
|
|
28
|
+
],
|
|
29
|
+
defaultVariant: 'off',
|
|
30
|
+
},
|
|
31
|
+
// Prompt flag
|
|
32
|
+
{
|
|
33
|
+
key: 'system-prompt',
|
|
34
|
+
type: 'prompt',
|
|
35
|
+
enabled: true,
|
|
36
|
+
variants: [
|
|
37
|
+
{ key: 'v1', value: 'You are a helpful assistant.' },
|
|
38
|
+
{ key: 'v2', value: 'You are a concise assistant.' },
|
|
39
|
+
],
|
|
40
|
+
defaultVariant: 'v1',
|
|
41
|
+
},
|
|
42
|
+
// Model flag
|
|
43
|
+
{
|
|
44
|
+
key: 'model-select',
|
|
45
|
+
type: 'model',
|
|
46
|
+
enabled: true,
|
|
47
|
+
variants: [
|
|
48
|
+
{ key: 'fast', value: { model: 'gpt-3.5-turbo', temperature: 0.7 } },
|
|
49
|
+
{ key: 'smart', value: { model: 'gpt-4o', temperature: 0.3 } },
|
|
50
|
+
],
|
|
51
|
+
defaultVariant: 'fast',
|
|
52
|
+
},
|
|
53
|
+
// Config flag
|
|
54
|
+
{
|
|
55
|
+
key: 'rate-limit',
|
|
56
|
+
type: 'config',
|
|
57
|
+
enabled: true,
|
|
58
|
+
variants: [
|
|
59
|
+
{ key: 'low', value: { rpm: 10 } },
|
|
60
|
+
{ key: 'high', value: { rpm: 100 } },
|
|
61
|
+
],
|
|
62
|
+
defaultVariant: 'low',
|
|
63
|
+
},
|
|
64
|
+
// Boolean flag with equals targeting rule
|
|
65
|
+
{
|
|
66
|
+
key: 'pro-feature',
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
enabled: true,
|
|
69
|
+
variants: [
|
|
70
|
+
{ key: 'on', value: true },
|
|
71
|
+
{ key: 'off', value: false },
|
|
72
|
+
],
|
|
73
|
+
defaultVariant: 'off',
|
|
74
|
+
rules: [
|
|
75
|
+
{
|
|
76
|
+
conditions: [{ attribute: 'plan', operator: 'equals', value: 'pro' }],
|
|
77
|
+
serve: { variant: 'on' },
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
// Boolean flag with 'in' targeting rule
|
|
82
|
+
{
|
|
83
|
+
key: 'beta-feature',
|
|
84
|
+
type: 'boolean',
|
|
85
|
+
enabled: true,
|
|
86
|
+
variants: [
|
|
87
|
+
{ key: 'on', value: true },
|
|
88
|
+
{ key: 'off', value: false },
|
|
89
|
+
],
|
|
90
|
+
defaultVariant: 'off',
|
|
91
|
+
rules: [
|
|
92
|
+
{
|
|
93
|
+
conditions: [
|
|
94
|
+
{ attribute: 'plan', operator: 'in', values: ['beta', 'enterprise'] },
|
|
95
|
+
],
|
|
96
|
+
serve: { variant: 'on' },
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
// Rollout flag (50/50)
|
|
101
|
+
{
|
|
102
|
+
key: 'rollout-flag',
|
|
103
|
+
type: 'boolean',
|
|
104
|
+
enabled: true,
|
|
105
|
+
variants: [
|
|
106
|
+
{ key: 'on', value: true },
|
|
107
|
+
{ key: 'off', value: false },
|
|
108
|
+
],
|
|
109
|
+
defaultVariant: 'off',
|
|
110
|
+
rules: [
|
|
111
|
+
{
|
|
112
|
+
serve: {
|
|
113
|
+
rollout: [
|
|
114
|
+
{ variant: 'on', weight: 50 },
|
|
115
|
+
{ variant: 'off', weight: 50 },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
// Prompt flag with contains condition
|
|
122
|
+
{
|
|
123
|
+
key: 'email-prompt',
|
|
124
|
+
type: 'prompt',
|
|
125
|
+
enabled: true,
|
|
126
|
+
variants: [
|
|
127
|
+
{ key: 'enterprise', value: 'Enterprise prompt.' },
|
|
128
|
+
{ key: 'default', value: 'Default prompt.' },
|
|
129
|
+
],
|
|
130
|
+
defaultVariant: 'default',
|
|
131
|
+
rules: [
|
|
132
|
+
{
|
|
133
|
+
conditions: [{ attribute: 'email', operator: 'endsWith', value: '@corp.com' }],
|
|
134
|
+
serve: { variant: 'enterprise' },
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
(0, vitest_1.describe)('createClient', () => {
|
|
141
|
+
let client;
|
|
142
|
+
(0, vitest_1.beforeEach)(() => {
|
|
143
|
+
client = (0, client_1.createClient)({ config });
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.describe)('boolean flag: isEnabled', () => {
|
|
146
|
+
(0, vitest_1.it)('returns false for default off variant', () => {
|
|
147
|
+
(0, vitest_1.expect)(client.isEnabled('feature-x', baseCtx)).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
(0, vitest_1.it)('returns false when flag is disabled (reason: disabled)', () => {
|
|
150
|
+
(0, vitest_1.expect)(client.isEnabled('feature-disabled', baseCtx)).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
(0, vitest_1.it)('evaluate returns reason=disabled when flag.enabled=false', () => {
|
|
153
|
+
const result = client.evaluate('feature-disabled', baseCtx);
|
|
154
|
+
(0, vitest_1.expect)(result.reason).toBe('disabled');
|
|
155
|
+
(0, vitest_1.expect)(result.flagEnabled).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
(0, vitest_1.describe)('prompt flag: getPrompt', () => {
|
|
159
|
+
(0, vitest_1.it)('returns default variant string', () => {
|
|
160
|
+
const prompt = client.getPrompt('system-prompt', baseCtx);
|
|
161
|
+
(0, vitest_1.expect)(prompt).toBe('You are a helpful assistant.');
|
|
162
|
+
});
|
|
163
|
+
(0, vitest_1.it)('throws FlagTypeMismatchError when called on a boolean flag', () => {
|
|
164
|
+
(0, vitest_1.expect)(() => client.getPrompt('feature-x', baseCtx)).toThrow(errors_1.FlagTypeMismatchError);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
(0, vitest_1.describe)('model flag: getModel', () => {
|
|
168
|
+
(0, vitest_1.it)('returns default ModelConfig', () => {
|
|
169
|
+
const model = client.getModel('model-select', baseCtx);
|
|
170
|
+
(0, vitest_1.expect)(model).toEqual({ model: 'gpt-3.5-turbo', temperature: 0.7 });
|
|
171
|
+
});
|
|
172
|
+
(0, vitest_1.it)('throws FlagTypeMismatchError when called on a boolean flag', () => {
|
|
173
|
+
(0, vitest_1.expect)(() => client.getModel('feature-x', baseCtx)).toThrow(errors_1.FlagTypeMismatchError);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
(0, vitest_1.describe)('config flag: getConfig', () => {
|
|
177
|
+
(0, vitest_1.it)('returns typed config value', () => {
|
|
178
|
+
const cfg = client.getConfig('rate-limit', baseCtx);
|
|
179
|
+
(0, vitest_1.expect)(cfg.rpm).toBe(10);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
(0, vitest_1.describe)('targeting rules: equals condition', () => {
|
|
183
|
+
(0, vitest_1.it)('matches pro plan user and enables feature', () => {
|
|
184
|
+
const proCtx = { key: 'user-pro', plan: 'pro' };
|
|
185
|
+
(0, vitest_1.expect)(client.isEnabled('pro-feature', proCtx)).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
(0, vitest_1.it)('does not match free plan user', () => {
|
|
188
|
+
const freeCtx = { key: 'user-free', plan: 'free' };
|
|
189
|
+
(0, vitest_1.expect)(client.isEnabled('pro-feature', freeCtx)).toBe(false);
|
|
190
|
+
});
|
|
191
|
+
(0, vitest_1.it)('evaluate returns reason=rule_match for matching rule', () => {
|
|
192
|
+
const proCtx = { key: 'user-pro', plan: 'pro' };
|
|
193
|
+
const result = client.evaluate('pro-feature', proCtx);
|
|
194
|
+
(0, vitest_1.expect)(result.reason).toBe('rule_match');
|
|
195
|
+
(0, vitest_1.expect)(result.variantKey).toBe('on');
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
(0, vitest_1.describe)('targeting rules: in condition', () => {
|
|
199
|
+
(0, vitest_1.it)('matches beta plan user', () => {
|
|
200
|
+
const betaCtx = { key: 'user-beta', plan: 'beta' };
|
|
201
|
+
(0, vitest_1.expect)(client.isEnabled('beta-feature', betaCtx)).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
(0, vitest_1.it)('matches enterprise plan user', () => {
|
|
204
|
+
const entCtx = { key: 'user-ent', plan: 'enterprise' };
|
|
205
|
+
(0, vitest_1.expect)(client.isEnabled('beta-feature', entCtx)).toBe(true);
|
|
206
|
+
});
|
|
207
|
+
(0, vitest_1.it)('does not match free plan user', () => {
|
|
208
|
+
const freeCtx = { key: 'user-free', plan: 'free' };
|
|
209
|
+
(0, vitest_1.expect)(client.isEnabled('beta-feature', freeCtx)).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
(0, vitest_1.describe)('targeting rules: endsWith condition', () => {
|
|
213
|
+
(0, vitest_1.it)('returns enterprise prompt for corp email', () => {
|
|
214
|
+
const ctx = { key: 'u1', email: 'alice@corp.com' };
|
|
215
|
+
(0, vitest_1.expect)(client.getPrompt('email-prompt', ctx)).toBe('Enterprise prompt.');
|
|
216
|
+
});
|
|
217
|
+
(0, vitest_1.it)('returns default prompt for non-corp email', () => {
|
|
218
|
+
const ctx = { key: 'u2', email: 'bob@gmail.com' };
|
|
219
|
+
(0, vitest_1.expect)(client.getPrompt('email-prompt', ctx)).toBe('Default prompt.');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
(0, vitest_1.describe)('rollout: deterministic bucketing', () => {
|
|
223
|
+
(0, vitest_1.it)('same context key always returns the same variant', () => {
|
|
224
|
+
const ctx = { key: 'stable-user' };
|
|
225
|
+
const first = client.evaluate('rollout-flag', ctx);
|
|
226
|
+
const second = client.evaluate('rollout-flag', ctx);
|
|
227
|
+
(0, vitest_1.expect)(first.variantKey).toBe(second.variantKey);
|
|
228
|
+
});
|
|
229
|
+
(0, vitest_1.it)('distributes roughly 50/50 across many users', () => {
|
|
230
|
+
let onCount = 0;
|
|
231
|
+
const total = 1000;
|
|
232
|
+
for (let i = 0; i < total; i++) {
|
|
233
|
+
const ctx = { key: `user-${i}` };
|
|
234
|
+
const result = client.evaluate('rollout-flag', ctx);
|
|
235
|
+
if (result.variantKey === 'on')
|
|
236
|
+
onCount++;
|
|
237
|
+
}
|
|
238
|
+
// Expect between 40% and 60% (deterministic hash, not truly random)
|
|
239
|
+
(0, vitest_1.expect)(onCount).toBeGreaterThan(350);
|
|
240
|
+
(0, vitest_1.expect)(onCount).toBeLessThan(650);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
(0, vitest_1.describe)('test overrides', () => {
|
|
244
|
+
(0, vitest_1.it)('overrideForTest forces a specific variant', () => {
|
|
245
|
+
client.overrideForTest('feature-x', 'on');
|
|
246
|
+
(0, vitest_1.expect)(client.isEnabled('feature-x', baseCtx)).toBe(true);
|
|
247
|
+
const result = client.evaluate('feature-x', baseCtx);
|
|
248
|
+
(0, vitest_1.expect)(result.reason).toBe('override');
|
|
249
|
+
});
|
|
250
|
+
(0, vitest_1.it)('clearOverride removes the override', () => {
|
|
251
|
+
client.overrideForTest('feature-x', 'on');
|
|
252
|
+
client.clearOverride('feature-x');
|
|
253
|
+
(0, vitest_1.expect)(client.isEnabled('feature-x', baseCtx)).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
(0, vitest_1.it)('clearAllOverrides removes all overrides', () => {
|
|
256
|
+
client.overrideForTest('feature-x', 'on');
|
|
257
|
+
client.overrideForTest('feature-disabled', 'on');
|
|
258
|
+
client.clearAllOverrides();
|
|
259
|
+
(0, vitest_1.expect)(client.isEnabled('feature-x', baseCtx)).toBe(false);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
(0, vitest_1.describe)('error cases', () => {
|
|
263
|
+
(0, vitest_1.it)('throws FlagNotFoundError for unknown flag key', () => {
|
|
264
|
+
(0, vitest_1.expect)(() => client.evaluate('nonexistent', baseCtx)).toThrow(errors_1.FlagNotFoundError);
|
|
265
|
+
});
|
|
266
|
+
(0, vitest_1.it)('FlagNotFoundError has the correct flagKey', () => {
|
|
267
|
+
try {
|
|
268
|
+
client.evaluate('nonexistent', baseCtx);
|
|
269
|
+
vitest_1.expect.fail('should have thrown');
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
(0, vitest_1.expect)(err).toBeInstanceOf(errors_1.FlagNotFoundError);
|
|
273
|
+
(0, vitest_1.expect)(err.flagKey).toBe('nonexistent');
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
(0, vitest_1.it)('FlagTypeMismatchError has the correct code', () => {
|
|
277
|
+
try {
|
|
278
|
+
client.getPrompt('feature-x', baseCtx);
|
|
279
|
+
vitest_1.expect.fail('should have thrown');
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
(0, vitest_1.expect)(err).toBeInstanceOf(errors_1.FlagTypeMismatchError);
|
|
283
|
+
(0, vitest_1.expect)(err.code).toBe('FLAG_TYPE_MISMATCH');
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
(0, vitest_1.describe)('utility methods', () => {
|
|
288
|
+
(0, vitest_1.it)('getFlagKeys returns all flag keys', () => {
|
|
289
|
+
const keys = client.getFlagKeys();
|
|
290
|
+
(0, vitest_1.expect)(keys).toContain('feature-x');
|
|
291
|
+
(0, vitest_1.expect)(keys).toContain('system-prompt');
|
|
292
|
+
(0, vitest_1.expect)(keys.length).toBe(config.flags.length);
|
|
293
|
+
});
|
|
294
|
+
(0, vitest_1.it)('getFlag returns FlagDefinition for known key', () => {
|
|
295
|
+
const flag = client.getFlag('feature-x');
|
|
296
|
+
(0, vitest_1.expect)(flag).not.toBeNull();
|
|
297
|
+
(0, vitest_1.expect)(flag.type).toBe('boolean');
|
|
298
|
+
});
|
|
299
|
+
(0, vitest_1.it)('getFlag returns null for unknown key', () => {
|
|
300
|
+
(0, vitest_1.expect)(client.getFlag('nope')).toBeNull();
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
(0, vitest_1.describe)('onEvaluation callback', () => {
|
|
304
|
+
(0, vitest_1.it)('calls onEvaluation with the result', () => {
|
|
305
|
+
const cb = vitest_1.vi.fn();
|
|
306
|
+
const c = (0, client_1.createClient)({ config, onEvaluation: cb });
|
|
307
|
+
c.isEnabled('feature-x', baseCtx);
|
|
308
|
+
(0, vitest_1.expect)(cb).toHaveBeenCalledOnce();
|
|
309
|
+
(0, vitest_1.expect)(cb.mock.calls[0][0].flagKey).toBe('feature-x');
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../src/__tests__/client.test.ts"],"names":[],"mappings":"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"}
|
package/dist/bucket.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket.d.ts","sourceRoot":"","sources":["../src/bucket.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACnD,MAAM,EAAE,MAAM,GACb,MAAM,CAQR"}
|
package/dist/bucket.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getBucket = getBucket;
|
|
7
|
+
exports.selectVariantByRollout = selectVariantByRollout;
|
|
8
|
+
const murmurhash3js_1 = __importDefault(require("murmurhash3js"));
|
|
9
|
+
function getBucket(contextKey, flagKey) {
|
|
10
|
+
const hash = murmurhash3js_1.default.x86.hash32(`${contextKey}:${flagKey}`, 0);
|
|
11
|
+
return Math.abs(hash) % 10000;
|
|
12
|
+
}
|
|
13
|
+
function selectVariantByRollout(rollout, bucket) {
|
|
14
|
+
const total = rollout.reduce((s, r) => s + r.weight, 0);
|
|
15
|
+
let cumulative = 0;
|
|
16
|
+
for (const r of rollout) {
|
|
17
|
+
cumulative += (r.weight / total) * 10000;
|
|
18
|
+
if (bucket < cumulative)
|
|
19
|
+
return r.variant;
|
|
20
|
+
}
|
|
21
|
+
return rollout[rollout.length - 1].variant;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=bucket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bucket.js","sourceRoot":"","sources":["../src/bucket.ts"],"names":[],"mappings":";;;;;AAEA,8BAGC;AAED,wDAWC;AAlBD,kEAAkC;AAElC,SAAgB,SAAS,CAAC,UAAkB,EAAE,OAAe;IAC3D,MAAM,IAAI,GAAG,uBAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,UAAU,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;IAC7D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;AAC/B,CAAC;AAED,SAAgB,sBAAsB,CACpC,OAAmD,EACnD,MAAc;IAEd,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACvD,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,UAAU,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,KAAK,CAAA;QACxC,IAAI,MAAM,GAAG,UAAU;YAAE,OAAO,CAAC,CAAC,OAAO,CAAA;IAC3C,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,CAAA;AAC5C,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,gBAAgB,EAKjB,MAAM,SAAS,CAAA;AAIhB,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAwFjE"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createClient = createClient;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
const evaluate_1 = require("./evaluate");
|
|
6
|
+
function createClient(config) {
|
|
7
|
+
const flagMap = new Map();
|
|
8
|
+
const overrides = new Map();
|
|
9
|
+
for (const flag of config.config.flags) {
|
|
10
|
+
flagMap.set(flag.key, flag);
|
|
11
|
+
}
|
|
12
|
+
function resolveFlag(key) {
|
|
13
|
+
const flag = flagMap.get(key);
|
|
14
|
+
if (!flag)
|
|
15
|
+
throw new errors_1.FlagNotFoundError(key);
|
|
16
|
+
return flag;
|
|
17
|
+
}
|
|
18
|
+
function runEvaluate(key, ctx) {
|
|
19
|
+
const flag = resolveFlag(key);
|
|
20
|
+
const mergedCtx = { ...config.defaultContext, ...ctx };
|
|
21
|
+
const result = (0, evaluate_1.evaluate)(flag, mergedCtx, overrides);
|
|
22
|
+
config.onEvaluation?.(result);
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
evaluate(key, ctx) {
|
|
27
|
+
try {
|
|
28
|
+
return runEvaluate(key, ctx);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
config.onError?.(err);
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
getPrompt(key, ctx) {
|
|
36
|
+
const flag = resolveFlag(key);
|
|
37
|
+
if (flag.type !== 'prompt') {
|
|
38
|
+
throw new errors_1.FlagTypeMismatchError(key, 'prompt', flag.type);
|
|
39
|
+
}
|
|
40
|
+
const result = runEvaluate(key, ctx);
|
|
41
|
+
return result.value;
|
|
42
|
+
},
|
|
43
|
+
getModel(key, ctx) {
|
|
44
|
+
const flag = resolveFlag(key);
|
|
45
|
+
if (flag.type !== 'model') {
|
|
46
|
+
throw new errors_1.FlagTypeMismatchError(key, 'model', flag.type);
|
|
47
|
+
}
|
|
48
|
+
const result = runEvaluate(key, ctx);
|
|
49
|
+
return result.value;
|
|
50
|
+
},
|
|
51
|
+
getConfig(key, ctx) {
|
|
52
|
+
const flag = resolveFlag(key);
|
|
53
|
+
if (flag.type !== 'config') {
|
|
54
|
+
throw new errors_1.FlagTypeMismatchError(key, 'config', flag.type);
|
|
55
|
+
}
|
|
56
|
+
const result = runEvaluate(key, ctx);
|
|
57
|
+
return result.value;
|
|
58
|
+
},
|
|
59
|
+
isEnabled(key, ctx) {
|
|
60
|
+
const flag = resolveFlag(key);
|
|
61
|
+
if (flag.type !== 'boolean') {
|
|
62
|
+
throw new errors_1.FlagTypeMismatchError(key, 'boolean', flag.type);
|
|
63
|
+
}
|
|
64
|
+
const result = runEvaluate(key, ctx);
|
|
65
|
+
return result.value;
|
|
66
|
+
},
|
|
67
|
+
getFlagKeys() {
|
|
68
|
+
return Array.from(flagMap.keys());
|
|
69
|
+
},
|
|
70
|
+
getFlag(key) {
|
|
71
|
+
return flagMap.get(key) ?? null;
|
|
72
|
+
},
|
|
73
|
+
overrideForTest(key, variantKey) {
|
|
74
|
+
overrides.set(key, variantKey);
|
|
75
|
+
},
|
|
76
|
+
clearOverride(key) {
|
|
77
|
+
overrides.delete(key);
|
|
78
|
+
},
|
|
79
|
+
clearAllOverrides() {
|
|
80
|
+
overrides.clear();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EvaluationContext, RuleCondition, TargetingRule } from './types';
|
|
2
|
+
export declare function getAttributeValue(ctx: EvaluationContext, attribute: string): unknown;
|
|
3
|
+
export declare function evaluateCondition(ctx: EvaluationContext, condition: RuleCondition): boolean;
|
|
4
|
+
export declare function evaluateRule(ctx: EvaluationContext, rule: TargetingRule): boolean;
|
|
5
|
+
//# sourceMappingURL=conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.d.ts","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEzE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAapF;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,GAAG,OAAO,CAsD3F;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAGjF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAttributeValue = getAttributeValue;
|
|
4
|
+
exports.evaluateCondition = evaluateCondition;
|
|
5
|
+
exports.evaluateRule = evaluateRule;
|
|
6
|
+
function getAttributeValue(ctx, attribute) {
|
|
7
|
+
if (attribute.startsWith('custom.')) {
|
|
8
|
+
const subKey = attribute.slice('custom.'.length);
|
|
9
|
+
return ctx.custom?.[subKey];
|
|
10
|
+
}
|
|
11
|
+
switch (attribute) {
|
|
12
|
+
case 'key': return ctx.key;
|
|
13
|
+
case 'plan': return ctx.plan;
|
|
14
|
+
case 'region': return ctx.region;
|
|
15
|
+
case 'email': return ctx.email;
|
|
16
|
+
case 'role': return ctx.role;
|
|
17
|
+
default: return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function evaluateCondition(ctx, condition) {
|
|
21
|
+
const attrValue = getAttributeValue(ctx, condition.attribute);
|
|
22
|
+
const { operator, value, values, negate } = condition;
|
|
23
|
+
let result;
|
|
24
|
+
switch (operator) {
|
|
25
|
+
case 'equals':
|
|
26
|
+
result = String(attrValue) === String(value);
|
|
27
|
+
break;
|
|
28
|
+
case 'notEquals':
|
|
29
|
+
result = String(attrValue) !== String(value);
|
|
30
|
+
break;
|
|
31
|
+
case 'in':
|
|
32
|
+
result = (values ?? []).map(String).includes(String(attrValue));
|
|
33
|
+
break;
|
|
34
|
+
case 'notIn':
|
|
35
|
+
result = !(values ?? []).map(String).includes(String(attrValue));
|
|
36
|
+
break;
|
|
37
|
+
case 'contains':
|
|
38
|
+
result = typeof attrValue === 'string' && attrValue.includes(String(value));
|
|
39
|
+
break;
|
|
40
|
+
case 'startsWith':
|
|
41
|
+
result = typeof attrValue === 'string' && attrValue.startsWith(String(value));
|
|
42
|
+
break;
|
|
43
|
+
case 'endsWith':
|
|
44
|
+
result = typeof attrValue === 'string' && attrValue.endsWith(String(value));
|
|
45
|
+
break;
|
|
46
|
+
case 'greaterThan':
|
|
47
|
+
result = Number(attrValue) > Number(value);
|
|
48
|
+
break;
|
|
49
|
+
case 'lessThan':
|
|
50
|
+
result = Number(attrValue) < Number(value);
|
|
51
|
+
break;
|
|
52
|
+
case 'greaterThanOrEqual':
|
|
53
|
+
result = Number(attrValue) >= Number(value);
|
|
54
|
+
break;
|
|
55
|
+
case 'lessThanOrEqual':
|
|
56
|
+
result = Number(attrValue) <= Number(value);
|
|
57
|
+
break;
|
|
58
|
+
case 'matches':
|
|
59
|
+
result = typeof attrValue === 'string' && new RegExp(String(value)).test(attrValue);
|
|
60
|
+
break;
|
|
61
|
+
case 'exists':
|
|
62
|
+
result = attrValue !== undefined && attrValue !== null;
|
|
63
|
+
break;
|
|
64
|
+
case 'notExists':
|
|
65
|
+
result = attrValue === undefined || attrValue === null;
|
|
66
|
+
break;
|
|
67
|
+
default:
|
|
68
|
+
result = false;
|
|
69
|
+
}
|
|
70
|
+
return negate === true ? !result : result;
|
|
71
|
+
}
|
|
72
|
+
function evaluateRule(ctx, rule) {
|
|
73
|
+
if (!rule.conditions || rule.conditions.length === 0)
|
|
74
|
+
return true;
|
|
75
|
+
return rule.conditions.every((cond) => evaluateCondition(ctx, cond));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=conditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.js","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare class FlagError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(msg: string, code: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class FlagNotFoundError extends FlagError {
|
|
6
|
+
readonly flagKey: string;
|
|
7
|
+
constructor(flagKey: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class FlagTypeMismatchError extends FlagError {
|
|
10
|
+
readonly flagKey: string;
|
|
11
|
+
constructor(flagKey: string, expected: string, actual: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class VariantNotFoundError extends FlagError {
|
|
14
|
+
readonly flagKey: string;
|
|
15
|
+
constructor(flagKey: string, variantKey: string);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;IACT,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAlC,GAAG,EAAE,MAAM,EAAW,IAAI,EAAE,MAAM;CAI/C;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAIrC;AAED,qBAAa,qBAAsB,SAAQ,SAAS;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAIvE;AAED,qBAAa,oBAAqB,SAAQ,SAAS;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAIzD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VariantNotFoundError = exports.FlagTypeMismatchError = exports.FlagNotFoundError = exports.FlagError = void 0;
|
|
4
|
+
class FlagError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(msg, code) {
|
|
7
|
+
super(msg);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = 'FlagError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.FlagError = FlagError;
|
|
13
|
+
class FlagNotFoundError extends FlagError {
|
|
14
|
+
flagKey;
|
|
15
|
+
constructor(flagKey) {
|
|
16
|
+
super(`Flag not found: "${flagKey}"`, 'FLAG_NOT_FOUND');
|
|
17
|
+
this.flagKey = flagKey;
|
|
18
|
+
this.name = 'FlagNotFoundError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.FlagNotFoundError = FlagNotFoundError;
|
|
22
|
+
class FlagTypeMismatchError extends FlagError {
|
|
23
|
+
flagKey;
|
|
24
|
+
constructor(flagKey, expected, actual) {
|
|
25
|
+
super(`Type mismatch for "${flagKey}": expected ${expected}, got ${actual}`, 'FLAG_TYPE_MISMATCH');
|
|
26
|
+
this.flagKey = flagKey;
|
|
27
|
+
this.name = 'FlagTypeMismatchError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.FlagTypeMismatchError = FlagTypeMismatchError;
|
|
31
|
+
class VariantNotFoundError extends FlagError {
|
|
32
|
+
flagKey;
|
|
33
|
+
constructor(flagKey, variantKey) {
|
|
34
|
+
super(`Variant "${variantKey}" not found in flag "${flagKey}"`, 'VARIANT_NOT_FOUND');
|
|
35
|
+
this.flagKey = flagKey;
|
|
36
|
+
this.name = 'VariantNotFoundError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.VariantNotFoundError = VariantNotFoundError;
|
|
40
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,SAAU,SAAQ,KAAK;IACA;IAAlC,YAAY,GAAW,EAAW,IAAY;QAC5C,KAAK,CAAC,GAAG,CAAC,CAAA;QADsB,SAAI,GAAJ,IAAI,CAAQ;QAE5C,IAAI,CAAC,IAAI,GAAG,WAAW,CAAA;IACzB,CAAC;CACF;AALD,8BAKC;AAED,MAAa,iBAAkB,SAAQ,SAAS;IACzB;IAArB,YAAqB,OAAe;QAClC,KAAK,CAAC,oBAAoB,OAAO,GAAG,EAAE,gBAAgB,CAAC,CAAA;QADpC,YAAO,GAAP,OAAO,CAAQ;QAElC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;IACjC,CAAC;CACF;AALD,8CAKC;AAED,MAAa,qBAAsB,SAAQ,SAAS;IAC7B;IAArB,YAAqB,OAAe,EAAE,QAAgB,EAAE,MAAc;QACpE,KAAK,CAAC,sBAAsB,OAAO,eAAe,QAAQ,SAAS,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAA;QAD/E,YAAO,GAAP,OAAO,CAAQ;QAElC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;IACrC,CAAC;CACF;AALD,sDAKC;AAED,MAAa,oBAAqB,SAAQ,SAAS;IAC5B;IAArB,YAAqB,OAAe,EAAE,UAAkB;QACtD,KAAK,CAAC,YAAY,UAAU,wBAAwB,OAAO,GAAG,EAAE,mBAAmB,CAAC,CAAA;QADjE,YAAO,GAAP,OAAO,CAAQ;QAElC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACpC,CAAC;CACF;AALD,oDAKC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { FlagDefinition, EvaluationContext, EvaluationResult } from './types';
|
|
2
|
+
export declare function getVariantValue(flag: FlagDefinition, variantKey: string): unknown;
|
|
3
|
+
export declare function evaluate<T>(flag: FlagDefinition, ctx: EvaluationContext, overrides: Map<string, string>): EvaluationResult<T>;
|
|
4
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK7E,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAMjF;AAED,wBAAgB,QAAQ,CAAC,CAAC,EACxB,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,iBAAiB,EACtB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,gBAAgB,CAAC,CAAC,CAAC,CA+DrB"}
|
package/dist/evaluate.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVariantValue = getVariantValue;
|
|
4
|
+
exports.evaluate = evaluate;
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const bucket_1 = require("./bucket");
|
|
7
|
+
const conditions_1 = require("./conditions");
|
|
8
|
+
function getVariantValue(flag, variantKey) {
|
|
9
|
+
const variant = flag.variants.find((v) => v.key === variantKey);
|
|
10
|
+
if (!variant) {
|
|
11
|
+
throw new errors_1.VariantNotFoundError(flag.key, variantKey);
|
|
12
|
+
}
|
|
13
|
+
return variant.value;
|
|
14
|
+
}
|
|
15
|
+
function evaluate(flag, ctx, overrides) {
|
|
16
|
+
const flagEnabled = flag.enabled !== false;
|
|
17
|
+
// 1. Check overrides
|
|
18
|
+
const overrideVariantKey = overrides.get(flag.key);
|
|
19
|
+
if (overrideVariantKey !== undefined) {
|
|
20
|
+
const value = getVariantValue(flag, overrideVariantKey);
|
|
21
|
+
return {
|
|
22
|
+
flagKey: flag.key,
|
|
23
|
+
variantKey: overrideVariantKey,
|
|
24
|
+
value,
|
|
25
|
+
reason: 'override',
|
|
26
|
+
flagEnabled,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// 2. If disabled, return default variant
|
|
30
|
+
if (!flagEnabled) {
|
|
31
|
+
const value = getVariantValue(flag, flag.defaultVariant);
|
|
32
|
+
return {
|
|
33
|
+
flagKey: flag.key,
|
|
34
|
+
variantKey: flag.defaultVariant,
|
|
35
|
+
value,
|
|
36
|
+
reason: 'disabled',
|
|
37
|
+
flagEnabled: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// 3. Evaluate targeting rules
|
|
41
|
+
for (const rule of flag.rules ?? []) {
|
|
42
|
+
if ((0, conditions_1.evaluateRule)(ctx, rule)) {
|
|
43
|
+
const serve = rule.serve;
|
|
44
|
+
let resolvedVariantKey;
|
|
45
|
+
if ('variant' in serve) {
|
|
46
|
+
resolvedVariantKey = serve.variant;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const bucket = (0, bucket_1.getBucket)(ctx.key, flag.key);
|
|
50
|
+
resolvedVariantKey = (0, bucket_1.selectVariantByRollout)(serve.rollout, bucket);
|
|
51
|
+
}
|
|
52
|
+
const value = getVariantValue(flag, resolvedVariantKey);
|
|
53
|
+
return {
|
|
54
|
+
flagKey: flag.key,
|
|
55
|
+
variantKey: resolvedVariantKey,
|
|
56
|
+
value,
|
|
57
|
+
reason: 'rule_match',
|
|
58
|
+
flagEnabled: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// 4. No rule matched — return default
|
|
63
|
+
const value = getVariantValue(flag, flag.defaultVariant);
|
|
64
|
+
return {
|
|
65
|
+
flagKey: flag.key,
|
|
66
|
+
variantKey: flag.defaultVariant,
|
|
67
|
+
value,
|
|
68
|
+
reason: 'default',
|
|
69
|
+
flagEnabled: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=evaluate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":";;AAKA,0CAMC;AAED,4BAmEC;AA/ED,qCAA+C;AAC/C,qCAA4D;AAC5D,6CAA2C;AAE3C,SAAgB,eAAe,CAAC,IAAoB,EAAE,UAAkB;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,UAAU,CAAC,CAAA;IAC/D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,6BAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAA;IACtD,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAA;AACtB,CAAC;AAED,SAAgB,QAAQ,CACtB,IAAoB,EACpB,GAAsB,EACtB,SAA8B;IAE9B,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,KAAK,KAAK,CAAA;IAE1C,qBAAqB;IACrB,MAAM,kBAAkB,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAClD,IAAI,kBAAkB,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,kBAAkB,CAAM,CAAA;QAC5D,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,GAAG;YACjB,UAAU,EAAE,kBAAkB;YAC9B,KAAK;YACL,MAAM,EAAE,UAAU;YAClB,WAAW;SACZ,CAAA;IACH,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAM,CAAA;QAC7D,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,GAAG;YACjB,UAAU,EAAE,IAAI,CAAC,cAAc;YAC/B,KAAK;YACL,MAAM,EAAE,UAAU;YAClB,WAAW,EAAE,KAAK;SACnB,CAAA;IACH,CAAC;IAED,8BAA8B;IAC9B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACpC,IAAI,IAAA,yBAAY,EAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAEwC,CAAA;YAE3D,IAAI,kBAA0B,CAAA;YAC9B,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;gBACvB,kBAAkB,GAAG,KAAK,CAAC,OAAO,CAAA;YACpC,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,IAAA,kBAAS,EAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;gBAC3C,kBAAkB,GAAG,IAAA,+BAAsB,EAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YACpE,CAAC;YAED,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,kBAAkB,CAAM,CAAA;YAC5D,OAAO;gBACL,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,UAAU,EAAE,kBAAkB;gBAC9B,KAAK;gBACL,MAAM,EAAE,YAAY;gBACpB,WAAW,EAAE,IAAI;aAClB,CAAA;QACH,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAM,CAAA;IAC7D,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,GAAG;QACjB,UAAU,EAAE,IAAI,CAAC,cAAc;QAC/B,KAAK;QACL,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,IAAI;KAClB,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createClient } from './client';
|
|
2
|
+
export type { FlagType, EvaluationReason, ComparisonOperator, EvaluationContext, ModelConfig, RuleCondition, TargetingRule, FlagVariant, FlagDefinition, FlagConfiguration, EvaluationResult, FlagClientConfig, FlagClient, } from './types';
|
|
3
|
+
export { FlagError, FlagNotFoundError, FlagTypeMismatchError, VariantNotFoundError, } from './errors';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,YAAY,EACV,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,GACX,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,UAAU,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VariantNotFoundError = exports.FlagTypeMismatchError = exports.FlagNotFoundError = exports.FlagError = exports.createClient = void 0;
|
|
4
|
+
// prompt-flags - AI-native feature flags for prompt variants and model selection
|
|
5
|
+
var client_1 = require("./client");
|
|
6
|
+
Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_1.createClient; } });
|
|
7
|
+
var errors_1 = require("./errors");
|
|
8
|
+
Object.defineProperty(exports, "FlagError", { enumerable: true, get: function () { return errors_1.FlagError; } });
|
|
9
|
+
Object.defineProperty(exports, "FlagNotFoundError", { enumerable: true, get: function () { return errors_1.FlagNotFoundError; } });
|
|
10
|
+
Object.defineProperty(exports, "FlagTypeMismatchError", { enumerable: true, get: function () { return errors_1.FlagTypeMismatchError; } });
|
|
11
|
+
Object.defineProperty(exports, "VariantNotFoundError", { enumerable: true, get: function () { return errors_1.VariantNotFoundError; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iFAAiF;AACjF,mCAAuC;AAA9B,sGAAA,YAAY,OAAA;AAgBrB,mCAKiB;AAJf,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,8GAAA,oBAAoB,OAAA"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type FlagType = 'prompt' | 'model' | 'config' | 'boolean';
|
|
2
|
+
export type EvaluationReason = 'rule_match' | 'default' | 'disabled' | 'error' | 'override';
|
|
3
|
+
export type ComparisonOperator = 'equals' | 'notEquals' | 'in' | 'notIn' | 'contains' | 'startsWith' | 'endsWith' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual' | 'matches' | 'exists' | 'notExists';
|
|
4
|
+
export interface EvaluationContext {
|
|
5
|
+
key: string;
|
|
6
|
+
plan?: string;
|
|
7
|
+
region?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
custom?: Record<string, string | number | boolean | string[]>;
|
|
11
|
+
}
|
|
12
|
+
export interface ModelConfig {
|
|
13
|
+
model: string;
|
|
14
|
+
temperature?: number;
|
|
15
|
+
maxTokens?: number;
|
|
16
|
+
[k: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface RuleCondition {
|
|
19
|
+
attribute: string;
|
|
20
|
+
operator: ComparisonOperator;
|
|
21
|
+
value?: string | number | boolean;
|
|
22
|
+
values?: (string | number)[];
|
|
23
|
+
negate?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface TargetingRule {
|
|
26
|
+
description?: string;
|
|
27
|
+
conditions?: RuleCondition[];
|
|
28
|
+
serve: {
|
|
29
|
+
variant: string;
|
|
30
|
+
} | {
|
|
31
|
+
rollout: Array<{
|
|
32
|
+
variant: string;
|
|
33
|
+
weight: number;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface FlagVariant {
|
|
38
|
+
key: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
}
|
|
41
|
+
export interface FlagDefinition {
|
|
42
|
+
key: string;
|
|
43
|
+
type: FlagType;
|
|
44
|
+
enabled?: boolean;
|
|
45
|
+
variants: FlagVariant[];
|
|
46
|
+
defaultVariant: string;
|
|
47
|
+
rules?: TargetingRule[];
|
|
48
|
+
}
|
|
49
|
+
export interface FlagConfiguration {
|
|
50
|
+
flags: FlagDefinition[];
|
|
51
|
+
segments?: Record<string, {
|
|
52
|
+
conditions: RuleCondition[];
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
export interface EvaluationResult<T = unknown> {
|
|
56
|
+
flagKey: string;
|
|
57
|
+
variantKey: string;
|
|
58
|
+
value: T;
|
|
59
|
+
reason: EvaluationReason;
|
|
60
|
+
flagEnabled: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface FlagClientConfig {
|
|
63
|
+
config: FlagConfiguration;
|
|
64
|
+
defaultContext?: Partial<EvaluationContext>;
|
|
65
|
+
onEvaluation?: (e: EvaluationResult<unknown>) => void;
|
|
66
|
+
onError?: (e: Error) => void;
|
|
67
|
+
}
|
|
68
|
+
export interface FlagClient {
|
|
69
|
+
getPrompt(key: string, ctx: EvaluationContext): string;
|
|
70
|
+
getModel(key: string, ctx: EvaluationContext): ModelConfig;
|
|
71
|
+
getConfig<T>(key: string, ctx: EvaluationContext): T;
|
|
72
|
+
isEnabled(key: string, ctx: EvaluationContext): boolean;
|
|
73
|
+
evaluate<T>(key: string, ctx: EvaluationContext): EvaluationResult<T>;
|
|
74
|
+
getFlagKeys(): string[];
|
|
75
|
+
getFlag(key: string): FlagDefinition | null;
|
|
76
|
+
overrideForTest(key: string, variantKey: string): void;
|
|
77
|
+
clearOverride(key: string): void;
|
|
78
|
+
clearAllOverrides(): void;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prompt-flags",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI-native feature flags for prompt variants and model selection",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"lint": "eslint src/",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"murmurhash3js": "^3.0.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/murmurhash3js": "^3.0.3",
|
|
30
|
+
"@types/node": "^20.0.0",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
32
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
33
|
+
"eslint": "^8.57.0",
|
|
34
|
+
"typescript": "^5.4.0",
|
|
35
|
+
"vitest": "^1.5.0"
|
|
36
|
+
}
|
|
37
|
+
}
|