@dr-sentry/sdk 1.0.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 +285 -0
- package/dist/cjs/cache.d.ts +33 -0
- package/dist/cjs/cache.d.ts.map +1 -0
- package/dist/cjs/cache.js +84 -0
- package/dist/cjs/cache.js.map +1 -0
- package/dist/cjs/client.d.ts +81 -0
- package/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +362 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/file-loader.d.ts +3 -0
- package/dist/cjs/file-loader.d.ts.map +1 -0
- package/dist/cjs/file-loader.js +53 -0
- package/dist/cjs/file-loader.js.map +1 -0
- package/dist/cjs/index.d.ts +29 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/local-evaluator.d.ts +6 -0
- package/dist/cjs/local-evaluator.d.ts.map +1 -0
- package/dist/cjs/local-evaluator.js +40 -0
- package/dist/cjs/local-evaluator.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/streaming.d.ts +42 -0
- package/dist/cjs/streaming.d.ts.map +1 -0
- package/dist/cjs/streaming.js +140 -0
- package/dist/cjs/streaming.js.map +1 -0
- package/dist/cjs/types.d.ts +126 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +6 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/cache.d.ts +33 -0
- package/dist/esm/cache.d.ts.map +1 -0
- package/dist/esm/cache.js +80 -0
- package/dist/esm/cache.js.map +1 -0
- package/dist/esm/client.d.ts +81 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +358 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/file-loader.d.ts +3 -0
- package/dist/esm/file-loader.d.ts.map +1 -0
- package/dist/esm/file-loader.js +17 -0
- package/dist/esm/file-loader.js.map +1 -0
- package/dist/esm/index.d.ts +29 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +28 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/local-evaluator.d.ts +6 -0
- package/dist/esm/local-evaluator.d.ts.map +1 -0
- package/dist/esm/local-evaluator.js +37 -0
- package/dist/esm/local-evaluator.js.map +1 -0
- package/dist/esm/streaming.d.ts +42 -0
- package/dist/esm/streaming.d.ts.map +1 -0
- package/dist/esm/streaming.js +136 -0
- package/dist/esm/streaming.js.map +1 -0
- package/dist/esm/types.d.ts +126 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +5 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# DeploySentry Node.js SDK
|
|
2
|
+
|
|
3
|
+
Official Node.js/TypeScript SDK for integrating with the DeploySentry platform. Provides feature flag evaluation with rich metadata including categories, ownership, expiration tracking, and real-time updates via SSE.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @deploysentry/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { DeploySentryClient } from '@deploysentry/sdk';
|
|
15
|
+
|
|
16
|
+
const client = new DeploySentryClient({
|
|
17
|
+
apiKey: 'ds_live_xxx',
|
|
18
|
+
environment: 'production',
|
|
19
|
+
project: 'my-app',
|
|
20
|
+
application: 'my-web-app',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await client.initialize();
|
|
24
|
+
|
|
25
|
+
// Boolean flag
|
|
26
|
+
const darkMode = await client.boolValue('dark-mode', false, {
|
|
27
|
+
userId: 'user-42',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// String flag
|
|
31
|
+
const variant = await client.stringValue('checkout-variant', 'control');
|
|
32
|
+
|
|
33
|
+
// Integer flag
|
|
34
|
+
const maxRetries = await client.intValue('max-retries', 3);
|
|
35
|
+
|
|
36
|
+
// JSON flag
|
|
37
|
+
const config = await client.jsonValue('rate-limits', { rpm: 100 });
|
|
38
|
+
|
|
39
|
+
// Clean up when done
|
|
40
|
+
client.close();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Evaluation Context
|
|
44
|
+
|
|
45
|
+
Pass targeting context with every evaluation:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const enabled = await client.boolValue('premium-feature', false, {
|
|
49
|
+
userId: 'user-42',
|
|
50
|
+
orgId: 'org-7',
|
|
51
|
+
attributes: {
|
|
52
|
+
plan: 'enterprise',
|
|
53
|
+
region: 'us-east-1',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Rich Metadata
|
|
59
|
+
|
|
60
|
+
Every flag carries structured metadata describing its purpose, ownership, and lifecycle:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const result = await client.detail('new-checkout');
|
|
64
|
+
|
|
65
|
+
console.log(result.metadata.category); // 'release' | 'feature' | 'experiment' | 'ops' | 'permission'
|
|
66
|
+
console.log(result.metadata.purpose); // "Gradual rollout of new checkout flow"
|
|
67
|
+
console.log(result.metadata.owners); // ["payments-team", "jane@example.com"]
|
|
68
|
+
console.log(result.metadata.isPermanent); // false
|
|
69
|
+
console.log(result.metadata.expiresAt); // "2026-06-01T00:00:00Z"
|
|
70
|
+
console.log(result.metadata.tags); // ["checkout", "q2-2026"]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Flag Categories
|
|
74
|
+
|
|
75
|
+
Flags are classified into categories that drive lifecycle policies:
|
|
76
|
+
|
|
77
|
+
| Category | Description |
|
|
78
|
+
| ------------ | -------------------------------------------------- |
|
|
79
|
+
| `release` | Gradual rollout gates for new releases |
|
|
80
|
+
| `feature` | Long-lived feature toggles |
|
|
81
|
+
| `experiment` | A/B tests and experiments with defined end dates |
|
|
82
|
+
| `ops` | Operational controls (kill switches, rate limits) |
|
|
83
|
+
| `permission` | Entitlement and access-control flags |
|
|
84
|
+
|
|
85
|
+
Query by category:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const experiments = client.flagsByCategory('experiment');
|
|
89
|
+
const killSwitches = client.flagsByCategory('ops');
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Lifecycle Management
|
|
93
|
+
|
|
94
|
+
Find expired flags that should be cleaned up:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const expired = client.expiredFlags();
|
|
98
|
+
for (const flag of expired) {
|
|
99
|
+
console.warn(`Flag "${flag.key}" expired at ${flag.metadata.expiresAt}`);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Look up flag owners:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const owners = client.flagOwners('new-checkout');
|
|
107
|
+
// ["payments-team", "jane@example.com"]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
List all cached flags:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const flags = client.allFlags();
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Register / Dispatch Pattern
|
|
117
|
+
|
|
118
|
+
The register/dispatch pattern lets you centralize all flag-gated behavior in one place instead of scattering `if/else` checks throughout your codebase. Register named operations with their handlers and optional flag keys, then dispatch by operation name at the call site.
|
|
119
|
+
|
|
120
|
+
### Basic usage
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// --- registrations.ts (single-point registration) ---
|
|
124
|
+
|
|
125
|
+
import { client } from './deploysentry';
|
|
126
|
+
|
|
127
|
+
// Default handler (no flag key) — always used as fallback
|
|
128
|
+
client.register('checkout', () => {
|
|
129
|
+
return processLegacyCheckout();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Flag-gated handler — used when 'new-checkout-flow' is enabled
|
|
133
|
+
client.register('checkout', () => {
|
|
134
|
+
return processNewCheckout();
|
|
135
|
+
}, 'new-checkout-flow');
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
// --- somewhere-else.ts (call site) ---
|
|
139
|
+
|
|
140
|
+
// Dispatch selects the right handler based on flag state.
|
|
141
|
+
// No flag logic here — the call site doesn't know or care.
|
|
142
|
+
const checkout = client.dispatch<() => Promise<Order>>('checkout');
|
|
143
|
+
await checkout();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### How it works
|
|
147
|
+
|
|
148
|
+
1. **`register(operation, handler, flagKey?)`** — Registers a handler for a named operation. If `flagKey` is provided, the handler is only selected when that flag is enabled. Omit `flagKey` to register the default/fallback handler.
|
|
149
|
+
|
|
150
|
+
2. **`dispatch(operation)`** — Returns the first registered handler whose flag is enabled. If no flagged handler matches, returns the default handler. Throws if no handlers are registered.
|
|
151
|
+
|
|
152
|
+
### Single-point registration
|
|
153
|
+
|
|
154
|
+
Keep all registrations in a single file (e.g. `src/flags/registrations.ts`) that runs at startup. This gives you:
|
|
155
|
+
|
|
156
|
+
- One place to see every flag-gated behavior in your app
|
|
157
|
+
- Easy auditing of which flags control which operations
|
|
158
|
+
- Simple cleanup when a flag is retired (remove the registration, keep the handler)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// src/flags/registrations.ts
|
|
162
|
+
import { client } from './deploysentry';
|
|
163
|
+
import { legacySearch, vectorSearch } from '../search';
|
|
164
|
+
import { standardPricing, tieredPricing } from '../pricing';
|
|
165
|
+
|
|
166
|
+
// Search
|
|
167
|
+
client.register('search', legacySearch);
|
|
168
|
+
client.register('search', vectorSearch, 'vector-search-v2');
|
|
169
|
+
|
|
170
|
+
// Pricing
|
|
171
|
+
client.register('calculate-price', standardPricing);
|
|
172
|
+
client.register('calculate-price', tieredPricing, 'tiered-pricing-experiment');
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Offline Mode
|
|
176
|
+
|
|
177
|
+
For testing or environments without network access, enable offline mode. The client will return default values without contacting the API:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const client = new DeploySentryClient({
|
|
181
|
+
apiKey: 'not-used',
|
|
182
|
+
environment: 'test',
|
|
183
|
+
project: 'my-app',
|
|
184
|
+
application: 'my-web-app',
|
|
185
|
+
offlineMode: true,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await client.initialize(); // no-op in offline mode
|
|
189
|
+
|
|
190
|
+
const value = await client.boolValue('any-flag', true);
|
|
191
|
+
// Returns the default value: true
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Configuration
|
|
195
|
+
|
|
196
|
+
| Option | Type | Required | Default | Description |
|
|
197
|
+
| -------------- | -------- | -------- | ------------------------------ | -------------------------------------- |
|
|
198
|
+
| `apiKey` | string | Yes | - | API key for authentication |
|
|
199
|
+
| `environment` | string | Yes | - | Target environment |
|
|
200
|
+
| `project` | string | Yes | - | Project identifier |
|
|
201
|
+
| `application` | string | Yes | - | Application identifier |
|
|
202
|
+
| `baseURL` | string | No | `https://api.dr-sentry.com` | API base URL |
|
|
203
|
+
| `cacheTimeout` | number | No | `60000` | Cache TTL in milliseconds |
|
|
204
|
+
| `offlineMode` | boolean | No | `false` | Return defaults without API calls |
|
|
205
|
+
| `mode` | string | No | `server` | `server`, `file`, or `server-with-fallback` |
|
|
206
|
+
| `flagFilePath` | string | No | `.deploysentry/flags.yaml` | Path to YAML flag config file |
|
|
207
|
+
|
|
208
|
+
## Offline / File Mode
|
|
209
|
+
|
|
210
|
+
The SDK can load flag configurations from a local YAML file instead of (or as fallback to) the server.
|
|
211
|
+
|
|
212
|
+
### Modes
|
|
213
|
+
|
|
214
|
+
| Mode | Behavior |
|
|
215
|
+
| --- | --- |
|
|
216
|
+
| `server` (default) | API calls + SSE streaming |
|
|
217
|
+
| `file` | Load from YAML file, evaluate locally. No server contact. |
|
|
218
|
+
| `server-with-fallback` | Try server first. If unavailable, fall back to YAML file. |
|
|
219
|
+
|
|
220
|
+
### Usage
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// File mode — local development, CI, testing
|
|
224
|
+
const client = new DeploySentryClient({
|
|
225
|
+
apiKey: 'not-used',
|
|
226
|
+
environment: 'staging',
|
|
227
|
+
project: 'my-project',
|
|
228
|
+
application: 'my-web-app',
|
|
229
|
+
mode: 'file',
|
|
230
|
+
flagFilePath: '.deploysentry/flags.yaml', // default
|
|
231
|
+
});
|
|
232
|
+
await client.initialize();
|
|
233
|
+
|
|
234
|
+
// Fallback mode — production resilience
|
|
235
|
+
const client = new DeploySentryClient({
|
|
236
|
+
apiKey: 'ds_live_xxx',
|
|
237
|
+
environment: 'production',
|
|
238
|
+
project: 'my-project',
|
|
239
|
+
application: 'my-web-app',
|
|
240
|
+
mode: 'server-with-fallback',
|
|
241
|
+
});
|
|
242
|
+
await client.initialize();
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Generating the YAML file
|
|
246
|
+
|
|
247
|
+
Export from the DeploySentry dashboard: App Settings → Export flags.yaml. Place the downloaded file at `.deploysentry/flags.yaml` in your project root.
|
|
248
|
+
|
|
249
|
+
### Local rule evaluation
|
|
250
|
+
|
|
251
|
+
In file mode, targeting rules are evaluated locally. The SDK matches context attributes against rule conditions using the same operators as the server: equals, not_equals, in, not_in, contains, starts_with, ends_with, greater_than, less_than.
|
|
252
|
+
|
|
253
|
+
## API Endpoints
|
|
254
|
+
|
|
255
|
+
The SDK communicates with these DeploySentry API endpoints:
|
|
256
|
+
|
|
257
|
+
- `POST /api/v1/flags/evaluate` - Evaluate a single flag
|
|
258
|
+
- `POST /api/v1/flags/batch-evaluate` - Evaluate multiple flags
|
|
259
|
+
- `GET /api/v1/flags/stream?project_id=X&environment_id=Y` - SSE stream for real-time updates
|
|
260
|
+
- `GET /api/v1/flags?project_id=X` - List all flags for a project
|
|
261
|
+
|
|
262
|
+
Authentication uses the `Authorization: ApiKey <key>` header.
|
|
263
|
+
|
|
264
|
+
### Session Consistency
|
|
265
|
+
|
|
266
|
+
Bind evaluations to a session so the server caches results for a consistent user experience:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
const client = new DeploySentryClient({
|
|
270
|
+
apiKey: 'ds_key_xxxxxxxxxxxx',
|
|
271
|
+
environment: 'production',
|
|
272
|
+
project: 'my-project',
|
|
273
|
+
application: 'my-web-app',
|
|
274
|
+
sessionId: `user:${userId}`,
|
|
275
|
+
});
|
|
276
|
+
await client.refreshSession();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
- The session ID is sent as an `X-DeploySentry-Session` header on every request.
|
|
280
|
+
- The server caches evaluation results per session for 30 minutes (sliding TTL).
|
|
281
|
+
- Omit the session ID to always get fresh evaluations on each request.
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
Apache-2.0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Flag } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory flag cache with per-entry TTL support.
|
|
4
|
+
*
|
|
5
|
+
* Entries are lazily evicted on read. A periodic sweep can be triggered
|
|
6
|
+
* via {@link purgeExpired} if deterministic memory reclamation is required.
|
|
7
|
+
*/
|
|
8
|
+
export declare class FlagCache {
|
|
9
|
+
private readonly store;
|
|
10
|
+
private readonly ttlMs;
|
|
11
|
+
/**
|
|
12
|
+
* @param ttlMs - Time-to-live for every cache entry in milliseconds.
|
|
13
|
+
* Defaults to 60 000 (1 minute).
|
|
14
|
+
*/
|
|
15
|
+
constructor(ttlMs?: number);
|
|
16
|
+
/** Store or update a single flag in the cache. */
|
|
17
|
+
set(flag: Flag): void;
|
|
18
|
+
/** Bulk-insert flags, replacing any stale entries. */
|
|
19
|
+
setMany(flags: Flag[]): void;
|
|
20
|
+
/** Retrieve a flag by key. Returns `undefined` if missing or expired. */
|
|
21
|
+
get(key: string): Flag | undefined;
|
|
22
|
+
/** Return all non-expired flags currently in the cache. */
|
|
23
|
+
getAll(): Flag[];
|
|
24
|
+
/** Remove a single key from the cache. */
|
|
25
|
+
delete(key: string): void;
|
|
26
|
+
/** Remove all expired entries. Returns the number of entries purged. */
|
|
27
|
+
purgeExpired(): number;
|
|
28
|
+
/** Drop every entry from the cache. */
|
|
29
|
+
clear(): void;
|
|
30
|
+
/** Number of entries (including potentially expired ones). */
|
|
31
|
+
get size(): number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAO/B;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B;;;OAGG;gBACS,KAAK,GAAE,MAAe;IAIlC,kDAAkD;IAClD,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAOrB,sDAAsD;IACtD,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI;IAM5B,yEAAyE;IACzE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAYlC,2DAA2D;IAC3D,MAAM,IAAI,IAAI,EAAE;IAehB,0CAA0C;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,wEAAwE;IACxE,YAAY,IAAI,MAAM;IActB,uCAAuC;IACvC,KAAK,IAAI,IAAI;IAIb,8DAA8D;IAC9D,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlagCache = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* In-memory flag cache with per-entry TTL support.
|
|
6
|
+
*
|
|
7
|
+
* Entries are lazily evicted on read. A periodic sweep can be triggered
|
|
8
|
+
* via {@link purgeExpired} if deterministic memory reclamation is required.
|
|
9
|
+
*/
|
|
10
|
+
class FlagCache {
|
|
11
|
+
store = new Map();
|
|
12
|
+
ttlMs;
|
|
13
|
+
/**
|
|
14
|
+
* @param ttlMs - Time-to-live for every cache entry in milliseconds.
|
|
15
|
+
* Defaults to 60 000 (1 minute).
|
|
16
|
+
*/
|
|
17
|
+
constructor(ttlMs = 60_000) {
|
|
18
|
+
this.ttlMs = ttlMs;
|
|
19
|
+
}
|
|
20
|
+
/** Store or update a single flag in the cache. */
|
|
21
|
+
set(flag) {
|
|
22
|
+
this.store.set(flag.key, {
|
|
23
|
+
flag,
|
|
24
|
+
expiresAt: Date.now() + this.ttlMs,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Bulk-insert flags, replacing any stale entries. */
|
|
28
|
+
setMany(flags) {
|
|
29
|
+
for (const flag of flags) {
|
|
30
|
+
this.set(flag);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Retrieve a flag by key. Returns `undefined` if missing or expired. */
|
|
34
|
+
get(key) {
|
|
35
|
+
const entry = this.store.get(key);
|
|
36
|
+
if (!entry)
|
|
37
|
+
return undefined;
|
|
38
|
+
if (Date.now() > entry.expiresAt) {
|
|
39
|
+
this.store.delete(key);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return entry.flag;
|
|
43
|
+
}
|
|
44
|
+
/** Return all non-expired flags currently in the cache. */
|
|
45
|
+
getAll() {
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const result = [];
|
|
48
|
+
for (const [key, entry] of this.store) {
|
|
49
|
+
if (now > entry.expiresAt) {
|
|
50
|
+
this.store.delete(key);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
result.push(entry.flag);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
/** Remove a single key from the cache. */
|
|
59
|
+
delete(key) {
|
|
60
|
+
this.store.delete(key);
|
|
61
|
+
}
|
|
62
|
+
/** Remove all expired entries. Returns the number of entries purged. */
|
|
63
|
+
purgeExpired() {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
let purged = 0;
|
|
66
|
+
for (const [key, entry] of this.store) {
|
|
67
|
+
if (now > entry.expiresAt) {
|
|
68
|
+
this.store.delete(key);
|
|
69
|
+
purged++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return purged;
|
|
73
|
+
}
|
|
74
|
+
/** Drop every entry from the cache. */
|
|
75
|
+
clear() {
|
|
76
|
+
this.store.clear();
|
|
77
|
+
}
|
|
78
|
+
/** Number of entries (including potentially expired ones). */
|
|
79
|
+
get size() {
|
|
80
|
+
return this.store.size;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.FlagCache = FlagCache;
|
|
84
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":";;;AAOA;;;;;GAKG;AACH,MAAa,SAAS;IACH,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtC,KAAK,CAAS;IAE/B;;;OAGG;IACH,YAAY,QAAgB,MAAM;QAChC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,kDAAkD;IAClD,GAAG,CAAC,IAAU;QACZ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE;YACvB,IAAI;YACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK;SACnC,CAAC,CAAC;IACL,CAAC;IAED,sDAAsD;IACtD,OAAO,CAAC,KAAa;QACnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,2DAA2D;IAC3D,MAAM;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAW,EAAE,CAAC;QAE1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,0CAA0C;IAC1C,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,wEAAwE;IACxE,YAAY;QACV,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACvB,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,uCAAuC;IACvC,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,8DAA8D;IAC9D,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AArFD,8BAqFC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ClientOptions, EvaluationContext, EvaluationResult, Flag, FlagCategory } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* DeploySentry feature-flag client.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const client = new DeploySentryClient({
|
|
7
|
+
* apiKey: 'ds_live_xxx',
|
|
8
|
+
* environment: 'production',
|
|
9
|
+
* project: 'my-app',
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* await client.initialize();
|
|
13
|
+
*
|
|
14
|
+
* const darkMode = client.boolValue('dark-mode', false, { userId: 'u1' });
|
|
15
|
+
*
|
|
16
|
+
* client.close();
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class DeploySentryClient {
|
|
20
|
+
private readonly apiKey;
|
|
21
|
+
private readonly baseURL;
|
|
22
|
+
private readonly environment;
|
|
23
|
+
private readonly project;
|
|
24
|
+
private readonly application;
|
|
25
|
+
private readonly offlineMode;
|
|
26
|
+
private readonly sessionId;
|
|
27
|
+
private readonly mode;
|
|
28
|
+
private readonly flagFilePath?;
|
|
29
|
+
private readonly cache;
|
|
30
|
+
private streamClient;
|
|
31
|
+
private _initialized;
|
|
32
|
+
private flagConfig;
|
|
33
|
+
private registry;
|
|
34
|
+
constructor(options: ClientOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Fetch the initial flag set and open an SSE connection for real-time
|
|
37
|
+
* updates. Must be called before evaluating flags.
|
|
38
|
+
*/
|
|
39
|
+
initialize(): Promise<void>;
|
|
40
|
+
/** Tear down the SSE connection and release resources. */
|
|
41
|
+
close(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Clear the local cache and re-fetch all flags from the server.
|
|
44
|
+
* Useful when session state may have changed and the client needs
|
|
45
|
+
* fresh evaluations.
|
|
46
|
+
*/
|
|
47
|
+
refreshSession(): Promise<void>;
|
|
48
|
+
/** Whether {@link initialize} has been called successfully. */
|
|
49
|
+
get isInitialized(): boolean;
|
|
50
|
+
/** Evaluate a flag as a boolean. */
|
|
51
|
+
boolValue(key: string, defaultValue: boolean, context?: EvaluationContext): Promise<boolean>;
|
|
52
|
+
/** Evaluate a flag as a string. */
|
|
53
|
+
stringValue(key: string, defaultValue: string, context?: EvaluationContext): Promise<string>;
|
|
54
|
+
/** Evaluate a flag as an integer. */
|
|
55
|
+
intValue(key: string, defaultValue: number, context?: EvaluationContext): Promise<number>;
|
|
56
|
+
/** Evaluate a flag and return the value as a parsed JSON object. */
|
|
57
|
+
jsonValue<T = unknown>(key: string, defaultValue: T, context?: EvaluationContext): Promise<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Return the full {@link EvaluationResult} for a flag including metadata,
|
|
60
|
+
* reason, and resolved value.
|
|
61
|
+
*/
|
|
62
|
+
detail(key: string, context?: EvaluationContext): Promise<EvaluationResult>;
|
|
63
|
+
/** Return all cached flags belonging to a given category. */
|
|
64
|
+
flagsByCategory(category: FlagCategory): Flag[];
|
|
65
|
+
/** Return all cached flags whose `expiresAt` is in the past. */
|
|
66
|
+
expiredFlags(): Flag[];
|
|
67
|
+
/** Return the owners array for a given flag key. */
|
|
68
|
+
flagOwners(key: string): string[];
|
|
69
|
+
/** Return every flag currently held in the local cache. */
|
|
70
|
+
allFlags(): Flag[];
|
|
71
|
+
register<T extends (...args: any[]) => any>(operation: string, handler: T, flagKey?: string): void;
|
|
72
|
+
dispatch<T extends (...args: any[]) => any>(operation: string, _context?: EvaluationContext): T;
|
|
73
|
+
private evaluate;
|
|
74
|
+
private fetchAllFlags;
|
|
75
|
+
private post;
|
|
76
|
+
private request;
|
|
77
|
+
private authHeaders;
|
|
78
|
+
private offlineResult;
|
|
79
|
+
private cachedResult;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,IAAI,EACJ,YAAY,EAEb,MAAM,SAAS,CAAC;AAUjB;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6C;IAClE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IAEvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,QAAQ,CAA0C;gBAE9C,OAAO,EAAE,aAAa;IAuBlC;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA0CjC,0DAA0D;IAC1D,KAAK,IAAI,IAAI;IAOb;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrC,+DAA+D;IAC/D,IAAI,aAAa,IAAI,OAAO,CAE3B;IAMD,oCAAoC;IAC9B,SAAS,CACb,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC;IAcnB,mCAAmC;IAC7B,WAAW,CACf,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC;IAKlB,qCAAqC;IAC/B,QAAQ,CACZ,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC;IAOlB,oEAAoE;IAC9D,SAAS,CAAC,CAAC,GAAG,OAAO,EACzB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,EACf,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,CAAC,CAAC;IASb;;;OAGG;IACG,MAAM,CACV,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC;IA2B5B,6DAA6D;IAC7D,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,EAAE;IAI/C,gEAAgE;IAChE,YAAY,IAAI,IAAI,EAAE;IAOtB,oDAAoD;IACpD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE;IAKjC,2DAA2D;IAC3D,QAAQ,IAAI,IAAI,EAAE;IAQlB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,EACV,OAAO,CAAC,EAAE,MAAM,GACf,IAAI;IAeP,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxC,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,CAAC;YA0BU,QAAQ;YAkCR,aAAa;YAQb,IAAI;YAIJ,OAAO;IAgCrB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,YAAY;CAmBrB"}
|