@nestarc/feature-flag 0.2.0 → 0.4.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 (57) hide show
  1. package/README.md +206 -81
  2. package/dist/admin/feature-flag-admin.controller.d.ts +8 -6
  3. package/dist/admin/feature-flag-admin.controller.js +22 -4
  4. package/dist/admin/feature-flag-admin.controller.js.map +1 -1
  5. package/dist/admin/feature-flag-admin.dto.d.ts +30 -0
  6. package/dist/admin/feature-flag-admin.dto.js +116 -0
  7. package/dist/admin/feature-flag-admin.dto.js.map +1 -0
  8. package/dist/admin/targeting-attributes.validator.d.ts +6 -0
  9. package/dist/admin/targeting-attributes.validator.js +36 -0
  10. package/dist/admin/targeting-attributes.validator.js.map +1 -0
  11. package/dist/events/feature-flag.events.d.ts +38 -6
  12. package/dist/events/feature-flag.events.js +1 -0
  13. package/dist/events/feature-flag.events.js.map +1 -1
  14. package/dist/flag-registry.d.ts +24 -0
  15. package/dist/flag-registry.js +62 -0
  16. package/dist/flag-registry.js.map +1 -0
  17. package/dist/guards/feature-flag.guard.js +5 -1
  18. package/dist/guards/feature-flag.guard.js.map +1 -1
  19. package/dist/index.d.ts +6 -3
  20. package/dist/index.js +7 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/interfaces/evaluation-context.interface.d.ts +8 -3
  23. package/dist/interfaces/evaluation-details.interface.d.ts +29 -0
  24. package/dist/interfaces/evaluation-details.interface.js +3 -0
  25. package/dist/interfaces/evaluation-details.interface.js.map +1 -0
  26. package/dist/interfaces/feature-flag-options.interface.d.ts +3 -0
  27. package/dist/interfaces/feature-flag-repository.interface.d.ts +8 -6
  28. package/dist/interfaces/feature-flag.interface.d.ts +18 -9
  29. package/dist/interfaces/flag-registry.interface.d.ts +17 -0
  30. package/dist/interfaces/flag-registry.interface.js +3 -0
  31. package/dist/interfaces/flag-registry.interface.js.map +1 -0
  32. package/dist/openfeature.d.ts +26 -0
  33. package/dist/openfeature.js +96 -0
  34. package/dist/openfeature.js.map +1 -0
  35. package/dist/repositories/prisma-feature-flag.repository.d.ts +3 -3
  36. package/dist/repositories/prisma-feature-flag.repository.js +18 -14
  37. package/dist/repositories/prisma-feature-flag.repository.js.map +1 -1
  38. package/dist/services/feature-flag.service.d.ts +15 -7
  39. package/dist/services/feature-flag.service.js +126 -38
  40. package/dist/services/feature-flag.service.js.map +1 -1
  41. package/dist/services/flag-context-resolver.js +14 -3
  42. package/dist/services/flag-context-resolver.js.map +1 -1
  43. package/dist/services/flag-evaluator.service.d.ts +8 -8
  44. package/dist/services/flag-evaluator.service.js +90 -36
  45. package/dist/services/flag-evaluator.service.js.map +1 -1
  46. package/dist/testing/index.d.ts +1 -1
  47. package/dist/testing/index.js +2 -1
  48. package/dist/testing/index.js.map +1 -1
  49. package/dist/testing/test-feature-flag.module.d.ts +21 -0
  50. package/dist/testing/test-feature-flag.module.js +78 -19
  51. package/dist/testing/test-feature-flag.module.js.map +1 -1
  52. package/dist/utils/targeting-attributes.d.ts +9 -0
  53. package/dist/utils/targeting-attributes.js +44 -0
  54. package/dist/utils/targeting-attributes.js.map +1 -0
  55. package/package.json +18 -2
  56. package/prisma/migrations/20260512000000_attribute_targeting/migration.sql +59 -0
  57. package/prisma/schema.prisma +7 -9
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.EvaluateFeatureFlagDto = exports.RemoveOverrideDto = exports.SetOverrideDto = exports.UpdateFeatureFlagDto = exports.CreateFeatureFlagDto = void 0;
13
+ const class_validator_1 = require("class-validator");
14
+ const targeting_attributes_validator_1 = require("./targeting-attributes.validator");
15
+ class CreateFeatureFlagDto {
16
+ }
17
+ exports.CreateFeatureFlagDto = CreateFeatureFlagDto;
18
+ __decorate([
19
+ (0, class_validator_1.IsString)(),
20
+ (0, class_validator_1.IsNotEmpty)(),
21
+ __metadata("design:type", String)
22
+ ], CreateFeatureFlagDto.prototype, "key", void 0);
23
+ __decorate([
24
+ (0, class_validator_1.IsOptional)(),
25
+ (0, class_validator_1.IsString)(),
26
+ __metadata("design:type", String)
27
+ ], CreateFeatureFlagDto.prototype, "description", void 0);
28
+ __decorate([
29
+ (0, class_validator_1.IsOptional)(),
30
+ (0, class_validator_1.IsBoolean)(),
31
+ __metadata("design:type", Boolean)
32
+ ], CreateFeatureFlagDto.prototype, "enabled", void 0);
33
+ __decorate([
34
+ (0, class_validator_1.IsOptional)(),
35
+ (0, class_validator_1.IsInt)(),
36
+ (0, class_validator_1.Min)(0),
37
+ (0, class_validator_1.Max)(100),
38
+ __metadata("design:type", Number)
39
+ ], CreateFeatureFlagDto.prototype, "percentage", void 0);
40
+ __decorate([
41
+ (0, class_validator_1.IsOptional)(),
42
+ (0, class_validator_1.IsObject)(),
43
+ __metadata("design:type", Object)
44
+ ], CreateFeatureFlagDto.prototype, "metadata", void 0);
45
+ class UpdateFeatureFlagDto {
46
+ }
47
+ exports.UpdateFeatureFlagDto = UpdateFeatureFlagDto;
48
+ __decorate([
49
+ (0, class_validator_1.IsOptional)(),
50
+ (0, class_validator_1.IsString)(),
51
+ __metadata("design:type", String)
52
+ ], UpdateFeatureFlagDto.prototype, "description", void 0);
53
+ __decorate([
54
+ (0, class_validator_1.IsOptional)(),
55
+ (0, class_validator_1.IsBoolean)(),
56
+ __metadata("design:type", Boolean)
57
+ ], UpdateFeatureFlagDto.prototype, "enabled", void 0);
58
+ __decorate([
59
+ (0, class_validator_1.IsOptional)(),
60
+ (0, class_validator_1.IsInt)(),
61
+ (0, class_validator_1.Min)(0),
62
+ (0, class_validator_1.Max)(100),
63
+ __metadata("design:type", Number)
64
+ ], UpdateFeatureFlagDto.prototype, "percentage", void 0);
65
+ __decorate([
66
+ (0, class_validator_1.IsOptional)(),
67
+ (0, class_validator_1.IsObject)(),
68
+ __metadata("design:type", Object)
69
+ ], UpdateFeatureFlagDto.prototype, "metadata", void 0);
70
+ class SetOverrideDto {
71
+ }
72
+ exports.SetOverrideDto = SetOverrideDto;
73
+ __decorate([
74
+ (0, targeting_attributes_validator_1.IsTargetingAttributes)(),
75
+ __metadata("design:type", Object)
76
+ ], SetOverrideDto.prototype, "attributes", void 0);
77
+ __decorate([
78
+ (0, class_validator_1.IsBoolean)(),
79
+ __metadata("design:type", Boolean)
80
+ ], SetOverrideDto.prototype, "enabled", void 0);
81
+ __decorate([
82
+ (0, class_validator_1.IsOptional)(),
83
+ (0, class_validator_1.IsInt)(),
84
+ __metadata("design:type", Number)
85
+ ], SetOverrideDto.prototype, "priority", void 0);
86
+ class RemoveOverrideDto {
87
+ }
88
+ exports.RemoveOverrideDto = RemoveOverrideDto;
89
+ __decorate([
90
+ (0, targeting_attributes_validator_1.IsTargetingAttributes)(),
91
+ __metadata("design:type", Object)
92
+ ], RemoveOverrideDto.prototype, "attributes", void 0);
93
+ class EvaluateFeatureFlagDto {
94
+ }
95
+ exports.EvaluateFeatureFlagDto = EvaluateFeatureFlagDto;
96
+ __decorate([
97
+ (0, class_validator_1.IsOptional)(),
98
+ (0, class_validator_1.IsObject)(),
99
+ __metadata("design:type", Object)
100
+ ], EvaluateFeatureFlagDto.prototype, "context", void 0);
101
+ __decorate([
102
+ (0, class_validator_1.IsOptional)(),
103
+ (0, class_validator_1.IsBoolean)(),
104
+ __metadata("design:type", Boolean)
105
+ ], EvaluateFeatureFlagDto.prototype, "defaultValue", void 0);
106
+ __decorate([
107
+ (0, class_validator_1.IsOptional)(),
108
+ (0, class_validator_1.IsBoolean)(),
109
+ __metadata("design:type", Boolean)
110
+ ], EvaluateFeatureFlagDto.prototype, "trackExposure", void 0);
111
+ __decorate([
112
+ (0, class_validator_1.IsOptional)(),
113
+ (0, class_validator_1.IsBoolean)(),
114
+ __metadata("design:type", Boolean)
115
+ ], EvaluateFeatureFlagDto.prototype, "includeContextInEvent", void 0);
116
+ //# sourceMappingURL=feature-flag-admin.dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-flag-admin.dto.js","sourceRoot":"","sources":["../../src/admin/feature-flag-admin.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDASyB;AAIzB,qFAAyE;AAEzE,MAAa,oBAAoB;CAsBhC;AAtBD,oDAsBC;AAnBC;IAFC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;iDACA;AAIb;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;yDACU;AAIrB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;qDACM;AAMlB;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;IACN,IAAA,qBAAG,EAAC,GAAG,CAAC;;wDACW;AAIpB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACwB;AAGrC,MAAa,oBAAoB;CAkBhC;AAlBD,oDAkBC;AAfC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;yDACU;AAIrB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;qDACM;AAMlB;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;IACP,IAAA,qBAAG,EAAC,CAAC,CAAC;IACN,IAAA,qBAAG,EAAC,GAAG,CAAC;;wDACW;AAIpB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACwB;AAGrC,MAAa,cAAc;CAU1B;AAVD,wCAUC;AARC;IADC,IAAA,sDAAqB,GAAE;;kDACS;AAGjC;IADC,IAAA,2BAAS,GAAE;;+CACM;AAIlB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,uBAAK,GAAE;;gDACU;AAGpB,MAAa,iBAAiB;CAG7B;AAHD,8CAGC;AADC;IADC,IAAA,sDAAqB,GAAE;;qDACS;AAGnC,MAAa,sBAAsB;CAgBlC;AAhBD,wDAgBC;AAbC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;uDACiB;AAI5B;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;4DACW;AAIvB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;6DACY;AAIxB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;qEACoB"}
@@ -0,0 +1,6 @@
1
+ import { ValidationArguments, ValidationOptions, ValidatorConstraintInterface } from 'class-validator';
2
+ export declare class IsTargetingAttributesConstraint implements ValidatorConstraintInterface {
3
+ validate(value: unknown): boolean;
4
+ defaultMessage(_args: ValidationArguments): string;
5
+ }
6
+ export declare function IsTargetingAttributes(validationOptions?: ValidationOptions): (target: object, propertyName: string) => void;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.IsTargetingAttributesConstraint = void 0;
10
+ exports.IsTargetingAttributes = IsTargetingAttributes;
11
+ const class_validator_1 = require("class-validator");
12
+ const targeting_attributes_1 = require("../utils/targeting-attributes");
13
+ let IsTargetingAttributesConstraint = class IsTargetingAttributesConstraint {
14
+ validate(value) {
15
+ return (0, targeting_attributes_1.isTargetingAttributes)(value, { allowEmpty: false });
16
+ }
17
+ defaultMessage(_args) {
18
+ return 'attributes must be a non-empty object with string, number, boolean, or null values';
19
+ }
20
+ };
21
+ exports.IsTargetingAttributesConstraint = IsTargetingAttributesConstraint;
22
+ exports.IsTargetingAttributesConstraint = IsTargetingAttributesConstraint = __decorate([
23
+ (0, class_validator_1.ValidatorConstraint)({ name: 'isTargetingAttributes', async: false })
24
+ ], IsTargetingAttributesConstraint);
25
+ function IsTargetingAttributes(validationOptions) {
26
+ return function registerTargetingAttributesDecorator(target, propertyName) {
27
+ (0, class_validator_1.registerDecorator)({
28
+ target: target.constructor,
29
+ propertyName,
30
+ options: validationOptions,
31
+ constraints: [],
32
+ validator: IsTargetingAttributesConstraint,
33
+ });
34
+ };
35
+ }
36
+ //# sourceMappingURL=targeting-attributes.validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targeting-attributes.validator.js","sourceRoot":"","sources":["../../src/admin/targeting-attributes.validator.ts"],"names":[],"mappings":";;;;;;;;;AAoBA,sDAUC;AA9BD,qDAMyB;AACzB,wEAAsE;AAG/D,IAAM,+BAA+B,GAArC,MAAM,+BAA+B;IAC1C,QAAQ,CAAC,KAAc;QACrB,OAAO,IAAA,4CAAqB,EAAC,KAAK,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,cAAc,CAAC,KAA0B;QACvC,OAAO,oFAAoF,CAAC;IAC9F,CAAC;CACF,CAAA;AARY,0EAA+B;0CAA/B,+BAA+B;IAD3C,IAAA,qCAAmB,EAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;GACxD,+BAA+B,CAQ3C;AAED,SAAgB,qBAAqB,CAAC,iBAAqC;IACzE,OAAO,SAAS,oCAAoC,CAAC,MAAc,EAAE,YAAoB;QACvF,IAAA,mCAAiB,EAAC;YAChB,MAAM,EAAE,MAAM,CAAC,WAAW;YAC1B,YAAY;YACZ,OAAO,EAAE,iBAAiB;YAC1B,WAAW,EAAE,EAAE;YACf,SAAS,EAAE,+BAA+B;SAC3C,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { EvaluationContext } from '../interfaces/evaluation-context.interface';
2
+ import { EvaluationReason, EvaluationSource } from '../interfaces/evaluation-details.interface';
2
3
  export declare const FeatureFlagEvents: {
3
4
  readonly EVALUATED: "feature-flag.evaluated";
5
+ readonly EXPOSED: "feature-flag.exposed";
4
6
  readonly CREATED: "feature-flag.created";
5
7
  readonly UPDATED: "feature-flag.updated";
6
8
  readonly ARCHIVED: "feature-flag.archived";
@@ -11,19 +13,49 @@ export declare const FeatureFlagEvents: {
11
13
  export interface FlagEvaluatedEvent {
12
14
  flagKey: string;
13
15
  result: boolean;
14
- context: EvaluationContext;
15
- source: 'user_override' | 'tenant_override' | 'env_override' | 'percentage' | 'global';
16
+ value?: boolean;
17
+ context?: EvaluationContext;
18
+ source: EvaluationSource;
19
+ reason?: EvaluationReason;
20
+ defaultUsed?: boolean;
21
+ errorCode?: string;
22
+ errorMessage?: string;
23
+ matchedOverrideId?: string;
24
+ bucket?: number;
25
+ targetingKey?: string;
16
26
  evaluationTimeMs: number;
17
27
  }
28
+ export interface FlagExposedEvent {
29
+ flagKey: string;
30
+ value: boolean;
31
+ result: boolean;
32
+ source: EvaluationSource;
33
+ reason: EvaluationReason;
34
+ defaultUsed: boolean;
35
+ context?: EvaluationContext;
36
+ matchedOverrideId?: string;
37
+ bucket?: number;
38
+ targetingKey?: string;
39
+ evaluationTimeMs?: number;
40
+ }
18
41
  export interface FlagMutationEvent {
19
42
  flagKey: string;
20
43
  action: 'created' | 'updated' | 'archived';
44
+ actorId?: string;
45
+ actorType?: string;
46
+ reason?: string;
47
+ requestId?: string;
48
+ correlationId?: string;
21
49
  }
22
50
  export interface FlagOverrideEvent {
23
51
  flagKey: string;
24
- tenantId?: string | null;
25
- userId?: string | null;
26
- environment?: string | null;
27
- enabled: boolean;
52
+ attributes: Record<string, string | number | boolean | null>;
53
+ enabled?: boolean;
54
+ priority?: number;
28
55
  action: 'set' | 'removed';
56
+ actorId?: string;
57
+ actorType?: string;
58
+ reason?: string;
59
+ requestId?: string;
60
+ correlationId?: string;
29
61
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FeatureFlagEvents = void 0;
4
4
  exports.FeatureFlagEvents = {
5
5
  EVALUATED: 'feature-flag.evaluated',
6
+ EXPOSED: 'feature-flag.exposed',
6
7
  CREATED: 'feature-flag.created',
7
8
  UPDATED: 'feature-flag.updated',
8
9
  ARCHIVED: 'feature-flag.archived',
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flag.events.js","sourceRoot":"","sources":["../../src/events/feature-flag.events.ts"],"names":[],"mappings":";;;AAEa,QAAA,iBAAiB,GAAG;IAC/B,SAAS,EAAE,wBAAwB;IACnC,OAAO,EAAE,sBAAsB;IAC/B,OAAO,EAAE,sBAAsB;IAC/B,QAAQ,EAAE,uBAAuB;IACjC,YAAY,EAAE,2BAA2B;IACzC,gBAAgB,EAAE,+BAA+B;IACjD,iBAAiB,EAAE,gCAAgC;CAC3C,CAAC"}
1
+ {"version":3,"file":"feature-flag.events.js","sourceRoot":"","sources":["../../src/events/feature-flag.events.ts"],"names":[],"mappings":";;;AAMa,QAAA,iBAAiB,GAAG;IAC/B,SAAS,EAAE,wBAAwB;IACnC,OAAO,EAAE,sBAAsB;IAC/B,OAAO,EAAE,sBAAsB;IAC/B,OAAO,EAAE,sBAAsB;IAC/B,QAAQ,EAAE,uBAAuB;IACjC,YAAY,EAAE,2BAA2B;IACzC,gBAAgB,EAAE,+BAA+B;IACjD,iBAAiB,EAAE,gCAAgC;CAC3C,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { EvaluationContext } from './interfaces/evaluation-context.interface';
2
+ import { BooleanEvaluationDetails, EvaluateBooleanOptions } from './interfaces/evaluation-details.interface';
3
+ import { FeatureFlagGuardOptions } from './interfaces/feature-flag.interface';
4
+ import { FeatureFlagLifecycleMetadata, FlagKey, FlagRegistry } from './interfaces/flag-registry.interface';
5
+ import { FeatureFlagService } from './services/feature-flag.service';
6
+ export interface TypedFeatureFlagClient<TFlags extends FlagRegistry> {
7
+ isEnabled<K extends FlagKey<TFlags>>(flagKey: K, context?: EvaluationContext, options?: EvaluateBooleanOptions): Promise<boolean>;
8
+ evaluateBoolean<K extends FlagKey<TFlags>>(flagKey: K, context?: EvaluationContext, options?: EvaluateBooleanOptions): Promise<BooleanEvaluationDetails>;
9
+ registry: TFlags;
10
+ }
11
+ export interface TypedFeatureFlagDecorators<TFlags extends FlagRegistry> {
12
+ FeatureFlag<K extends FlagKey<TFlags>>(flagKey: K, options?: FeatureFlagGuardOptions): ClassDecorator & MethodDecorator;
13
+ }
14
+ export type FlagLifecycleStatusName = 'active' | 'stale' | 'expired';
15
+ export interface FlagLifecycleStatus extends FeatureFlagLifecycleMetadata {
16
+ status: FlagLifecycleStatusName;
17
+ tags: string[];
18
+ staleAt?: Date;
19
+ expiresAt?: Date;
20
+ }
21
+ export declare function defineFlags<const TFlags extends FlagRegistry>(flags: TFlags): TFlags;
22
+ export declare function createFeatureFlagClient<TFlags extends FlagRegistry>(service: FeatureFlagService, registry: TFlags): TypedFeatureFlagClient<TFlags>;
23
+ export declare function createFeatureFlagDecorators<TFlags extends FlagRegistry>(registry: TFlags): TypedFeatureFlagDecorators<TFlags>;
24
+ export declare function getFlagLifecycleStatus(metadata: FeatureFlagLifecycleMetadata, now?: Date): FlagLifecycleStatus;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineFlags = defineFlags;
4
+ exports.createFeatureFlagClient = createFeatureFlagClient;
5
+ exports.createFeatureFlagDecorators = createFeatureFlagDecorators;
6
+ exports.getFlagLifecycleStatus = getFlagLifecycleStatus;
7
+ const feature_flag_decorator_1 = require("./decorators/feature-flag.decorator");
8
+ function defineFlags(flags) {
9
+ return flags;
10
+ }
11
+ function createFeatureFlagClient(service, registry) {
12
+ return {
13
+ registry,
14
+ isEnabled: (flagKey, context, options) => service.isEnabled(flagKey, context, mergeRegistryOptions(registry[flagKey], options)),
15
+ evaluateBoolean: (flagKey, context, options) => service.evaluateBoolean(flagKey, context, mergeRegistryOptions(registry[flagKey], options)),
16
+ };
17
+ }
18
+ function createFeatureFlagDecorators(registry) {
19
+ return {
20
+ FeatureFlag: (flagKey, options = {}) => (0, feature_flag_decorator_1.FeatureFlag)(flagKey, {
21
+ defaultValue: registry[flagKey].defaultValue,
22
+ ...options,
23
+ }),
24
+ };
25
+ }
26
+ function getFlagLifecycleStatus(metadata, now = new Date()) {
27
+ const staleAt = parseLifecycleDate(metadata.staleAt);
28
+ const expiresAt = parseLifecycleDate(metadata.expiresAt);
29
+ const status = getLifecycleStatusName(now, staleAt, expiresAt);
30
+ return {
31
+ ...metadata,
32
+ tags: [...(metadata.tags ?? [])],
33
+ staleAt,
34
+ expiresAt,
35
+ status,
36
+ };
37
+ }
38
+ function mergeRegistryOptions(definition, options = {}) {
39
+ const merged = {
40
+ defaultValue: definition.defaultValue,
41
+ };
42
+ if (definition.trackExposure !== undefined) {
43
+ merged.trackExposure = definition.trackExposure;
44
+ }
45
+ return { ...merged, ...options };
46
+ }
47
+ function getLifecycleStatusName(now, staleAt, expiresAt) {
48
+ if (expiresAt && now >= expiresAt) {
49
+ return 'expired';
50
+ }
51
+ if (staleAt && now >= staleAt) {
52
+ return 'stale';
53
+ }
54
+ return 'active';
55
+ }
56
+ function parseLifecycleDate(value) {
57
+ if (!value) {
58
+ return undefined;
59
+ }
60
+ return value instanceof Date ? value : new Date(value);
61
+ }
62
+ //# sourceMappingURL=flag-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flag-registry.js","sourceRoot":"","sources":["../src/flag-registry.ts"],"names":[],"mappings":";;AA8CA,kCAEC;AAED,0DAeC;AAED,kEAUC;AAED,wDAeC;AA9FD,gFAAkE;AA8ClE,SAAgB,WAAW,CAAoC,KAAa;IAC1E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,uBAAuB,CACrC,OAA2B,EAC3B,QAAgB;IAEhB,OAAO;QACL,QAAQ;QACR,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CACvC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QACvF,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAC7C,OAAO,CAAC,eAAe,CACrB,OAAO,EACP,OAAO,EACP,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CACjD;KACJ,CAAC;AACJ,CAAC;AAED,SAAgB,2BAA2B,CACzC,QAAgB;IAEhB,OAAO;QACL,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CACrC,IAAA,oCAAW,EAAC,OAAO,EAAE;YACnB,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,YAAY;YAC5C,GAAG,OAAO;SACX,CAAC;KACL,CAAC;AACJ,CAAC;AAED,SAAgB,sBAAsB,CACpC,QAAsC,EACtC,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,kBAAkB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAE/D,OAAO;QACL,GAAG,QAAQ;QACX,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAChC,OAAO;QACP,SAAS;QACT,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,UAAgC,EAChC,UAAkC,EAAE;IAEpC,MAAM,MAAM,GAA2B;QACrC,YAAY,EAAE,UAAU,CAAC,YAAY;KACtC,CAAC;IAEF,IAAI,UAAU,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QAC3C,MAAM,CAAC,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC;IAClD,CAAC;IAED,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,sBAAsB,CAC7B,GAAS,EACT,OAAc,EACd,SAAgB;IAEhB,IAAI,SAAS,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAgC;IAC1D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC"}
@@ -35,7 +35,11 @@ let FeatureFlagGuard = class FeatureFlagGuard {
35
35
  const options = this.reflector.get(feature_flag_constants_1.FEATURE_FLAG_OPTIONS_KEY, handler) ??
36
36
  this.reflector.get(feature_flag_constants_1.FEATURE_FLAG_OPTIONS_KEY, classRef) ??
37
37
  {};
38
- const enabled = await this.featureFlagService.isEnabled(flagKey);
38
+ const enabled = options.defaultValue === undefined
39
+ ? await this.featureFlagService.isEnabled(flagKey)
40
+ : await this.featureFlagService.isEnabled(flagKey, undefined, {
41
+ defaultValue: options.defaultValue,
42
+ });
39
43
  if (!enabled) {
40
44
  const statusCode = options.statusCode ?? 403;
41
45
  const body = options.fallback ?? { message: 'Feature not available' };
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flag.guard.js","sourceRoot":"","sources":["../../src/guards/feature-flag.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA0F;AAC1F,uCAAyC;AACzC,2EAAsE;AACtE,sEAImC;AAI5B,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B,YACmB,SAAoB,EACpB,kBAAsC;QADtC,cAAS,GAAT,SAAS,CAAW;QACpB,uBAAkB,GAAlB,kBAAkB,CAAoB;IACtD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAEpC,eAAe;QACf,MAAM,MAAM,GACV,IAAI,CAAC,SAAS,CAAC,GAAG,CAAU,gDAAuB,EAAE,OAAO,CAAC;YAC7D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAU,gDAAuB,EAAE,QAAQ,CAAC,CAAC;QACjE,IAAI,MAAM;YAAE,OAAO,IAAI,CAAC;QAExB,yDAAyD;QACzD,MAAM,OAAO,GACX,IAAI,CAAC,SAAS,CAAC,GAAG,CAAS,yCAAgB,EAAE,OAAO,CAAC;YACrD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAS,yCAAgB,EAAE,QAAQ,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAE1B,MAAM,OAAO,GACX,IAAI,CAAC,SAAS,CAAC,GAAG,CAA0B,iDAAwB,EAAE,OAAO,CAAC;YAC9E,IAAI,CAAC,SAAS,CAAC,GAAG,CAA0B,iDAAwB,EAAE,QAAQ,CAAC;YAC/E,EAAE,CAAC;QAEL,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAEjE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC;YAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;YACtE,MAAM,IAAI,sBAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AArCY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAGmB,gBAAS;QACA,yCAAkB;GAH9C,gBAAgB,CAqC5B"}
1
+ {"version":3,"file":"feature-flag.guard.js","sourceRoot":"","sources":["../../src/guards/feature-flag.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA0F;AAC1F,uCAAyC;AACzC,2EAAsE;AACtE,sEAImC;AAI5B,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B,YACmB,SAAoB,EACpB,kBAAsC;QADtC,cAAS,GAAT,SAAS,CAAW;QACpB,uBAAkB,GAAlB,kBAAkB,CAAoB;IACtD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAEpC,eAAe;QACf,MAAM,MAAM,GACV,IAAI,CAAC,SAAS,CAAC,GAAG,CAAU,gDAAuB,EAAE,OAAO,CAAC;YAC7D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAU,gDAAuB,EAAE,QAAQ,CAAC,CAAC;QACjE,IAAI,MAAM;YAAE,OAAO,IAAI,CAAC;QAExB,yDAAyD;QACzD,MAAM,OAAO,GACX,IAAI,CAAC,SAAS,CAAC,GAAG,CAAS,yCAAgB,EAAE,OAAO,CAAC;YACrD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAS,yCAAgB,EAAE,QAAQ,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAE1B,MAAM,OAAO,GACX,IAAI,CAAC,SAAS,CAAC,GAAG,CAA0B,iDAAwB,EAAE,OAAO,CAAC;YAC9E,IAAI,CAAC,SAAS,CAAC,GAAG,CAA0B,iDAAwB,EAAE,QAAQ,CAAC;YAC/E,EAAE,CAAC;QAEL,MAAM,OAAO,GACX,OAAO,CAAC,YAAY,KAAK,SAAS;YAChC,CAAC,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC;YAClD,CAAC,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE;gBAC1D,YAAY,EAAE,OAAO,CAAC,YAAY;aACnC,CAAC,CAAC;QAET,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC;YAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;YACtE,MAAM,IAAI,sBAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC5C,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA1CY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAGmB,gBAAS;QACA,yCAAkB;GAH9C,gBAAgB,CA0C5B"}
package/dist/index.d.ts CHANGED
@@ -6,13 +6,16 @@ export { FeatureFlag } from './decorators/feature-flag.decorator';
6
6
  export { BypassFeatureFlag } from './decorators/bypass-feature-flag.decorator';
7
7
  export { FeatureFlagModuleOptions, FeatureFlagModuleAsyncOptions, FeatureFlagModuleOptionsFactory, } from './interfaces/feature-flag-options.interface';
8
8
  export { EvaluationContext } from './interfaces/evaluation-context.interface';
9
- export { CreateFeatureFlagInput, UpdateFeatureFlagInput, SetOverrideInput, RemoveOverrideInput, FeatureFlagGuardOptions, FeatureFlagWithOverrides, FlagOverride, } from './interfaces/feature-flag.interface';
10
- export { FeatureFlagEvents, FlagEvaluatedEvent, FlagMutationEvent, FlagOverrideEvent, } from './events/feature-flag.events';
9
+ export { EvaluationSource, EvaluationReason, EvaluateBooleanOptions, BooleanEvaluationDetails, BucketBy, FlagEvaluatorOptions, } from './interfaces/evaluation-details.interface';
10
+ export { FeatureFlagLifecycleMetadata, FeatureFlagType, FlagDefinition, FlagRegistry, FlagKey, } from './interfaces/flag-registry.interface';
11
+ export { CreateFeatureFlagInput, UpdateFeatureFlagInput, SetOverrideInput, RemoveOverrideInput, TargetingAttributeValue, TargetingAttributes, FeatureFlagGuardOptions, FeatureFlagWithOverrides, FlagOverride, FlagMutationMetadata, } from './interfaces/feature-flag.interface';
12
+ export { defineFlags, createFeatureFlagClient, createFeatureFlagDecorators, getFlagLifecycleStatus, TypedFeatureFlagClient, TypedFeatureFlagDecorators, FlagLifecycleStatus, FlagLifecycleStatusName, } from './flag-registry';
13
+ export { FeatureFlagEvents, FlagEvaluatedEvent, FlagExposedEvent, FlagMutationEvent, FlagOverrideEvent, } from './events/feature-flag.events';
11
14
  export { FEATURE_FLAG_MODULE_OPTIONS, CACHE_ADAPTER, FEATURE_FLAG_REPOSITORY, TENANT_CONTEXT_PROVIDER, } from './feature-flag.constants';
12
15
  export type { CacheAdapter } from './interfaces/cache-adapter.interface';
13
16
  export { MemoryCacheAdapter } from './cache/memory-cache.adapter';
14
17
  export { RedisCacheAdapter, type RedisCacheAdapterOptions } from './cache/redis-cache.adapter';
15
- export type { FeatureFlagRepository, OverrideCriteria } from './interfaces/feature-flag-repository.interface';
18
+ export type { FeatureFlagRepository, OverrideCriteria, UpdateOverrideInput, } from './interfaces/feature-flag-repository.interface';
16
19
  export { PrismaFeatureFlagRepository } from './repositories/prisma-feature-flag.repository';
17
20
  export type { TenantContextProvider } from './interfaces/tenant-context-provider.interface';
18
21
  export { FeatureFlagAdminModule } from './admin/feature-flag-admin.module';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FeatureFlagAdminModule = exports.PrismaFeatureFlagRepository = exports.RedisCacheAdapter = exports.MemoryCacheAdapter = exports.TENANT_CONTEXT_PROVIDER = exports.FEATURE_FLAG_REPOSITORY = exports.CACHE_ADAPTER = exports.FEATURE_FLAG_MODULE_OPTIONS = exports.FeatureFlagEvents = exports.BypassFeatureFlag = exports.FeatureFlag = exports.FeatureFlagGuard = exports.FlagContext = exports.FeatureFlagService = exports.FeatureFlagModule = void 0;
3
+ exports.FeatureFlagAdminModule = exports.PrismaFeatureFlagRepository = exports.RedisCacheAdapter = exports.MemoryCacheAdapter = exports.TENANT_CONTEXT_PROVIDER = exports.FEATURE_FLAG_REPOSITORY = exports.CACHE_ADAPTER = exports.FEATURE_FLAG_MODULE_OPTIONS = exports.FeatureFlagEvents = exports.getFlagLifecycleStatus = exports.createFeatureFlagDecorators = exports.createFeatureFlagClient = exports.defineFlags = exports.BypassFeatureFlag = exports.FeatureFlag = exports.FeatureFlagGuard = exports.FlagContext = exports.FeatureFlagService = exports.FeatureFlagModule = void 0;
4
4
  // Module
5
5
  var feature_flag_module_1 = require("./feature-flag.module");
6
6
  Object.defineProperty(exports, "FeatureFlagModule", { enumerable: true, get: function () { return feature_flag_module_1.FeatureFlagModule; } });
@@ -17,6 +17,12 @@ var feature_flag_decorator_1 = require("./decorators/feature-flag.decorator");
17
17
  Object.defineProperty(exports, "FeatureFlag", { enumerable: true, get: function () { return feature_flag_decorator_1.FeatureFlag; } });
18
18
  var bypass_feature_flag_decorator_1 = require("./decorators/bypass-feature-flag.decorator");
19
19
  Object.defineProperty(exports, "BypassFeatureFlag", { enumerable: true, get: function () { return bypass_feature_flag_decorator_1.BypassFeatureFlag; } });
20
+ // Registry helpers
21
+ var flag_registry_1 = require("./flag-registry");
22
+ Object.defineProperty(exports, "defineFlags", { enumerable: true, get: function () { return flag_registry_1.defineFlags; } });
23
+ Object.defineProperty(exports, "createFeatureFlagClient", { enumerable: true, get: function () { return flag_registry_1.createFeatureFlagClient; } });
24
+ Object.defineProperty(exports, "createFeatureFlagDecorators", { enumerable: true, get: function () { return flag_registry_1.createFeatureFlagDecorators; } });
25
+ Object.defineProperty(exports, "getFlagLifecycleStatus", { enumerable: true, get: function () { return flag_registry_1.getFlagLifecycleStatus; } });
20
26
  // Events
21
27
  var feature_flag_events_1 = require("./events/feature-flag.events");
22
28
  Object.defineProperty(exports, "FeatureFlagEvents", { enumerable: true, get: function () { return feature_flag_events_1.FeatureFlagEvents; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,6DAA2H;AAAlH,wHAAA,iBAAiB,OAAA;AAE1B,WAAW;AACX,wEAAqE;AAA5D,0HAAA,kBAAkB,OAAA;AAC3B,wDAAsD;AAA7C,2GAAA,WAAW,OAAA;AAEpB,QAAQ;AACR,kEAA+D;AAAtD,sHAAA,gBAAgB,OAAA;AAEzB,aAAa;AACb,8EAAkE;AAAzD,qHAAA,WAAW,OAAA;AACpB,4FAA+E;AAAtE,kIAAA,iBAAiB,OAAA;AAmB1B,SAAS;AACT,oEAKsC;AAJpC,wHAAA,iBAAiB,OAAA;AAMnB,YAAY;AACZ,mEAKkC;AAJhC,qIAAA,2BAA2B,OAAA;AAC3B,uHAAA,aAAa,OAAA;AACb,iIAAA,uBAAuB,OAAA;AACvB,iIAAA,uBAAuB,OAAA;AAKzB,qEAAkE;AAAzD,0HAAA,kBAAkB,OAAA;AAC3B,mEAA+F;AAAtF,wHAAA,iBAAiB,OAAA;AAI1B,gGAA4F;AAAnF,6IAAA,2BAA2B,OAAA;AAKpC,eAAe;AACf,+EAA2E;AAAlE,mIAAA,sBAAsB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,6DAA2H;AAAlH,wHAAA,iBAAiB,OAAA;AAE1B,WAAW;AACX,wEAAqE;AAA5D,0HAAA,kBAAkB,OAAA;AAC3B,wDAAsD;AAA7C,2GAAA,WAAW,OAAA;AAEpB,QAAQ;AACR,kEAA+D;AAAtD,sHAAA,gBAAgB,OAAA;AAEzB,aAAa;AACb,8EAAkE;AAAzD,qHAAA,WAAW,OAAA;AACpB,4FAA+E;AAAtE,kIAAA,iBAAiB,OAAA;AAqC1B,mBAAmB;AACnB,iDASyB;AARvB,4GAAA,WAAW,OAAA;AACX,wHAAA,uBAAuB,OAAA;AACvB,4HAAA,2BAA2B,OAAA;AAC3B,uHAAA,sBAAsB,OAAA;AAOxB,SAAS;AACT,oEAMsC;AALpC,wHAAA,iBAAiB,OAAA;AAOnB,YAAY;AACZ,mEAKkC;AAJhC,qIAAA,2BAA2B,OAAA;AAC3B,uHAAA,aAAa,OAAA;AACb,iIAAA,uBAAuB,OAAA;AACvB,iIAAA,uBAAuB,OAAA;AAKzB,qEAAkE;AAAzD,0HAAA,kBAAkB,OAAA;AAC3B,mEAA+F;AAAtF,wHAAA,iBAAiB,OAAA;AAQ1B,gGAA4F;AAAnF,6IAAA,2BAA2B,OAAA;AAKpC,eAAe;AACf,+EAA2E;AAAlE,mIAAA,sBAAsB,OAAA"}
@@ -1,8 +1,13 @@
1
+ import { TargetingAttributes } from './feature-flag.interface';
1
2
  export interface EvaluationContext {
2
- /** User ID used for user overrides and percentage hash */
3
+ /** User ID - used for user-scoped targeting and percentage hash */
3
4
  userId?: string | null;
4
- /** Tenant ID used for tenant overrides. Ignored if tenancy is not installed */
5
+ /** Tenant ID - used for tenant-scoped targeting. Ignored if tenancy is not installed */
5
6
  tenantId?: string | null;
6
- /** Environment auto-injected from module options. Can be explicitly overridden */
7
+ /** Environment - auto-injected from module options. Can be explicitly overridden */
7
8
  environment?: string;
9
+ /** Explicit stable key for percentage rollout bucketing */
10
+ targetingKey?: string | null;
11
+ /** Additional exact-match targeting attributes */
12
+ attributes?: TargetingAttributes;
8
13
  }
@@ -0,0 +1,29 @@
1
+ export type EvaluationSource = 'override' | 'percentage' | 'global' | 'default';
2
+ export type EvaluationReason = 'ARCHIVED' | 'OVERRIDE_MATCH' | 'PERCENTAGE_MATCH' | 'PERCENTAGE_MISS' | 'PERCENTAGE_NO_TARGETING_KEY' | 'GLOBAL' | 'FLAG_NOT_FOUND' | 'ERROR';
3
+ export type BucketBy = 'userId' | 'tenantId' | 'environment' | 'targetingKey' | (string & {});
4
+ export interface EvaluateBooleanOptions {
5
+ /** Invocation-level default used when the flag is missing or evaluation fails. */
6
+ defaultValue?: boolean;
7
+ /** Emit an exposure event for this evaluation. */
8
+ trackExposure?: boolean;
9
+ /** Include the full resolved context in evaluation/exposure events. */
10
+ includeContextInEvent?: boolean;
11
+ }
12
+ export interface BooleanEvaluationDetails {
13
+ flagKey: string;
14
+ value: boolean;
15
+ /** Backward-compatible alias for value. */
16
+ result: boolean;
17
+ source: EvaluationSource;
18
+ reason: EvaluationReason;
19
+ defaultUsed: boolean;
20
+ errorCode?: string;
21
+ errorMessage?: string;
22
+ matchedOverrideId?: string;
23
+ bucket?: number;
24
+ targetingKey?: string;
25
+ evaluationTimeMs?: number;
26
+ }
27
+ export interface FlagEvaluatorOptions {
28
+ bucketBy?: BucketBy;
29
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=evaluation-details.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluation-details.interface.js","sourceRoot":"","sources":["../../src/interfaces/evaluation-details.interface.ts"],"names":[],"mappings":""}
@@ -1,6 +1,7 @@
1
1
  import { ModuleMetadata, Type } from '@nestjs/common';
2
2
  import { Request } from 'express';
3
3
  import { CacheAdapter } from './cache-adapter.interface';
4
+ import { FlagRegistry } from './flag-registry.interface';
4
5
  export interface FeatureFlagModuleOptions {
5
6
  /** Current environment (e.g., 'development', 'staging', 'production') */
6
7
  environment: string;
@@ -14,6 +15,8 @@ export interface FeatureFlagModuleOptions {
14
15
  emitEvents?: boolean;
15
16
  /** Custom cache adapter implementation. If not provided, an in-memory cache is used. */
16
17
  cacheAdapter?: CacheAdapter;
18
+ /** Optional type-safe flag registry used for defaults and evaluation metadata. */
19
+ flags?: FlagRegistry;
17
20
  }
18
21
  export interface FeatureFlagModuleOptionsFactory {
19
22
  createFeatureFlagOptions(): Promise<FeatureFlagModuleOptions & {
@@ -1,8 +1,10 @@
1
- import { CreateFeatureFlagInput, UpdateFeatureFlagInput, FeatureFlagWithOverrides } from './feature-flag.interface';
1
+ import { CreateFeatureFlagInput, UpdateFeatureFlagInput, FeatureFlagWithOverrides, TargetingAttributes } from './feature-flag.interface';
2
2
  export interface OverrideCriteria {
3
- tenantId: string | null;
4
- userId: string | null;
5
- environment: string | null;
3
+ attributes: TargetingAttributes;
4
+ }
5
+ export interface UpdateOverrideInput {
6
+ enabled: boolean;
7
+ priority: number;
6
8
  }
7
9
  export interface FeatureFlagRepository {
8
10
  createFlag(input: CreateFeatureFlagInput): Promise<FeatureFlagWithOverrides>;
@@ -14,7 +16,7 @@ export interface FeatureFlagRepository {
14
16
  findOverride(flagId: string, criteria: OverrideCriteria): Promise<{
15
17
  id: string;
16
18
  } | null>;
17
- createOverride(flagId: string, criteria: OverrideCriteria, enabled: boolean): Promise<void>;
18
- updateOverrideEnabled(id: string, enabled: boolean): Promise<void>;
19
+ createOverride(flagId: string, criteria: OverrideCriteria, enabled: boolean, priority: number): Promise<void>;
20
+ updateOverride(id: string, input: UpdateOverrideInput): Promise<void>;
19
21
  deleteOverride(id: string): Promise<void>;
20
22
  }
@@ -1,3 +1,5 @@
1
+ export type TargetingAttributeValue = string | number | boolean | null;
2
+ export type TargetingAttributes = Record<string, TargetingAttributeValue>;
1
3
  export interface CreateFeatureFlagInput {
2
4
  key: string;
3
5
  description?: string;
@@ -12,16 +14,17 @@ export interface UpdateFeatureFlagInput {
12
14
  metadata?: Record<string, unknown>;
13
15
  }
14
16
  export interface SetOverrideInput {
15
- tenantId?: string;
16
- userId?: string;
17
- environment?: string;
17
+ attributes: TargetingAttributes;
18
18
  enabled: boolean;
19
+ priority?: number;
19
20
  }
20
21
  export interface FeatureFlagGuardOptions {
21
22
  /** HTTP status code when flag is OFF. Default: 403 */
22
23
  statusCode?: number;
23
24
  /** Response body when flag is OFF */
24
25
  fallback?: Record<string, unknown>;
26
+ /** Invocation-level default when the guard cannot find or evaluate the flag. Default: false */
27
+ defaultValue?: boolean;
25
28
  }
26
29
  export interface FeatureFlagWithOverrides {
27
30
  id: string;
@@ -38,13 +41,19 @@ export interface FeatureFlagWithOverrides {
38
41
  export interface FlagOverride {
39
42
  id: string;
40
43
  flagId: string;
41
- tenantId: string | null;
42
- userId: string | null;
43
- environment: string | null;
44
+ attributes: TargetingAttributes;
45
+ priority: number;
44
46
  enabled: boolean;
47
+ createdAt: Date;
48
+ updatedAt: Date;
45
49
  }
46
50
  export interface RemoveOverrideInput {
47
- tenantId?: string;
48
- userId?: string;
49
- environment?: string;
51
+ attributes: TargetingAttributes;
52
+ }
53
+ export interface FlagMutationMetadata {
54
+ actorId?: string;
55
+ actorType?: string;
56
+ reason?: string;
57
+ requestId?: string;
58
+ correlationId?: string;
50
59
  }
@@ -0,0 +1,17 @@
1
+ import { BucketBy } from './evaluation-details.interface';
2
+ export type FeatureFlagType = 'release' | 'experiment' | 'ops' | 'permission';
3
+ export interface FeatureFlagLifecycleMetadata {
4
+ type?: FeatureFlagType;
5
+ owner?: string;
6
+ tags?: readonly string[];
7
+ expiresAt?: string | Date;
8
+ staleAt?: string | Date;
9
+ }
10
+ export interface FlagDefinition extends FeatureFlagLifecycleMetadata {
11
+ defaultValue: boolean;
12
+ description?: string;
13
+ bucketBy?: BucketBy;
14
+ trackExposure?: boolean;
15
+ }
16
+ export type FlagRegistry = Record<string, FlagDefinition>;
17
+ export type FlagKey<TFlags extends FlagRegistry> = Extract<keyof TFlags, string>;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=flag-registry.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flag-registry.interface.js","sourceRoot":"","sources":["../../src/interfaces/flag-registry.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { FeatureFlagService } from './services/feature-flag.service';
2
+ export interface OpenFeatureBooleanProviderOptions {
3
+ name?: string;
4
+ }
5
+ export interface OpenFeatureEvaluationContext {
6
+ targetingKey?: unknown;
7
+ userId?: unknown;
8
+ tenantId?: unknown;
9
+ environment?: unknown;
10
+ [key: string]: unknown;
11
+ }
12
+ export interface OpenFeatureBooleanResolutionDetails {
13
+ value: boolean;
14
+ reason: string;
15
+ variant?: string;
16
+ errorCode?: string;
17
+ errorMessage?: string;
18
+ flagMetadata: Record<string, unknown>;
19
+ }
20
+ export interface OpenFeatureBooleanProvider {
21
+ metadata: {
22
+ name: string;
23
+ };
24
+ resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: OpenFeatureEvaluationContext): Promise<OpenFeatureBooleanResolutionDetails>;
25
+ }
26
+ export declare function createOpenFeatureBooleanProvider(service: FeatureFlagService, options?: OpenFeatureBooleanProviderOptions): OpenFeatureBooleanProvider;