@nestarc/feature-flag 0.1.0 → 0.3.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 (71) hide show
  1. package/README.md +252 -87
  2. package/dist/admin/admin-options.interface.d.ts +5 -0
  3. package/dist/admin/admin-options.interface.js +3 -0
  4. package/dist/admin/admin-options.interface.js.map +1 -0
  5. package/dist/admin/feature-flag-admin.controller.d.ts +14 -0
  6. package/dist/admin/feature-flag-admin.controller.js +106 -0
  7. package/dist/admin/feature-flag-admin.controller.js.map +1 -0
  8. package/dist/admin/feature-flag-admin.dto.d.ts +22 -0
  9. package/dist/admin/feature-flag-admin.dto.js +93 -0
  10. package/dist/admin/feature-flag-admin.dto.js.map +1 -0
  11. package/dist/admin/feature-flag-admin.module.d.ts +5 -0
  12. package/dist/admin/feature-flag-admin.module.js +33 -0
  13. package/dist/admin/feature-flag-admin.module.js.map +1 -0
  14. package/dist/admin/targeting-attributes.validator.d.ts +6 -0
  15. package/dist/admin/targeting-attributes.validator.js +36 -0
  16. package/dist/admin/targeting-attributes.validator.js.map +1 -0
  17. package/dist/cache/memory-cache.adapter.d.ts +11 -0
  18. package/dist/{services/flag-cache.service.js → cache/memory-cache.adapter.js} +18 -31
  19. package/dist/cache/memory-cache.adapter.js.map +1 -0
  20. package/dist/cache/redis-cache.adapter.d.ts +26 -0
  21. package/dist/cache/redis-cache.adapter.js +102 -0
  22. package/dist/cache/redis-cache.adapter.js.map +1 -0
  23. package/dist/events/feature-flag.events.d.ts +4 -5
  24. package/dist/feature-flag.constants.d.ts +3 -0
  25. package/dist/feature-flag.constants.js +4 -1
  26. package/dist/feature-flag.constants.js.map +1 -1
  27. package/dist/feature-flag.module.js +49 -10
  28. package/dist/feature-flag.module.js.map +1 -1
  29. package/dist/index.d.ts +10 -2
  30. package/dist/index.js +13 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/interfaces/cache-adapter.interface.d.ts +9 -0
  33. package/dist/interfaces/cache-adapter.interface.js +3 -0
  34. package/dist/interfaces/cache-adapter.interface.js.map +1 -0
  35. package/dist/interfaces/evaluation-context.interface.d.ts +6 -3
  36. package/dist/interfaces/feature-flag-options.interface.d.ts +3 -0
  37. package/dist/interfaces/feature-flag-repository.interface.d.ts +22 -0
  38. package/dist/interfaces/feature-flag-repository.interface.js +3 -0
  39. package/dist/interfaces/feature-flag-repository.interface.js.map +1 -0
  40. package/dist/interfaces/feature-flag.interface.d.ts +11 -6
  41. package/dist/interfaces/tenant-context-provider.interface.d.ts +3 -0
  42. package/dist/interfaces/tenant-context-provider.interface.js +3 -0
  43. package/dist/interfaces/tenant-context-provider.interface.js.map +1 -0
  44. package/dist/repositories/prisma-feature-flag.repository.d.ts +18 -0
  45. package/dist/repositories/prisma-feature-flag.repository.js +161 -0
  46. package/dist/repositories/prisma-feature-flag.repository.js.map +1 -0
  47. package/dist/services/default-tenant-context-provider.d.ts +7 -0
  48. package/dist/services/default-tenant-context-provider.js +35 -0
  49. package/dist/services/default-tenant-context-provider.js.map +1 -0
  50. package/dist/services/feature-flag.service.d.ts +21 -13
  51. package/dist/services/feature-flag.service.js +111 -126
  52. package/dist/services/feature-flag.service.js.map +1 -1
  53. package/dist/services/flag-context-resolver.d.ts +11 -0
  54. package/dist/services/flag-context-resolver.js +51 -0
  55. package/dist/services/flag-context-resolver.js.map +1 -0
  56. package/dist/services/flag-evaluator.service.d.ts +1 -0
  57. package/dist/services/flag-evaluator.service.js +29 -27
  58. package/dist/services/flag-evaluator.service.js.map +1 -1
  59. package/dist/services/flag-event-publisher.d.ts +7 -0
  60. package/dist/services/flag-event-publisher.js +37 -0
  61. package/dist/services/flag-event-publisher.js.map +1 -0
  62. package/dist/testing/test-feature-flag.module.js +26 -5
  63. package/dist/testing/test-feature-flag.module.js.map +1 -1
  64. package/dist/utils/targeting-attributes.d.ts +9 -0
  65. package/dist/utils/targeting-attributes.js +44 -0
  66. package/dist/utils/targeting-attributes.js.map +1 -0
  67. package/package.json +20 -3
  68. package/prisma/migrations/20260512000000_attribute_targeting/migration.sql +59 -0
  69. package/prisma/schema.prisma +7 -9
  70. package/dist/services/flag-cache.service.d.ts +0 -13
  71. package/dist/services/flag-cache.service.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"test-feature-flag.module.js","sourceRoot":"","sources":["../../src/testing/test-feature-flag.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,2EAAsE;AAG/D,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,QAAQ,CAAC,KAA+B;QAC7C,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,yCAAkB;oBAC3B,QAAQ,EAAE;wBACR,SAAS,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK;wBACvD,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE;wBACpC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;wBACxB,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;wBACxB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;wBACzB,WAAW,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;wBAC3B,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;wBACvB,eAAe,EAAE,GAAG,EAAE,GAAE,CAAC;qBAC1B;iBACF;aACF;YACD,OAAO,EAAE,CAAC,yCAAkB,CAAC;SAC9B,CAAC;IACJ,CAAC;CACF,CAAA;AAvBY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,qBAAqB,CAuBjC"}
1
+ {"version":3,"file":"test-feature-flag.module.js","sourceRoot":"","sources":["../../src/testing/test-feature-flag.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA0E;AAC1E,2EAAsE;AAMtE,SAAS,YAAY,CACnB,UAA6C,EAAE;IAE/C,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS;QAC3B,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,MAAM;QAC1B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;QACxC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK;QACjC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC;QACnC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;QAChC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC;QACtD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC;QACtD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;KACnC,CAAC;AACJ,CAAC;AAGM,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,MAAM,CAAC,QAAQ,CAAC,KAA+B;QAC7C,OAAO;YACL,MAAM,EAAE,uBAAqB;YAC7B,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,yCAAkB;oBAC3B,QAAQ,EAAE;wBACR,SAAS,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK;wBACvD,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE;wBACpC,MAAM,EAAE,KAAK,EAAE,KAA6B,EAAE,EAAE,CAC9C,YAAY,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC;wBACnE,MAAM,EAAE,KAAK,EAAE,GAAW,EAAE,KAAwC,EAAE,EAAE,CACtE,YAAY,CAAC,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;wBACjC,OAAO,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE,CAC7B,YAAY,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;wBAC/C,WAAW,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;wBAC3B,cAAc,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;wBAC9B,OAAO,EAAE,KAAK,IAAI,EAAE,CAClB,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,CACjD,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAC/B;wBACH,SAAS,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE;4BAC/B,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;gCAC1B,OAAO,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;4BACpD,CAAC;4BACD,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,GAAG,aAAa,CAAC,CAAC;wBACjE,CAAC;wBACD,eAAe,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;qBAChC;iBACF;aACF;YACD,OAAO,EAAE,CAAC,yCAAkB,CAAC;SAC9B,CAAC;IACJ,CAAC;CACF,CAAA;AApCY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,qBAAqB,CAoCjC"}
@@ -0,0 +1,9 @@
1
+ import { TargetingAttributes, TargetingAttributeValue } from '../interfaces/feature-flag.interface';
2
+ interface AttributeValidationOptions {
3
+ allowEmpty: boolean;
4
+ }
5
+ export declare function isTargetingAttributeValue(value: unknown): value is TargetingAttributeValue;
6
+ export declare function isTargetingAttributes(value: unknown, options?: AttributeValidationOptions): value is TargetingAttributes;
7
+ export declare function normalizeTargetingAttributes(value: unknown, options?: AttributeValidationOptions): TargetingAttributes;
8
+ export declare function matchesTargetingAttributes(overrideAttributes: TargetingAttributes, contextAttributes: TargetingAttributes): boolean;
9
+ export {};
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTargetingAttributeValue = isTargetingAttributeValue;
4
+ exports.isTargetingAttributes = isTargetingAttributes;
5
+ exports.normalizeTargetingAttributes = normalizeTargetingAttributes;
6
+ exports.matchesTargetingAttributes = matchesTargetingAttributes;
7
+ const INVALID_ATTRIBUTES_MESSAGE = 'attributes must be a non-empty object with string, number, boolean, or null values';
8
+ function isTargetingAttributeValue(value) {
9
+ return (value === null ||
10
+ typeof value === 'string' ||
11
+ (typeof value === 'number' && Number.isFinite(value)) ||
12
+ typeof value === 'boolean');
13
+ }
14
+ function isTargetingAttributes(value, options = { allowEmpty: false }) {
15
+ if (!isPlainObject(value)) {
16
+ return false;
17
+ }
18
+ const entries = Object.entries(value);
19
+ if (!options.allowEmpty && entries.length === 0) {
20
+ return false;
21
+ }
22
+ return entries.every(([, attributeValue]) => isTargetingAttributeValue(attributeValue));
23
+ }
24
+ function normalizeTargetingAttributes(value, options = { allowEmpty: false }) {
25
+ if (!isTargetingAttributes(value, options)) {
26
+ throw new Error(INVALID_ATTRIBUTES_MESSAGE);
27
+ }
28
+ return { ...value };
29
+ }
30
+ function matchesTargetingAttributes(overrideAttributes, contextAttributes) {
31
+ const entries = Object.entries(overrideAttributes);
32
+ if (entries.length === 0) {
33
+ return false;
34
+ }
35
+ return entries.every(([key, value]) => contextAttributes[key] === value);
36
+ }
37
+ function isPlainObject(value) {
38
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
39
+ return false;
40
+ }
41
+ const prototype = Object.getPrototypeOf(value);
42
+ return prototype === Object.prototype || prototype === null;
43
+ }
44
+ //# sourceMappingURL=targeting-attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targeting-attributes.js","sourceRoot":"","sources":["../../src/utils/targeting-attributes.ts"],"names":[],"mappings":";;AAYA,8DAOC;AAED,sDAcC;AAED,oEASC;AAED,gEAUC;AAjDD,MAAM,0BAA0B,GAC9B,oFAAoF,CAAC;AAEvF,SAAgB,yBAAyB,CAAC,KAAc;IACtD,OAAO,CACL,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrD,OAAO,KAAK,KAAK,SAAS,CAC3B,CAAC;AACJ,CAAC;AAED,SAAgB,qBAAqB,CACnC,KAAc,EACd,UAAsC,EAAE,UAAU,EAAE,KAAK,EAAE;IAE3D,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,EAAE,EAAE,CAAC,yBAAyB,CAAC,cAAc,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,SAAgB,4BAA4B,CAC1C,KAAc,EACd,UAAsC,EAAE,UAAU,EAAE,KAAK,EAAE;IAE3D,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;AACtB,CAAC;AAED,SAAgB,0BAA0B,CACxC,kBAAuC,EACvC,iBAAsC;IAEtC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACnD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAC9D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestarc/feature-flag",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "DB-backed feature flags for NestJS + Prisma + PostgreSQL with tenant-aware overrides",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,6 +22,7 @@
22
22
  "build": "tsc -p tsconfig.build.json",
23
23
  "test": "jest",
24
24
  "test:cov": "jest --coverage",
25
+ "bench": "dotenv -e .env.test -- npx ts-node benchmarks/evaluation-overhead.ts",
25
26
  "lint": "eslint 'src/**/*.ts' 'test/**/*.ts'",
26
27
  "format": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
27
28
  "prisma:generate": "prisma generate",
@@ -34,15 +35,20 @@
34
35
  },
35
36
  "repository": {
36
37
  "type": "git",
37
- "url": "https://github.com/nestarc/nestjs-feature-flag"
38
+ "url": "git+https://github.com/nestarc/nestjs-feature-flag.git"
38
39
  },
39
40
  "keywords": [
40
41
  "nestjs",
41
42
  "feature-flag",
43
+ "feature-flags",
42
44
  "feature-toggle",
45
+ "rollout",
46
+ "percentage-rollout",
43
47
  "prisma",
44
48
  "postgresql",
45
- "multi-tenant"
49
+ "multi-tenant",
50
+ "nestjs-module",
51
+ "saas"
46
52
  ],
47
53
  "author": "nestarc",
48
54
  "license": "MIT",
@@ -50,6 +56,9 @@
50
56
  "@nestjs/common": "^10.0.0 || ^11.0.0",
51
57
  "@nestjs/core": "^10.0.0 || ^11.0.0",
52
58
  "@prisma/client": "^5.0.0 || ^6.0.0",
59
+ "class-transformer": "^0.5.1",
60
+ "class-validator": "^0.14.0 || ^0.15.0",
61
+ "ioredis": "^5.0.0",
53
62
  "reflect-metadata": "^0.1.13 || ^0.2.0",
54
63
  "rxjs": "^7.0.0"
55
64
  },
@@ -59,6 +68,9 @@
59
68
  },
60
69
  "@nestjs/event-emitter": {
61
70
  "optional": true
71
+ },
72
+ "ioredis": {
73
+ "optional": true
62
74
  }
63
75
  },
64
76
  "devDependencies": {
@@ -70,10 +82,15 @@
70
82
  "@nestjs/testing": "^11.0.0",
71
83
  "@prisma/client": "^6.0.0",
72
84
  "@types/express": "^5.0.0",
85
+ "@types/ioredis-mock": "^8.2.7",
73
86
  "@types/jest": "^29.5.0",
74
87
  "@types/supertest": "^7.2.0",
88
+ "class-transformer": "^0.5.1",
89
+ "class-validator": "^0.15.1",
75
90
  "dotenv-cli": "^11.0.0",
76
91
  "eslint": "^9.0.0",
92
+ "ioredis": "^5.10.1",
93
+ "ioredis-mock": "^8.13.1",
77
94
  "jest": "^29.7.0",
78
95
  "prettier": "^3.0.0",
79
96
  "prisma": "^6.0.0",
@@ -0,0 +1,59 @@
1
+ ALTER TABLE "feature_flag_overrides"
2
+ ADD COLUMN "attributes" JSONB NOT NULL DEFAULT '{}',
3
+ ADD COLUMN "priority" INTEGER NOT NULL DEFAULT 0;
4
+
5
+ ALTER TABLE "feature_flag_overrides"
6
+ ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP;
7
+
8
+ UPDATE "feature_flag_overrides"
9
+ SET "attributes" = jsonb_strip_nulls(
10
+ jsonb_build_object(
11
+ 'tenantId', "tenant_id",
12
+ 'userId', "user_id",
13
+ 'environment', "environment"
14
+ )
15
+ );
16
+
17
+ -- v0.3.0 rejects empty targeting attributes. Legacy all-null/global overrides
18
+ -- become empty objects during backfill and are intentionally removed by this
19
+ -- breaking migration.
20
+ DELETE FROM "feature_flag_overrides"
21
+ WHERE "attributes" = '{}'::jsonb;
22
+
23
+ WITH ranked_overrides AS (
24
+ SELECT
25
+ "id",
26
+ row_number() OVER (
27
+ PARTITION BY "flag_id", "attributes"
28
+ ORDER BY "updated_at" DESC, "created_at" DESC, "id" DESC
29
+ ) AS row_number
30
+ FROM "feature_flag_overrides"
31
+ )
32
+ DELETE FROM "feature_flag_overrides" target
33
+ USING ranked_overrides ranked
34
+ WHERE target."id" = ranked."id"
35
+ AND ranked.row_number > 1;
36
+
37
+ DROP INDEX IF EXISTS "uq_override_000";
38
+ DROP INDEX IF EXISTS "uq_override_001";
39
+ DROP INDEX IF EXISTS "uq_override_010";
40
+ DROP INDEX IF EXISTS "uq_override_011";
41
+ DROP INDEX IF EXISTS "uq_override_100";
42
+ DROP INDEX IF EXISTS "uq_override_101";
43
+ DROP INDEX IF EXISTS "uq_override_110";
44
+ DROP INDEX IF EXISTS "uq_override_111";
45
+
46
+ ALTER TABLE "feature_flag_overrides"
47
+ DROP COLUMN "tenant_id",
48
+ DROP COLUMN "user_id",
49
+ DROP COLUMN "environment";
50
+
51
+ ALTER TABLE "feature_flag_overrides"
52
+ ALTER COLUMN "attributes" DROP DEFAULT;
53
+
54
+ CREATE UNIQUE INDEX "uq_feature_flag_override_attributes"
55
+ ON "feature_flag_overrides"("flag_id", "attributes");
56
+
57
+ ALTER TABLE "feature_flag_overrides"
58
+ ADD CONSTRAINT "chk_feature_flag_override_attributes_non_empty"
59
+ CHECK (jsonb_typeof("attributes") = 'object' AND "attributes" <> '{}'::jsonb);
@@ -24,18 +24,16 @@ model FeatureFlag {
24
24
  }
25
25
 
26
26
  model FeatureFlagOverride {
27
- id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
28
- flagId String @map("flag_id") @db.Uuid
29
- tenantId String? @map("tenant_id")
30
- userId String? @map("user_id")
31
- environment String?
32
- enabled Boolean
33
- createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz()
34
- updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz()
27
+ id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
28
+ flagId String @map("flag_id") @db.Uuid
29
+ attributes Json
30
+ priority Int @default(0)
31
+ enabled Boolean
32
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz()
33
+ updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz()
35
34
 
36
35
  flag FeatureFlag @relation(fields: [flagId], references: [id], onDelete: Cascade)
37
36
 
38
- // Unique indexes defined in raw SQL migration (0001) to handle NULL equality correctly
39
37
  @@index([flagId], map: "idx_override_flag_id")
40
38
  @@map("feature_flag_overrides")
41
39
  }
@@ -1,13 +0,0 @@
1
- import { FeatureFlagModuleOptions } from '../interfaces/feature-flag-options.interface';
2
- import { FeatureFlagWithOverrides } from '../interfaces/feature-flag.interface';
3
- export declare class FlagCacheService {
4
- private readonly options;
5
- private cache;
6
- private allFlagsCache;
7
- constructor(options: FeatureFlagModuleOptions);
8
- get(key: string): FeatureFlagWithOverrides | null;
9
- set(key: string, data: FeatureFlagWithOverrides): void;
10
- getAll(): FeatureFlagWithOverrides[] | null;
11
- setAll(data: FeatureFlagWithOverrides[]): void;
12
- invalidate(key?: string): void;
13
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"flag-cache.service.js","sourceRoot":"","sources":["../../src/services/flag-cache.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,sEAAwE;AAUjE,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAI3B,YACuC,OAAkD;QAAjC,YAAO,GAAP,OAAO,CAA0B;QAJjF,UAAK,GAAG,IAAI,GAAG,EAAgD,CAAC;QAChE,kBAAa,GAAkD,IAAI,CAAC;IAIzE,CAAC;IAEJ,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,IAA8B;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;QAC9C,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO;QACtB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QACrC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;YAC9C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,IAAgC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;QAC9C,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO;QACtB,IAAI,CAAC,aAAa,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;IAC7D,CAAC;IAED,UAAU,CAAC,GAAY;QACrB,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACnB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;CACF,CAAA;AAhDY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;IAMR,WAAA,IAAA,eAAM,EAAC,oDAA2B,CAAC,CAAA;;GAL3B,gBAAgB,CAgD5B"}