@guidelytech/flags 0.0.0-stage → 0.7.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.
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Wire format of the feature-flag payload served by `GET /api/v1/sdk/flags` and the results of
3
+ * evaluating it. See docs/feature-flags-spec.md for the semantics; every SDK shares them.
4
+ */
5
+ type JsonValue = null | boolean | number | string | JsonValue[] | {
6
+ [key: string]: JsonValue;
7
+ };
8
+ type Attributes = Record<string, unknown>;
9
+ type FlagValueType = "boolean" | "string" | "number" | "json";
10
+ interface FlagsPayload {
11
+ schemaVersion: 1;
12
+ projectId: string;
13
+ environment: string;
14
+ /** Bumps on every change that affects this environment. */
15
+ version: number;
16
+ generatedAt: string;
17
+ /** Salt for the hashed (confidential) operators. */
18
+ salt: string;
19
+ flags: Record<string, FlagDefinition>;
20
+ segments: Record<string, SegmentDefinition>;
21
+ holdouts?: HoldoutDefinition[];
22
+ }
23
+ /** What `GET /flags` returns when the environment encrypts its payload. */
24
+ interface EncryptedFlagsPayload {
25
+ schemaVersion: 1;
26
+ projectId: string;
27
+ environment: string;
28
+ version: number;
29
+ generatedAt: string;
30
+ encryptedPayload: string;
31
+ }
32
+ interface FlagDefinition {
33
+ key: string;
34
+ type: FlagValueType;
35
+ version: number;
36
+ /** Environment kill switch. */
37
+ enabled: boolean;
38
+ /** Served when the flag is disabled or a prerequisite fails. */
39
+ offValue: JsonValue;
40
+ /** Served when the flag is enabled and no rule serves a value. */
41
+ defaultValue: JsonValue;
42
+ prerequisites?: Prerequisite[];
43
+ rules?: FlagRule[];
44
+ }
45
+ type FlagComparisonOperator = "isOn" | "isOff" | "equals" | "notEquals";
46
+ interface Prerequisite {
47
+ flag: string;
48
+ operator: FlagComparisonOperator;
49
+ value?: JsonValue;
50
+ }
51
+ interface RuleSchedule {
52
+ start?: string | null;
53
+ end?: string | null;
54
+ }
55
+ interface RuleBase {
56
+ id: string;
57
+ description?: string;
58
+ /** All must match; absent or empty means everyone. */
59
+ conditions?: Condition[];
60
+ schedule?: RuleSchedule;
61
+ }
62
+ interface ForceRule extends RuleBase {
63
+ type: "force";
64
+ value: JsonValue;
65
+ variationId?: string;
66
+ }
67
+ interface RampStep {
68
+ at: string;
69
+ coverage: number;
70
+ }
71
+ interface RolloutRule extends RuleBase {
72
+ type: "rollout";
73
+ value: JsonValue;
74
+ variationId?: string;
75
+ hashAttribute: string;
76
+ seed: string;
77
+ /** Share of matching users (0–1) that get `value`; the rest fall through. */
78
+ coverage: number;
79
+ /** Scheduled ramp: the coverage of the last step whose `at` has passed applies. */
80
+ ramp?: RampStep[];
81
+ }
82
+ interface SplitBucket {
83
+ weight: number;
84
+ value: JsonValue;
85
+ variationId?: string;
86
+ }
87
+ interface SplitRule extends RuleBase {
88
+ type: "split";
89
+ hashAttribute: string;
90
+ seed: string;
91
+ buckets: SplitBucket[];
92
+ }
93
+ interface ExperimentVariation {
94
+ id: string;
95
+ name?: string;
96
+ value: JsonValue;
97
+ weight: number;
98
+ }
99
+ interface ExperimentNamespace {
100
+ id: string;
101
+ start: number;
102
+ end: number;
103
+ }
104
+ interface ExperimentRule extends RuleBase {
105
+ type: "experiment";
106
+ key: string;
107
+ name?: string;
108
+ hashAttribute: string;
109
+ fallbackAttribute?: string;
110
+ seed: string;
111
+ coverage: number;
112
+ variations: ExperimentVariation[];
113
+ namespace?: ExperimentNamespace;
114
+ bucketVersion?: number;
115
+ minBucketVersion?: number;
116
+ disableStickyBucketing?: boolean;
117
+ }
118
+ type FlagRule = ForceRule | RolloutRule | SplitRule | ExperimentRule;
119
+ type FlagRuleType = FlagRule["type"];
120
+ declare const TEXT_OPERATORS: readonly ["equals", "notEquals", "in", "notIn", "contains", "notContains", "startsWith", "notStartsWith", "endsWith", "notEndsWith", "regex", "notRegex"];
121
+ declare const NUMBER_OPERATORS: readonly ["numEquals", "numNotEquals", "gt", "gte", "lt", "lte"];
122
+ declare const SEMVER_OPERATORS: readonly ["semverEquals", "semverNotEquals", "semverGt", "semverGte", "semverLt", "semverLte", "semverIn", "semverNotIn"];
123
+ declare const DATE_OPERATORS: readonly ["before", "after"];
124
+ declare const ARRAY_OPERATORS: readonly ["arrayContainsAny", "arrayNotContainsAny", "arrayContainsAll"];
125
+ declare const BOOLEAN_OPERATORS: readonly ["isTrue", "isFalse"];
126
+ declare const PRESENCE_OPERATORS: readonly ["exists", "notExists", "isType"];
127
+ declare const HASHED_OPERATORS: readonly ["equalsHashed", "notEqualsHashed", "inHashed", "notInHashed", "startsWithHashed", "notStartsWithHashed", "endsWithHashed", "notEndsWithHashed", "arrayContainsAnyHashed", "arrayNotContainsAnyHashed"];
128
+ declare const ATTRIBUTE_OPERATORS: readonly ["equals", "notEquals", "in", "notIn", "contains", "notContains", "startsWith", "notStartsWith", "endsWith", "notEndsWith", "regex", "notRegex", "numEquals", "numNotEquals", "gt", "gte", "lt", "lte", "semverEquals", "semverNotEquals", "semverGt", "semverGte", "semverLt", "semverLte", "semverIn", "semverNotIn", "before", "after", "arrayContainsAny", "arrayNotContainsAny", "arrayContainsAll", "isTrue", "isFalse", "exists", "notExists", "isType", "equalsHashed", "notEqualsHashed", "inHashed", "notInHashed", "startsWithHashed", "notStartsWithHashed", "endsWithHashed", "notEndsWithHashed", "arrayContainsAnyHashed", "arrayNotContainsAnyHashed"];
129
+ type AttributeOperator = (typeof ATTRIBUTE_OPERATORS)[number];
130
+ interface AttributeCondition {
131
+ kind: "attribute";
132
+ attribute: string;
133
+ operator: AttributeOperator;
134
+ values?: JsonValue[];
135
+ caseInsensitive?: boolean;
136
+ }
137
+ interface SegmentCondition {
138
+ kind: "segment";
139
+ operator: "in" | "notIn";
140
+ segment: string;
141
+ }
142
+ interface FlagCondition {
143
+ kind: "flag";
144
+ flag: string;
145
+ operator: FlagComparisonOperator;
146
+ value?: JsonValue;
147
+ }
148
+ interface GroupCondition {
149
+ kind: "group";
150
+ op: "and" | "or";
151
+ negate?: boolean;
152
+ conditions: Condition[];
153
+ }
154
+ type Condition = AttributeCondition | SegmentCondition | FlagCondition | GroupCondition;
155
+ interface ConditionSegment {
156
+ key: string;
157
+ kind: "conditions";
158
+ conditions: Condition[];
159
+ }
160
+ interface ListSegment {
161
+ key: string;
162
+ kind: "list";
163
+ attribute: string;
164
+ values: string[];
165
+ hashed?: boolean;
166
+ }
167
+ type SegmentDefinition = ConditionSegment | ListSegment;
168
+ interface HoldoutDefinition {
169
+ key: string;
170
+ hashAttribute: string;
171
+ seed: string;
172
+ coverage: number;
173
+ flags: string[];
174
+ }
175
+ type EvaluationReason = "FLAG_NOT_FOUND" | "DISABLED" | "PREREQUISITE_FAILED" | "HOLDOUT" | "RULE_MATCH" | "EXPERIMENT" | "DEFAULT" | "OVERRIDE" | "ERROR";
176
+ interface ExperimentAssignment {
177
+ key: string;
178
+ name?: string;
179
+ variationId: string;
180
+ variationName?: string;
181
+ variationIndex: number;
182
+ hashAttribute: string;
183
+ hashValue: string;
184
+ bucket: number;
185
+ stickyBucketUsed: boolean;
186
+ }
187
+ interface EvaluationResult<T = JsonValue> {
188
+ key: string;
189
+ value: T | null;
190
+ reason: EvaluationReason;
191
+ ruleId?: string;
192
+ variationId?: string;
193
+ inExperiment: boolean;
194
+ experiment?: ExperimentAssignment;
195
+ errorMessage?: string;
196
+ }
197
+ /** Sticky bucket documents: `"<attribute>||<value>"` → `{ "<experimentKey>__<bucketVersion>": variationId }`. */
198
+ type StickyAssignments = Record<string, string>;
199
+ interface StickyBucketStore {
200
+ get(docKey: string): StickyAssignments | null | undefined;
201
+ set(docKey: string, assignments: StickyAssignments): void;
202
+ }
203
+ /** Response of `POST /api/v1/sdk/flags/evaluate`. */
204
+ interface RemoteEvaluationResponse {
205
+ version: number;
206
+ flags: Record<string, EvaluationResult>;
207
+ }
208
+ /** Body of `POST /api/v1/sdk/flags/events`. */
209
+ interface FlagEventsBatch {
210
+ sdk?: {
211
+ name: string;
212
+ version: string;
213
+ };
214
+ usage?: Array<{
215
+ flag: string;
216
+ variationId?: string;
217
+ value?: string;
218
+ count: number;
219
+ }>;
220
+ exposures?: Array<{
221
+ experiment: string;
222
+ flag?: string;
223
+ variationId: string;
224
+ hashAttribute: string;
225
+ hashValue: string;
226
+ userId?: string;
227
+ anonymousId?: string;
228
+ timestamp: string;
229
+ }>;
230
+ }
231
+
232
+ declare function fnv1a32(bytes: Uint8Array): number;
233
+ /** Deterministic bucket in [0, 1) with 4 decimals (GrowthBook hash v2 for ASCII input). */
234
+ declare function hashBucket(seed: string, value: string): number;
235
+ /** Lowercase hex SHA-256 of the UTF-8 bytes of `input`. */
236
+ declare function sha256Hex(input: string | Uint8Array): string;
237
+ /** `H(x)` for the hashed operators: SHA-256 of `salt + value`. */
238
+ declare function hashConfidential(salt: string, value: string | Uint8Array): string;
239
+ /** Comparison value for the hashed prefix/suffix operators: `"<byteLength>_<H(part)>"`. */
240
+ declare function hashConfidentialAffix(salt: string, part: string): string;
241
+
242
+ /** Value conversions used by the condition operators (docs/feature-flags-spec.md §3.2–3.3). */
243
+ /** Text form of a scalar, or `undefined` for arrays, objects, null and non-finite numbers. */
244
+ declare function textOf(v: unknown): string | undefined;
245
+ declare function numberOf(v: unknown): number | undefined;
246
+ interface SemVer {
247
+ major: number;
248
+ minor: number;
249
+ patch: number;
250
+ pre: string[];
251
+ }
252
+ declare function parseSemver(v: unknown): SemVer | undefined;
253
+ /** SemVer 2.0 precedence: -1, 0 or 1. */
254
+ declare function compareSemver(a: SemVer, b: SemVer): number;
255
+ /** Milliseconds since the epoch: numbers are Unix seconds, strings ISO 8601, Date objects as-is. */
256
+ declare function instantOf(v: unknown): number | undefined;
257
+ /** JSON deep equality; numbers compare numerically, object keys unordered. */
258
+ declare function jsonEquals(a: unknown, b: unknown): boolean;
259
+ /** `isOn`: anything except null, false, 0 and "". */
260
+ declare function isOn(v: unknown): boolean;
261
+
262
+ interface EvaluateOptions {
263
+ /** Evaluation time (default: now). */
264
+ now?: Date | number | string;
265
+ /** Sticky bucketing store; experiments keep a user's variation across config changes. */
266
+ stickyBuckets?: StickyBucketStore;
267
+ }
268
+ /** Attribute by literal key, then by dot path. `null` counts as missing (`undefined`). */
269
+ declare function lookupAttribute(attributes: Attributes, name: string): unknown;
270
+ declare function isScheduleActive(schedule: RuleSchedule | undefined, now: number): boolean;
271
+ declare function effectiveCoverage(rule: {
272
+ coverage: number;
273
+ ramp?: Array<{
274
+ at: string;
275
+ coverage: number;
276
+ }>;
277
+ }, now: number): number;
278
+ /** Evaluate one flag. Never throws: problems come back as reason `ERROR`. */
279
+ declare function evaluateFlag(payload: FlagsPayload, key: string, attributes?: Attributes, options?: EvaluateOptions): EvaluationResult;
280
+ /** Evaluate every flag in the payload. */
281
+ declare function evaluateAllFlags(payload: FlagsPayload, attributes?: Attributes, options?: EvaluateOptions): Record<string, EvaluationResult>;
282
+
283
+ export { ARRAY_OPERATORS, ATTRIBUTE_OPERATORS, type AttributeCondition, type AttributeOperator, type Attributes, BOOLEAN_OPERATORS, type Condition, type ConditionSegment, DATE_OPERATORS, type EncryptedFlagsPayload, type EvaluateOptions, type EvaluationReason, type EvaluationResult, type ExperimentAssignment, type ExperimentNamespace, type ExperimentRule, type ExperimentVariation, type FlagComparisonOperator, type FlagCondition, type FlagDefinition, type FlagEventsBatch, type FlagRule, type FlagRuleType, type FlagValueType, type FlagsPayload, type ForceRule, type GroupCondition, HASHED_OPERATORS, type HoldoutDefinition, type JsonValue, type ListSegment, NUMBER_OPERATORS, PRESENCE_OPERATORS, type Prerequisite, type RampStep, type RemoteEvaluationResponse, type RolloutRule, type RuleSchedule, SEMVER_OPERATORS, type SegmentCondition, type SegmentDefinition, type SplitBucket, type SplitRule, type StickyAssignments, type StickyBucketStore, TEXT_OPERATORS, compareSemver, effectiveCoverage, evaluateAllFlags, evaluateFlag, fnv1a32, hashBucket, hashConfidential, hashConfidentialAffix, instantOf, isOn, isScheduleActive, jsonEquals, lookupAttribute, numberOf, parseSemver, sha256Hex, textOf };
@@ -0,0 +1,57 @@
1
+ import {
2
+ ARRAY_OPERATORS,
3
+ ATTRIBUTE_OPERATORS,
4
+ BOOLEAN_OPERATORS,
5
+ DATE_OPERATORS,
6
+ HASHED_OPERATORS,
7
+ NUMBER_OPERATORS,
8
+ PRESENCE_OPERATORS,
9
+ SEMVER_OPERATORS,
10
+ TEXT_OPERATORS,
11
+ compareSemver,
12
+ effectiveCoverage,
13
+ evaluateAllFlags,
14
+ evaluateFlag,
15
+ fnv1a32,
16
+ hashBucket,
17
+ hashConfidential,
18
+ hashConfidentialAffix,
19
+ instantOf,
20
+ isOn,
21
+ isScheduleActive,
22
+ jsonEquals,
23
+ lookupAttribute,
24
+ numberOf,
25
+ parseSemver,
26
+ sha256Hex,
27
+ textOf
28
+ } from "./chunk-OY4QS3WL.js";
29
+ export {
30
+ ARRAY_OPERATORS,
31
+ ATTRIBUTE_OPERATORS,
32
+ BOOLEAN_OPERATORS,
33
+ DATE_OPERATORS,
34
+ HASHED_OPERATORS,
35
+ NUMBER_OPERATORS,
36
+ PRESENCE_OPERATORS,
37
+ SEMVER_OPERATORS,
38
+ TEXT_OPERATORS,
39
+ compareSemver,
40
+ effectiveCoverage,
41
+ evaluateAllFlags,
42
+ evaluateFlag,
43
+ fnv1a32,
44
+ hashBucket,
45
+ hashConfidential,
46
+ hashConfidentialAffix,
47
+ instantOf,
48
+ isOn,
49
+ isScheduleActive,
50
+ jsonEquals,
51
+ lookupAttribute,
52
+ numberOf,
53
+ parseSemver,
54
+ sha256Hex,
55
+ textOf
56
+ };
57
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}