@nestarc/feature-flag 0.1.0 → 0.2.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 +150 -2
- 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 +100 -0
- package/dist/admin/feature-flag-admin.controller.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/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/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/feature-flag-options.interface.d.ts +3 -0
- package/dist/interfaces/feature-flag-repository.interface.d.ts +20 -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 +5 -0
- 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 +157 -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 +20 -13
- package/dist/services/feature-flag.service.js +99 -123
- 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 +40 -0
- package/dist/services/flag-context-resolver.js.map +1 -0
- 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/package.json +15 -2
- package/dist/services/flag-cache.service.d.ts +0 -13
- package/dist/services/flag-cache.service.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @nestarc/feature-flag
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@nestarc/feature-flag)
|
|
4
|
+
[](https://www.npmjs.com/package/@nestarc/feature-flag)
|
|
5
|
+
[](https://github.com/nestarc/nestjs-feature-flag/actions/workflows/ci.yml)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nestarc.dev/packages/feature-flag/)
|
|
8
|
+
|
|
3
9
|
DB-backed feature flags for NestJS + Prisma + PostgreSQL -- tenant-aware overrides, percentage rollouts, and zero external dependencies.
|
|
4
10
|
|
|
5
11
|
## Features
|
|
@@ -10,7 +16,10 @@ DB-backed feature flags for NestJS + Prisma + PostgreSQL -- tenant-aware overrid
|
|
|
10
16
|
- **Guard decorator** -- `@FeatureFlag()` automatically gates routes and controllers
|
|
11
17
|
- **Bypass decorator** -- `@BypassFeatureFlag()` exempts health checks and public endpoints
|
|
12
18
|
- **Programmatic evaluation** -- `isEnabled()` and `evaluateAll()` for service-layer logic
|
|
13
|
-
- **Built-in caching** -- configurable TTL with manual invalidation
|
|
19
|
+
- **Built-in caching** -- configurable TTL with manual invalidation; Redis Pub/Sub for multi-instance
|
|
20
|
+
- **Pluggable persistence** -- `FeatureFlagRepository` interface for custom backends (Prisma default)
|
|
21
|
+
- **Pluggable tenancy** -- `TenantContextProvider` interface for custom tenant resolution
|
|
22
|
+
- **Admin REST API** -- opt-in `FeatureFlagAdminModule` with guard injection and proper error responses
|
|
14
23
|
- **Event system** -- optional integration with `@nestjs/event-emitter` for audit and observability
|
|
15
24
|
- **Testing utilities** -- drop-in `TestFeatureFlagModule` for unit and integration tests
|
|
16
25
|
|
|
@@ -31,8 +40,35 @@ npm install @nestjs/common @nestjs/core @prisma/client rxjs reflect-metadata
|
|
|
31
40
|
```bash
|
|
32
41
|
# Required only if you enable emitEvents
|
|
33
42
|
npm install @nestjs/event-emitter
|
|
43
|
+
|
|
44
|
+
# Required only if you use RedisCacheAdapter
|
|
45
|
+
npm install ioredis
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Redis Cache (Multi-Instance)
|
|
49
|
+
|
|
50
|
+
For production deployments with multiple instances, use `RedisCacheAdapter` for shared caching and real-time invalidation via Redis Pub/Sub:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { FeatureFlagModule, RedisCacheAdapter } from '@nestarc/feature-flag';
|
|
54
|
+
import { Redis } from 'ioredis';
|
|
55
|
+
|
|
56
|
+
const redisClient = new Redis({ host: 'localhost', port: 6379 });
|
|
57
|
+
|
|
58
|
+
FeatureFlagModule.forRoot({
|
|
59
|
+
environment: 'production',
|
|
60
|
+
prisma,
|
|
61
|
+
cacheAdapter: new RedisCacheAdapter({
|
|
62
|
+
client: redisClient,
|
|
63
|
+
// subscriber is auto-created via client.duplicate()
|
|
64
|
+
// keyPrefix: 'feature-flag:', // default
|
|
65
|
+
// channel: 'feature-flag:invalidate', // default
|
|
66
|
+
}),
|
|
67
|
+
})
|
|
34
68
|
```
|
|
35
69
|
|
|
70
|
+
When a flag is updated on any instance, all other instances are notified via Pub/Sub and invalidate their cache immediately — eliminating the stale-cache window.
|
|
71
|
+
|
|
36
72
|
## Prisma Schema
|
|
37
73
|
|
|
38
74
|
Add the following models to your `schema.prisma`:
|
|
@@ -438,7 +474,13 @@ describe('DashboardController', () => {
|
|
|
438
474
|
});
|
|
439
475
|
```
|
|
440
476
|
|
|
441
|
-
`TestFeatureFlagModule.register()` provides a global mock of `FeatureFlagService
|
|
477
|
+
`TestFeatureFlagModule.register()` provides a global mock of `FeatureFlagService`:
|
|
478
|
+
- `isEnabled(key)` returns the boolean you specified (defaulting to `false` for unregistered keys)
|
|
479
|
+
- `evaluateAll()` returns the full flag map
|
|
480
|
+
- `create()`, `update()`, `archive()`, `findByKey()`, `findAll()` return full `FeatureFlagWithOverrides` stub objects
|
|
481
|
+
- `findByKey()` throws `NotFoundException` for unknown keys
|
|
482
|
+
|
|
483
|
+
This is a **stateless boolean stub** -- write operations do not persist state across calls. For stateful test doubles, use your own mock implementation.
|
|
442
484
|
|
|
443
485
|
## Evaluation Priority
|
|
444
486
|
|
|
@@ -466,6 +508,7 @@ Percentage rollout uses murmurhash3 for deterministic bucketing: the same user a
|
|
|
466
508
|
| `userIdExtractor` | `(req: Request) => string \| null`| `undefined`| Extracts user ID from the incoming request |
|
|
467
509
|
| `defaultOnMissing` | `boolean` | `false` | Value returned when a flag key does not exist in the database |
|
|
468
510
|
| `emitEvents` | `boolean` | `false` | Emit lifecycle events via `@nestjs/event-emitter` |
|
|
511
|
+
| `cacheAdapter` | `CacheAdapter` | `MemoryCacheAdapter` | Pluggable cache backend (e.g. `RedisCacheAdapter`) |
|
|
469
512
|
|
|
470
513
|
### FeatureFlagModuleRootOptions
|
|
471
514
|
|
|
@@ -504,6 +547,111 @@ const allFlags = await this.flags.findAll();
|
|
|
504
547
|
this.flags.invalidateCache();
|
|
505
548
|
```
|
|
506
549
|
|
|
550
|
+
## Admin REST API
|
|
551
|
+
|
|
552
|
+
`FeatureFlagAdminModule` provides a REST API for managing flags. It requires a guard — the module won't register without one:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
import { FeatureFlagAdminModule } from '@nestarc/feature-flag';
|
|
556
|
+
import { AdminAuthGuard } from './guards/admin-auth.guard';
|
|
557
|
+
|
|
558
|
+
@Module({
|
|
559
|
+
imports: [
|
|
560
|
+
FeatureFlagModule.forRoot({ ... }),
|
|
561
|
+
FeatureFlagAdminModule.register({
|
|
562
|
+
guard: AdminAuthGuard,
|
|
563
|
+
// path: 'feature-flags', // default
|
|
564
|
+
}),
|
|
565
|
+
],
|
|
566
|
+
})
|
|
567
|
+
export class AppModule {}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### Endpoints
|
|
571
|
+
|
|
572
|
+
| Method | Route | Description | Error Responses |
|
|
573
|
+
|--------|-------|-------------|-----------------|
|
|
574
|
+
| POST | `/feature-flags` | Create a flag | 409 duplicate key, 400 invalid percentage |
|
|
575
|
+
| GET | `/feature-flags` | List all flags | |
|
|
576
|
+
| GET | `/feature-flags/:key` | Get a single flag | 404 not found |
|
|
577
|
+
| PATCH | `/feature-flags/:key` | Update a flag | 404 not found, 400 invalid percentage |
|
|
578
|
+
| DELETE | `/feature-flags/:key` | Archive a flag | 404 not found |
|
|
579
|
+
| POST | `/feature-flags/:key/overrides` | Set an override | 404 flag not found |
|
|
580
|
+
| DELETE | `/feature-flags/:key/overrides` | Remove an override | 404 flag not found |
|
|
581
|
+
|
|
582
|
+
Percentage values must be between 0 and 100 (inclusive). Invalid values return 400 Bad Request.
|
|
583
|
+
|
|
584
|
+
## Custom Persistence (Advanced)
|
|
585
|
+
|
|
586
|
+
The default `PrismaFeatureFlagRepository` can be replaced with any implementation of `FeatureFlagRepository`:
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
import {
|
|
590
|
+
FeatureFlagModule,
|
|
591
|
+
FEATURE_FLAG_REPOSITORY,
|
|
592
|
+
FeatureFlagRepository,
|
|
593
|
+
} from '@nestarc/feature-flag';
|
|
594
|
+
|
|
595
|
+
@Module({
|
|
596
|
+
imports: [
|
|
597
|
+
FeatureFlagModule.forRoot({
|
|
598
|
+
environment: 'production',
|
|
599
|
+
prisma, // still required for module init, but unused if you override the repository
|
|
600
|
+
}),
|
|
601
|
+
],
|
|
602
|
+
providers: [
|
|
603
|
+
{
|
|
604
|
+
provide: FEATURE_FLAG_REPOSITORY,
|
|
605
|
+
useClass: MyCustomRepository, // implements FeatureFlagRepository
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
})
|
|
609
|
+
export class AppModule {}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
## Custom Tenant Resolution (Advanced)
|
|
613
|
+
|
|
614
|
+
Override the default `@nestarc/tenancy` integration with your own `TenantContextProvider`:
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
import {
|
|
618
|
+
FeatureFlagModule,
|
|
619
|
+
TENANT_CONTEXT_PROVIDER,
|
|
620
|
+
TenantContextProvider,
|
|
621
|
+
} from '@nestarc/feature-flag';
|
|
622
|
+
|
|
623
|
+
@Injectable()
|
|
624
|
+
class MyTenantProvider implements TenantContextProvider {
|
|
625
|
+
getCurrentTenantId(): string | null {
|
|
626
|
+
// your custom tenant resolution logic
|
|
627
|
+
return 'tenant-from-custom-source';
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
@Module({
|
|
632
|
+
imports: [FeatureFlagModule.forRoot({ ... })],
|
|
633
|
+
providers: [
|
|
634
|
+
{ provide: TENANT_CONTEXT_PROVIDER, useClass: MyTenantProvider },
|
|
635
|
+
],
|
|
636
|
+
})
|
|
637
|
+
export class AppModule {}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Performance
|
|
641
|
+
|
|
642
|
+
Measured with PostgreSQL 16, Prisma 6, 500 iterations on Apple Silicon:
|
|
643
|
+
|
|
644
|
+
| Scenario | Avg | P50 | P95 | P99 |
|
|
645
|
+
|----------|-----|-----|-----|-----|
|
|
646
|
+
| **isEnabled() — cache hit** | **0.04ms** | **0.03ms** | **0.05ms** | **0.07ms** |
|
|
647
|
+
| isEnabled() — cache miss (DB lookup) | 1.30ms | 1.14ms | 2.54ms | 3.69ms |
|
|
648
|
+
| isEnabled() — override cascade (cold) | 1.07ms | 1.02ms | 1.43ms | 2.11ms |
|
|
649
|
+
| **evaluateAll() — 50 flags (mixed)** | **0.19ms** | **0.04ms** | **1.55ms** | **1.71ms** |
|
|
650
|
+
|
|
651
|
+
Cache speedup: **32.5x** (hit vs miss). Keep the default 30s cache TTL for optimal performance.
|
|
652
|
+
|
|
653
|
+
> Reproduce: `docker compose up -d && dotenv -e .env.test -- npx ts-node benchmarks/evaluation-overhead.ts`
|
|
654
|
+
|
|
507
655
|
## License
|
|
508
656
|
|
|
509
657
|
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-options.interface.js","sourceRoot":"","sources":["../../src/admin/admin-options.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FeatureFlagService } from '../services/feature-flag.service';
|
|
2
|
+
import { CreateFeatureFlagInput, UpdateFeatureFlagInput, SetOverrideInput, FeatureFlagWithOverrides } from '../interfaces/feature-flag.interface';
|
|
3
|
+
import { RemoveOverrideInput } from '../interfaces/feature-flag.interface';
|
|
4
|
+
export declare class FeatureFlagAdminController {
|
|
5
|
+
private readonly service;
|
|
6
|
+
constructor(service: FeatureFlagService);
|
|
7
|
+
create(input: CreateFeatureFlagInput): Promise<FeatureFlagWithOverrides>;
|
|
8
|
+
findAll(): Promise<FeatureFlagWithOverrides[]>;
|
|
9
|
+
findByKey(key: string): Promise<FeatureFlagWithOverrides>;
|
|
10
|
+
update(key: string, input: UpdateFeatureFlagInput): Promise<FeatureFlagWithOverrides>;
|
|
11
|
+
archive(key: string): Promise<FeatureFlagWithOverrides>;
|
|
12
|
+
setOverride(key: string, input: SetOverrideInput): Promise<void>;
|
|
13
|
+
removeOverride(key: string, input: RemoveOverrideInput): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.FeatureFlagAdminController = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const feature_flag_service_1 = require("../services/feature-flag.service");
|
|
18
|
+
let FeatureFlagAdminController = class FeatureFlagAdminController {
|
|
19
|
+
constructor(service) {
|
|
20
|
+
this.service = service;
|
|
21
|
+
}
|
|
22
|
+
create(input) {
|
|
23
|
+
return this.service.create(input);
|
|
24
|
+
}
|
|
25
|
+
findAll() {
|
|
26
|
+
return this.service.findAll();
|
|
27
|
+
}
|
|
28
|
+
findByKey(key) {
|
|
29
|
+
return this.service.findByKey(key);
|
|
30
|
+
}
|
|
31
|
+
update(key, input) {
|
|
32
|
+
return this.service.update(key, input);
|
|
33
|
+
}
|
|
34
|
+
archive(key) {
|
|
35
|
+
return this.service.archive(key);
|
|
36
|
+
}
|
|
37
|
+
setOverride(key, input) {
|
|
38
|
+
return this.service.setOverride(key, input);
|
|
39
|
+
}
|
|
40
|
+
removeOverride(key, input) {
|
|
41
|
+
return this.service.removeOverride(key, input);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.FeatureFlagAdminController = FeatureFlagAdminController;
|
|
45
|
+
__decorate([
|
|
46
|
+
(0, common_1.Post)(),
|
|
47
|
+
__param(0, (0, common_1.Body)()),
|
|
48
|
+
__metadata("design:type", Function),
|
|
49
|
+
__metadata("design:paramtypes", [Object]),
|
|
50
|
+
__metadata("design:returntype", Promise)
|
|
51
|
+
], FeatureFlagAdminController.prototype, "create", null);
|
|
52
|
+
__decorate([
|
|
53
|
+
(0, common_1.Get)(),
|
|
54
|
+
__metadata("design:type", Function),
|
|
55
|
+
__metadata("design:paramtypes", []),
|
|
56
|
+
__metadata("design:returntype", Promise)
|
|
57
|
+
], FeatureFlagAdminController.prototype, "findAll", null);
|
|
58
|
+
__decorate([
|
|
59
|
+
(0, common_1.Get)(':key'),
|
|
60
|
+
__param(0, (0, common_1.Param)('key')),
|
|
61
|
+
__metadata("design:type", Function),
|
|
62
|
+
__metadata("design:paramtypes", [String]),
|
|
63
|
+
__metadata("design:returntype", Promise)
|
|
64
|
+
], FeatureFlagAdminController.prototype, "findByKey", null);
|
|
65
|
+
__decorate([
|
|
66
|
+
(0, common_1.Patch)(':key'),
|
|
67
|
+
__param(0, (0, common_1.Param)('key')),
|
|
68
|
+
__param(1, (0, common_1.Body)()),
|
|
69
|
+
__metadata("design:type", Function),
|
|
70
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
71
|
+
__metadata("design:returntype", Promise)
|
|
72
|
+
], FeatureFlagAdminController.prototype, "update", null);
|
|
73
|
+
__decorate([
|
|
74
|
+
(0, common_1.Delete)(':key'),
|
|
75
|
+
__param(0, (0, common_1.Param)('key')),
|
|
76
|
+
__metadata("design:type", Function),
|
|
77
|
+
__metadata("design:paramtypes", [String]),
|
|
78
|
+
__metadata("design:returntype", Promise)
|
|
79
|
+
], FeatureFlagAdminController.prototype, "archive", null);
|
|
80
|
+
__decorate([
|
|
81
|
+
(0, common_1.Post)(':key/overrides'),
|
|
82
|
+
__param(0, (0, common_1.Param)('key')),
|
|
83
|
+
__param(1, (0, common_1.Body)()),
|
|
84
|
+
__metadata("design:type", Function),
|
|
85
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
86
|
+
__metadata("design:returntype", Promise)
|
|
87
|
+
], FeatureFlagAdminController.prototype, "setOverride", null);
|
|
88
|
+
__decorate([
|
|
89
|
+
(0, common_1.Delete)(':key/overrides'),
|
|
90
|
+
__param(0, (0, common_1.Param)('key')),
|
|
91
|
+
__param(1, (0, common_1.Body)()),
|
|
92
|
+
__metadata("design:type", Function),
|
|
93
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
94
|
+
__metadata("design:returntype", Promise)
|
|
95
|
+
], FeatureFlagAdminController.prototype, "removeOverride", null);
|
|
96
|
+
exports.FeatureFlagAdminController = FeatureFlagAdminController = __decorate([
|
|
97
|
+
(0, common_1.Controller)(),
|
|
98
|
+
__metadata("design:paramtypes", [feature_flag_service_1.FeatureFlagService])
|
|
99
|
+
], FeatureFlagAdminController);
|
|
100
|
+
//# sourceMappingURL=feature-flag-admin.controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-flag-admin.controller.js","sourceRoot":"","sources":["../../src/admin/feature-flag-admin.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAQwB;AACxB,2EAAsE;AAU/D,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IACrC,YAA6B,OAA2B;QAA3B,YAAO,GAAP,OAAO,CAAoB;IAAG,CAAC;IAG5D,MAAM,CAAS,KAA6B;QAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAGD,OAAO;QACL,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;IAGD,SAAS,CAAe,GAAW;QACjC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAGD,MAAM,CACU,GAAW,EACjB,KAA6B;QAErC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAGD,OAAO,CAAe,GAAW;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAGD,WAAW,CACK,GAAW,EACjB,KAAuB;QAE/B,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IAGD,cAAc,CACE,GAAW,EACjB,KAA0B;QAElC,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;CACF,CAAA;AA9CY,gEAA0B;AAIrC;IADC,IAAA,aAAI,GAAE;IACC,WAAA,IAAA,aAAI,GAAE,CAAA;;;;wDAEb;AAGD;IADC,IAAA,YAAG,GAAE;;;;yDAGL;AAGD;IADC,IAAA,YAAG,EAAC,MAAM,CAAC;IACD,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;2DAEtB;AAGD;IADC,IAAA,cAAK,EAAC,MAAM,CAAC;IAEX,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;IACZ,WAAA,IAAA,aAAI,GAAE,CAAA;;;;wDAGR;AAGD;IADC,IAAA,eAAM,EAAC,MAAM,CAAC;IACN,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;yDAEpB;AAGD;IADC,IAAA,aAAI,EAAC,gBAAgB,CAAC;IAEpB,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;IACZ,WAAA,IAAA,aAAI,GAAE,CAAA;;;;6DAGR;AAGD;IADC,IAAA,eAAM,EAAC,gBAAgB,CAAC;IAEtB,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;IACZ,WAAA,IAAA,aAAI,GAAE,CAAA;;;;gEAGR;qCA7CU,0BAA0B;IADtC,IAAA,mBAAU,GAAE;qCAE2B,yCAAkB;GAD7C,0BAA0B,CA8CtC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
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 FeatureFlagAdminModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.FeatureFlagAdminModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const feature_flag_admin_controller_1 = require("./feature-flag-admin.controller");
|
|
13
|
+
let FeatureFlagAdminModule = FeatureFlagAdminModule_1 = class FeatureFlagAdminModule {
|
|
14
|
+
static register(options) {
|
|
15
|
+
if (!options.guard) {
|
|
16
|
+
throw new Error('FeatureFlagAdminModule requires a guard. ' +
|
|
17
|
+
'Pass your auth guard via register({ guard: MyGuard }).');
|
|
18
|
+
}
|
|
19
|
+
const path = options.path ?? 'feature-flags';
|
|
20
|
+
Reflect.defineMetadata('path', path, feature_flag_admin_controller_1.FeatureFlagAdminController);
|
|
21
|
+
Reflect.defineMetadata('__guards__', [options.guard], feature_flag_admin_controller_1.FeatureFlagAdminController);
|
|
22
|
+
return {
|
|
23
|
+
module: FeatureFlagAdminModule_1,
|
|
24
|
+
controllers: [feature_flag_admin_controller_1.FeatureFlagAdminController],
|
|
25
|
+
providers: [options.guard],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.FeatureFlagAdminModule = FeatureFlagAdminModule;
|
|
30
|
+
exports.FeatureFlagAdminModule = FeatureFlagAdminModule = FeatureFlagAdminModule_1 = __decorate([
|
|
31
|
+
(0, common_1.Module)({})
|
|
32
|
+
], FeatureFlagAdminModule);
|
|
33
|
+
//# sourceMappingURL=feature-flag-admin.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-flag-admin.module.js","sourceRoot":"","sources":["../../src/admin/feature-flag-admin.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,mFAA6E;AAItE,IAAM,sBAAsB,8BAA5B,MAAM,sBAAsB;IACjC,MAAM,CAAC,QAAQ,CAAC,OAAgC;QAC9C,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,2CAA2C;gBAC3C,wDAAwD,CACzD,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,eAAe,CAAC;QAE7C,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,0DAA0B,CAAC,CAAC;QACjE,OAAO,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,0DAA0B,CAAC,CAAC;QAElF,OAAO;YACL,MAAM,EAAE,wBAAsB;YAC9B,WAAW,EAAE,CAAC,0DAA0B,CAAC;YACzC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;SAC3B,CAAC;IACJ,CAAC;CACF,CAAA;AApBY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,sBAAsB,CAoBlC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CacheAdapter } from '../interfaces/cache-adapter.interface';
|
|
2
|
+
import { FeatureFlagWithOverrides } from '../interfaces/feature-flag.interface';
|
|
3
|
+
export declare class MemoryCacheAdapter implements CacheAdapter {
|
|
4
|
+
private cache;
|
|
5
|
+
private allFlagsCache;
|
|
6
|
+
get(key: string): Promise<FeatureFlagWithOverrides | null>;
|
|
7
|
+
set(key: string, data: FeatureFlagWithOverrides, ttlMs: number): Promise<void>;
|
|
8
|
+
getAll(): Promise<FeatureFlagWithOverrides[] | null>;
|
|
9
|
+
setAll(data: FeatureFlagWithOverrides[], ttlMs: number): Promise<void>;
|
|
10
|
+
invalidate(key?: string): Promise<void>;
|
|
11
|
+
}
|
|
@@ -5,23 +5,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
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
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
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
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
-
};
|
|
14
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
9
|
+
exports.MemoryCacheAdapter = void 0;
|
|
16
10
|
const common_1 = require("@nestjs/common");
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
constructor(options) {
|
|
20
|
-
this.options = options;
|
|
11
|
+
let MemoryCacheAdapter = class MemoryCacheAdapter {
|
|
12
|
+
constructor() {
|
|
21
13
|
this.cache = new Map();
|
|
22
14
|
this.allFlagsCache = null;
|
|
23
15
|
}
|
|
24
|
-
get(key) {
|
|
16
|
+
async get(key) {
|
|
25
17
|
const entry = this.cache.get(key);
|
|
26
18
|
if (!entry)
|
|
27
19
|
return null;
|
|
@@ -31,13 +23,12 @@ let FlagCacheService = class FlagCacheService {
|
|
|
31
23
|
}
|
|
32
24
|
return entry.data;
|
|
33
25
|
}
|
|
34
|
-
set(key, data) {
|
|
35
|
-
|
|
36
|
-
if (ttl === 0)
|
|
26
|
+
async set(key, data, ttlMs) {
|
|
27
|
+
if (ttlMs === 0)
|
|
37
28
|
return;
|
|
38
|
-
this.cache.set(key, { data, expiresAt: Date.now() +
|
|
29
|
+
this.cache.set(key, { data, expiresAt: Date.now() + ttlMs });
|
|
39
30
|
}
|
|
40
|
-
getAll() {
|
|
31
|
+
async getAll() {
|
|
41
32
|
if (!this.allFlagsCache)
|
|
42
33
|
return null;
|
|
43
34
|
if (Date.now() > this.allFlagsCache.expiresAt) {
|
|
@@ -46,27 +37,23 @@ let FlagCacheService = class FlagCacheService {
|
|
|
46
37
|
}
|
|
47
38
|
return this.allFlagsCache.data;
|
|
48
39
|
}
|
|
49
|
-
setAll(data) {
|
|
50
|
-
|
|
51
|
-
if (ttl === 0)
|
|
40
|
+
async setAll(data, ttlMs) {
|
|
41
|
+
if (ttlMs === 0)
|
|
52
42
|
return;
|
|
53
|
-
this.allFlagsCache = { data, expiresAt: Date.now() +
|
|
43
|
+
this.allFlagsCache = { data, expiresAt: Date.now() + ttlMs };
|
|
54
44
|
}
|
|
55
|
-
invalidate(key) {
|
|
45
|
+
async invalidate(key) {
|
|
56
46
|
if (key) {
|
|
57
47
|
this.cache.delete(key);
|
|
58
|
-
this.allFlagsCache = null;
|
|
59
48
|
}
|
|
60
49
|
else {
|
|
61
50
|
this.cache.clear();
|
|
62
|
-
this.allFlagsCache = null;
|
|
63
51
|
}
|
|
52
|
+
this.allFlagsCache = null;
|
|
64
53
|
}
|
|
65
54
|
};
|
|
66
|
-
exports.
|
|
67
|
-
exports.
|
|
68
|
-
(0, common_1.Injectable)()
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
], FlagCacheService);
|
|
72
|
-
//# sourceMappingURL=flag-cache.service.js.map
|
|
55
|
+
exports.MemoryCacheAdapter = MemoryCacheAdapter;
|
|
56
|
+
exports.MemoryCacheAdapter = MemoryCacheAdapter = __decorate([
|
|
57
|
+
(0, common_1.Injectable)()
|
|
58
|
+
], MemoryCacheAdapter);
|
|
59
|
+
//# sourceMappingURL=memory-cache.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-cache.adapter.js","sourceRoot":"","sources":["../../src/cache/memory-cache.adapter.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAUrC,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAAxB;QACG,UAAK,GAAG,IAAI,GAAG,EAAgD,CAAC;QAChE,kBAAa,GAAkD,IAAI,CAAC;IAuC9E,CAAC;IArCC,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,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,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,IAA8B,EAAE,KAAa;QAClE,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO;QACxB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,MAAM;QACV,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,KAAK,CAAC,MAAM,CAAC,IAAgC,EAAE,KAAa;QAC1D,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO;QACxB,IAAI,CAAC,aAAa,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,GAAY;QAC3B,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC5B,CAAC;CACF,CAAA;AAzCY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;GACA,kBAAkB,CAyC9B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
import { CacheAdapter } from '../interfaces/cache-adapter.interface';
|
|
3
|
+
import { FeatureFlagWithOverrides } from '../interfaces/feature-flag.interface';
|
|
4
|
+
export interface RedisCacheAdapterOptions {
|
|
5
|
+
client: Redis;
|
|
6
|
+
subscriber?: Redis;
|
|
7
|
+
keyPrefix?: string;
|
|
8
|
+
channel?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class RedisCacheAdapter implements CacheAdapter {
|
|
11
|
+
private readonly client;
|
|
12
|
+
private readonly subscriber;
|
|
13
|
+
private readonly keyPrefix;
|
|
14
|
+
private readonly channel;
|
|
15
|
+
private readonly ownsSubscriber;
|
|
16
|
+
constructor(options: RedisCacheAdapterOptions);
|
|
17
|
+
private setupSubscription;
|
|
18
|
+
get(key: string): Promise<FeatureFlagWithOverrides | null>;
|
|
19
|
+
set(key: string, data: FeatureFlagWithOverrides, ttlMs: number): Promise<void>;
|
|
20
|
+
getAll(): Promise<FeatureFlagWithOverrides[] | null>;
|
|
21
|
+
setAll(data: FeatureFlagWithOverrides[], ttlMs: number): Promise<void>;
|
|
22
|
+
invalidate(key?: string): Promise<void>;
|
|
23
|
+
onModuleDestroy(): Promise<void>;
|
|
24
|
+
private prefixedKey;
|
|
25
|
+
private flushLocal;
|
|
26
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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.RedisCacheAdapter = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
let RedisCacheAdapter = class RedisCacheAdapter {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.client = options.client;
|
|
17
|
+
this.keyPrefix = options.keyPrefix ?? 'feature-flag:';
|
|
18
|
+
this.channel = options.channel ?? 'feature-flag:invalidate';
|
|
19
|
+
if (options.subscriber) {
|
|
20
|
+
this.subscriber = options.subscriber;
|
|
21
|
+
this.ownsSubscriber = false;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.subscriber = this.client.duplicate();
|
|
25
|
+
this.ownsSubscriber = true;
|
|
26
|
+
}
|
|
27
|
+
this.setupSubscription();
|
|
28
|
+
}
|
|
29
|
+
setupSubscription() {
|
|
30
|
+
this.subscriber.subscribe(this.channel);
|
|
31
|
+
this.subscriber.on('message', (ch, message) => {
|
|
32
|
+
if (ch !== this.channel)
|
|
33
|
+
return;
|
|
34
|
+
if (message === '__all__') {
|
|
35
|
+
this.flushLocal();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.client.del(this.prefixedKey(message));
|
|
39
|
+
this.client.del(this.prefixedKey('__all__'));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async get(key) {
|
|
44
|
+
const raw = await this.client.get(this.prefixedKey(key));
|
|
45
|
+
if (!raw)
|
|
46
|
+
return null;
|
|
47
|
+
return JSON.parse(raw);
|
|
48
|
+
}
|
|
49
|
+
async set(key, data, ttlMs) {
|
|
50
|
+
if (ttlMs === 0)
|
|
51
|
+
return;
|
|
52
|
+
await this.client.set(this.prefixedKey(key), JSON.stringify(data), 'PX', ttlMs);
|
|
53
|
+
}
|
|
54
|
+
async getAll() {
|
|
55
|
+
const raw = await this.client.get(this.prefixedKey('__all__'));
|
|
56
|
+
if (!raw)
|
|
57
|
+
return null;
|
|
58
|
+
return JSON.parse(raw);
|
|
59
|
+
}
|
|
60
|
+
async setAll(data, ttlMs) {
|
|
61
|
+
if (ttlMs === 0)
|
|
62
|
+
return;
|
|
63
|
+
await this.client.set(this.prefixedKey('__all__'), JSON.stringify(data), 'PX', ttlMs);
|
|
64
|
+
}
|
|
65
|
+
async invalidate(key) {
|
|
66
|
+
if (key) {
|
|
67
|
+
await this.client.del(this.prefixedKey(key));
|
|
68
|
+
await this.client.del(this.prefixedKey('__all__'));
|
|
69
|
+
await this.client.publish(this.channel, key);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
await this.flushLocal();
|
|
73
|
+
await this.client.publish(this.channel, '__all__');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async onModuleDestroy() {
|
|
77
|
+
await this.subscriber.unsubscribe(this.channel);
|
|
78
|
+
if (this.ownsSubscriber) {
|
|
79
|
+
await this.subscriber.quit();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
prefixedKey(key) {
|
|
83
|
+
return `${this.keyPrefix}${key}`;
|
|
84
|
+
}
|
|
85
|
+
async flushLocal() {
|
|
86
|
+
const pattern = `${this.keyPrefix}*`;
|
|
87
|
+
let cursor = '0';
|
|
88
|
+
do {
|
|
89
|
+
const [nextCursor, keys] = await this.client.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
|
|
90
|
+
cursor = nextCursor;
|
|
91
|
+
if (keys.length > 0) {
|
|
92
|
+
await this.client.del(...keys);
|
|
93
|
+
}
|
|
94
|
+
} while (cursor !== '0');
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
exports.RedisCacheAdapter = RedisCacheAdapter;
|
|
98
|
+
exports.RedisCacheAdapter = RedisCacheAdapter = __decorate([
|
|
99
|
+
(0, common_1.Injectable)(),
|
|
100
|
+
__metadata("design:paramtypes", [Object])
|
|
101
|
+
], RedisCacheAdapter);
|
|
102
|
+
//# sourceMappingURL=redis-cache.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.adapter.js","sourceRoot":"","sources":["../../src/cache/redis-cache.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,3 +3,6 @@ export declare const DEFAULT_CACHE_TTL_MS = 30000;
|
|
|
3
3
|
export declare const FEATURE_FLAG_KEY = "FEATURE_FLAG_KEY";
|
|
4
4
|
export declare const FEATURE_FLAG_OPTIONS_KEY = "FEATURE_FLAG_OPTIONS_KEY";
|
|
5
5
|
export declare const BYPASS_FEATURE_FLAG_KEY = "BYPASS_FEATURE_FLAG_KEY";
|
|
6
|
+
export declare const CACHE_ADAPTER: unique symbol;
|
|
7
|
+
export declare const FEATURE_FLAG_REPOSITORY: unique symbol;
|
|
8
|
+
export declare const TENANT_CONTEXT_PROVIDER: unique symbol;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BYPASS_FEATURE_FLAG_KEY = exports.FEATURE_FLAG_OPTIONS_KEY = exports.FEATURE_FLAG_KEY = exports.DEFAULT_CACHE_TTL_MS = exports.FEATURE_FLAG_MODULE_OPTIONS = void 0;
|
|
3
|
+
exports.TENANT_CONTEXT_PROVIDER = exports.FEATURE_FLAG_REPOSITORY = exports.CACHE_ADAPTER = exports.BYPASS_FEATURE_FLAG_KEY = exports.FEATURE_FLAG_OPTIONS_KEY = exports.FEATURE_FLAG_KEY = exports.DEFAULT_CACHE_TTL_MS = exports.FEATURE_FLAG_MODULE_OPTIONS = void 0;
|
|
4
4
|
exports.FEATURE_FLAG_MODULE_OPTIONS = Symbol('FEATURE_FLAG_MODULE_OPTIONS');
|
|
5
5
|
exports.DEFAULT_CACHE_TTL_MS = 30_000;
|
|
6
6
|
exports.FEATURE_FLAG_KEY = 'FEATURE_FLAG_KEY';
|
|
7
7
|
exports.FEATURE_FLAG_OPTIONS_KEY = 'FEATURE_FLAG_OPTIONS_KEY';
|
|
8
8
|
exports.BYPASS_FEATURE_FLAG_KEY = 'BYPASS_FEATURE_FLAG_KEY';
|
|
9
|
+
exports.CACHE_ADAPTER = Symbol('CACHE_ADAPTER');
|
|
10
|
+
exports.FEATURE_FLAG_REPOSITORY = Symbol('FEATURE_FLAG_REPOSITORY');
|
|
11
|
+
exports.TENANT_CONTEXT_PROVIDER = Symbol('TENANT_CONTEXT_PROVIDER');
|
|
9
12
|
//# sourceMappingURL=feature-flag.constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-flag.constants.js","sourceRoot":"","sources":["../src/feature-flag.constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,2BAA2B,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAC;AAEpE,QAAA,oBAAoB,GAAG,MAAM,CAAC;AAE9B,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AACtC,QAAA,wBAAwB,GAAG,0BAA0B,CAAC;AACtD,QAAA,uBAAuB,GAAG,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"feature-flag.constants.js","sourceRoot":"","sources":["../src/feature-flag.constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,2BAA2B,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAC;AAEpE,QAAA,oBAAoB,GAAG,MAAM,CAAC;AAE9B,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AACtC,QAAA,wBAAwB,GAAG,0BAA0B,CAAC;AACtD,QAAA,uBAAuB,GAAG,yBAAyB,CAAC;AAEpD,QAAA,aAAa,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;AACxC,QAAA,uBAAuB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAC5D,QAAA,uBAAuB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC"}
|