@breadstone/archipel-platform-feature-flags 0.0.32
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 +10 -0
- package/package.json +100 -0
- package/src/FeatureFlagModule.d.ts +20 -0
- package/src/FeatureFlagModule.js +59 -0
- package/src/azure/AzureFeatureFlagReader.d.ts +46 -0
- package/src/azure/AzureFeatureFlagReader.js +190 -0
- package/src/azure/AzureFeatureFlagWriter.d.ts +43 -0
- package/src/azure/AzureFeatureFlagWriter.js +231 -0
- package/src/azure/env.d.ts +11 -0
- package/src/azure/env.js +44 -0
- package/src/azure/index.d.ts +3 -0
- package/src/azure/index.js +14 -0
- package/src/contracts/FeatureFlagReaderPort.d.ts +54 -0
- package/src/contracts/FeatureFlagReaderPort.js +27 -0
- package/src/contracts/FeatureFlagWriterPort.d.ts +64 -0
- package/src/contracts/FeatureFlagWriterPort.js +31 -0
- package/src/contracts/index.d.ts +2 -0
- package/src/contracts/index.js +8 -0
- package/src/decorators/FeatureFlag.d.ts +11 -0
- package/src/decorators/FeatureFlag.js +18 -0
- package/src/decorators/index.d.ts +1 -0
- package/src/decorators/index.js +7 -0
- package/src/env.d.ts +5 -0
- package/src/env.js +35 -0
- package/src/errors/FeatureFlagError.d.ts +10 -0
- package/src/errors/FeatureFlagError.js +21 -0
- package/src/errors/index.d.ts +1 -0
- package/src/errors/index.js +6 -0
- package/src/guards/FeatureFlagGuard.d.ts +30 -0
- package/src/guards/FeatureFlagGuard.js +69 -0
- package/src/guards/index.d.ts +1 -0
- package/src/guards/index.js +6 -0
- package/src/index.d.ts +11 -0
- package/src/index.js +19 -0
- package/src/models/IFeatureFlagContext.d.ts +13 -0
- package/src/models/IFeatureFlagContext.js +3 -0
- package/src/models/IFeatureFlagDefinition.d.ts +17 -0
- package/src/models/IFeatureFlagDefinition.js +3 -0
- package/src/models/IFeatureFlagEvaluation.d.ts +13 -0
- package/src/models/IFeatureFlagEvaluation.js +3 -0
- package/src/models/IFeatureFlagModuleOptions.d.ts +30 -0
- package/src/models/IFeatureFlagModuleOptions.js +4 -0
- package/src/models/index.d.ts +4 -0
- package/src/models/index.js +3 -0
- package/src/vercel/VercelFeatureFlagReader.d.ts +54 -0
- package/src/vercel/VercelFeatureFlagReader.js +158 -0
- package/src/vercel/VercelFeatureFlagWriter.d.ts +71 -0
- package/src/vercel/VercelFeatureFlagWriter.js +308 -0
- package/src/vercel/env.d.ts +17 -0
- package/src/vercel/env.js +69 -0
- package/src/vercel/index.d.ts +3 -0
- package/src/vercel/index.js +17 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #region Imports
|
|
3
|
+
var AzureFeatureFlagWriter_1;
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.AzureFeatureFlagWriter = void 0;
|
|
6
|
+
const tslib_1 = require("tslib");
|
|
7
|
+
const app_configuration_1 = require("@azure/app-configuration");
|
|
8
|
+
const identity_1 = require("@azure/identity");
|
|
9
|
+
const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const FeatureFlagWriterPort_1 = require("../contracts/FeatureFlagWriterPort");
|
|
12
|
+
const FeatureFlagError_1 = require("../errors/FeatureFlagError");
|
|
13
|
+
const env_1 = require("./env");
|
|
14
|
+
// #endregion
|
|
15
|
+
/**
|
|
16
|
+
* Azure App Configuration implementation of the {@link FeatureFlagWriterPort}.
|
|
17
|
+
* Uses `@azure/app-configuration` (`AppConfigurationClient`) for CRUD operations
|
|
18
|
+
* on feature flags stored in Azure App Configuration.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
let AzureFeatureFlagWriter = AzureFeatureFlagWriter_1 = class AzureFeatureFlagWriter extends FeatureFlagWriterPort_1.FeatureFlagWriterPort {
|
|
23
|
+
// #endregion
|
|
24
|
+
// #region Ctor
|
|
25
|
+
constructor(configService) {
|
|
26
|
+
super();
|
|
27
|
+
this._logger = new common_1.Logger(AzureFeatureFlagWriter_1.name);
|
|
28
|
+
this._configService = configService;
|
|
29
|
+
}
|
|
30
|
+
// #endregion
|
|
31
|
+
// #region Methods
|
|
32
|
+
/**
|
|
33
|
+
* Initializes the Azure App Configuration management client.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
async onModuleInit() {
|
|
38
|
+
try {
|
|
39
|
+
const endpoint = this._configService.get(env_1.AZURE_APPCONFIG_ENDPOINT.key);
|
|
40
|
+
const connectionString = this._configService.get(env_1.AZURE_APPCONFIG_CONNECTION_STRING.key);
|
|
41
|
+
if (!endpoint && !connectionString) {
|
|
42
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', 'Either AZURE_APPCONFIG_ENDPOINT or AZURE_APPCONFIG_CONNECTION_STRING must be configured.');
|
|
43
|
+
}
|
|
44
|
+
if (endpoint) {
|
|
45
|
+
this._client = new app_configuration_1.AppConfigurationClient(endpoint, new identity_1.DefaultAzureCredential());
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this._client = new app_configuration_1.AppConfigurationClient(connectionString);
|
|
49
|
+
}
|
|
50
|
+
this._logger.log('[AzureFeatureFlagWriter] Initialized successfully');
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof FeatureFlagError_1.FeatureFlagError) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', 'Failed to initialize Azure App Configuration admin client', error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** @inheritdoc */
|
|
60
|
+
async getFlag(key) {
|
|
61
|
+
this.ensureInitialized();
|
|
62
|
+
try {
|
|
63
|
+
const setting = await this._client.getConfigurationSetting({
|
|
64
|
+
key: `${app_configuration_1.featureFlagPrefix}${key}`,
|
|
65
|
+
});
|
|
66
|
+
if (!(0, app_configuration_1.isFeatureFlag)(setting)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const parsed = (0, app_configuration_1.parseFeatureFlag)(setting);
|
|
70
|
+
return {
|
|
71
|
+
key,
|
|
72
|
+
enabled: parsed.value.enabled,
|
|
73
|
+
description: parsed.value.description,
|
|
74
|
+
label: parsed.label,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (this.isNotFoundError(error)) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
this._logger.error(`[AzureFeatureFlagWriter] getFlag failed for "${key}"`, {
|
|
82
|
+
key,
|
|
83
|
+
error: error.message,
|
|
84
|
+
});
|
|
85
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', `Failed to get feature flag "${key}"`, error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** @inheritdoc */
|
|
89
|
+
async listFlags() {
|
|
90
|
+
this.ensureInitialized();
|
|
91
|
+
try {
|
|
92
|
+
const flags = [];
|
|
93
|
+
const settings = this._client.listConfigurationSettings({
|
|
94
|
+
keyFilter: `${app_configuration_1.featureFlagPrefix}*`,
|
|
95
|
+
});
|
|
96
|
+
for await (const setting of settings) {
|
|
97
|
+
if (!(0, app_configuration_1.isFeatureFlag)(setting)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const parsed = (0, app_configuration_1.parseFeatureFlag)(setting);
|
|
101
|
+
const key = setting.key.replace(app_configuration_1.featureFlagPrefix, '');
|
|
102
|
+
flags.push({
|
|
103
|
+
key,
|
|
104
|
+
enabled: parsed.value.enabled,
|
|
105
|
+
description: parsed.value.description,
|
|
106
|
+
label: parsed.label,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return flags;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
this._logger.error('[AzureFeatureFlagWriter] listFlags failed', {
|
|
113
|
+
error: error.message,
|
|
114
|
+
});
|
|
115
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', 'Failed to list feature flags', error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** @inheritdoc */
|
|
119
|
+
async createFlag(definition) {
|
|
120
|
+
this.ensureInitialized();
|
|
121
|
+
try {
|
|
122
|
+
await this._client.addConfigurationSetting({
|
|
123
|
+
key: `${app_configuration_1.featureFlagPrefix}${definition.key}`,
|
|
124
|
+
contentType: app_configuration_1.featureFlagContentType,
|
|
125
|
+
label: definition.label,
|
|
126
|
+
value: JSON.stringify({
|
|
127
|
+
id: definition.key,
|
|
128
|
+
enabled: definition.enabled,
|
|
129
|
+
description: definition.description ?? '',
|
|
130
|
+
conditions: { client_filters: [] },
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
this._logger.log(`[AzureFeatureFlagWriter] Created flag "${definition.key}"`);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
this._logger.error(`[AzureFeatureFlagWriter] createFlag failed for "${definition.key}"`, {
|
|
137
|
+
key: definition.key,
|
|
138
|
+
error: error.message,
|
|
139
|
+
});
|
|
140
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', `Failed to create feature flag "${definition.key}"`, error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** @inheritdoc */
|
|
144
|
+
async updateFlag(key, updates) {
|
|
145
|
+
this.ensureInitialized();
|
|
146
|
+
try {
|
|
147
|
+
const settingKey = `${app_configuration_1.featureFlagPrefix}${key}`;
|
|
148
|
+
const existing = await this._client.getConfigurationSetting({ key: settingKey });
|
|
149
|
+
if (!(0, app_configuration_1.isFeatureFlag)(existing)) {
|
|
150
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', `Configuration setting "${key}" is not a feature flag.`);
|
|
151
|
+
}
|
|
152
|
+
const parsed = (0, app_configuration_1.parseFeatureFlag)(existing);
|
|
153
|
+
const updatedValue = {
|
|
154
|
+
id: key,
|
|
155
|
+
enabled: updates.enabled ?? parsed.value.enabled,
|
|
156
|
+
description: updates.description ?? parsed.value.description ?? '',
|
|
157
|
+
conditions: parsed.value.conditions ?? { client_filters: [] },
|
|
158
|
+
};
|
|
159
|
+
await this._client.setConfigurationSetting({
|
|
160
|
+
key: settingKey,
|
|
161
|
+
contentType: app_configuration_1.featureFlagContentType,
|
|
162
|
+
label: updates.label ?? existing.label,
|
|
163
|
+
value: JSON.stringify(updatedValue),
|
|
164
|
+
});
|
|
165
|
+
this._logger.log(`[AzureFeatureFlagWriter] Updated flag "${key}"`);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (error instanceof FeatureFlagError_1.FeatureFlagError) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
this._logger.error(`[AzureFeatureFlagWriter] updateFlag failed for "${key}"`, {
|
|
172
|
+
key,
|
|
173
|
+
error: error.message,
|
|
174
|
+
});
|
|
175
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', `Failed to update feature flag "${key}"`, error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** @inheritdoc */
|
|
179
|
+
async deleteFlag(key) {
|
|
180
|
+
this.ensureInitialized();
|
|
181
|
+
try {
|
|
182
|
+
await this._client.deleteConfigurationSetting({
|
|
183
|
+
key: `${app_configuration_1.featureFlagPrefix}${key}`,
|
|
184
|
+
});
|
|
185
|
+
this._logger.log(`[AzureFeatureFlagWriter] Deleted flag "${key}"`);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
this._logger.error(`[AzureFeatureFlagWriter] deleteFlag failed for "${key}"`, {
|
|
189
|
+
key,
|
|
190
|
+
error: error.message,
|
|
191
|
+
});
|
|
192
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', `Failed to delete feature flag "${key}"`, error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** @inheritdoc */
|
|
196
|
+
async ping() {
|
|
197
|
+
try {
|
|
198
|
+
this.ensureInitialized();
|
|
199
|
+
const iterator = this._client.listConfigurationSettings({ keyFilter: `${app_configuration_1.featureFlagPrefix}*` });
|
|
200
|
+
// Perform a minimal read to verify connectivity.
|
|
201
|
+
await iterator.next();
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Checks whether the error is a 404 (resource not found).
|
|
210
|
+
*/
|
|
211
|
+
isNotFoundError(error) {
|
|
212
|
+
return (typeof error === 'object' &&
|
|
213
|
+
error !== null &&
|
|
214
|
+
'statusCode' in error &&
|
|
215
|
+
error.statusCode === 404);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Ensures the management client has been initialized.
|
|
219
|
+
*/
|
|
220
|
+
ensureInitialized() {
|
|
221
|
+
if (!this._client) {
|
|
222
|
+
throw new FeatureFlagError_1.FeatureFlagError('azure', 'AzureFeatureFlagWriter has not been initialized. Ensure onModuleInit() ran.');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
exports.AzureFeatureFlagWriter = AzureFeatureFlagWriter;
|
|
227
|
+
exports.AzureFeatureFlagWriter = AzureFeatureFlagWriter = AzureFeatureFlagWriter_1 = tslib_1.__decorate([
|
|
228
|
+
(0, common_1.Injectable)(),
|
|
229
|
+
tslib_1.__metadata("design:paramtypes", [archipel_platform_configuration_1.ConfigService])
|
|
230
|
+
], AzureFeatureFlagWriter);
|
|
231
|
+
//# sourceMappingURL=AzureFeatureFlagWriter.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
|
|
2
|
+
/** Azure App Configuration endpoint URL. Required when using Entra ID authentication. */
|
|
3
|
+
export declare const AZURE_APPCONFIG_ENDPOINT: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
|
|
4
|
+
/** Azure App Configuration connection string. Alternative to endpoint + Entra ID. */
|
|
5
|
+
export declare const AZURE_APPCONFIG_CONNECTION_STRING: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
|
|
6
|
+
/** Refresh interval in milliseconds for feature flag state. Defaults to 30000. */
|
|
7
|
+
export declare const AZURE_FEATURE_FLAG_REFRESH_INTERVAL: import("@breadstone/archipel-platform-configuration").IConfigKey<number>;
|
|
8
|
+
/** Key filter for selecting feature flags. Defaults to `*` (all). */
|
|
9
|
+
export declare const AZURE_FEATURE_FLAG_KEY_FILTER: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
|
|
10
|
+
/** Configuration entries required by the Azure feature flag provider. */
|
|
11
|
+
export declare const AZURE_FEATURE_FLAG_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
|
package/src/azure/env.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #region Imports
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AZURE_FEATURE_FLAG_CONFIG_ENTRIES = exports.AZURE_FEATURE_FLAG_KEY_FILTER = exports.AZURE_FEATURE_FLAG_REFRESH_INTERVAL = exports.AZURE_APPCONFIG_CONNECTION_STRING = exports.AZURE_APPCONFIG_ENDPOINT = void 0;
|
|
5
|
+
const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
|
|
6
|
+
// #endregion
|
|
7
|
+
// ──────────────────────────────────────────────────────────────
|
|
8
|
+
// Azure App Configuration
|
|
9
|
+
// ──────────────────────────────────────────────────────────────
|
|
10
|
+
/** Azure App Configuration endpoint URL. Required when using Entra ID authentication. */
|
|
11
|
+
exports.AZURE_APPCONFIG_ENDPOINT = (0, archipel_platform_configuration_1.createConfigKey)('AZURE_APPCONFIG_ENDPOINT');
|
|
12
|
+
/** Azure App Configuration connection string. Alternative to endpoint + Entra ID. */
|
|
13
|
+
exports.AZURE_APPCONFIG_CONNECTION_STRING = (0, archipel_platform_configuration_1.createConfigKey)('AZURE_APPCONFIG_CONNECTION_STRING');
|
|
14
|
+
/** Refresh interval in milliseconds for feature flag state. Defaults to 30000. */
|
|
15
|
+
exports.AZURE_FEATURE_FLAG_REFRESH_INTERVAL = (0, archipel_platform_configuration_1.createConfigKey)('AZURE_FEATURE_FLAG_REFRESH_INTERVAL');
|
|
16
|
+
/** Key filter for selecting feature flags. Defaults to `*` (all). */
|
|
17
|
+
exports.AZURE_FEATURE_FLAG_KEY_FILTER = (0, archipel_platform_configuration_1.createConfigKey)('AZURE_FEATURE_FLAG_KEY_FILTER');
|
|
18
|
+
// ──────────────────────────────────────────────────────────────
|
|
19
|
+
// Registry entries
|
|
20
|
+
// ──────────────────────────────────────────────────────────────
|
|
21
|
+
/** Configuration entries required by the Azure feature flag provider. */
|
|
22
|
+
exports.AZURE_FEATURE_FLAG_CONFIG_ENTRIES = [
|
|
23
|
+
{
|
|
24
|
+
key: exports.AZURE_APPCONFIG_ENDPOINT,
|
|
25
|
+
required: false,
|
|
26
|
+
description: 'Azure App Configuration endpoint URL. Required when using Entra ID authentication.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: exports.AZURE_APPCONFIG_CONNECTION_STRING,
|
|
30
|
+
required: false,
|
|
31
|
+
description: 'Azure App Configuration connection string. Alternative to endpoint + Entra ID.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: exports.AZURE_FEATURE_FLAG_REFRESH_INTERVAL,
|
|
35
|
+
required: false,
|
|
36
|
+
description: 'Refresh interval in milliseconds for feature flag state. Defaults to 30000.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: exports.AZURE_FEATURE_FLAG_KEY_FILTER,
|
|
40
|
+
required: false,
|
|
41
|
+
description: 'Key filter for selecting feature flags. Defaults to * (all).',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { AzureFeatureFlagReader } from './AzureFeatureFlagReader';
|
|
2
|
+
export { AzureFeatureFlagWriter } from './AzureFeatureFlagWriter';
|
|
3
|
+
export { AZURE_APPCONFIG_CONNECTION_STRING, AZURE_APPCONFIG_ENDPOINT, AZURE_FEATURE_FLAG_CONFIG_ENTRIES, AZURE_FEATURE_FLAG_KEY_FILTER, AZURE_FEATURE_FLAG_REFRESH_INTERVAL, } from './env';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AZURE_FEATURE_FLAG_REFRESH_INTERVAL = exports.AZURE_FEATURE_FLAG_KEY_FILTER = exports.AZURE_FEATURE_FLAG_CONFIG_ENTRIES = exports.AZURE_APPCONFIG_ENDPOINT = exports.AZURE_APPCONFIG_CONNECTION_STRING = exports.AzureFeatureFlagWriter = exports.AzureFeatureFlagReader = void 0;
|
|
4
|
+
var AzureFeatureFlagReader_1 = require("./AzureFeatureFlagReader");
|
|
5
|
+
Object.defineProperty(exports, "AzureFeatureFlagReader", { enumerable: true, get: function () { return AzureFeatureFlagReader_1.AzureFeatureFlagReader; } });
|
|
6
|
+
var AzureFeatureFlagWriter_1 = require("./AzureFeatureFlagWriter");
|
|
7
|
+
Object.defineProperty(exports, "AzureFeatureFlagWriter", { enumerable: true, get: function () { return AzureFeatureFlagWriter_1.AzureFeatureFlagWriter; } });
|
|
8
|
+
var env_1 = require("./env");
|
|
9
|
+
Object.defineProperty(exports, "AZURE_APPCONFIG_CONNECTION_STRING", { enumerable: true, get: function () { return env_1.AZURE_APPCONFIG_CONNECTION_STRING; } });
|
|
10
|
+
Object.defineProperty(exports, "AZURE_APPCONFIG_ENDPOINT", { enumerable: true, get: function () { return env_1.AZURE_APPCONFIG_ENDPOINT; } });
|
|
11
|
+
Object.defineProperty(exports, "AZURE_FEATURE_FLAG_CONFIG_ENTRIES", { enumerable: true, get: function () { return env_1.AZURE_FEATURE_FLAG_CONFIG_ENTRIES; } });
|
|
12
|
+
Object.defineProperty(exports, "AZURE_FEATURE_FLAG_KEY_FILTER", { enumerable: true, get: function () { return env_1.AZURE_FEATURE_FLAG_KEY_FILTER; } });
|
|
13
|
+
Object.defineProperty(exports, "AZURE_FEATURE_FLAG_REFRESH_INTERVAL", { enumerable: true, get: function () { return env_1.AZURE_FEATURE_FLAG_REFRESH_INTERVAL; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { IFeatureFlagContext } from '../models/IFeatureFlagContext';
|
|
2
|
+
import type { IFeatureFlagEvaluation } from '../models/IFeatureFlagEvaluation';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract port for feature flag reader operations.
|
|
5
|
+
* Each provider (Azure App Configuration, Vercel Edge Config) must implement this contract
|
|
6
|
+
* so that the consuming application can switch providers without changing business logic.
|
|
7
|
+
*
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class FeatureFlagReaderPort {
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates whether a feature flag is enabled.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
* @param key - The unique key identifying the feature flag.
|
|
16
|
+
* @param context - Optional evaluation context for targeting and segmentation.
|
|
17
|
+
* @returns `true` if the feature flag is enabled.
|
|
18
|
+
*/
|
|
19
|
+
abstract isEnabled(key: string, context?: IFeatureFlagContext): Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the variant value of a feature flag for multivariate flags and A/B testing.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
* @param key - The unique key identifying the feature flag.
|
|
25
|
+
* @param defaultValue - The default value to return if the flag is not found.
|
|
26
|
+
* @param context - Optional evaluation context for targeting and segmentation.
|
|
27
|
+
* @returns The variant value of the feature flag.
|
|
28
|
+
*/
|
|
29
|
+
abstract getVariant<T = unknown>(key: string, defaultValue: T, context?: IFeatureFlagContext): Promise<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Returns all known feature flags with their current evaluation.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
* @param context - Optional evaluation context for targeting and segmentation.
|
|
35
|
+
* @returns Array of evaluated feature flags.
|
|
36
|
+
*/
|
|
37
|
+
abstract getAllFlags(context?: IFeatureFlagContext): Promise<ReadonlyArray<IFeatureFlagEvaluation>>;
|
|
38
|
+
/**
|
|
39
|
+
* Refreshes the flag state from the remote provider.
|
|
40
|
+
* Implementations should reload configuration from the upstream source.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
abstract refresh(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Lightweight connectivity check for the feature flag provider.
|
|
47
|
+
* Override in adapters to perform a real API call.
|
|
48
|
+
* Defaults to `true` if not overridden.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
* @returns `true` if the provider is reachable.
|
|
52
|
+
*/
|
|
53
|
+
ping(): Promise<boolean>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #region Imports
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FeatureFlagReaderPort = void 0;
|
|
5
|
+
// #endregion
|
|
6
|
+
/**
|
|
7
|
+
* Abstract port for feature flag reader operations.
|
|
8
|
+
* Each provider (Azure App Configuration, Vercel Edge Config) must implement this contract
|
|
9
|
+
* so that the consuming application can switch providers without changing business logic.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
class FeatureFlagReaderPort {
|
|
14
|
+
/**
|
|
15
|
+
* Lightweight connectivity check for the feature flag provider.
|
|
16
|
+
* Override in adapters to perform a real API call.
|
|
17
|
+
* Defaults to `true` if not overridden.
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
* @returns `true` if the provider is reachable.
|
|
21
|
+
*/
|
|
22
|
+
async ping() {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.FeatureFlagReaderPort = FeatureFlagReaderPort;
|
|
27
|
+
//# sourceMappingURL=FeatureFlagReaderPort.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { IFeatureFlagDefinition } from '../models/IFeatureFlagDefinition';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract port for feature flag writer operations (CRUD).
|
|
4
|
+
* Each provider (Azure App Configuration, Vercel Edge Config) must implement this contract
|
|
5
|
+
* so that the admin UI backend can manage flags without being coupled to a specific provider.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This port is intentionally separated from {@link FeatureFlagReaderPort} because
|
|
9
|
+
* the read path (consumed by application code) and the write path (consumed by admin tooling)
|
|
10
|
+
* have different deployment contexts, security requirements, and SDK dependencies.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export declare abstract class FeatureFlagWriterPort {
|
|
15
|
+
/**
|
|
16
|
+
* Returns a single feature flag definition by key.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
* @param key - The unique key identifying the feature flag.
|
|
20
|
+
* @returns The flag definition, or `undefined` if not found.
|
|
21
|
+
*/
|
|
22
|
+
abstract getFlag(key: string): Promise<IFeatureFlagDefinition | undefined>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns all feature flag definitions managed by the provider.
|
|
25
|
+
*
|
|
26
|
+
* @public
|
|
27
|
+
* @returns Array of flag definitions.
|
|
28
|
+
*/
|
|
29
|
+
abstract listFlags(): Promise<ReadonlyArray<IFeatureFlagDefinition>>;
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new feature flag.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
* @param definition - The flag definition to create.
|
|
35
|
+
* @throws {FeatureFlagError} If the flag already exists or creation fails.
|
|
36
|
+
*/
|
|
37
|
+
abstract createFlag(definition: IFeatureFlagDefinition): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Updates an existing feature flag.
|
|
40
|
+
* Only the fields present in the partial definition are updated.
|
|
41
|
+
*
|
|
42
|
+
* @public
|
|
43
|
+
* @param key - The unique key of the flag to update.
|
|
44
|
+
* @param updates - Partial definition with the fields to update.
|
|
45
|
+
* @throws {FeatureFlagError} If the flag does not exist or the update fails.
|
|
46
|
+
*/
|
|
47
|
+
abstract updateFlag(key: string, updates: Partial<Omit<IFeatureFlagDefinition, 'key'>>): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Deletes a feature flag.
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
* @param key - The unique key of the flag to delete.
|
|
53
|
+
* @throws {FeatureFlagError} If the deletion fails.
|
|
54
|
+
*/
|
|
55
|
+
abstract deleteFlag(key: string): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Lightweight connectivity check for the writer provider.
|
|
58
|
+
* Defaults to `true` if not overridden.
|
|
59
|
+
*
|
|
60
|
+
* @public
|
|
61
|
+
* @returns `true` if the provider is reachable.
|
|
62
|
+
*/
|
|
63
|
+
ping(): Promise<boolean>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #region Imports
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FeatureFlagWriterPort = void 0;
|
|
5
|
+
// #endregion
|
|
6
|
+
/**
|
|
7
|
+
* Abstract port for feature flag writer operations (CRUD).
|
|
8
|
+
* Each provider (Azure App Configuration, Vercel Edge Config) must implement this contract
|
|
9
|
+
* so that the admin UI backend can manage flags without being coupled to a specific provider.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* This port is intentionally separated from {@link FeatureFlagReaderPort} because
|
|
13
|
+
* the read path (consumed by application code) and the write path (consumed by admin tooling)
|
|
14
|
+
* have different deployment contexts, security requirements, and SDK dependencies.
|
|
15
|
+
*
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
class FeatureFlagWriterPort {
|
|
19
|
+
/**
|
|
20
|
+
* Lightweight connectivity check for the writer provider.
|
|
21
|
+
* Defaults to `true` if not overridden.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
* @returns `true` if the provider is reachable.
|
|
25
|
+
*/
|
|
26
|
+
async ping() {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.FeatureFlagWriterPort = FeatureFlagWriterPort;
|
|
31
|
+
//# sourceMappingURL=FeatureFlagWriterPort.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeatureFlagWriterPort = exports.FeatureFlagReaderPort = void 0;
|
|
4
|
+
var FeatureFlagReaderPort_1 = require("./FeatureFlagReaderPort");
|
|
5
|
+
Object.defineProperty(exports, "FeatureFlagReaderPort", { enumerable: true, get: function () { return FeatureFlagReaderPort_1.FeatureFlagReaderPort; } });
|
|
6
|
+
var FeatureFlagWriterPort_1 = require("./FeatureFlagWriterPort");
|
|
7
|
+
Object.defineProperty(exports, "FeatureFlagWriterPort", { enumerable: true, get: function () { return FeatureFlagWriterPort_1.FeatureFlagWriterPort; } });
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SetMetadata } from '@nestjs/common';
|
|
2
|
+
/** Metadata key used to store the feature flag key on route handlers. */
|
|
3
|
+
export declare const FEATURE_FLAG_KEY_METADATA = "feature_flag_key";
|
|
4
|
+
/**
|
|
5
|
+
* Decorator to mark endpoints that require a specific feature flag to be enabled.
|
|
6
|
+
* The guard reads this metadata to evaluate the flag before allowing access.
|
|
7
|
+
*
|
|
8
|
+
* @public
|
|
9
|
+
* @param flagKey - The unique key of the feature flag to check.
|
|
10
|
+
*/
|
|
11
|
+
export declare const FeatureFlag: (flagKey: string) => ReturnType<typeof SetMetadata>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #region Imports
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FeatureFlag = exports.FEATURE_FLAG_KEY_METADATA = void 0;
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
// #endregion
|
|
7
|
+
/** Metadata key used to store the feature flag key on route handlers. */
|
|
8
|
+
exports.FEATURE_FLAG_KEY_METADATA = 'feature_flag_key';
|
|
9
|
+
/**
|
|
10
|
+
* Decorator to mark endpoints that require a specific feature flag to be enabled.
|
|
11
|
+
* The guard reads this metadata to evaluate the flag before allowing access.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
* @param flagKey - The unique key of the feature flag to check.
|
|
15
|
+
*/
|
|
16
|
+
const FeatureFlag = (flagKey) => (0, common_1.SetMetadata)(exports.FEATURE_FLAG_KEY_METADATA, flagKey);
|
|
17
|
+
exports.FeatureFlag = FeatureFlag;
|
|
18
|
+
//# sourceMappingURL=FeatureFlag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FEATURE_FLAG_KEY_METADATA, FeatureFlag } from './FeatureFlag';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeatureFlag = exports.FEATURE_FLAG_KEY_METADATA = void 0;
|
|
4
|
+
var FeatureFlag_1 = require("./FeatureFlag");
|
|
5
|
+
Object.defineProperty(exports, "FEATURE_FLAG_KEY_METADATA", { enumerable: true, get: function () { return FeatureFlag_1.FEATURE_FLAG_KEY_METADATA; } });
|
|
6
|
+
Object.defineProperty(exports, "FeatureFlag", { enumerable: true, get: function () { return FeatureFlag_1.FeatureFlag; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
|
|
2
|
+
export { AZURE_APPCONFIG_CONNECTION_STRING, AZURE_APPCONFIG_ENDPOINT, AZURE_FEATURE_FLAG_CONFIG_ENTRIES, AZURE_FEATURE_FLAG_KEY_FILTER, AZURE_FEATURE_FLAG_REFRESH_INTERVAL, } from './azure/env';
|
|
3
|
+
export { VERCEL_API_TOKEN, VERCEL_EDGE_CONFIG, VERCEL_EDGE_CONFIG_ID, VERCEL_EDGE_CONFIG_TOKEN, VERCEL_FEATURE_FLAG_CONFIG_ENTRIES, VERCEL_FEATURE_FLAG_PREFIX, VERCEL_FEATURE_FLAG_WRITER_CONFIG_ENTRIES, VERCEL_TEAM_ID, } from './vercel/env';
|
|
4
|
+
/** All configuration entries required by the platform-feature-flags library across all providers. */
|
|
5
|
+
export declare const PLATFORM_FEATURE_FLAGS_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
|
package/src/env.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #region Imports
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PLATFORM_FEATURE_FLAGS_CONFIG_ENTRIES = exports.VERCEL_TEAM_ID = exports.VERCEL_FEATURE_FLAG_WRITER_CONFIG_ENTRIES = exports.VERCEL_FEATURE_FLAG_PREFIX = exports.VERCEL_FEATURE_FLAG_CONFIG_ENTRIES = exports.VERCEL_EDGE_CONFIG_TOKEN = exports.VERCEL_EDGE_CONFIG_ID = exports.VERCEL_EDGE_CONFIG = exports.VERCEL_API_TOKEN = exports.AZURE_FEATURE_FLAG_REFRESH_INTERVAL = exports.AZURE_FEATURE_FLAG_KEY_FILTER = exports.AZURE_FEATURE_FLAG_CONFIG_ENTRIES = exports.AZURE_APPCONFIG_ENDPOINT = exports.AZURE_APPCONFIG_CONNECTION_STRING = void 0;
|
|
5
|
+
const env_1 = require("./azure/env");
|
|
6
|
+
const env_2 = require("./vercel/env");
|
|
7
|
+
// #endregion
|
|
8
|
+
// ──────────────────────────────────────────────────────────────
|
|
9
|
+
// Re-exports
|
|
10
|
+
// ──────────────────────────────────────────────────────────────
|
|
11
|
+
var env_3 = require("./azure/env");
|
|
12
|
+
Object.defineProperty(exports, "AZURE_APPCONFIG_CONNECTION_STRING", { enumerable: true, get: function () { return env_3.AZURE_APPCONFIG_CONNECTION_STRING; } });
|
|
13
|
+
Object.defineProperty(exports, "AZURE_APPCONFIG_ENDPOINT", { enumerable: true, get: function () { return env_3.AZURE_APPCONFIG_ENDPOINT; } });
|
|
14
|
+
Object.defineProperty(exports, "AZURE_FEATURE_FLAG_CONFIG_ENTRIES", { enumerable: true, get: function () { return env_3.AZURE_FEATURE_FLAG_CONFIG_ENTRIES; } });
|
|
15
|
+
Object.defineProperty(exports, "AZURE_FEATURE_FLAG_KEY_FILTER", { enumerable: true, get: function () { return env_3.AZURE_FEATURE_FLAG_KEY_FILTER; } });
|
|
16
|
+
Object.defineProperty(exports, "AZURE_FEATURE_FLAG_REFRESH_INTERVAL", { enumerable: true, get: function () { return env_3.AZURE_FEATURE_FLAG_REFRESH_INTERVAL; } });
|
|
17
|
+
var env_4 = require("./vercel/env");
|
|
18
|
+
Object.defineProperty(exports, "VERCEL_API_TOKEN", { enumerable: true, get: function () { return env_4.VERCEL_API_TOKEN; } });
|
|
19
|
+
Object.defineProperty(exports, "VERCEL_EDGE_CONFIG", { enumerable: true, get: function () { return env_4.VERCEL_EDGE_CONFIG; } });
|
|
20
|
+
Object.defineProperty(exports, "VERCEL_EDGE_CONFIG_ID", { enumerable: true, get: function () { return env_4.VERCEL_EDGE_CONFIG_ID; } });
|
|
21
|
+
Object.defineProperty(exports, "VERCEL_EDGE_CONFIG_TOKEN", { enumerable: true, get: function () { return env_4.VERCEL_EDGE_CONFIG_TOKEN; } });
|
|
22
|
+
Object.defineProperty(exports, "VERCEL_FEATURE_FLAG_CONFIG_ENTRIES", { enumerable: true, get: function () { return env_4.VERCEL_FEATURE_FLAG_CONFIG_ENTRIES; } });
|
|
23
|
+
Object.defineProperty(exports, "VERCEL_FEATURE_FLAG_PREFIX", { enumerable: true, get: function () { return env_4.VERCEL_FEATURE_FLAG_PREFIX; } });
|
|
24
|
+
Object.defineProperty(exports, "VERCEL_FEATURE_FLAG_WRITER_CONFIG_ENTRIES", { enumerable: true, get: function () { return env_4.VERCEL_FEATURE_FLAG_WRITER_CONFIG_ENTRIES; } });
|
|
25
|
+
Object.defineProperty(exports, "VERCEL_TEAM_ID", { enumerable: true, get: function () { return env_4.VERCEL_TEAM_ID; } });
|
|
26
|
+
// ──────────────────────────────────────────────────────────────
|
|
27
|
+
// Aggregated registry entries
|
|
28
|
+
// ──────────────────────────────────────────────────────────────
|
|
29
|
+
/** All configuration entries required by the platform-feature-flags library across all providers. */
|
|
30
|
+
exports.PLATFORM_FEATURE_FLAGS_CONFIG_ENTRIES = [
|
|
31
|
+
...env_1.AZURE_FEATURE_FLAG_CONFIG_ENTRIES,
|
|
32
|
+
...env_2.VERCEL_FEATURE_FLAG_CONFIG_ENTRIES,
|
|
33
|
+
...env_2.VERCEL_FEATURE_FLAG_WRITER_CONFIG_ENTRIES,
|
|
34
|
+
];
|
|
35
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain error thrown when a feature flag operation fails.
|
|
3
|
+
*
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export declare class FeatureFlagError extends Error {
|
|
7
|
+
readonly code = "FEATURE_FLAG";
|
|
8
|
+
readonly provider: string;
|
|
9
|
+
constructor(provider: string, message: string, cause?: unknown);
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeatureFlagError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Domain error thrown when a feature flag operation fails.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
class FeatureFlagError extends Error {
|
|
10
|
+
// #endregion
|
|
11
|
+
// #region Ctor
|
|
12
|
+
constructor(provider, message, cause) {
|
|
13
|
+
super(message, { cause });
|
|
14
|
+
// #region Fields
|
|
15
|
+
this.code = 'FEATURE_FLAG';
|
|
16
|
+
this.name = 'FeatureFlagError';
|
|
17
|
+
this.provider = provider;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.FeatureFlagError = FeatureFlagError;
|
|
21
|
+
//# sourceMappingURL=FeatureFlagError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FeatureFlagError } from './FeatureFlagError';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeatureFlagError = void 0;
|
|
4
|
+
var FeatureFlagError_1 = require("./FeatureFlagError");
|
|
5
|
+
Object.defineProperty(exports, "FeatureFlagError", { enumerable: true, get: function () { return FeatureFlagError_1.FeatureFlagError; } });
|
|
6
|
+
//# sourceMappingURL=index.js.map
|