@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.
- package/README.md +252 -87
- package/dist/admin/admin-options.interface.d.ts +5 -0
- package/dist/admin/admin-options.interface.js +3 -0
- package/dist/admin/admin-options.interface.js.map +1 -0
- package/dist/admin/feature-flag-admin.controller.d.ts +14 -0
- package/dist/admin/feature-flag-admin.controller.js +106 -0
- package/dist/admin/feature-flag-admin.controller.js.map +1 -0
- package/dist/admin/feature-flag-admin.dto.d.ts +22 -0
- package/dist/admin/feature-flag-admin.dto.js +93 -0
- package/dist/admin/feature-flag-admin.dto.js.map +1 -0
- package/dist/admin/feature-flag-admin.module.d.ts +5 -0
- package/dist/admin/feature-flag-admin.module.js +33 -0
- package/dist/admin/feature-flag-admin.module.js.map +1 -0
- package/dist/admin/targeting-attributes.validator.d.ts +6 -0
- package/dist/admin/targeting-attributes.validator.js +36 -0
- package/dist/admin/targeting-attributes.validator.js.map +1 -0
- package/dist/cache/memory-cache.adapter.d.ts +11 -0
- package/dist/{services/flag-cache.service.js → cache/memory-cache.adapter.js} +18 -31
- package/dist/cache/memory-cache.adapter.js.map +1 -0
- package/dist/cache/redis-cache.adapter.d.ts +26 -0
- package/dist/cache/redis-cache.adapter.js +102 -0
- package/dist/cache/redis-cache.adapter.js.map +1 -0
- package/dist/events/feature-flag.events.d.ts +4 -5
- package/dist/feature-flag.constants.d.ts +3 -0
- package/dist/feature-flag.constants.js +4 -1
- package/dist/feature-flag.constants.js.map +1 -1
- package/dist/feature-flag.module.js +49 -10
- package/dist/feature-flag.module.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/cache-adapter.interface.d.ts +9 -0
- package/dist/interfaces/cache-adapter.interface.js +3 -0
- package/dist/interfaces/cache-adapter.interface.js.map +1 -0
- package/dist/interfaces/evaluation-context.interface.d.ts +6 -3
- package/dist/interfaces/feature-flag-options.interface.d.ts +3 -0
- package/dist/interfaces/feature-flag-repository.interface.d.ts +22 -0
- package/dist/interfaces/feature-flag-repository.interface.js +3 -0
- package/dist/interfaces/feature-flag-repository.interface.js.map +1 -0
- package/dist/interfaces/feature-flag.interface.d.ts +11 -6
- package/dist/interfaces/tenant-context-provider.interface.d.ts +3 -0
- package/dist/interfaces/tenant-context-provider.interface.js +3 -0
- package/dist/interfaces/tenant-context-provider.interface.js.map +1 -0
- package/dist/repositories/prisma-feature-flag.repository.d.ts +18 -0
- package/dist/repositories/prisma-feature-flag.repository.js +161 -0
- package/dist/repositories/prisma-feature-flag.repository.js.map +1 -0
- package/dist/services/default-tenant-context-provider.d.ts +7 -0
- package/dist/services/default-tenant-context-provider.js +35 -0
- package/dist/services/default-tenant-context-provider.js.map +1 -0
- package/dist/services/feature-flag.service.d.ts +21 -13
- package/dist/services/feature-flag.service.js +111 -126
- package/dist/services/feature-flag.service.js.map +1 -1
- package/dist/services/flag-context-resolver.d.ts +11 -0
- package/dist/services/flag-context-resolver.js +51 -0
- package/dist/services/flag-context-resolver.js.map +1 -0
- package/dist/services/flag-evaluator.service.d.ts +1 -0
- package/dist/services/flag-evaluator.service.js +29 -27
- package/dist/services/flag-evaluator.service.js.map +1 -1
- package/dist/services/flag-event-publisher.d.ts +7 -0
- package/dist/services/flag-event-publisher.js +37 -0
- package/dist/services/flag-event-publisher.js.map +1 -0
- package/dist/testing/test-feature-flag.module.js +26 -5
- package/dist/testing/test-feature-flag.module.js.map +1 -1
- package/dist/utils/targeting-attributes.d.ts +9 -0
- package/dist/utils/targeting-attributes.js +44 -0
- package/dist/utils/targeting-attributes.js.map +1 -0
- package/package.json +20 -3
- package/prisma/migrations/20260512000000_attribute_targeting/migration.sql +59 -0
- package/prisma/schema.prisma +7 -9
- package/dist/services/flag-cache.service.d.ts +0 -13
- 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,
|
|
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.
|
|
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);
|
package/prisma/schema.prisma
CHANGED
|
@@ -24,18 +24,16 @@ model FeatureFlag {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
model FeatureFlagOverride {
|
|
27
|
-
id
|
|
28
|
-
flagId
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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"}
|