@guidelytech/flags 0.0.0-stage → 0.8.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 +181 -2
- package/dist/chunk-2RZKAT37.js +1085 -0
- package/dist/chunk-2RZKAT37.js.map +1 -0
- package/dist/chunk-OY4QS3WL.js +804 -0
- package/dist/chunk-OY4QS3WL.js.map +1 -0
- package/dist/chunk-ZTY7K7P2.js +988 -0
- package/dist/chunk-ZTY7K7P2.js.map +1 -0
- package/dist/cli.cjs +2010 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +229 -0
- package/dist/cli.js.map +1 -0
- package/dist/evaluate.cjs +835 -0
- package/dist/evaluate.cjs.map +1 -0
- package/dist/evaluate.d.cts +283 -0
- package/dist/evaluate.d.ts +283 -0
- package/dist/evaluate.js +57 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/index.cjs +1988 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +355 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +139 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -3
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/evaluate.ts
|
|
21
|
+
var evaluate_exports = {};
|
|
22
|
+
__export(evaluate_exports, {
|
|
23
|
+
ARRAY_OPERATORS: () => ARRAY_OPERATORS,
|
|
24
|
+
ATTRIBUTE_OPERATORS: () => ATTRIBUTE_OPERATORS,
|
|
25
|
+
BOOLEAN_OPERATORS: () => BOOLEAN_OPERATORS,
|
|
26
|
+
DATE_OPERATORS: () => DATE_OPERATORS,
|
|
27
|
+
HASHED_OPERATORS: () => HASHED_OPERATORS,
|
|
28
|
+
NUMBER_OPERATORS: () => NUMBER_OPERATORS,
|
|
29
|
+
PRESENCE_OPERATORS: () => PRESENCE_OPERATORS,
|
|
30
|
+
SEMVER_OPERATORS: () => SEMVER_OPERATORS,
|
|
31
|
+
TEXT_OPERATORS: () => TEXT_OPERATORS,
|
|
32
|
+
compareSemver: () => compareSemver,
|
|
33
|
+
effectiveCoverage: () => effectiveCoverage,
|
|
34
|
+
evaluateAllFlags: () => evaluateAllFlags,
|
|
35
|
+
evaluateFlag: () => evaluateFlag,
|
|
36
|
+
fnv1a32: () => fnv1a32,
|
|
37
|
+
hashBucket: () => hashBucket,
|
|
38
|
+
hashConfidential: () => hashConfidential,
|
|
39
|
+
hashConfidentialAffix: () => hashConfidentialAffix,
|
|
40
|
+
instantOf: () => instantOf,
|
|
41
|
+
isOn: () => isOn,
|
|
42
|
+
isScheduleActive: () => isScheduleActive,
|
|
43
|
+
jsonEquals: () => jsonEquals,
|
|
44
|
+
lookupAttribute: () => lookupAttribute,
|
|
45
|
+
numberOf: () => numberOf,
|
|
46
|
+
parseSemver: () => parseSemver,
|
|
47
|
+
sha256Hex: () => sha256Hex,
|
|
48
|
+
textOf: () => textOf
|
|
49
|
+
});
|
|
50
|
+
module.exports = __toCommonJS(evaluate_exports);
|
|
51
|
+
|
|
52
|
+
// src/hash.ts
|
|
53
|
+
function utf8(input) {
|
|
54
|
+
const out = [];
|
|
55
|
+
for (let i = 0; i < input.length; i++) {
|
|
56
|
+
let c = input.charCodeAt(i);
|
|
57
|
+
if (c >= 55296 && c <= 56319 && i + 1 < input.length) {
|
|
58
|
+
const d = input.charCodeAt(i + 1);
|
|
59
|
+
if (d >= 56320 && d <= 57343) {
|
|
60
|
+
c = 65536 + (c - 55296 << 10) + (d - 56320);
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (c >= 55296 && c <= 57343) c = 65533;
|
|
65
|
+
if (c < 128) out.push(c);
|
|
66
|
+
else if (c < 2048) out.push(192 | c >> 6, 128 | c & 63);
|
|
67
|
+
else if (c < 65536) out.push(224 | c >> 12, 128 | c >> 6 & 63, 128 | c & 63);
|
|
68
|
+
else out.push(240 | c >> 18, 128 | c >> 12 & 63, 128 | c >> 6 & 63, 128 | c & 63);
|
|
69
|
+
}
|
|
70
|
+
return Uint8Array.from(out);
|
|
71
|
+
}
|
|
72
|
+
function fnv1a32(bytes) {
|
|
73
|
+
let h = 2166136261;
|
|
74
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
75
|
+
h ^= bytes[i];
|
|
76
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
77
|
+
}
|
|
78
|
+
return h >>> 0;
|
|
79
|
+
}
|
|
80
|
+
function hashBucket(seed, value) {
|
|
81
|
+
const inner = fnv1a32(utf8(seed + value));
|
|
82
|
+
return fnv1a32(utf8(String(inner))) % 1e4 / 1e4;
|
|
83
|
+
}
|
|
84
|
+
var K = new Uint32Array([
|
|
85
|
+
1116352408,
|
|
86
|
+
1899447441,
|
|
87
|
+
3049323471,
|
|
88
|
+
3921009573,
|
|
89
|
+
961987163,
|
|
90
|
+
1508970993,
|
|
91
|
+
2453635748,
|
|
92
|
+
2870763221,
|
|
93
|
+
3624381080,
|
|
94
|
+
310598401,
|
|
95
|
+
607225278,
|
|
96
|
+
1426881987,
|
|
97
|
+
1925078388,
|
|
98
|
+
2162078206,
|
|
99
|
+
2614888103,
|
|
100
|
+
3248222580,
|
|
101
|
+
3835390401,
|
|
102
|
+
4022224774,
|
|
103
|
+
264347078,
|
|
104
|
+
604807628,
|
|
105
|
+
770255983,
|
|
106
|
+
1249150122,
|
|
107
|
+
1555081692,
|
|
108
|
+
1996064986,
|
|
109
|
+
2554220882,
|
|
110
|
+
2821834349,
|
|
111
|
+
2952996808,
|
|
112
|
+
3210313671,
|
|
113
|
+
3336571891,
|
|
114
|
+
3584528711,
|
|
115
|
+
113926993,
|
|
116
|
+
338241895,
|
|
117
|
+
666307205,
|
|
118
|
+
773529912,
|
|
119
|
+
1294757372,
|
|
120
|
+
1396182291,
|
|
121
|
+
1695183700,
|
|
122
|
+
1986661051,
|
|
123
|
+
2177026350,
|
|
124
|
+
2456956037,
|
|
125
|
+
2730485921,
|
|
126
|
+
2820302411,
|
|
127
|
+
3259730800,
|
|
128
|
+
3345764771,
|
|
129
|
+
3516065817,
|
|
130
|
+
3600352804,
|
|
131
|
+
4094571909,
|
|
132
|
+
275423344,
|
|
133
|
+
430227734,
|
|
134
|
+
506948616,
|
|
135
|
+
659060556,
|
|
136
|
+
883997877,
|
|
137
|
+
958139571,
|
|
138
|
+
1322822218,
|
|
139
|
+
1537002063,
|
|
140
|
+
1747873779,
|
|
141
|
+
1955562222,
|
|
142
|
+
2024104815,
|
|
143
|
+
2227730452,
|
|
144
|
+
2361852424,
|
|
145
|
+
2428436474,
|
|
146
|
+
2756734187,
|
|
147
|
+
3204031479,
|
|
148
|
+
3329325298
|
|
149
|
+
]);
|
|
150
|
+
function sha256Hex(input) {
|
|
151
|
+
const msg = typeof input === "string" ? utf8(input) : input;
|
|
152
|
+
const bitLen = msg.length * 8;
|
|
153
|
+
const padded = new Uint8Array(msg.length + 9 + 63 >> 6 << 6);
|
|
154
|
+
padded.set(msg);
|
|
155
|
+
padded[msg.length] = 128;
|
|
156
|
+
const view = new DataView(padded.buffer);
|
|
157
|
+
view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
|
|
158
|
+
view.setUint32(padded.length - 4, bitLen >>> 0);
|
|
159
|
+
const h = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
160
|
+
const w = new Uint32Array(64);
|
|
161
|
+
for (let off = 0; off < padded.length; off += 64) {
|
|
162
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
|
|
163
|
+
for (let i = 16; i < 64; i++) {
|
|
164
|
+
const a2 = w[i - 15], b2 = w[i - 2];
|
|
165
|
+
const s0 = (a2 >>> 7 | a2 << 25) ^ (a2 >>> 18 | a2 << 14) ^ a2 >>> 3;
|
|
166
|
+
const s1 = (b2 >>> 17 | b2 << 15) ^ (b2 >>> 19 | b2 << 13) ^ b2 >>> 10;
|
|
167
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
168
|
+
}
|
|
169
|
+
let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];
|
|
170
|
+
for (let i = 0; i < 64; i++) {
|
|
171
|
+
const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
172
|
+
const ch = e & f ^ ~e & g;
|
|
173
|
+
const t1 = hh + S1 + ch + K[i] + w[i] >>> 0;
|
|
174
|
+
const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
|
|
175
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
176
|
+
const t2 = S0 + maj >>> 0;
|
|
177
|
+
hh = g;
|
|
178
|
+
g = f;
|
|
179
|
+
f = e;
|
|
180
|
+
e = d + t1 >>> 0;
|
|
181
|
+
d = c;
|
|
182
|
+
c = b;
|
|
183
|
+
b = a;
|
|
184
|
+
a = t1 + t2 >>> 0;
|
|
185
|
+
}
|
|
186
|
+
h[0] = h[0] + a >>> 0;
|
|
187
|
+
h[1] = h[1] + b >>> 0;
|
|
188
|
+
h[2] = h[2] + c >>> 0;
|
|
189
|
+
h[3] = h[3] + d >>> 0;
|
|
190
|
+
h[4] = h[4] + e >>> 0;
|
|
191
|
+
h[5] = h[5] + f >>> 0;
|
|
192
|
+
h[6] = h[6] + g >>> 0;
|
|
193
|
+
h[7] = h[7] + hh >>> 0;
|
|
194
|
+
}
|
|
195
|
+
let hex = "";
|
|
196
|
+
for (let i = 0; i < 8; i++) hex += h[i].toString(16).padStart(8, "0");
|
|
197
|
+
return hex;
|
|
198
|
+
}
|
|
199
|
+
function hashConfidential(salt, value) {
|
|
200
|
+
if (typeof value === "string") return sha256Hex(salt + value);
|
|
201
|
+
const s = utf8(salt);
|
|
202
|
+
const joined = new Uint8Array(s.length + value.length);
|
|
203
|
+
joined.set(s);
|
|
204
|
+
joined.set(value, s.length);
|
|
205
|
+
return sha256Hex(joined);
|
|
206
|
+
}
|
|
207
|
+
function hashConfidentialAffix(salt, part) {
|
|
208
|
+
return `${utf8(part).length}_${hashConfidential(salt, part)}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/values.ts
|
|
212
|
+
function textOf(v) {
|
|
213
|
+
if (typeof v === "string") return v;
|
|
214
|
+
if (typeof v === "boolean") return v ? "true" : "false";
|
|
215
|
+
if (typeof v === "number") {
|
|
216
|
+
if (!Number.isFinite(v)) return void 0;
|
|
217
|
+
if (Number.isInteger(v) && Math.abs(v) < 1e21) return v.toFixed(0);
|
|
218
|
+
return String(v);
|
|
219
|
+
}
|
|
220
|
+
if (typeof v === "bigint") return v.toString();
|
|
221
|
+
return void 0;
|
|
222
|
+
}
|
|
223
|
+
var NUMERIC = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
|
|
224
|
+
function numberOf(v) {
|
|
225
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : void 0;
|
|
226
|
+
if (typeof v === "string") {
|
|
227
|
+
const s = v.trim();
|
|
228
|
+
if (!NUMERIC.test(s)) return void 0;
|
|
229
|
+
const n = Number(s);
|
|
230
|
+
return Number.isFinite(n) ? n : void 0;
|
|
231
|
+
}
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
function arrayOf(v) {
|
|
235
|
+
if (Array.isArray(v)) return v;
|
|
236
|
+
if (typeof v === "string") {
|
|
237
|
+
const s = v.trim();
|
|
238
|
+
if (!s.startsWith("[")) return void 0;
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(s);
|
|
241
|
+
return Array.isArray(parsed) ? parsed : void 0;
|
|
242
|
+
} catch {
|
|
243
|
+
return void 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return void 0;
|
|
247
|
+
}
|
|
248
|
+
var SEMVER = /^[vV]?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
249
|
+
function parseSemver(v) {
|
|
250
|
+
const s = typeof v === "number" ? textOf(v) : typeof v === "string" ? v.trim() : void 0;
|
|
251
|
+
if (s === void 0) return void 0;
|
|
252
|
+
const m = SEMVER.exec(s);
|
|
253
|
+
if (!m) return void 0;
|
|
254
|
+
return { major: Number(m[1]), minor: Number(m[2] ?? 0), patch: Number(m[3] ?? 0), pre: m[4] ? m[4].split(".") : [] };
|
|
255
|
+
}
|
|
256
|
+
function cmp(a, b) {
|
|
257
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
258
|
+
}
|
|
259
|
+
function compareSemver(a, b) {
|
|
260
|
+
const c = cmp(a.major, b.major) || cmp(a.minor, b.minor) || cmp(a.patch, b.patch);
|
|
261
|
+
if (c) return c;
|
|
262
|
+
if (!a.pre.length || !b.pre.length) return a.pre.length ? -1 : b.pre.length ? 1 : 0;
|
|
263
|
+
for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
|
|
264
|
+
const x = a.pre[i], y = b.pre[i];
|
|
265
|
+
if (x === void 0) return -1;
|
|
266
|
+
if (y === void 0) return 1;
|
|
267
|
+
const xn = /^\d+$/.test(x), yn = /^\d+$/.test(y);
|
|
268
|
+
if (xn && yn) {
|
|
269
|
+
const d = cmp(Number(x), Number(y));
|
|
270
|
+
if (d) return d;
|
|
271
|
+
} else if (xn) return -1;
|
|
272
|
+
else if (yn) return 1;
|
|
273
|
+
else if (x !== y) return x < y ? -1 : 1;
|
|
274
|
+
}
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
function daysInMonth(year, month) {
|
|
278
|
+
if (month === 2) return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0 ? 29 : 28;
|
|
279
|
+
return [4, 6, 9, 11].includes(month) ? 30 : 31;
|
|
280
|
+
}
|
|
281
|
+
function daysFromCivil(year, month, day) {
|
|
282
|
+
const y = month <= 2 ? year - 1 : year;
|
|
283
|
+
const era = Math.floor(y / 400);
|
|
284
|
+
const yoe = y - era * 400;
|
|
285
|
+
const doy = Math.floor((153 * (month + (month > 2 ? -3 : 9)) + 2) / 5) + day - 1;
|
|
286
|
+
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
287
|
+
return era * 146097 + doe - 719468;
|
|
288
|
+
}
|
|
289
|
+
var ISO = /^(\d{4})-(\d{2})-(\d{2})(?:[Tt ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|z|[+-]\d{2}:\d{2})?)?$/;
|
|
290
|
+
function instantOf(v) {
|
|
291
|
+
if (v instanceof Date) return Number.isNaN(v.getTime()) ? void 0 : v.getTime();
|
|
292
|
+
if (typeof v === "number") return Number.isFinite(v) ? v * 1e3 : void 0;
|
|
293
|
+
if (typeof v !== "string") return void 0;
|
|
294
|
+
const m = ISO.exec(v.trim());
|
|
295
|
+
if (!m) return void 0;
|
|
296
|
+
const [, y, mo, d, h = "0", mi = "0", s = "0", frac, tz] = m;
|
|
297
|
+
const month = Number(mo), day = Number(d), hour = Number(h), minute = Number(mi), second = Number(s);
|
|
298
|
+
const year = Number(y);
|
|
299
|
+
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month) || hour > 23 || minute > 59 || second > 59) return void 0;
|
|
300
|
+
let ms = (daysFromCivil(year, month, day) * 86400 + hour * 3600 + minute * 60 + second) * 1e3 + (frac ? Math.floor(Number(`0.${frac}`) * 1e3) : 0);
|
|
301
|
+
if (tz && tz !== "Z" && tz !== "z") {
|
|
302
|
+
const sign = tz[0] === "-" ? -1 : 1;
|
|
303
|
+
ms -= sign * (Number(tz.slice(1, 3)) * 60 + Number(tz.slice(4, 6))) * 6e4;
|
|
304
|
+
}
|
|
305
|
+
return ms;
|
|
306
|
+
}
|
|
307
|
+
function jsonEquals(a, b) {
|
|
308
|
+
if (a === b) return true;
|
|
309
|
+
if (a === null || b === null || a === void 0 || b === void 0) return (a ?? null) === (b ?? null);
|
|
310
|
+
if (typeof a === "number" && typeof b === "number") return a === b;
|
|
311
|
+
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((x, i) => jsonEquals(x, b[i]));
|
|
312
|
+
if (typeof a === "object" && typeof b === "object" && !Array.isArray(b)) {
|
|
313
|
+
const ka = Object.keys(a).filter((k) => a[k] !== void 0);
|
|
314
|
+
const kb = Object.keys(b).filter((k) => b[k] !== void 0);
|
|
315
|
+
return ka.length === kb.length && ka.every((k) => Object.prototype.hasOwnProperty.call(b, k) && jsonEquals(a[k], b[k]));
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
function isOn(v) {
|
|
320
|
+
return !(v === null || v === void 0 || v === false || v === 0 || v === "");
|
|
321
|
+
}
|
|
322
|
+
function jsonTypeOf(v) {
|
|
323
|
+
if (v === null || v === void 0) return void 0;
|
|
324
|
+
if (Array.isArray(v)) return "array";
|
|
325
|
+
if (typeof v === "object") return "object";
|
|
326
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") return typeof v;
|
|
327
|
+
return void 0;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/types.ts
|
|
331
|
+
var TEXT_OPERATORS = ["equals", "notEquals", "in", "notIn", "contains", "notContains", "startsWith", "notStartsWith", "endsWith", "notEndsWith", "regex", "notRegex"];
|
|
332
|
+
var NUMBER_OPERATORS = ["numEquals", "numNotEquals", "gt", "gte", "lt", "lte"];
|
|
333
|
+
var SEMVER_OPERATORS = ["semverEquals", "semverNotEquals", "semverGt", "semverGte", "semverLt", "semverLte", "semverIn", "semverNotIn"];
|
|
334
|
+
var DATE_OPERATORS = ["before", "after"];
|
|
335
|
+
var ARRAY_OPERATORS = ["arrayContainsAny", "arrayNotContainsAny", "arrayContainsAll"];
|
|
336
|
+
var BOOLEAN_OPERATORS = ["isTrue", "isFalse"];
|
|
337
|
+
var PRESENCE_OPERATORS = ["exists", "notExists", "isType"];
|
|
338
|
+
var HASHED_OPERATORS = ["equalsHashed", "notEqualsHashed", "inHashed", "notInHashed", "startsWithHashed", "notStartsWithHashed", "endsWithHashed", "notEndsWithHashed", "arrayContainsAnyHashed", "arrayNotContainsAnyHashed"];
|
|
339
|
+
var ATTRIBUTE_OPERATORS = [...TEXT_OPERATORS, ...NUMBER_OPERATORS, ...SEMVER_OPERATORS, ...DATE_OPERATORS, ...ARRAY_OPERATORS, ...BOOLEAN_OPERATORS, ...PRESENCE_OPERATORS, ...HASHED_OPERATORS];
|
|
340
|
+
|
|
341
|
+
// src/evaluate.ts
|
|
342
|
+
var MAX_DEPTH = 32;
|
|
343
|
+
var CycleError = class extends Error {
|
|
344
|
+
};
|
|
345
|
+
function nowOf(now) {
|
|
346
|
+
if (now === void 0) return Date.now();
|
|
347
|
+
if (typeof now === "number") return now;
|
|
348
|
+
if (now instanceof Date) return now.getTime();
|
|
349
|
+
return instantOf(now) ?? Date.now();
|
|
350
|
+
}
|
|
351
|
+
function lookupAttribute(attributes, name) {
|
|
352
|
+
if (Object.prototype.hasOwnProperty.call(attributes, name)) return attributes[name] ?? void 0;
|
|
353
|
+
if (!name.includes(".")) return void 0;
|
|
354
|
+
let cur = attributes;
|
|
355
|
+
for (const part of name.split(".")) {
|
|
356
|
+
if (cur && typeof cur === "object" && !Array.isArray(cur) && Object.prototype.hasOwnProperty.call(cur, part)) cur = cur[part];
|
|
357
|
+
else return void 0;
|
|
358
|
+
}
|
|
359
|
+
return cur ?? void 0;
|
|
360
|
+
}
|
|
361
|
+
function hashValueOf(attributes, name) {
|
|
362
|
+
const t = textOf(lookupAttribute(attributes, name));
|
|
363
|
+
return t === void 0 || t === "" ? void 0 : t;
|
|
364
|
+
}
|
|
365
|
+
function isScheduleActive(schedule, now) {
|
|
366
|
+
if (!schedule) return true;
|
|
367
|
+
if (schedule.start) {
|
|
368
|
+
const start = instantOf(schedule.start);
|
|
369
|
+
if (start === void 0 || now < start) return false;
|
|
370
|
+
}
|
|
371
|
+
if (schedule.end) {
|
|
372
|
+
const end = instantOf(schedule.end);
|
|
373
|
+
if (end === void 0 || now >= end) return false;
|
|
374
|
+
}
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
function clamp01(n) {
|
|
378
|
+
return typeof n === "number" && Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
|
|
379
|
+
}
|
|
380
|
+
function effectiveCoverage(rule, now) {
|
|
381
|
+
let coverage = clamp01(rule.coverage);
|
|
382
|
+
let latest = -Infinity;
|
|
383
|
+
for (const step of rule.ramp ?? []) {
|
|
384
|
+
const at = instantOf(step.at);
|
|
385
|
+
if (at !== void 0 && at <= now && at >= latest) {
|
|
386
|
+
latest = at;
|
|
387
|
+
coverage = clamp01(step.coverage);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return coverage;
|
|
391
|
+
}
|
|
392
|
+
var regexCache = /* @__PURE__ */ new Map();
|
|
393
|
+
function compile(pattern, ci) {
|
|
394
|
+
const k = `${ci ? "i" : "-"}:${pattern}`;
|
|
395
|
+
let re = regexCache.get(k);
|
|
396
|
+
if (re === void 0) {
|
|
397
|
+
try {
|
|
398
|
+
re = new RegExp(pattern, ci ? "i" : "");
|
|
399
|
+
} catch {
|
|
400
|
+
re = null;
|
|
401
|
+
}
|
|
402
|
+
if (regexCache.size > 500) regexCache.clear();
|
|
403
|
+
regexCache.set(k, re);
|
|
404
|
+
}
|
|
405
|
+
return re;
|
|
406
|
+
}
|
|
407
|
+
function lower(s, ci) {
|
|
408
|
+
return ci ? s.toLowerCase() : s;
|
|
409
|
+
}
|
|
410
|
+
function textValues(values, ci) {
|
|
411
|
+
const out = [];
|
|
412
|
+
for (const v of values) {
|
|
413
|
+
const t = textOf(v);
|
|
414
|
+
if (t !== void 0) out.push(lower(t, ci));
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
function affixMatches(salt, text, values, suffix) {
|
|
419
|
+
const bytes = utf8(text);
|
|
420
|
+
return values.some((v) => {
|
|
421
|
+
if (typeof v !== "string") return false;
|
|
422
|
+
const sep = v.indexOf("_");
|
|
423
|
+
if (sep <= 0) return false;
|
|
424
|
+
const n = Number(v.slice(0, sep));
|
|
425
|
+
if (!Number.isInteger(n) || n < 0 || n > bytes.length) return false;
|
|
426
|
+
const part = suffix ? bytes.slice(bytes.length - n) : bytes.slice(0, n);
|
|
427
|
+
return hashConfidential(salt, part) === v.slice(sep + 1);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function evalAttribute(ctx, c) {
|
|
431
|
+
const raw = lookupAttribute(ctx.attributes, c.attribute);
|
|
432
|
+
const values = Array.isArray(c.values) ? c.values : [];
|
|
433
|
+
const op = c.operator;
|
|
434
|
+
if (op === "notExists") return raw === void 0;
|
|
435
|
+
if (raw === void 0) return false;
|
|
436
|
+
const ci = c.caseInsensitive;
|
|
437
|
+
switch (op) {
|
|
438
|
+
case "exists":
|
|
439
|
+
return true;
|
|
440
|
+
case "isType":
|
|
441
|
+
return jsonTypeOf(raw) === values[0];
|
|
442
|
+
case "isTrue":
|
|
443
|
+
return raw === true || typeof raw === "string" && raw.toLowerCase() === "true";
|
|
444
|
+
case "isFalse":
|
|
445
|
+
return raw === false || typeof raw === "string" && raw.toLowerCase() === "false";
|
|
446
|
+
case "equals":
|
|
447
|
+
case "notEquals":
|
|
448
|
+
case "in":
|
|
449
|
+
case "notIn":
|
|
450
|
+
case "contains":
|
|
451
|
+
case "notContains":
|
|
452
|
+
case "startsWith":
|
|
453
|
+
case "notStartsWith":
|
|
454
|
+
case "endsWith":
|
|
455
|
+
case "notEndsWith": {
|
|
456
|
+
const t0 = textOf(raw);
|
|
457
|
+
if (t0 === void 0) return false;
|
|
458
|
+
const t = lower(t0, ci);
|
|
459
|
+
const vs = textValues(values, ci);
|
|
460
|
+
switch (op) {
|
|
461
|
+
case "equals":
|
|
462
|
+
return vs.length > 0 && t === vs[0];
|
|
463
|
+
case "notEquals":
|
|
464
|
+
return vs.length > 0 && t !== vs[0];
|
|
465
|
+
case "in":
|
|
466
|
+
return vs.includes(t);
|
|
467
|
+
case "notIn":
|
|
468
|
+
return !vs.includes(t);
|
|
469
|
+
case "contains":
|
|
470
|
+
return vs.some((v) => t.includes(v));
|
|
471
|
+
case "notContains":
|
|
472
|
+
return !vs.some((v) => t.includes(v));
|
|
473
|
+
case "startsWith":
|
|
474
|
+
return vs.some((v) => t.startsWith(v));
|
|
475
|
+
case "notStartsWith":
|
|
476
|
+
return !vs.some((v) => t.startsWith(v));
|
|
477
|
+
case "endsWith":
|
|
478
|
+
return vs.some((v) => t.endsWith(v));
|
|
479
|
+
default:
|
|
480
|
+
return !vs.some((v) => t.endsWith(v));
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
case "regex":
|
|
484
|
+
case "notRegex": {
|
|
485
|
+
const t = textOf(raw);
|
|
486
|
+
if (t === void 0) return false;
|
|
487
|
+
const res = [];
|
|
488
|
+
for (const v of values) {
|
|
489
|
+
const re = typeof v === "string" ? compile(v, !!ci) : null;
|
|
490
|
+
if (!re) return false;
|
|
491
|
+
res.push(re);
|
|
492
|
+
}
|
|
493
|
+
const any = res.some((re) => re.test(t));
|
|
494
|
+
return op === "regex" ? any : !any;
|
|
495
|
+
}
|
|
496
|
+
case "numEquals":
|
|
497
|
+
case "numNotEquals":
|
|
498
|
+
case "gt":
|
|
499
|
+
case "gte":
|
|
500
|
+
case "lt":
|
|
501
|
+
case "lte": {
|
|
502
|
+
const a = numberOf(raw);
|
|
503
|
+
const b = numberOf(values[0]);
|
|
504
|
+
if (a === void 0 || b === void 0) return false;
|
|
505
|
+
if (op === "numEquals") return a === b;
|
|
506
|
+
if (op === "numNotEquals") return a !== b;
|
|
507
|
+
if (op === "gt") return a > b;
|
|
508
|
+
if (op === "gte") return a >= b;
|
|
509
|
+
if (op === "lt") return a < b;
|
|
510
|
+
return a <= b;
|
|
511
|
+
}
|
|
512
|
+
case "semverEquals":
|
|
513
|
+
case "semverNotEquals":
|
|
514
|
+
case "semverGt":
|
|
515
|
+
case "semverGte":
|
|
516
|
+
case "semverLt":
|
|
517
|
+
case "semverLte":
|
|
518
|
+
case "semverIn":
|
|
519
|
+
case "semverNotIn": {
|
|
520
|
+
const a = parseSemver(raw);
|
|
521
|
+
if (!a) return false;
|
|
522
|
+
const bs = values.map(parseSemver);
|
|
523
|
+
if (bs.some((b) => !b)) return false;
|
|
524
|
+
if (op === "semverIn") return bs.some((b) => compareSemver(a, b) === 0);
|
|
525
|
+
if (op === "semverNotIn") return !bs.some((b) => compareSemver(a, b) === 0);
|
|
526
|
+
if (!bs[0]) return false;
|
|
527
|
+
const d = compareSemver(a, bs[0]);
|
|
528
|
+
if (op === "semverEquals") return d === 0;
|
|
529
|
+
if (op === "semverNotEquals") return d !== 0;
|
|
530
|
+
if (op === "semverGt") return d > 0;
|
|
531
|
+
if (op === "semverGte") return d >= 0;
|
|
532
|
+
if (op === "semverLt") return d < 0;
|
|
533
|
+
return d <= 0;
|
|
534
|
+
}
|
|
535
|
+
case "before":
|
|
536
|
+
case "after": {
|
|
537
|
+
const a = instantOf(raw);
|
|
538
|
+
const b = instantOf(values[0]);
|
|
539
|
+
if (a === void 0 || b === void 0) return false;
|
|
540
|
+
return op === "before" ? a < b : a > b;
|
|
541
|
+
}
|
|
542
|
+
case "arrayContainsAny":
|
|
543
|
+
case "arrayNotContainsAny":
|
|
544
|
+
case "arrayContainsAll": {
|
|
545
|
+
const arr = arrayOf(raw);
|
|
546
|
+
if (!arr) return false;
|
|
547
|
+
const elems = textValues(arr, ci);
|
|
548
|
+
const vs = textValues(values, ci);
|
|
549
|
+
if (op === "arrayContainsAll") return vs.every((v) => elems.includes(v));
|
|
550
|
+
const any = vs.some((v) => elems.includes(v));
|
|
551
|
+
return op === "arrayContainsAny" ? any : !any;
|
|
552
|
+
}
|
|
553
|
+
case "equalsHashed":
|
|
554
|
+
case "notEqualsHashed":
|
|
555
|
+
case "inHashed":
|
|
556
|
+
case "notInHashed":
|
|
557
|
+
case "startsWithHashed":
|
|
558
|
+
case "notStartsWithHashed":
|
|
559
|
+
case "endsWithHashed":
|
|
560
|
+
case "notEndsWithHashed": {
|
|
561
|
+
const t = textOf(raw);
|
|
562
|
+
if (t === void 0) return false;
|
|
563
|
+
const salt = ctx.payload.salt ?? "";
|
|
564
|
+
if (op === "startsWithHashed" || op === "notStartsWithHashed" || op === "endsWithHashed" || op === "notEndsWithHashed") {
|
|
565
|
+
const any = affixMatches(salt, t, values, op === "endsWithHashed" || op === "notEndsWithHashed");
|
|
566
|
+
return op.startsWith("not") ? !any : any;
|
|
567
|
+
}
|
|
568
|
+
const h = hashConfidential(salt, t);
|
|
569
|
+
if (op === "equalsHashed") return typeof values[0] === "string" && h === values[0];
|
|
570
|
+
if (op === "notEqualsHashed") return typeof values[0] === "string" && h !== values[0];
|
|
571
|
+
if (op === "inHashed") return values.includes(h);
|
|
572
|
+
return !values.includes(h);
|
|
573
|
+
}
|
|
574
|
+
case "arrayContainsAnyHashed":
|
|
575
|
+
case "arrayNotContainsAnyHashed": {
|
|
576
|
+
const arr = arrayOf(raw);
|
|
577
|
+
if (!arr) return false;
|
|
578
|
+
const salt = ctx.payload.salt ?? "";
|
|
579
|
+
const any = arr.some((e) => {
|
|
580
|
+
const t = textOf(e);
|
|
581
|
+
return t !== void 0 && values.includes(hashConfidential(salt, t));
|
|
582
|
+
});
|
|
583
|
+
return op === "arrayContainsAnyHashed" ? any : !any;
|
|
584
|
+
}
|
|
585
|
+
default:
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function enter(ctx, frame, fn) {
|
|
590
|
+
if (ctx.stack.includes(frame)) throw new CycleError(`Cycle detected: ${[...ctx.stack, frame].join(" \u2192 ")}`);
|
|
591
|
+
if (ctx.stack.length >= MAX_DEPTH) throw new CycleError(`Flag references nest deeper than ${MAX_DEPTH} levels: ${ctx.stack.join(" \u2192 ")}`);
|
|
592
|
+
ctx.stack.push(frame);
|
|
593
|
+
try {
|
|
594
|
+
return fn();
|
|
595
|
+
} finally {
|
|
596
|
+
ctx.stack.pop();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
function segmentMatches(ctx, key) {
|
|
600
|
+
const seg = ctx.payload.segments?.[key];
|
|
601
|
+
if (!seg) return void 0;
|
|
602
|
+
return enter(ctx, `segment:${key}`, () => {
|
|
603
|
+
if (seg.kind === "list") {
|
|
604
|
+
const t = textOf(lookupAttribute(ctx.attributes, seg.attribute));
|
|
605
|
+
if (t === void 0) return false;
|
|
606
|
+
const needle = seg.hashed ? hashConfidential(ctx.payload.salt ?? "", t) : t;
|
|
607
|
+
return Array.isArray(seg.values) && seg.values.includes(needle);
|
|
608
|
+
}
|
|
609
|
+
if (seg.kind === "conditions") return allMatch(ctx, seg.conditions);
|
|
610
|
+
return false;
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
function compareFlag(op, actual, expected) {
|
|
614
|
+
switch (op) {
|
|
615
|
+
case "isOn":
|
|
616
|
+
return isOn(actual);
|
|
617
|
+
case "isOff":
|
|
618
|
+
return !isOn(actual);
|
|
619
|
+
case "equals":
|
|
620
|
+
return jsonEquals(actual, expected ?? null);
|
|
621
|
+
case "notEquals":
|
|
622
|
+
return !jsonEquals(actual, expected ?? null);
|
|
623
|
+
default:
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function referencedValue(ctx, key) {
|
|
628
|
+
const res = evalFlag(ctx, key);
|
|
629
|
+
return res.reason === "FLAG_NOT_FOUND" ? void 0 : { value: res.value };
|
|
630
|
+
}
|
|
631
|
+
function conditionMatches(ctx, c) {
|
|
632
|
+
if (!c || typeof c !== "object") return false;
|
|
633
|
+
switch (c.kind) {
|
|
634
|
+
case "attribute":
|
|
635
|
+
return evalAttribute(ctx, c);
|
|
636
|
+
case "segment": {
|
|
637
|
+
const m = segmentMatches(ctx, c.segment);
|
|
638
|
+
if (m === void 0) return false;
|
|
639
|
+
if (c.operator === "in") return m;
|
|
640
|
+
if (c.operator === "notIn") return !m;
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
case "flag": {
|
|
644
|
+
const ref = referencedValue(ctx, c.flag);
|
|
645
|
+
return ref ? compareFlag(c.operator, ref.value, c.value) : false;
|
|
646
|
+
}
|
|
647
|
+
case "group": {
|
|
648
|
+
const list = Array.isArray(c.conditions) ? c.conditions : [];
|
|
649
|
+
let r;
|
|
650
|
+
if (c.op === "and") r = allMatch(ctx, list);
|
|
651
|
+
else if (c.op === "or") r = list.some((x) => conditionMatches(ctx, x));
|
|
652
|
+
else return false;
|
|
653
|
+
return c.negate ? !r : r;
|
|
654
|
+
}
|
|
655
|
+
default:
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function allMatch(ctx, conditions) {
|
|
660
|
+
if (!conditions) return true;
|
|
661
|
+
for (const c of conditions) if (!conditionMatches(ctx, c)) return false;
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
function base(flag, value, reason) {
|
|
665
|
+
return { key: flag.key, value: value === void 0 ? null : value, reason, inExperiment: false };
|
|
666
|
+
}
|
|
667
|
+
function stickyDocKey(attribute, value) {
|
|
668
|
+
return `${attribute}||${value}`;
|
|
669
|
+
}
|
|
670
|
+
function runExperiment(ctx, flag, rule) {
|
|
671
|
+
const variations = Array.isArray(rule.variations) ? rule.variations : [];
|
|
672
|
+
if (!variations.length) return void 0;
|
|
673
|
+
let hashAttribute = rule.hashAttribute;
|
|
674
|
+
let hv = hashValueOf(ctx.attributes, rule.hashAttribute);
|
|
675
|
+
const fallbackValue = rule.fallbackAttribute ? hashValueOf(ctx.attributes, rule.fallbackAttribute) : void 0;
|
|
676
|
+
if (hv === void 0 && rule.fallbackAttribute && fallbackValue !== void 0) {
|
|
677
|
+
hashAttribute = rule.fallbackAttribute;
|
|
678
|
+
hv = fallbackValue;
|
|
679
|
+
}
|
|
680
|
+
if (hv === void 0) return void 0;
|
|
681
|
+
const bucketVersion = rule.bucketVersion ?? 0;
|
|
682
|
+
const useSticky = !!ctx.sticky && !rule.disableStickyBucketing;
|
|
683
|
+
let index = -1;
|
|
684
|
+
const bucket = hashBucket(rule.seed, hv);
|
|
685
|
+
let stickyUsed = false;
|
|
686
|
+
if (useSticky) {
|
|
687
|
+
const merged = {};
|
|
688
|
+
if (rule.fallbackAttribute && fallbackValue !== void 0) Object.assign(merged, ctx.sticky.get(stickyDocKey(rule.fallbackAttribute, fallbackValue)) ?? {});
|
|
689
|
+
const primary = hashValueOf(ctx.attributes, rule.hashAttribute);
|
|
690
|
+
if (primary !== void 0) Object.assign(merged, ctx.sticky.get(stickyDocKey(rule.hashAttribute, primary)) ?? {});
|
|
691
|
+
const prefix = `${rule.key}__`;
|
|
692
|
+
const minVersion = rule.minBucketVersion ?? 0;
|
|
693
|
+
for (const k of Object.keys(merged)) {
|
|
694
|
+
if (!k.startsWith(prefix)) continue;
|
|
695
|
+
const v = Number(k.slice(prefix.length));
|
|
696
|
+
if (Number.isInteger(v) && v < minVersion) return void 0;
|
|
697
|
+
}
|
|
698
|
+
const assigned = merged[`${prefix}${bucketVersion}`];
|
|
699
|
+
if (assigned !== void 0) {
|
|
700
|
+
const i = variations.findIndex((v) => v.id === assigned);
|
|
701
|
+
if (i >= 0) {
|
|
702
|
+
index = i;
|
|
703
|
+
stickyUsed = true;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (index < 0) {
|
|
708
|
+
if (rule.namespace) {
|
|
709
|
+
const n = hashBucket(`__${rule.namespace.id}`, hv);
|
|
710
|
+
if (!(n >= rule.namespace.start && n < rule.namespace.end)) return void 0;
|
|
711
|
+
}
|
|
712
|
+
const coverage = clamp01(rule.coverage);
|
|
713
|
+
let cum = 0;
|
|
714
|
+
for (let i = 0; i < variations.length; i++) {
|
|
715
|
+
const w = Math.max(0, Number(variations[i].weight) || 0);
|
|
716
|
+
const start = cum;
|
|
717
|
+
const end = cum + coverage * w;
|
|
718
|
+
cum += w;
|
|
719
|
+
if (bucket >= start && bucket < end) {
|
|
720
|
+
index = i;
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (index < 0) return void 0;
|
|
725
|
+
if (useSticky) {
|
|
726
|
+
const docKey = stickyDocKey(hashAttribute, hv);
|
|
727
|
+
const doc = { ...ctx.sticky.get(docKey) ?? {} };
|
|
728
|
+
doc[`${rule.key}__${bucketVersion}`] = variations[index].id;
|
|
729
|
+
ctx.sticky.set(docKey, doc);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const variation = variations[index];
|
|
733
|
+
return {
|
|
734
|
+
key: flag.key,
|
|
735
|
+
value: variation.value === void 0 ? null : variation.value,
|
|
736
|
+
reason: "EXPERIMENT",
|
|
737
|
+
ruleId: rule.id,
|
|
738
|
+
variationId: variation.id,
|
|
739
|
+
inExperiment: true,
|
|
740
|
+
experiment: { key: rule.key, name: rule.name, variationId: variation.id, variationName: variation.name, variationIndex: index, hashAttribute, hashValue: hv, bucket, stickyBucketUsed: stickyUsed }
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function applyRule(ctx, flag, rule) {
|
|
744
|
+
switch (rule.type) {
|
|
745
|
+
case "force":
|
|
746
|
+
return { ...base(flag, rule.value, "RULE_MATCH"), ruleId: rule.id, ...rule.variationId ? { variationId: rule.variationId } : {} };
|
|
747
|
+
case "rollout": {
|
|
748
|
+
const hv = hashValueOf(ctx.attributes, rule.hashAttribute);
|
|
749
|
+
if (hv === void 0) return void 0;
|
|
750
|
+
if (hashBucket(rule.seed, hv) >= effectiveCoverage(rule, ctx.now)) return void 0;
|
|
751
|
+
return { ...base(flag, rule.value, "RULE_MATCH"), ruleId: rule.id, ...rule.variationId ? { variationId: rule.variationId } : {} };
|
|
752
|
+
}
|
|
753
|
+
case "split": {
|
|
754
|
+
const hv = hashValueOf(ctx.attributes, rule.hashAttribute);
|
|
755
|
+
if (hv === void 0 || !Array.isArray(rule.buckets)) return void 0;
|
|
756
|
+
const n = hashBucket(rule.seed, hv);
|
|
757
|
+
let cum = 0;
|
|
758
|
+
for (const b of rule.buckets) {
|
|
759
|
+
cum += Math.max(0, Number(b.weight) || 0);
|
|
760
|
+
if (n < cum) return { ...base(flag, b.value, "RULE_MATCH"), ruleId: rule.id, ...b.variationId ? { variationId: b.variationId } : {} };
|
|
761
|
+
}
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
case "experiment":
|
|
765
|
+
return runExperiment(ctx, flag, rule);
|
|
766
|
+
default:
|
|
767
|
+
return void 0;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function evalFlag(ctx, key) {
|
|
771
|
+
const flag = ctx.payload.flags?.[key];
|
|
772
|
+
if (!flag) return { key, value: null, reason: "FLAG_NOT_FOUND", inExperiment: false };
|
|
773
|
+
return enter(ctx, `flag:${key}`, () => {
|
|
774
|
+
if (!flag.enabled) return base(flag, flag.offValue, "DISABLED");
|
|
775
|
+
for (const p of flag.prerequisites ?? []) {
|
|
776
|
+
const ref = referencedValue(ctx, p.flag);
|
|
777
|
+
if (!ref || !compareFlag(p.operator, ref.value, p.value)) return base(flag, flag.offValue, "PREREQUISITE_FAILED");
|
|
778
|
+
}
|
|
779
|
+
for (const h of ctx.payload.holdouts ?? []) {
|
|
780
|
+
if (!Array.isArray(h.flags) || !h.flags.includes(key)) continue;
|
|
781
|
+
const hv = hashValueOf(ctx.attributes, h.hashAttribute);
|
|
782
|
+
if (hv !== void 0 && hashBucket(h.seed, hv) < clamp01(h.coverage)) return base(flag, flag.defaultValue, "HOLDOUT");
|
|
783
|
+
}
|
|
784
|
+
for (const rule of flag.rules ?? []) {
|
|
785
|
+
if (!rule || !isScheduleActive(rule.schedule, ctx.now)) continue;
|
|
786
|
+
if (!allMatch(ctx, rule.conditions)) continue;
|
|
787
|
+
const res = applyRule(ctx, flag, rule);
|
|
788
|
+
if (res) return res;
|
|
789
|
+
}
|
|
790
|
+
return base(flag, flag.defaultValue, "DEFAULT");
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
function evaluateFlag(payload, key, attributes = {}, options = {}) {
|
|
794
|
+
const ctx = { payload, attributes: attributes ?? {}, now: nowOf(options.now), sticky: options.stickyBuckets, stack: [] };
|
|
795
|
+
try {
|
|
796
|
+
return evalFlag(ctx, key);
|
|
797
|
+
} catch (err) {
|
|
798
|
+
return { key, value: null, reason: "ERROR", inExperiment: false, errorMessage: err instanceof Error ? err.message : String(err) };
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function evaluateAllFlags(payload, attributes = {}, options = {}) {
|
|
802
|
+
const out = {};
|
|
803
|
+
for (const key of Object.keys(payload.flags ?? {})) out[key] = evaluateFlag(payload, key, attributes, options);
|
|
804
|
+
return out;
|
|
805
|
+
}
|
|
806
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
807
|
+
0 && (module.exports = {
|
|
808
|
+
ARRAY_OPERATORS,
|
|
809
|
+
ATTRIBUTE_OPERATORS,
|
|
810
|
+
BOOLEAN_OPERATORS,
|
|
811
|
+
DATE_OPERATORS,
|
|
812
|
+
HASHED_OPERATORS,
|
|
813
|
+
NUMBER_OPERATORS,
|
|
814
|
+
PRESENCE_OPERATORS,
|
|
815
|
+
SEMVER_OPERATORS,
|
|
816
|
+
TEXT_OPERATORS,
|
|
817
|
+
compareSemver,
|
|
818
|
+
effectiveCoverage,
|
|
819
|
+
evaluateAllFlags,
|
|
820
|
+
evaluateFlag,
|
|
821
|
+
fnv1a32,
|
|
822
|
+
hashBucket,
|
|
823
|
+
hashConfidential,
|
|
824
|
+
hashConfidentialAffix,
|
|
825
|
+
instantOf,
|
|
826
|
+
isOn,
|
|
827
|
+
isScheduleActive,
|
|
828
|
+
jsonEquals,
|
|
829
|
+
lookupAttribute,
|
|
830
|
+
numberOf,
|
|
831
|
+
parseSemver,
|
|
832
|
+
sha256Hex,
|
|
833
|
+
textOf
|
|
834
|
+
});
|
|
835
|
+
//# sourceMappingURL=evaluate.cjs.map
|