@jewel998/config 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/dist/cache/browserStorage.d.ts +3 -0
- package/dist/cache/browserStorage.d.ts.map +1 -0
- package/dist/cache/browserStorage.js +54 -0
- package/dist/cache/browserStorage.js.map +1 -0
- package/dist/cache/memoryStorage.d.ts +3 -0
- package/dist/cache/memoryStorage.d.ts.map +1 -0
- package/dist/cache/memoryStorage.js +30 -0
- package/dist/cache/memoryStorage.js.map +1 -0
- package/dist/cache/storage.d.ts +15 -0
- package/dist/cache/storage.d.ts.map +1 -0
- package/dist/cache/storage.js +43 -0
- package/dist/cache/storage.js.map +1 -0
- package/dist/cache/types.d.ts +3 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +2 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/client/ConfigClient.d.ts +16 -0
- package/dist/client/ConfigClient.d.ts.map +1 -0
- package/dist/client/ConfigClient.js +215 -0
- package/dist/client/ConfigClient.js.map +1 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +80 -0
- package/dist/client.js.map +1 -0
- package/dist/core/resolver.d.ts +8 -0
- package/dist/core/resolver.d.ts.map +1 -0
- package/dist/core/resolver.js +22 -0
- package/dist/core/resolver.js.map +1 -0
- package/dist/createConfig.d.ts +6 -0
- package/dist/createConfig.d.ts.map +1 -0
- package/dist/createConfig.js +110 -0
- package/dist/createConfig.js.map +1 -0
- package/dist/errors/index.d.ts +17 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +29 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/events/EventEmitter.d.ts +9 -0
- package/dist/events/EventEmitter.d.ts.map +1 -0
- package/dist/events/EventEmitter.js +30 -0
- package/dist/events/EventEmitter.js.map +1 -0
- package/dist/example.d.ts +2 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +23 -0
- package/dist/example.js.map +1 -0
- package/dist/fetch/batchFetcher.d.ts +3 -0
- package/dist/fetch/batchFetcher.d.ts.map +1 -0
- package/dist/fetch/batchFetcher.js +12 -0
- package/dist/fetch/batchFetcher.js.map +1 -0
- package/dist/fetch/projectedFetcher.d.ts +3 -0
- package/dist/fetch/projectedFetcher.d.ts.map +1 -0
- package/dist/fetch/projectedFetcher.js +43 -0
- package/dist/fetch/projectedFetcher.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/loading/deferred.d.ts +3 -0
- package/dist/loading/deferred.d.ts.map +1 -0
- package/dist/loading/deferred.js +5 -0
- package/dist/loading/deferred.js.map +1 -0
- package/dist/loading/optimistic.d.ts +3 -0
- package/dist/loading/optimistic.d.ts.map +1 -0
- package/dist/loading/optimistic.js +35 -0
- package/dist/loading/optimistic.js.map +1 -0
- package/dist/loading/pessimistic.d.ts +3 -0
- package/dist/loading/pessimistic.d.ts.map +1 -0
- package/dist/loading/pessimistic.js +24 -0
- package/dist/loading/pessimistic.js.map +1 -0
- package/dist/management/index.d.ts +3 -0
- package/dist/management/index.d.ts.map +1 -0
- package/dist/management/index.js +2 -0
- package/dist/management/index.js.map +1 -0
- package/dist/management/manager.d.ts +34 -0
- package/dist/management/manager.d.ts.map +1 -0
- package/dist/management/manager.js +21 -0
- package/dist/management/manager.js.map +1 -0
- package/dist/management/memory-backend.d.ts +6 -0
- package/dist/management/memory-backend.d.ts.map +1 -0
- package/dist/management/memory-backend.js +78 -0
- package/dist/management/memory-backend.js.map +1 -0
- package/dist/plugins/evaluatePipeline.d.ts +15 -0
- package/dist/plugins/evaluatePipeline.d.ts.map +1 -0
- package/dist/plugins/evaluatePipeline.js +39 -0
- package/dist/plugins/evaluatePipeline.js.map +1 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +6 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/lifecycle/archivedPlugin.d.ts +14 -0
- package/dist/plugins/lifecycle/archivedPlugin.d.ts.map +1 -0
- package/dist/plugins/lifecycle/archivedPlugin.js +26 -0
- package/dist/plugins/lifecycle/archivedPlugin.js.map +1 -0
- package/dist/plugins/lifecycle/index.d.ts +4 -0
- package/dist/plugins/lifecycle/index.d.ts.map +1 -0
- package/dist/plugins/lifecycle/index.js +6 -0
- package/dist/plugins/lifecycle/index.js.map +1 -0
- package/dist/plugins/lifecycle/stateTransitions.d.ts +27 -0
- package/dist/plugins/lifecycle/stateTransitions.d.ts.map +1 -0
- package/dist/plugins/lifecycle/stateTransitions.js +33 -0
- package/dist/plugins/lifecycle/stateTransitions.js.map +1 -0
- package/dist/plugins/models.d.ts +50 -0
- package/dist/plugins/models.d.ts.map +1 -0
- package/dist/plugins/models.js +5 -0
- package/dist/plugins/models.js.map +1 -0
- package/dist/plugins/overrides/index.d.ts +2 -0
- package/dist/plugins/overrides/index.d.ts.map +1 -0
- package/dist/plugins/overrides/index.js +5 -0
- package/dist/plugins/overrides/index.js.map +1 -0
- package/dist/plugins/overrides/overridesPlugin.d.ts +12 -0
- package/dist/plugins/overrides/overridesPlugin.d.ts.map +1 -0
- package/dist/plugins/overrides/overridesPlugin.js +40 -0
- package/dist/plugins/overrides/overridesPlugin.js.map +1 -0
- package/dist/plugins/prerequisites/index.d.ts +3 -0
- package/dist/plugins/prerequisites/index.d.ts.map +1 -0
- package/dist/plugins/prerequisites/index.js +5 -0
- package/dist/plugins/prerequisites/index.js.map +1 -0
- package/dist/plugins/prerequisites/prerequisitePlugin.d.ts +24 -0
- package/dist/plugins/prerequisites/prerequisitePlugin.d.ts.map +1 -0
- package/dist/plugins/prerequisites/prerequisitePlugin.js +62 -0
- package/dist/plugins/prerequisites/prerequisitePlugin.js.map +1 -0
- package/dist/plugins/rollout/computeBucket.d.ts +12 -0
- package/dist/plugins/rollout/computeBucket.d.ts.map +1 -0
- package/dist/plugins/rollout/computeBucket.js +20 -0
- package/dist/plugins/rollout/computeBucket.js.map +1 -0
- package/dist/plugins/rollout/index.d.ts +3 -0
- package/dist/plugins/rollout/index.d.ts.map +1 -0
- package/dist/plugins/rollout/index.js +6 -0
- package/dist/plugins/rollout/index.js.map +1 -0
- package/dist/plugins/rollout/murmurhash3.d.ts +6 -0
- package/dist/plugins/rollout/murmurhash3.d.ts.map +1 -0
- package/dist/plugins/rollout/murmurhash3.js +56 -0
- package/dist/plugins/rollout/murmurhash3.js.map +1 -0
- package/dist/plugins/rollout/rolloutPlugin.d.ts +13 -0
- package/dist/plugins/rollout/rolloutPlugin.d.ts.map +1 -0
- package/dist/plugins/rollout/rolloutPlugin.js +44 -0
- package/dist/plugins/rollout/rolloutPlugin.js.map +1 -0
- package/dist/plugins/schedule/index.d.ts +2 -0
- package/dist/plugins/schedule/index.d.ts.map +1 -0
- package/dist/plugins/schedule/index.js +5 -0
- package/dist/plugins/schedule/index.js.map +1 -0
- package/dist/plugins/schedule/schedulePlugin.d.ts +14 -0
- package/dist/plugins/schedule/schedulePlugin.d.ts.map +1 -0
- package/dist/plugins/schedule/schedulePlugin.js +38 -0
- package/dist/plugins/schedule/schedulePlugin.js.map +1 -0
- package/dist/plugins/targeting/index.d.ts +4 -0
- package/dist/plugins/targeting/index.d.ts.map +1 -0
- package/dist/plugins/targeting/index.js +4 -0
- package/dist/plugins/targeting/index.js.map +1 -0
- package/dist/plugins/targeting/predicates.d.ts +23 -0
- package/dist/plugins/targeting/predicates.d.ts.map +1 -0
- package/dist/plugins/targeting/predicates.js +110 -0
- package/dist/plugins/targeting/predicates.js.map +1 -0
- package/dist/plugins/targeting/segmentResolver.d.ts +16 -0
- package/dist/plugins/targeting/segmentResolver.d.ts.map +1 -0
- package/dist/plugins/targeting/segmentResolver.js +27 -0
- package/dist/plugins/targeting/segmentResolver.js.map +1 -0
- package/dist/plugins/targeting/targetingPlugin.d.ts +19 -0
- package/dist/plugins/targeting/targetingPlugin.d.ts.map +1 -0
- package/dist/plugins/targeting/targetingPlugin.js +80 -0
- package/dist/plugins/targeting/targetingPlugin.js.map +1 -0
- package/dist/plugins/types.d.ts +35 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +13 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/registry.d.ts +9 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +22 -0
- package/dist/registry.js.map +1 -0
- package/dist/remote/firebase-adapter.d.ts +6 -0
- package/dist/remote/firebase-adapter.d.ts.map +1 -0
- package/dist/remote/firebase-adapter.js +12 -0
- package/dist/remote/firebase-adapter.js.map +1 -0
- package/dist/remote/provider.d.ts +6 -0
- package/dist/remote/provider.d.ts.map +1 -0
- package/dist/remote/provider.js +5 -0
- package/dist/remote/provider.js.map +1 -0
- package/dist/retry/RetryEngine.d.ts +5 -0
- package/dist/retry/RetryEngine.d.ts.map +1 -0
- package/dist/retry/RetryEngine.js +36 -0
- package/dist/retry/RetryEngine.js.map +1 -0
- package/dist/transport/HttpTransport.d.ts +7 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -0
- package/dist/transport/HttpTransport.js +34 -0
- package/dist/transport/HttpTransport.js.map +1 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computeBucket.js","sourceRoot":"","sources":["../../../src/plugins/rollout/computeBucket.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,6BAA6B;AAC7B,kEAAkE;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,MAAc;IAC7D,MAAM,KAAK,GAAG,GAAG,SAAS,IAAI,MAAM,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACtC,OAAO,IAAI,GAAG,GAAG,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/rollout/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// Rollout Plugin — Public Barrel Export
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
export { computeBucket } from "./computeBucket.js";
|
|
5
|
+
export { rolloutPlugin } from "./rolloutPlugin.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/rollout/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,wCAAwC;AACxC,kEAAkE;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"murmurhash3.d.ts","sourceRoot":"","sources":["../../../src/plugins/rollout/murmurhash3.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,MAAM,CAkDpE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// MurmurHash3_x86_32
|
|
3
|
+
// Public domain — Austin Appleby
|
|
4
|
+
// Adapted for TypeScript (operates on UTF-8 encoded string bytes)
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════
|
|
6
|
+
/**
|
|
7
|
+
* MurmurHash3_x86_32 — deterministic 32-bit hash.
|
|
8
|
+
* Used for percentage rollout bucketing (same algorithm as flagd/OpenFeature).
|
|
9
|
+
*/
|
|
10
|
+
export function murmurhash3_32(key, seed = 0) {
|
|
11
|
+
const encoder = new TextEncoder();
|
|
12
|
+
const bytes = encoder.encode(key);
|
|
13
|
+
const len = bytes.length;
|
|
14
|
+
let h1 = seed >>> 0;
|
|
15
|
+
const c1 = 0xcc9e2d51;
|
|
16
|
+
const c2 = 0x1b873593;
|
|
17
|
+
let i = 0;
|
|
18
|
+
while (i + 4 <= len) {
|
|
19
|
+
let k1 = (bytes[i] |
|
|
20
|
+
(bytes[i + 1] << 8) |
|
|
21
|
+
(bytes[i + 2] << 16) |
|
|
22
|
+
(bytes[i + 3] << 24)) >>>
|
|
23
|
+
0;
|
|
24
|
+
k1 = Math.imul(k1, c1) >>> 0;
|
|
25
|
+
k1 = ((k1 << 15) | (k1 >>> 17)) >>> 0;
|
|
26
|
+
k1 = Math.imul(k1, c2) >>> 0;
|
|
27
|
+
h1 ^= k1;
|
|
28
|
+
h1 = ((h1 << 13) | (h1 >>> 19)) >>> 0;
|
|
29
|
+
h1 = (Math.imul(h1, 5) + 0xe6546b64) >>> 0;
|
|
30
|
+
i += 4;
|
|
31
|
+
}
|
|
32
|
+
let k1 = 0;
|
|
33
|
+
const remainder = len & 3;
|
|
34
|
+
if (remainder >= 3) {
|
|
35
|
+
k1 ^= bytes[i + 2] << 16;
|
|
36
|
+
}
|
|
37
|
+
if (remainder >= 2) {
|
|
38
|
+
k1 ^= bytes[i + 1] << 8;
|
|
39
|
+
}
|
|
40
|
+
if (remainder >= 1) {
|
|
41
|
+
k1 ^= bytes[i];
|
|
42
|
+
k1 = Math.imul(k1, c1) >>> 0;
|
|
43
|
+
k1 = ((k1 << 15) | (k1 >>> 17)) >>> 0;
|
|
44
|
+
k1 = Math.imul(k1, c2) >>> 0;
|
|
45
|
+
h1 ^= k1;
|
|
46
|
+
}
|
|
47
|
+
h1 ^= len;
|
|
48
|
+
// fmix32
|
|
49
|
+
h1 ^= h1 >>> 16;
|
|
50
|
+
h1 = Math.imul(h1, 0x85ebca6b) >>> 0;
|
|
51
|
+
h1 ^= h1 >>> 13;
|
|
52
|
+
h1 = Math.imul(h1, 0xc2b2ae35) >>> 0;
|
|
53
|
+
h1 ^= h1 >>> 16;
|
|
54
|
+
return h1 >>> 0;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=murmurhash3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"murmurhash3.js","sourceRoot":"","sources":["../../../src/plugins/rollout/murmurhash3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EvaluationPlugin } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Factory function that creates the rollout evaluation plugin.
|
|
4
|
+
*
|
|
5
|
+
* Evaluation logic:
|
|
6
|
+
* - If `rolloutPercentage` is undefined/null → skip (resolved: false)
|
|
7
|
+
* - If rolloutPercentage is 0 → skip (let pipeline continue to default)
|
|
8
|
+
* - If rolloutPercentage is 100 → return rollout value
|
|
9
|
+
* - If no userId in context → skip (can't bucket without userId)
|
|
10
|
+
* - Otherwise: compute bucket, if bucket < rolloutPercentage → return rollout value, else skip
|
|
11
|
+
*/
|
|
12
|
+
export declare function rolloutPlugin(): EvaluationPlugin;
|
|
13
|
+
//# sourceMappingURL=rolloutPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolloutPlugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/rollout/rolloutPlugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI,gBAAgB,CAsChD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// Rollout Evaluation Plugin
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
import { computeBucket } from "./computeBucket.js";
|
|
5
|
+
/**
|
|
6
|
+
* Factory function that creates the rollout evaluation plugin.
|
|
7
|
+
*
|
|
8
|
+
* Evaluation logic:
|
|
9
|
+
* - If `rolloutPercentage` is undefined/null → skip (resolved: false)
|
|
10
|
+
* - If rolloutPercentage is 0 → skip (let pipeline continue to default)
|
|
11
|
+
* - If rolloutPercentage is 100 → return rollout value
|
|
12
|
+
* - If no userId in context → skip (can't bucket without userId)
|
|
13
|
+
* - Otherwise: compute bucket, if bucket < rolloutPercentage → return rollout value, else skip
|
|
14
|
+
*/
|
|
15
|
+
export function rolloutPlugin() {
|
|
16
|
+
return {
|
|
17
|
+
stepId: "rollout",
|
|
18
|
+
evaluate(flag, context, _helpers) {
|
|
19
|
+
// No rollout configured — skip
|
|
20
|
+
if (flag.rolloutPercentage == null) {
|
|
21
|
+
return { resolved: false };
|
|
22
|
+
}
|
|
23
|
+
// 0% — no one gets the rollout value; let pipeline continue to default
|
|
24
|
+
if (flag.rolloutPercentage === 0) {
|
|
25
|
+
return { resolved: false };
|
|
26
|
+
}
|
|
27
|
+
// 100% — everyone gets the rollout value
|
|
28
|
+
if (flag.rolloutPercentage === 100) {
|
|
29
|
+
return { resolved: true, value: flag.rolloutValue };
|
|
30
|
+
}
|
|
31
|
+
// No userId — can't compute a bucket; skip
|
|
32
|
+
if (!context.userId) {
|
|
33
|
+
return { resolved: false };
|
|
34
|
+
}
|
|
35
|
+
// Compute deterministic bucket and compare against percentage
|
|
36
|
+
const bucket = computeBucket(flag.key, context.userId);
|
|
37
|
+
if (bucket < flag.rolloutPercentage) {
|
|
38
|
+
return { resolved: true, value: flag.rolloutValue };
|
|
39
|
+
}
|
|
40
|
+
return { resolved: false };
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=rolloutPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolloutPlugin.js","sourceRoot":"","sources":["../../../src/plugins/rollout/rolloutPlugin.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,4BAA4B;AAC5B,kEAAkE;AASlE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,MAAM,EAAE,SAAS;QAEjB,QAAQ,CACN,IAAoB,EACpB,OAA0B,EAC1B,QAAyB;YAEzB,+BAA+B;YAC/B,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,EAAE,CAAC;gBACnC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAED,uEAAuE;YACvE,IAAI,IAAI,CAAC,iBAAiB,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAED,yCAAyC;YACzC,IAAI,IAAI,CAAC,iBAAiB,KAAK,GAAG,EAAE,CAAC;gBACnC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;YACtD,CAAC;YAED,2CAA2C;YAC3C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAED,8DAA8D;YAC9D,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YACvD,IAAI,MAAM,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACpC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;YACtD,CAAC;YAED,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/schedule/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/schedule/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,yCAAyC;AACzC,kEAAkE;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EvaluationPlugin } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Factory function that creates the schedule evaluation plugin.
|
|
4
|
+
*
|
|
5
|
+
* Evaluation logic:
|
|
6
|
+
* - If `flag.schedule` is undefined/null → skip (resolved: false)
|
|
7
|
+
* - Parse `flag.schedule.activateAt` as a Date
|
|
8
|
+
* - If activateAt is an invalid date string → skip (graceful degradation)
|
|
9
|
+
* - If Date.parse(activateAt) <= helpers.now() (schedule is in the past or exactly now)
|
|
10
|
+
* → return scheduled targetValue (Requirement 6.4)
|
|
11
|
+
* - If activateAt is in the future → skip (Requirement 6.5)
|
|
12
|
+
*/
|
|
13
|
+
export declare function schedulePlugin(): EvaluationPlugin;
|
|
14
|
+
//# sourceMappingURL=schedulePlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedulePlugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/schedule/schedulePlugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,gBAAgB,CA+BjD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// Schedule Evaluation Plugin
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
/**
|
|
5
|
+
* Factory function that creates the schedule evaluation plugin.
|
|
6
|
+
*
|
|
7
|
+
* Evaluation logic:
|
|
8
|
+
* - If `flag.schedule` is undefined/null → skip (resolved: false)
|
|
9
|
+
* - Parse `flag.schedule.activateAt` as a Date
|
|
10
|
+
* - If activateAt is an invalid date string → skip (graceful degradation)
|
|
11
|
+
* - If Date.parse(activateAt) <= helpers.now() (schedule is in the past or exactly now)
|
|
12
|
+
* → return scheduled targetValue (Requirement 6.4)
|
|
13
|
+
* - If activateAt is in the future → skip (Requirement 6.5)
|
|
14
|
+
*/
|
|
15
|
+
export function schedulePlugin() {
|
|
16
|
+
return {
|
|
17
|
+
stepId: "schedule",
|
|
18
|
+
evaluate(flag, _context, helpers) {
|
|
19
|
+
// No schedule configured — skip
|
|
20
|
+
if (flag.schedule == null) {
|
|
21
|
+
return { resolved: false };
|
|
22
|
+
}
|
|
23
|
+
// Parse the activateAt timestamp
|
|
24
|
+
const activateAtMs = Date.parse(flag.schedule.activateAt);
|
|
25
|
+
// Invalid date string — graceful degradation, skip
|
|
26
|
+
if (Number.isNaN(activateAtMs)) {
|
|
27
|
+
return { resolved: false };
|
|
28
|
+
}
|
|
29
|
+
// Schedule is in the past or exactly now — activate
|
|
30
|
+
if (activateAtMs <= helpers.now()) {
|
|
31
|
+
return { resolved: true, value: flag.schedule.targetValue };
|
|
32
|
+
}
|
|
33
|
+
// Schedule is in the future — skip
|
|
34
|
+
return { resolved: false };
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=schedulePlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedulePlugin.js","sourceRoot":"","sources":["../../../src/plugins/schedule/schedulePlugin.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,6BAA6B;AAC7B,kEAAkE;AAUlE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,MAAM,EAAE,UAAU;QAElB,QAAQ,CACN,IAAoB,EACpB,QAA2B,EAC3B,OAAwB;YAExB,gCAAgC;YAChC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;gBAC1B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAED,iCAAiC;YACjC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAE1D,mDAAmD;YACnD,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC/B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAED,oDAAoD;YACpD,IAAI,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;gBAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC9D,CAAC;YAED,mCAAmC;YACnC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/targeting/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/targeting/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Predicate, PredicateGroup } from "../models.js";
|
|
2
|
+
type AttributeValue = string | number | boolean | string[];
|
|
3
|
+
/**
|
|
4
|
+
* Evaluate a single predicate against the provided attributes.
|
|
5
|
+
*
|
|
6
|
+
* Returns false for:
|
|
7
|
+
* - Missing attributes in context
|
|
8
|
+
* - Type mismatches (e.g., greater_than on a non-numeric value)
|
|
9
|
+
* - Invalid regex patterns (also calls emitError)
|
|
10
|
+
* - in_segment / not_in_segment (handled externally by segment resolver)
|
|
11
|
+
*/
|
|
12
|
+
export declare function evaluatePredicate(predicate: Predicate, attributes: Record<string, AttributeValue>, emitError?: (message: string) => void): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Evaluate predicate groups in Disjunctive Normal Form (DNF).
|
|
15
|
+
*
|
|
16
|
+
* Groups are OR'd — any group matching means the whole expression matches.
|
|
17
|
+
* Within each group, predicates are AND'd — all must match.
|
|
18
|
+
*
|
|
19
|
+
* Returns false if groups array is empty.
|
|
20
|
+
*/
|
|
21
|
+
export declare function evaluatePredicateGroups(groups: PredicateGroup[], attributes: Record<string, AttributeValue>, emitError?: (message: string) => void): boolean;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=predicates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicates.d.ts","sourceRoot":"","sources":["../../../src/plugins/targeting/predicates.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9D,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC1C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CA4FT;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EAAE,EACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC1C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAYT"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// Predicate Evaluator — DNF logic for targeting rules
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
/**
|
|
5
|
+
* Evaluate a single predicate against the provided attributes.
|
|
6
|
+
*
|
|
7
|
+
* Returns false for:
|
|
8
|
+
* - Missing attributes in context
|
|
9
|
+
* - Type mismatches (e.g., greater_than on a non-numeric value)
|
|
10
|
+
* - Invalid regex patterns (also calls emitError)
|
|
11
|
+
* - in_segment / not_in_segment (handled externally by segment resolver)
|
|
12
|
+
*/
|
|
13
|
+
export function evaluatePredicate(predicate, attributes, emitError) {
|
|
14
|
+
const { attribute, operator, value } = predicate;
|
|
15
|
+
// in_segment / not_in_segment are resolved externally by the segment resolver
|
|
16
|
+
if (operator === "in_segment" || operator === "not_in_segment") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
// Missing attribute → predicate is non-matching (Property 4)
|
|
20
|
+
if (!(attribute in attributes)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const attrValue = attributes[attribute];
|
|
24
|
+
// Attribute value is undefined/null → non-matching
|
|
25
|
+
if (attrValue === undefined || attrValue === null) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
switch (operator) {
|
|
29
|
+
case "equals":
|
|
30
|
+
return attrValue === value;
|
|
31
|
+
case "not_equals":
|
|
32
|
+
return attrValue !== value;
|
|
33
|
+
case "contains": {
|
|
34
|
+
if (typeof attrValue !== "string" || typeof value !== "string") {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return attrValue.includes(value);
|
|
38
|
+
}
|
|
39
|
+
case "starts_with": {
|
|
40
|
+
if (typeof attrValue !== "string" || typeof value !== "string") {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return attrValue.startsWith(value);
|
|
44
|
+
}
|
|
45
|
+
case "ends_with": {
|
|
46
|
+
if (typeof attrValue !== "string" || typeof value !== "string") {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return attrValue.endsWith(value);
|
|
50
|
+
}
|
|
51
|
+
case "in_list": {
|
|
52
|
+
if (!Array.isArray(value)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return value.includes(attrValue);
|
|
56
|
+
}
|
|
57
|
+
case "not_in_list": {
|
|
58
|
+
if (!Array.isArray(value)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return !value.includes(attrValue);
|
|
62
|
+
}
|
|
63
|
+
case "greater_than": {
|
|
64
|
+
if (typeof attrValue !== "number" || typeof value !== "number") {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return attrValue > value;
|
|
68
|
+
}
|
|
69
|
+
case "less_than": {
|
|
70
|
+
if (typeof attrValue !== "number" || typeof value !== "number") {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return attrValue < value;
|
|
74
|
+
}
|
|
75
|
+
case "regex_match": {
|
|
76
|
+
if (typeof attrValue !== "string" || typeof value !== "string") {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const regex = new RegExp(value);
|
|
81
|
+
return regex.test(attrValue);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
emitError?.(`Invalid regex pattern: ${value}`);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Evaluate predicate groups in Disjunctive Normal Form (DNF).
|
|
94
|
+
*
|
|
95
|
+
* Groups are OR'd — any group matching means the whole expression matches.
|
|
96
|
+
* Within each group, predicates are AND'd — all must match.
|
|
97
|
+
*
|
|
98
|
+
* Returns false if groups array is empty.
|
|
99
|
+
*/
|
|
100
|
+
export function evaluatePredicateGroups(groups, attributes, emitError) {
|
|
101
|
+
if (groups.length === 0) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
// OR across groups: at least one group must fully match
|
|
105
|
+
return groups.some((group) => {
|
|
106
|
+
// AND within each group: all predicates must match
|
|
107
|
+
return group.predicates.every((predicate) => evaluatePredicate(predicate, attributes, emitError));
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=predicates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../../src/plugins/targeting/predicates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Segment } from "../models.js";
|
|
2
|
+
type AttributeValue = string | number | boolean | string[];
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a segment by ID and evaluate its predicate groups against
|
|
5
|
+
* the provided attributes.
|
|
6
|
+
*
|
|
7
|
+
* - Returns false if the segment does not exist (Requirement 3.6)
|
|
8
|
+
* - Nested segment references (in_segment/not_in_segment within a segment)
|
|
9
|
+
* are disallowed — evaluatePredicateGroups already returns false for those
|
|
10
|
+
* operators (Requirement 3.4)
|
|
11
|
+
* - Evaluates the segment's conditions in DNF (OR of ANDs) using the same
|
|
12
|
+
* predicate operators as targeting rules (Requirement 3.4)
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveSegment(segmentId: string, segments: Record<string, Segment>, attributes: Record<string, AttributeValue>, emitError?: (message: string) => void): boolean;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=segmentResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segmentResolver.d.ts","sourceRoot":"","sources":["../../../src/plugins/targeting/segmentResolver.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAC1C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAYT"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// Segment Resolver — resolves segment predicates against context
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
import { evaluatePredicateGroups } from "./predicates.js";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a segment by ID and evaluate its predicate groups against
|
|
7
|
+
* the provided attributes.
|
|
8
|
+
*
|
|
9
|
+
* - Returns false if the segment does not exist (Requirement 3.6)
|
|
10
|
+
* - Nested segment references (in_segment/not_in_segment within a segment)
|
|
11
|
+
* are disallowed — evaluatePredicateGroups already returns false for those
|
|
12
|
+
* operators (Requirement 3.4)
|
|
13
|
+
* - Evaluates the segment's conditions in DNF (OR of ANDs) using the same
|
|
14
|
+
* predicate operators as targeting rules (Requirement 3.4)
|
|
15
|
+
*/
|
|
16
|
+
export function resolveSegment(segmentId, segments, attributes, emitError) {
|
|
17
|
+
const segment = segments[segmentId];
|
|
18
|
+
// Segment not found → non-matching (Requirement 3.6)
|
|
19
|
+
if (!segment) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// Evaluate the segment's predicate groups in DNF against the attributes.
|
|
23
|
+
// in_segment/not_in_segment predicates within a segment will automatically
|
|
24
|
+
// return false (no nesting allowed, Requirement 3.4)
|
|
25
|
+
return evaluatePredicateGroups(segment.conditions, attributes, emitError);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=segmentResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segmentResolver.js","sourceRoot":"","sources":["../../../src/plugins/targeting/segmentResolver.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,iEAAiE;AACjE,kEAAkE;AAGlE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAI1D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,QAAiC,EACjC,UAA0C,EAC1C,SAAqC;IAErC,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAEpC,qDAAqD;IACrD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IAED,yEAAyE;IACzE,2EAA2E;IAC3E,qDAAqD;IACrD,OAAO,uBAAuB,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Segment } from "../models.js";
|
|
2
|
+
import type { EvaluationPlugin } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Factory function that creates the targeting rules evaluation plugin.
|
|
5
|
+
*
|
|
6
|
+
* Accepts an optional segments map for resolving `in_segment`/`not_in_segment`
|
|
7
|
+
* predicates within targeting rules.
|
|
8
|
+
*
|
|
9
|
+
* Evaluation logic:
|
|
10
|
+
* 1. stepId: "targeting"
|
|
11
|
+
* 2. If `flag.targetingRules` is undefined or empty → return { resolved: false }
|
|
12
|
+
* 3. Sort rules by priority (lowest number first); same priority → preserve insertion order
|
|
13
|
+
* 4. For each rule: evaluate its conditions (predicate groups in DNF)
|
|
14
|
+
* 5. For predicates with in_segment/not_in_segment: use segment resolver
|
|
15
|
+
* 6. First matching rule → return { resolved: true, value: rule.value }
|
|
16
|
+
* 7. No rule matches → return { resolved: false }
|
|
17
|
+
*/
|
|
18
|
+
export declare function targetingPlugin(segments?: Record<string, Segment>): EvaluationPlugin;
|
|
19
|
+
//# sourceMappingURL=targetingPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targetingPlugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/targeting/targetingPlugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA6C,OAAO,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,aAAa,CAAC;AAwDrB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,gBAAgB,CA2ClB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// Targeting Rules Evaluation Plugin
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
import { evaluatePredicate } from "./predicates.js";
|
|
5
|
+
import { resolveSegment } from "./segmentResolver.js";
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate a single predicate, handling segment-based predicates specially.
|
|
8
|
+
*
|
|
9
|
+
* For `in_segment` / `not_in_segment` operators, delegates to the segment
|
|
10
|
+
* resolver rather than the default predicate evaluator.
|
|
11
|
+
*/
|
|
12
|
+
function evaluatePredicateWithSegments(predicate, attributes, segments, emitError) {
|
|
13
|
+
if (predicate.operator === "in_segment") {
|
|
14
|
+
const segmentId = String(predicate.value);
|
|
15
|
+
return resolveSegment(segmentId, segments, attributes, emitError);
|
|
16
|
+
}
|
|
17
|
+
if (predicate.operator === "not_in_segment") {
|
|
18
|
+
const segmentId = String(predicate.value);
|
|
19
|
+
return !resolveSegment(segmentId, segments, attributes, emitError);
|
|
20
|
+
}
|
|
21
|
+
return evaluatePredicate(predicate, attributes, emitError);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Evaluate predicate groups in DNF with segment resolution support.
|
|
25
|
+
*
|
|
26
|
+
* Groups are OR'd — any group matching means the whole expression matches.
|
|
27
|
+
* Within each group, predicates are AND'd — all must match.
|
|
28
|
+
*/
|
|
29
|
+
function evaluatePredicateGroupsWithSegments(groups, attributes, segments, emitError) {
|
|
30
|
+
if (groups.length === 0) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// OR across groups: at least one group must fully match
|
|
34
|
+
return groups.some((group) => {
|
|
35
|
+
// AND within each group: all predicates must match
|
|
36
|
+
return group.predicates.every((predicate) => evaluatePredicateWithSegments(predicate, attributes, segments, emitError));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Factory function that creates the targeting rules evaluation plugin.
|
|
41
|
+
*
|
|
42
|
+
* Accepts an optional segments map for resolving `in_segment`/`not_in_segment`
|
|
43
|
+
* predicates within targeting rules.
|
|
44
|
+
*
|
|
45
|
+
* Evaluation logic:
|
|
46
|
+
* 1. stepId: "targeting"
|
|
47
|
+
* 2. If `flag.targetingRules` is undefined or empty → return { resolved: false }
|
|
48
|
+
* 3. Sort rules by priority (lowest number first); same priority → preserve insertion order
|
|
49
|
+
* 4. For each rule: evaluate its conditions (predicate groups in DNF)
|
|
50
|
+
* 5. For predicates with in_segment/not_in_segment: use segment resolver
|
|
51
|
+
* 6. First matching rule → return { resolved: true, value: rule.value }
|
|
52
|
+
* 7. No rule matches → return { resolved: false }
|
|
53
|
+
*/
|
|
54
|
+
export function targetingPlugin(segments) {
|
|
55
|
+
const segmentsMap = segments ?? {};
|
|
56
|
+
return {
|
|
57
|
+
stepId: "targeting",
|
|
58
|
+
evaluate(flag, context, helpers) {
|
|
59
|
+
// No targeting rules configured — skip
|
|
60
|
+
if (!flag.targetingRules || flag.targetingRules.length === 0) {
|
|
61
|
+
return { resolved: false };
|
|
62
|
+
}
|
|
63
|
+
// Sort rules by priority (lowest number = highest priority).
|
|
64
|
+
// Array.prototype.sort is stable in modern JS — same priority preserves insertion order.
|
|
65
|
+
const sortedRules = [...flag.targetingRules].sort((a, b) => a.priority - b.priority);
|
|
66
|
+
// Build attributes from context (default to empty object)
|
|
67
|
+
const attributes = context.attributes ?? {};
|
|
68
|
+
// Evaluate each rule in priority order
|
|
69
|
+
for (const rule of sortedRules) {
|
|
70
|
+
const matches = evaluatePredicateGroupsWithSegments(rule.conditions, attributes, segmentsMap, helpers.emitError);
|
|
71
|
+
if (matches) {
|
|
72
|
+
return { resolved: true, value: rule.value };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// No rule matched
|
|
76
|
+
return { resolved: false };
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=targetingPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targetingPlugin.js","sourceRoot":"","sources":["../../../src/plugins/targeting/targetingPlugin.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,oCAAoC;AACpC,kEAAkE;AASlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD;;;;;GAKG;AACH,SAAS,6BAA6B,CACpC,SAAoB,EACpB,UAA0C,EAC1C,QAAiC,EACjC,SAAqC;IAErC,IAAI,SAAS,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QACxC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1C,OAAO,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,SAAS,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,iBAAiB,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,SAAS,mCAAmC,CAC1C,MAAwB,EACxB,UAA0C,EAC1C,QAAiC,EACjC,SAAqC;IAErC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,wDAAwD;IACxD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3B,mDAAmD;QACnD,OAAO,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAC1C,6BAA6B,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAC1E,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAkC;IAElC,MAAM,WAAW,GAAG,QAAQ,IAAI,EAAE,CAAC;IAEnC,OAAO;QACL,MAAM,EAAE,WAAW;QAEnB,QAAQ,CACN,IAAoB,EACpB,OAA0B,EAC1B,OAAwB;YAExB,uCAAuC;YACvC,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;YAED,6DAA6D;YAC7D,yFAAyF;YACzF,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAC/C,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAClC,CAAC;YAEF,0DAA0D;YAC1D,MAAM,UAAU,GAAmC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;YAE5E,uCAAuC;YACvC,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,mCAAmC,CACjD,IAAI,CAAC,UAAU,EACf,UAAU,EACV,WAAW,EACX,OAAO,CAAC,SAAS,CAClB,CAAC;gBAEF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC/C,CAAC;YACH,CAAC;YAED,kBAAkB;YAClB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ConfigFlagData } from "./models.js";
|
|
2
|
+
/** Pipeline step identifier — determines execution order */
|
|
3
|
+
export type PipelineStepId = "archived" | "prerequisites" | "overrides" | "schedule" | "targeting" | "rollout";
|
|
4
|
+
/** The fixed execution order (per Requirement 11) */
|
|
5
|
+
export declare const PIPELINE_ORDER: PipelineStepId[];
|
|
6
|
+
/** Evaluation context provided by the consumer */
|
|
7
|
+
export interface EvaluationContext {
|
|
8
|
+
userId?: string;
|
|
9
|
+
attributes?: Record<string, string | number | boolean | string[]>;
|
|
10
|
+
consentGranted?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Result from a pipeline step */
|
|
13
|
+
export type PipelineStepResult = {
|
|
14
|
+
resolved: true;
|
|
15
|
+
value: unknown;
|
|
16
|
+
} | {
|
|
17
|
+
resolved: false;
|
|
18
|
+
};
|
|
19
|
+
/** A registered evaluation plugin */
|
|
20
|
+
export interface EvaluationPlugin {
|
|
21
|
+
/** Which pipeline step this plugin handles */
|
|
22
|
+
stepId: PipelineStepId;
|
|
23
|
+
/** Evaluate a config flag at this pipeline step */
|
|
24
|
+
evaluate(flag: ConfigFlagData, context: EvaluationContext, helpers: PipelineHelpers): PipelineStepResult;
|
|
25
|
+
}
|
|
26
|
+
/** Helpers provided to plugins for cross-cutting concerns */
|
|
27
|
+
export interface PipelineHelpers {
|
|
28
|
+
/** Evaluate another flag (for prerequisites) */
|
|
29
|
+
evaluateFlag(key: string, context: EvaluationContext): unknown;
|
|
30
|
+
/** Emit an error */
|
|
31
|
+
emitError(message: string): void;
|
|
32
|
+
/** Current timestamp (injectable for testing) */
|
|
33
|
+
now(): number;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/plugins/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,eAAe,GACf,WAAW,GACX,UAAU,GACV,WAAW,GACX,SAAS,CAAC;AAEd,qDAAqD;AACrD,eAAO,MAAM,cAAc,EAAE,cAAc,EAO1C,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;IAClE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,kCAAkC;AAClC,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,QAAQ,EAAE,KAAK,CAAA;CAAE,CAAC;AAExB,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,MAAM,EAAE,cAAc,CAAC;IAEvB,mDAAmD;IACnD,QAAQ,CACN,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,eAAe,GACvB,kBAAkB,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC/D,oBAAoB;IACpB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iDAAiD;IACjD,GAAG,IAAI,MAAM,CAAC;CACf"}
|