@zudojs/feature-flags 1.4.2 → 1.5.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.
Files changed (74) hide show
  1. package/README.md +9 -1
  2. package/dist/evaluator/evaluator.core.d.ts +0 -1
  3. package/dist/evaluator/evaluator.core.js +0 -1
  4. package/dist/evaluator/evaluatorAttribute.d.ts +0 -1
  5. package/dist/evaluator/evaluatorAttribute.js +0 -1
  6. package/dist/evaluator/evaluatorGate.core.d.ts +0 -1
  7. package/dist/evaluator/evaluatorGate.core.js +0 -1
  8. package/dist/evaluator/evaluatorPattern.safety.d.ts +0 -1
  9. package/dist/evaluator/evaluatorPattern.safety.js +0 -1
  10. package/dist/evaluator/evaluatorRule.core.d.ts +0 -1
  11. package/dist/evaluator/evaluatorRule.core.js +7 -4
  12. package/dist/evaluator/index.d.ts +0 -1
  13. package/dist/evaluator/index.js +0 -1
  14. package/dist/featureFlagErrors/featureFlagError.base.d.ts +0 -1
  15. package/dist/featureFlagErrors/featureFlagError.base.js +0 -1
  16. package/dist/featureFlagErrors/featureFlagError.types.d.ts +0 -1
  17. package/dist/featureFlagErrors/featureFlagError.types.js +0 -1
  18. package/dist/featureFlagErrors/index.d.ts +0 -1
  19. package/dist/featureFlagErrors/index.js +0 -1
  20. package/dist/featureFlagTypes/featureFlag.interface.d.ts +0 -1
  21. package/dist/featureFlagTypes/featureFlag.interface.js +0 -1
  22. package/dist/featureFlagTypes/featureFlagContext.d.ts +0 -1
  23. package/dist/featureFlagTypes/featureFlagContext.js +0 -1
  24. package/dist/featureFlagTypes/featureFlagEvaluation.d.ts +0 -1
  25. package/dist/featureFlagTypes/featureFlagEvaluation.js +0 -1
  26. package/dist/featureFlagTypes/featureFlagProvider.d.ts +0 -1
  27. package/dist/featureFlagTypes/featureFlagProvider.js +0 -1
  28. package/dist/featureFlagTypes/featureFlagRule/featureFlagRule.type.d.ts +15 -1
  29. package/dist/featureFlagTypes/featureFlagRule/featureFlagRule.type.js +0 -1
  30. package/dist/featureFlagTypes/featureFlagRule/featureFlagValue.type.d.ts +0 -1
  31. package/dist/featureFlagTypes/featureFlagRule/featureFlagValue.type.js +0 -1
  32. package/dist/featureFlagTypes/featureFlagRule/index.d.ts +0 -1
  33. package/dist/featureFlagTypes/featureFlagRule/index.js +0 -1
  34. package/dist/featureFlagTypes/index.d.ts +0 -1
  35. package/dist/featureFlagTypes/index.js +0 -1
  36. package/dist/featureFlags/featureFlags.cooloff.d.ts +0 -1
  37. package/dist/featureFlags/featureFlags.cooloff.js +0 -1
  38. package/dist/featureFlags/featureFlags.core.d.ts +0 -1
  39. package/dist/featureFlags/featureFlags.core.js +0 -1
  40. package/dist/featureFlags/featureFlags.dependency.d.ts +0 -1
  41. package/dist/featureFlags/featureFlags.dependency.js +0 -1
  42. package/dist/featureFlags/featureFlags.missCache.d.ts +0 -1
  43. package/dist/featureFlags/featureFlags.missCache.js +0 -1
  44. package/dist/featureFlags/featureFlags.resolve.d.ts +0 -1
  45. package/dist/featureFlags/featureFlags.resolve.js +0 -1
  46. package/dist/featureFlags/index.d.ts +0 -1
  47. package/dist/featureFlags/index.js +0 -1
  48. package/dist/index.d.ts +1 -2
  49. package/dist/index.js +1 -2
  50. package/dist/provider/index.d.ts +0 -1
  51. package/dist/provider/index.js +0 -1
  52. package/dist/provider/providerCached.core.d.ts +0 -1
  53. package/dist/provider/providerCached.core.js +0 -1
  54. package/dist/provider/providerComposite.core.d.ts +0 -1
  55. package/dist/provider/providerComposite.core.js +0 -1
  56. package/dist/provider/providerEnvironment.core.d.ts +0 -1
  57. package/dist/provider/providerEnvironment.core.js +0 -1
  58. package/dist/provider/providerMemory.core.d.ts +0 -1
  59. package/dist/provider/providerMemory.core.js +0 -1
  60. package/dist/registry/index.d.ts +0 -1
  61. package/dist/registry/index.js +0 -1
  62. package/dist/registry/registry.core.d.ts +0 -1
  63. package/dist/registry/registry.core.js +0 -1
  64. package/dist/rollout/index.d.ts +1 -2
  65. package/dist/rollout/index.js +1 -2
  66. package/dist/rollout/rolloutBucketing.d.ts +15 -1
  67. package/dist/rollout/rolloutBucketing.js +19 -1
  68. package/dist/rollout/rolloutHashing.d.ts +0 -1
  69. package/dist/rollout/rolloutHashing.js +0 -1
  70. package/dist/utils/index.d.ts +0 -1
  71. package/dist/utils/index.js +0 -1
  72. package/dist/utils/utils.helper.d.ts +0 -1
  73. package/dist/utils/utils.helper.js +0 -1
  74. package/package.json +3 -3
package/README.md CHANGED
@@ -105,7 +105,15 @@ default was `true`. For a variant flag, declare the variant to fall back to:
105
105
 
106
106
  Rules are evaluated in declaration order and the first match wins. The
107
107
  subject for `percentage` and `variant` is `userId`, then `tenantId`, then
108
- `sessionId`, then `"anonymous"`.
108
+ `sessionId`, then `"anonymous"`. That default splits an organisation whenever
109
+ the context carries both a `userId` and a `tenantId`; pin the subject with
110
+ `bucketBy` to roll out per tenant (or per session):
111
+
112
+ ```ts
113
+ { type: "percentage", percentage: 10, value: true, bucketBy: "tenantId" }
114
+ ```
115
+
116
+ With `bucketBy` set, a context missing that field does not match the rule.
109
117
 
110
118
  Operators: `equals`, `not_equals`, `contains`, `starts_with`, `ends_with`,
111
119
  `in`, `not_in`, `greater_than`, `greater_than_or_equal`, `less_than`,
@@ -35,4 +35,3 @@ export interface EvaluateFlagOptions {
35
35
  * @returns A structured evaluation result.
36
36
  */
37
37
  export declare function evaluateFlag<TValue extends FeatureFlagValue = FeatureFlagValue>(flag: FeatureFlag, context?: FeatureFlagContext, options?: EvaluateFlagOptions): FeatureFlagEvaluation<TValue>;
38
- //# sourceMappingURL=evaluator.core.d.ts.map
@@ -85,4 +85,3 @@ export function evaluateFlag(flag, context = {}, options = {}) {
85
85
  defaulted: true,
86
86
  };
87
87
  }
88
- //# sourceMappingURL=evaluator.core.js.map
@@ -28,4 +28,3 @@ export declare function resolvePath(obj: unknown, path: string): unknown;
28
28
  * @returns Whether the condition matches.
29
29
  */
30
30
  export declare function matchAttribute(actual: unknown, operator: FeatureFlagOperator, expected: unknown): boolean;
31
- //# sourceMappingURL=evaluatorAttribute.d.ts.map
@@ -143,4 +143,3 @@ export function matchAttribute(actual, operator, expected) {
143
143
  return false;
144
144
  }
145
145
  }
146
- //# sourceMappingURL=evaluatorAttribute.js.map
@@ -32,4 +32,3 @@ export declare function gateReason(flag: FeatureFlag, options: EvaluateFlagOptio
32
32
  * @returns The value to serve.
33
33
  */
34
34
  export declare function offValueOf(flag: FeatureFlag): FeatureFlagValue;
35
- //# sourceMappingURL=evaluatorGate.core.d.ts.map
@@ -65,4 +65,3 @@ export function offValueOf(flag) {
65
65
  return false;
66
66
  return flag.defaultValue;
67
67
  }
68
- //# sourceMappingURL=evaluatorGate.core.js.map
@@ -20,4 +20,3 @@ export declare const MAX_MATCH_INPUT_LENGTH = 1024;
20
20
  * `^(beta|alpha)-` — contain neither.
21
21
  */
22
22
  export declare function isUnsafePattern(pattern: string): boolean;
23
- //# sourceMappingURL=evaluatorPattern.safety.d.ts.map
@@ -76,4 +76,3 @@ export function isUnsafePattern(pattern) {
76
76
  }
77
77
  return false;
78
78
  }
79
- //# sourceMappingURL=evaluatorPattern.safety.js.map
@@ -26,4 +26,3 @@ export interface RuleEvaluationResult {
26
26
  * @returns The evaluation result.
27
27
  */
28
28
  export declare function evaluateRule(rule: FeatureFlagRule, context: FeatureFlagContext, flagKey: string): RuleEvaluationResult;
29
- //# sourceMappingURL=evaluatorRule.core.d.ts.map
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @module evaluator/evaluatorRule
7
7
  */
8
- import { getBucket, isInRollout } from "../rollout/rolloutBucketing.js";
8
+ import { getBucket, isInRollout, resolveRolloutSubject, } from "../rollout/rolloutBucketing.js";
9
9
  import { matchAttribute, resolvePath } from "./evaluatorAttribute.js";
10
10
  /** Bucket resolution for variant assignment — 0.01% precision. */
11
11
  const VARIANT_BUCKETS = 10_000;
@@ -42,7 +42,9 @@ export function evaluateRule(rule, context, flagKey) {
42
42
  return { matched, value: matched ? (rule.result ?? true) : undefined };
43
43
  }
44
44
  case "percentage": {
45
- const subject = context.userId ?? context.tenantId ?? context.sessionId ?? "anonymous";
45
+ const subject = resolveRolloutSubject(context, rule.bucketBy);
46
+ if (subject === undefined)
47
+ return { matched: false };
46
48
  const matched = isInRollout(flagKey, subject, rule.percentage);
47
49
  return { matched, value: matched ? rule.value : undefined };
48
50
  }
@@ -58,7 +60,9 @@ export function evaluateRule(rule, context, flagKey) {
58
60
  return { matched, value: matched ? rule.value : undefined };
59
61
  }
60
62
  case "variant": {
61
- const subject = context.userId ?? context.tenantId ?? context.sessionId ?? "anonymous";
63
+ const subject = resolveRolloutSubject(context, rule.bucketBy);
64
+ if (subject === undefined)
65
+ return { matched: false };
62
66
  // Negative or non-finite weights would make the cumulative walk
63
67
  // non-monotonic, shifting every downstream variant's band, so they are
64
68
  // treated as zero.
@@ -95,4 +99,3 @@ export function evaluateRule(rule, context, flagKey) {
95
99
  return { matched: false };
96
100
  }
97
101
  }
98
- //# sourceMappingURL=evaluatorRule.core.js.map
@@ -7,4 +7,3 @@ export { resolvePath, matchAttribute } from "./evaluatorAttribute.js";
7
7
  export { evaluateRule } from "./evaluatorRule.core.js";
8
8
  export type { RuleEvaluationResult } from "./evaluatorRule.core.js";
9
9
  export { evaluateFlag } from "./evaluator.core.js";
10
- //# sourceMappingURL=index.d.ts.map
@@ -6,4 +6,3 @@
6
6
  export { resolvePath, matchAttribute } from "./evaluatorAttribute.js";
7
7
  export { evaluateRule } from "./evaluatorRule.core.js";
8
8
  export { evaluateFlag } from "./evaluator.core.js";
9
- //# sourceMappingURL=index.js.map
@@ -21,4 +21,3 @@ export interface FeatureFlagErrorOptions {
21
21
  export declare class FeatureFlagError extends ApplicationError {
22
22
  constructor(message: string, options?: FeatureFlagErrorOptions);
23
23
  }
24
- //# sourceMappingURL=featureFlagError.base.d.ts.map
@@ -21,4 +21,3 @@ export class FeatureFlagError extends ApplicationError {
21
21
  this.name = "FeatureFlagError";
22
22
  }
23
23
  }
24
- //# sourceMappingURL=featureFlagError.base.js.map
@@ -35,4 +35,3 @@ export declare class FeatureFlagConfigurationError extends FeatureFlagError {
35
35
  export declare class FeatureFlagTypeError extends FeatureFlagError {
36
36
  constructor(key: string, expected: string, actual: string);
37
37
  }
38
- //# sourceMappingURL=featureFlagError.types.d.ts.map
@@ -76,4 +76,3 @@ export class FeatureFlagTypeError extends FeatureFlagError {
76
76
  this.name = "FeatureFlagTypeError";
77
77
  }
78
78
  }
79
- //# sourceMappingURL=featureFlagError.types.js.map
@@ -6,4 +6,3 @@
6
6
  export { FeatureFlagError } from "./featureFlagError.base.js";
7
7
  export type { FeatureFlagErrorOptions } from "./featureFlagError.base.js";
8
8
  export { FeatureFlagNotFoundError, FeatureFlagProviderError, FeatureFlagEvaluationError, FeatureFlagRuleError, FeatureFlagDependencyError, FeatureFlagConfigurationError, FeatureFlagTypeError, } from "./featureFlagError.types.js";
9
- //# sourceMappingURL=index.d.ts.map
@@ -5,4 +5,3 @@
5
5
  */
6
6
  export { FeatureFlagError } from "./featureFlagError.base.js";
7
7
  export { FeatureFlagNotFoundError, FeatureFlagProviderError, FeatureFlagEvaluationError, FeatureFlagRuleError, FeatureFlagDependencyError, FeatureFlagConfigurationError, FeatureFlagTypeError, } from "./featureFlagError.types.js";
8
- //# sourceMappingURL=index.js.map
@@ -56,4 +56,3 @@ export interface FeatureFlag {
56
56
  /** Additional metadata. */
57
57
  readonly metadata?: FeatureFlagMetadata;
58
58
  }
59
- //# sourceMappingURL=featureFlag.interface.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * @module featureFlagTypes/featureFlag
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=featureFlag.interface.js.map
@@ -18,4 +18,3 @@ export interface FeatureFlagContext {
18
18
  /** Custom attributes for attribute-based targeting. */
19
19
  readonly attributes?: Readonly<Record<string, unknown>>;
20
20
  }
21
- //# sourceMappingURL=featureFlagContext.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * @module featureFlagTypes/featureFlagContext
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=featureFlagContext.js.map
@@ -23,4 +23,3 @@ export interface FeatureFlagEvaluation<TValue = FeatureFlagValue> {
23
23
  /** Whether the default value was used. */
24
24
  readonly defaulted: boolean;
25
25
  }
26
- //# sourceMappingURL=featureFlagEvaluation.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * @module featureFlagTypes/featureFlagEvaluation
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=featureFlagEvaluation.js.map
@@ -31,4 +31,3 @@ export interface FeatureFlagProvider {
31
31
  export interface RefreshableFeatureFlagProvider extends FeatureFlagProvider {
32
32
  refresh(): Promise<void>;
33
33
  }
34
- //# sourceMappingURL=featureFlagProvider.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * @module featureFlagTypes/featureFlagProvider
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=featureFlagProvider.js.map
@@ -42,11 +42,24 @@ export interface FeatureFlagAttributeRule {
42
42
  /** The value served when the rule matches. Default: `true`. */
43
43
  readonly result?: FeatureFlagValue;
44
44
  }
45
+ /**
46
+ * Which context field a rollout hashes on.
47
+ *
48
+ * Without `bucketBy`, the subject is `userId`, then `tenantId`, then
49
+ * `sessionId`, then `"anonymous"` — so for a context carrying both a user
50
+ * and a tenant, members of one tenant land in different buckets. Set
51
+ * `bucketBy: "tenantId"` to roll a flag out per organisation. When the
52
+ * named field is absent from the context the rule does not match, rather
53
+ * than silently bucketing on something else.
54
+ */
55
+ export type FeatureFlagBucketBy = "userId" | "tenantId" | "sessionId";
45
56
  /** Percentage-based rollout — deterministic per subject. */
46
57
  export interface FeatureFlagPercentageRule {
47
58
  readonly type: "percentage";
48
59
  readonly percentage: number;
49
60
  readonly value: FeatureFlagValue;
61
+ /** Context field to bucket on; see {@link FeatureFlagBucketBy}. */
62
+ readonly bucketBy?: FeatureFlagBucketBy;
50
63
  }
51
64
  /** Time-windowed rule — enabled only within a date range. */
52
65
  export interface FeatureFlagScheduleRule {
@@ -59,6 +72,8 @@ export interface FeatureFlagScheduleRule {
59
72
  export interface FeatureFlagVariantRule {
60
73
  readonly type: "variant";
61
74
  readonly variants: readonly FeatureFlagVariant[];
75
+ /** Context field to bucket on; see {@link FeatureFlagBucketBy}. */
76
+ readonly bucketBy?: FeatureFlagBucketBy;
62
77
  }
63
78
  /** A variant with a weight for percentage-based assignment. */
64
79
  export interface FeatureFlagVariant {
@@ -67,4 +82,3 @@ export interface FeatureFlagVariant {
67
82
  }
68
83
  /** Union of all feature flag rule types. */
69
84
  export type FeatureFlagRule = FeatureFlagStaticRule | FeatureFlagUserRule | FeatureFlagTenantRule | FeatureFlagAttributeRule | FeatureFlagPercentageRule | FeatureFlagScheduleRule | FeatureFlagVariantRule;
70
- //# sourceMappingURL=featureFlagRule.type.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * @module featureFlagTypes/featureFlagRule
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=featureFlagRule.type.js.map
@@ -11,4 +11,3 @@ export type FeatureFlagValue = boolean | string | number | null | Record<string,
11
11
  export type FeatureFlagState = "active" | "disabled" | "archived" | "draft";
12
12
  /** Feature flag visibility scope. */
13
13
  export type FeatureFlagVisibility = "server" | "client";
14
- //# sourceMappingURL=featureFlagValue.type.d.ts.map
@@ -6,4 +6,3 @@
6
6
  * @module featureFlagTypes/featureFlagValue
7
7
  */
8
8
  export {};
9
- //# sourceMappingURL=featureFlagValue.type.js.map
@@ -5,4 +5,3 @@
5
5
  */
6
6
  export * from "./featureFlagRule.type.js";
7
7
  export * from "./featureFlagValue.type.js";
8
- //# sourceMappingURL=index.d.ts.map
@@ -5,4 +5,3 @@
5
5
  */
6
6
  export * from "./featureFlagRule.type.js";
7
7
  export * from "./featureFlagValue.type.js";
8
- //# sourceMappingURL=index.js.map
@@ -6,4 +6,3 @@ export type { FeatureFlagContext } from "./featureFlagContext.js";
6
6
  export type { FeatureFlagMetadata, FeatureFlag, } from "./featureFlag.interface.js";
7
7
  export type { FeatureFlagEvaluationReason, FeatureFlagEvaluation, } from "./featureFlagEvaluation.js";
8
8
  export type { FeatureFlagProvider, RefreshableFeatureFlagProvider, FeatureFlagChangeListener, Unsubscribe, } from "./featureFlagProvider.js";
9
- //# sourceMappingURL=index.d.ts.map
@@ -2,4 +2,3 @@
2
2
  * Feature flag type definitions.
3
3
  */
4
4
  export {};
5
- //# sourceMappingURL=index.js.map
@@ -24,4 +24,3 @@ export interface ProviderCooloff {
24
24
  * clears the window immediately. `cooloffMs <= 0` disables the gate.
25
25
  */
26
26
  export declare function createProviderCooloff(cooloffMs: number): ProviderCooloff;
27
- //# sourceMappingURL=featureFlags.cooloff.d.ts.map
@@ -31,4 +31,3 @@ export function createProviderCooloff(cooloffMs) {
31
31
  },
32
32
  };
33
33
  }
34
- //# sourceMappingURL=featureFlags.cooloff.js.map
@@ -67,4 +67,3 @@ export interface FeatureFlags {
67
67
  * @returns A FeatureFlags API object.
68
68
  */
69
69
  export declare function createFeatureFlags(options: FeatureFlagsOptions): FeatureFlags;
70
- //# sourceMappingURL=featureFlags.core.d.ts.map
@@ -200,4 +200,3 @@ export function createFeatureFlags(options) {
200
200
  };
201
201
  return api;
202
202
  }
203
- //# sourceMappingURL=featureFlags.core.js.map
@@ -31,4 +31,3 @@ export declare function isDependencyOn(evaluation: FeatureFlagEvaluation): boole
31
31
  * @param memo - Results for this context, so a diamond is walked once.
32
32
  */
33
33
  export declare function dependenciesOn(flag: FeatureFlag, registry: FeatureFlagRegistry, context: FeatureFlagContext, chain?: Set<string>, memo?: Map<string, boolean>): boolean;
34
- //# sourceMappingURL=featureFlags.dependency.d.ts.map
@@ -69,4 +69,3 @@ export function dependenciesOn(flag, registry, context, chain = new Set([flag.ke
69
69
  }
70
70
  return true;
71
71
  }
72
- //# sourceMappingURL=featureFlags.dependency.js.map
@@ -23,4 +23,3 @@ export interface MissCache {
23
23
  * evicted). `ttlMs <= 0` disables it.
24
24
  */
25
25
  export declare function createMissCache(ttlMs: number): MissCache;
26
- //# sourceMappingURL=featureFlags.missCache.d.ts.map
@@ -46,4 +46,3 @@ export function createMissCache(ttlMs) {
46
46
  },
47
47
  };
48
48
  }
49
- //# sourceMappingURL=featureFlags.missCache.js.map
@@ -24,4 +24,3 @@ export declare function resolveDependencies(key: string, registry: FeatureFlagRe
24
24
  * Merge default context with provided context.
25
25
  */
26
26
  export declare function mergeContext(defaultContext: FeatureFlagContext, context?: FeatureFlagContext): FeatureFlagContext;
27
- //# sourceMappingURL=featureFlags.resolve.d.ts.map
@@ -46,4 +46,3 @@ export function mergeContext(defaultContext, context) {
46
46
  },
47
47
  };
48
48
  }
49
- //# sourceMappingURL=featureFlags.resolve.js.map
@@ -5,4 +5,3 @@
5
5
  */
6
6
  export { createFeatureFlags } from "./featureFlags.core.js";
7
7
  export type { FeatureFlagsOptions } from "./featureFlags.core.js";
8
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * @module featureFlags
5
5
  */
6
6
  export { createFeatureFlags } from "./featureFlags.core.js";
7
- //# sourceMappingURL=index.js.map
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { FeatureFlagError } from "./featureFlagErrors/featureFlagError.base.js";
10
10
  export type { FeatureFlagErrorOptions } from "./featureFlagErrors/featureFlagError.base.js";
11
11
  export { FeatureFlagNotFoundError, FeatureFlagProviderError, FeatureFlagEvaluationError, FeatureFlagRuleError, FeatureFlagDependencyError, FeatureFlagConfigurationError, FeatureFlagTypeError, } from "./featureFlagErrors/featureFlagError.types.js";
12
12
  export { hashString } from "./rollout/rolloutHashing.js";
13
- export { getBucket, isInRollout } from "./rollout/rolloutBucketing.js";
13
+ export { getBucket, isInRollout, resolveRolloutSubject, } from "./rollout/rolloutBucketing.js";
14
14
  export { createMemoryProvider } from "./provider/providerMemory.core.js";
15
15
  export type { MemoryFeatureFlagProvider } from "./provider/providerMemory.core.js";
16
16
  export { createEnvironmentProvider } from "./provider/providerEnvironment.core.js";
@@ -29,4 +29,3 @@ export { createFeatureFlags } from "./featureFlags/featureFlags.core.js";
29
29
  export type { FeatureFlags, FeatureFlagsOptions, } from "./featureFlags/featureFlags.core.js";
30
30
  export { resolveDependencies, mergeContext, } from "./featureFlags/featureFlags.resolve.js";
31
31
  export { isPlainObject, valuesEqual } from "./utils/utils.helper.js";
32
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ export { FeatureFlagError } from "./featureFlagErrors/featureFlagError.base.js";
10
10
  export { FeatureFlagNotFoundError, FeatureFlagProviderError, FeatureFlagEvaluationError, FeatureFlagRuleError, FeatureFlagDependencyError, FeatureFlagConfigurationError, FeatureFlagTypeError, } from "./featureFlagErrors/featureFlagError.types.js";
11
11
  // ─── Rollout ──────────────────────────────────────────────────────────────
12
12
  export { hashString } from "./rollout/rolloutHashing.js";
13
- export { getBucket, isInRollout } from "./rollout/rolloutBucketing.js";
13
+ export { getBucket, isInRollout, resolveRolloutSubject, } from "./rollout/rolloutBucketing.js";
14
14
  // ─── Providers ────────────────────────────────────────────────────────────
15
15
  export { createMemoryProvider } from "./provider/providerMemory.core.js";
16
16
  export { createEnvironmentProvider } from "./provider/providerEnvironment.core.js";
@@ -27,4 +27,3 @@ export { createFeatureFlags } from "./featureFlags/featureFlags.core.js";
27
27
  export { resolveDependencies, mergeContext, } from "./featureFlags/featureFlags.resolve.js";
28
28
  // ─── Utils ────────────────────────────────────────────────────────────────
29
29
  export { isPlainObject, valuesEqual } from "./utils/utils.helper.js";
30
- //# sourceMappingURL=index.js.map
@@ -9,4 +9,3 @@ export type { EnvironmentProviderOptions } from "./providerEnvironment.core.js";
9
9
  export { createCompositeProvider } from "./providerComposite.core.js";
10
10
  export { createCachedProvider } from "./providerCached.core.js";
11
11
  export type { CachedProviderOptions } from "./providerCached.core.js";
12
- //# sourceMappingURL=index.d.ts.map
@@ -7,4 +7,3 @@ export { createMemoryProvider } from "./providerMemory.core.js";
7
7
  export { createEnvironmentProvider } from "./providerEnvironment.core.js";
8
8
  export { createCompositeProvider } from "./providerComposite.core.js";
9
9
  export { createCachedProvider } from "./providerCached.core.js";
10
- //# sourceMappingURL=index.js.map
@@ -25,4 +25,3 @@ export interface CachedProviderOptions {
25
25
  * @returns A provider with in-memory TTL caching.
26
26
  */
27
27
  export declare function createCachedProvider(inner: FeatureFlagProvider, options?: CachedProviderOptions): RefreshableFeatureFlagProvider;
28
- //# sourceMappingURL=providerCached.core.d.ts.map
@@ -78,4 +78,3 @@ export function createCachedProvider(inner, options = {}) {
78
78
  },
79
79
  };
80
80
  }
81
- //# sourceMappingURL=providerCached.core.js.map
@@ -16,4 +16,3 @@ import type { FeatureFlagProvider, RefreshableFeatureFlagProvider } from "../fea
16
16
  * @returns A composite FeatureFlagProvider.
17
17
  */
18
18
  export declare function createCompositeProvider(providers: readonly FeatureFlagProvider[]): RefreshableFeatureFlagProvider;
19
- //# sourceMappingURL=providerComposite.core.d.ts.map
@@ -93,4 +93,3 @@ export function createCompositeProvider(providers) {
93
93
  },
94
94
  };
95
95
  }
96
- //# sourceMappingURL=providerComposite.core.js.map
@@ -44,4 +44,3 @@ export declare function toEnvironmentFlagKey(key: string): string;
44
44
  * @returns A provider that reads from environment variables.
45
45
  */
46
46
  export declare function createEnvironmentProvider(options?: EnvironmentProviderOptions): RefreshableFeatureFlagProvider;
47
- //# sourceMappingURL=providerEnvironment.core.d.ts.map
@@ -79,4 +79,3 @@ export function createEnvironmentProvider(options = {}) {
79
79
  },
80
80
  };
81
81
  }
82
- //# sourceMappingURL=providerEnvironment.core.js.map
@@ -30,4 +30,3 @@ export interface MemoryFeatureFlagProvider extends FeatureFlagProvider {
30
30
  * @returns A FeatureFlagProvider backed by a Map.
31
31
  */
32
32
  export declare function createMemoryProvider(flags?: readonly FeatureFlag[]): MemoryFeatureFlagProvider;
33
- //# sourceMappingURL=providerMemory.core.d.ts.map
@@ -67,4 +67,3 @@ export function createMemoryProvider(flags = []) {
67
67
  },
68
68
  };
69
69
  }
70
- //# sourceMappingURL=providerMemory.core.js.map
@@ -5,4 +5,3 @@
5
5
  */
6
6
  export { createFeatureFlagRegistry } from "./registry.core.js";
7
7
  export type { FeatureFlagRegistry } from "./registry.core.js";
8
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * @module registry
5
5
  */
6
6
  export { createFeatureFlagRegistry } from "./registry.core.js";
7
- //# sourceMappingURL=index.js.map
@@ -32,4 +32,3 @@ export interface FeatureFlagRegistry {
32
32
  * @returns A FeatureFlagRegistry instance.
33
33
  */
34
34
  export declare function createFeatureFlagRegistry(flags?: readonly FeatureFlag[]): FeatureFlagRegistry;
35
- //# sourceMappingURL=registry.core.d.ts.map
@@ -44,4 +44,3 @@ export function createFeatureFlagRegistry(flags = []) {
44
44
  },
45
45
  };
46
46
  }
47
- //# sourceMappingURL=registry.core.js.map
@@ -4,5 +4,4 @@
4
4
  * @module rollout
5
5
  */
6
6
  export { hashString } from "./rolloutHashing.js";
7
- export { getBucket, isInRollout } from "./rolloutBucketing.js";
8
- //# sourceMappingURL=index.d.ts.map
7
+ export { getBucket, isInRollout, resolveRolloutSubject, } from "./rolloutBucketing.js";
@@ -4,5 +4,4 @@
4
4
  * @module rollout
5
5
  */
6
6
  export { hashString } from "./rolloutHashing.js";
7
- export { getBucket, isInRollout } from "./rolloutBucketing.js";
8
- //# sourceMappingURL=index.js.map
7
+ export { getBucket, isInRollout, resolveRolloutSubject, } from "./rolloutBucketing.js";
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * @module rollout/rolloutBucketing
8
8
  */
9
+ import type { FeatureFlagContext } from "../featureFlagTypes/featureFlagContext.js";
10
+ import type { FeatureFlagBucketBy } from "../featureFlagTypes/featureFlagRule/featureFlagRule.type.js";
9
11
  /**
10
12
  * Compute a deterministic bucket for a flag rollout.
11
13
  *
@@ -24,4 +26,16 @@ export declare function getBucket(key: string, subject: string, buckets?: number
24
26
  * @returns Whether the subject is in the rollout.
25
27
  */
26
28
  export declare function isInRollout(key: string, subject: string, percentage: number): boolean;
27
- //# sourceMappingURL=rolloutBucketing.d.ts.map
29
+ /**
30
+ * Pick the identifier a percentage or variant rule buckets on.
31
+ *
32
+ * With `bucketBy` set, only that field counts: `undefined` is returned when
33
+ * the context lacks it, and the caller treats the rule as not matched.
34
+ * Without it, the default chain applies — `userId`, then `tenantId`, then
35
+ * `sessionId`, then `"anonymous"`.
36
+ *
37
+ * @param context - The evaluation context.
38
+ * @param bucketBy - The rule's `bucketBy`, if any.
39
+ * @returns The subject to hash, or `undefined` when a pinned field is absent.
40
+ */
41
+ export declare function resolveRolloutSubject(context: FeatureFlagContext, bucketBy?: FeatureFlagBucketBy): string | undefined;
@@ -39,4 +39,22 @@ export function isInRollout(key, subject, percentage) {
39
39
  const bucket = getBucket(key, subject, buckets);
40
40
  return bucket < threshold;
41
41
  }
42
- //# sourceMappingURL=rolloutBucketing.js.map
42
+ /**
43
+ * Pick the identifier a percentage or variant rule buckets on.
44
+ *
45
+ * With `bucketBy` set, only that field counts: `undefined` is returned when
46
+ * the context lacks it, and the caller treats the rule as not matched.
47
+ * Without it, the default chain applies — `userId`, then `tenantId`, then
48
+ * `sessionId`, then `"anonymous"`.
49
+ *
50
+ * @param context - The evaluation context.
51
+ * @param bucketBy - The rule's `bucketBy`, if any.
52
+ * @returns The subject to hash, or `undefined` when a pinned field is absent.
53
+ */
54
+ export function resolveRolloutSubject(context, bucketBy) {
55
+ if (bucketBy !== undefined) {
56
+ const pinned = context[bucketBy];
57
+ return typeof pinned === "string" && pinned.length > 0 ? pinned : undefined;
58
+ }
59
+ return context.userId ?? context.tenantId ?? context.sessionId ?? "anonymous";
60
+ }
@@ -12,4 +12,3 @@
12
12
  * @returns An unsigned 32-bit integer hash.
13
13
  */
14
14
  export declare function hashString(value: string): number;
15
- //# sourceMappingURL=rolloutHashing.d.ts.map
@@ -23,4 +23,3 @@ export function hashString(value) {
23
23
  }
24
24
  return hash >>> 0;
25
25
  }
26
- //# sourceMappingURL=rolloutHashing.js.map
@@ -4,4 +4,3 @@
4
4
  * @module utils
5
5
  */
6
6
  export { isPlainObject, valuesEqual } from "./utils.helper.js";
7
- //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,3 @@
4
4
  * @module utils
5
5
  */
6
6
  export { isPlainObject, valuesEqual } from "./utils.helper.js";
7
- //# sourceMappingURL=index.js.map
@@ -24,4 +24,3 @@ export { isPlainObject } from "@zudojs/types";
24
24
  * so the walk lives here rather than pulling in a new dependency.
25
25
  */
26
26
  export declare function valuesEqual(a: FeatureFlagValue, b: FeatureFlagValue): boolean;
27
- //# sourceMappingURL=utils.helper.d.ts.map
@@ -77,4 +77,3 @@ function isComparableObject(value) {
77
77
  const proto = Object.getPrototypeOf(value);
78
78
  return proto === Object.prototype || proto === null;
79
79
  }
80
- //# sourceMappingURL=utils.helper.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zudojs/feature-flags",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "description": "Feature flag system with deterministic rollouts, rule engine, providers, variants, snapshots, and evaluation context.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -11,8 +11,8 @@
11
11
  }
12
12
  },
13
13
  "dependencies": {
14
- "@zudojs/errors": "1.3.2",
15
- "@zudojs/types": "1.2.0"
14
+ "@zudojs/errors": "1.4.0",
15
+ "@zudojs/types": "1.3.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "typescript": "7.0.2",