@cloudflare/flagship 0.0.3 → 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 +162 -2
- package/dist/{index-C_sW3e_7.d.mts → index-BMGtqsWU.d.mts} +63 -3
- package/dist/{index-D8YLMfBG.d.cts → index-BsvVosek.d.cts} +63 -3
- package/dist/index.cjs +1 -7
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -2
- package/dist/server.cjs +1 -298
- package/dist/server.d.cts +46 -21
- package/dist/server.d.mts +46 -21
- package/dist/server.mjs +1 -290
- package/dist/src-DW2QohY9.mjs +1 -0
- package/dist/src-QEIBSid3.cjs +1 -0
- package/dist/web.cjs +1 -148
- package/dist/web.d.cts +1 -1
- package/dist/web.d.mts +1 -1
- package/dist/web.mjs +1 -142
- package/package.json +4 -8
- package/dist/src-CiVDWmng.mjs +0 -202
- package/dist/src-De-abNIr.cjs +0 -231
package/README.md
CHANGED
|
@@ -1,5 +1,165 @@
|
|
|
1
1
|
# @cloudflare/flagship
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@cloudflare/flagship)
|
|
4
|
+
[](https://www.npmjs.com/package/@cloudflare/flagship)
|
|
5
|
+
[](https://github.com/cloudflare/flagship/blob/main/LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
[OpenFeature](https://openfeature.dev)-compliant provider SDK for [Flagship](https://github.com/cloudflare/flagship), Cloudflare's globally distributed, low-latency feature flag platform.
|
|
8
|
+
|
|
9
|
+
Server-side (Node.js, Cloudflare Workers) and client-side (browser) support via isolated sub-path exports. Tree-shakeable — importing `@cloudflare/flagship/server` never loads `@openfeature/web-sdk` and vice versa.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
**Server-side** (Node.js, Cloudflare Workers):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @cloudflare/flagship @openfeature/server-sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Client-side** (browser):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @cloudflare/flagship @openfeature/web-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick start — server
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { OpenFeature } from '@openfeature/server-sdk';
|
|
29
|
+
import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
30
|
+
|
|
31
|
+
await OpenFeature.setProviderAndWait(
|
|
32
|
+
new FlagshipServerProvider({ appId: 'your-app-id', accountId: 'your-account-id', authToken: 'your-token' }),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const client = OpenFeature.getClient();
|
|
36
|
+
const enabled = await client.getBooleanValue('dark-mode', false, { userId: 'user-123' });
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick start — Cloudflare Workers
|
|
40
|
+
|
|
41
|
+
The recommended approach for Cloudflare Workers. Uses a wrangler binding — no HTTP overhead, no auth tokens needed.
|
|
42
|
+
|
|
43
|
+
Configure in `wrangler.json`:
|
|
44
|
+
|
|
45
|
+
```jsonc
|
|
46
|
+
{
|
|
47
|
+
"flagship": [{ "binding": "FLAGS", "app_id": "<your-app-id>" }]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { OpenFeature } from '@openfeature/server-sdk';
|
|
53
|
+
import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
54
|
+
import type { FlagshipBinding } from '@cloudflare/flagship/server';
|
|
55
|
+
|
|
56
|
+
export default {
|
|
57
|
+
async fetch(request: Request, env: { FLAGS: FlagshipBinding }): Promise<Response> {
|
|
58
|
+
await OpenFeature.setProviderAndWait(new FlagshipServerProvider({ binding: env.FLAGS }));
|
|
59
|
+
|
|
60
|
+
const client = OpenFeature.getClient();
|
|
61
|
+
const darkMode = await client.getBooleanValue('dark-mode', false, {
|
|
62
|
+
targetingKey: new URL(request.url).searchParams.get('userId') ?? 'anonymous',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return Response.json({ darkMode });
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick start — HTTP
|
|
71
|
+
|
|
72
|
+
For Workers without a Flagship binding, or non-Workers server environments:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { OpenFeature } from '@openfeature/server-sdk';
|
|
76
|
+
import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
77
|
+
|
|
78
|
+
let initialized = false;
|
|
79
|
+
|
|
80
|
+
export default {
|
|
81
|
+
async fetch(request: Request): Promise<Response> {
|
|
82
|
+
if (!initialized) {
|
|
83
|
+
await OpenFeature.setProviderAndWait(
|
|
84
|
+
new FlagshipServerProvider({ appId: 'your-app-id', accountId: 'your-account-id', authToken: 'your-token' }),
|
|
85
|
+
);
|
|
86
|
+
initialized = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const client = OpenFeature.getClient();
|
|
90
|
+
const darkMode = await client.getBooleanValue('dark-mode', false, {
|
|
91
|
+
userId: new URL(request.url).searchParams.get('userId') ?? 'anonymous',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return Response.json({ darkMode });
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Quick start — browser
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { OpenFeature } from '@openfeature/web-sdk';
|
|
103
|
+
import { FlagshipClientProvider } from '@cloudflare/flagship/web';
|
|
104
|
+
|
|
105
|
+
await OpenFeature.setProviderAndWait(
|
|
106
|
+
new FlagshipClientProvider({
|
|
107
|
+
appId: 'your-app-id',
|
|
108
|
+
accountId: 'your-account-id',
|
|
109
|
+
authToken: 'your-token',
|
|
110
|
+
prefetchFlags: ['dark-mode', 'welcome-message'],
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
await OpenFeature.setContext({ userId: 'user-123', plan: 'premium' });
|
|
115
|
+
|
|
116
|
+
const client = OpenFeature.getClient();
|
|
117
|
+
const darkMode = client.getBooleanValue('dark-mode', false);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Features
|
|
121
|
+
|
|
122
|
+
| Feature | Description |
|
|
123
|
+
| --------------------- | ---------------------------------------------------------------------------- |
|
|
124
|
+
| OpenFeature compliant | Implements the CNCF OpenFeature specification |
|
|
125
|
+
| Workers binding | Native wrangler binding support — zero HTTP overhead, no auth tokens |
|
|
126
|
+
| Server + client | Async per-request (server) and sync cache-based (browser) providers |
|
|
127
|
+
| Server providers | `FlagshipServerProvider` works via HTTP or wrangler binding |
|
|
128
|
+
| All flag types | Boolean, string, number, and object (JSON) |
|
|
129
|
+
| Authentication | `authToken` option adds `Authorization: Bearer` to every request (HTTP only) |
|
|
130
|
+
| Logging | `logging` option surfaces fetch errors and cache misses (off by default) |
|
|
131
|
+
| Retries + timeouts | Configurable retry logic with `AbortController`-based timeouts (HTTP only) |
|
|
132
|
+
| Hooks | Built-in `LoggingHook` and `TelemetryHook` |
|
|
133
|
+
| Tree-shakeable | Server and client bundles are fully isolated |
|
|
134
|
+
| TypeScript | Strict types throughout |
|
|
135
|
+
|
|
136
|
+
## Packages
|
|
137
|
+
|
|
138
|
+
| Export | Description | Peer dependency |
|
|
139
|
+
| ----------------------------- | -------------------------------- | ------------------------- |
|
|
140
|
+
| `@cloudflare/flagship` | Core client, types, errors | None |
|
|
141
|
+
| `@cloudflare/flagship/server` | `FlagshipServerProvider` + hooks | `@openfeature/server-sdk` |
|
|
142
|
+
| `@cloudflare/flagship/web` | `FlagshipClientProvider` | `@openfeature/web-sdk` |
|
|
143
|
+
|
|
144
|
+
## Documentation
|
|
145
|
+
|
|
146
|
+
- [API reference](../../docs/API.md)
|
|
147
|
+
- [OpenFeature specification](https://openfeature.dev/specification/)
|
|
148
|
+
- [Examples](./examples/)
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pnpm install # install dependencies
|
|
154
|
+
pnpm run dev # watch mode
|
|
155
|
+
pnpm run test # run tests
|
|
156
|
+
pnpm run build # build for distribution
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Contributing
|
|
160
|
+
|
|
161
|
+
Contributions are welcome. Please open an issue first to discuss what you'd like to change. See the [repository](https://github.com/cloudflare/flagship) for more details.
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
[Apache-2.0](../../LICENSE)
|
|
@@ -33,8 +33,10 @@ interface FlagshipProviderOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
accountId?: string;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Evaluation endpoint. Mutually exclusive with `appId`.
|
|
37
|
+
* Accepts an absolute URL or a root-relative path. Relative paths are
|
|
38
|
+
* only supported by `FlagshipClientProvider` and are resolved against
|
|
39
|
+
* `window.location.origin`.
|
|
38
40
|
*/
|
|
39
41
|
endpoint?: string;
|
|
40
42
|
/**
|
|
@@ -130,6 +132,64 @@ interface FlagshipEvaluationResponse {
|
|
|
130
132
|
*/
|
|
131
133
|
reason: 'TARGETING_MATCH' | 'DEFAULT' | 'DISABLED' | 'SPLIT';
|
|
132
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Shape of the Flagship wrangler binding exposed on `env` in Cloudflare Workers.
|
|
137
|
+
*
|
|
138
|
+
* This is an alias for the `Flagship` class from `@cloudflare/workers-types`.
|
|
139
|
+
* The binding communicates directly with the Flagship service via workerd RPC —
|
|
140
|
+
* no HTTP overhead, no auth tokens required. Configure it in `wrangler.json`:
|
|
141
|
+
*
|
|
142
|
+
* ```jsonc
|
|
143
|
+
* {
|
|
144
|
+
* "flagship": [
|
|
145
|
+
* { "binding": "FLAGS", "app_id": "<your-app-id>" }
|
|
146
|
+
* ]
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
type FlagshipBinding = Flagship;
|
|
151
|
+
/**
|
|
152
|
+
* Evaluation details returned by the Flagship binding's `*Details` methods.
|
|
153
|
+
* Contains the resolved value along with metadata about why that value was
|
|
154
|
+
* chosen.
|
|
155
|
+
*
|
|
156
|
+
* This is an alias for the `FlagshipEvaluationDetails` interface from
|
|
157
|
+
* `@cloudflare/workers-types`.
|
|
158
|
+
*/
|
|
159
|
+
type FlagshipBindingEvaluationDetails<T> = FlagshipEvaluationDetails<T>;
|
|
160
|
+
/**
|
|
161
|
+
* Configuration for `FlagshipServerProvider` when using a wrangler binding.
|
|
162
|
+
*
|
|
163
|
+
* In this mode the provider delegates all evaluations to the Flagship binding
|
|
164
|
+
* on `env`, bypassing HTTP entirely. No `appId`, `accountId`, or `authToken`
|
|
165
|
+
* is required — the binding handles authentication and routing.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* new FlagshipServerProvider({ binding: env.FLAGS })
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
interface FlagshipBindingProviderOptions {
|
|
173
|
+
/** The Flagship binding from the Worker's `env` object. */
|
|
174
|
+
binding: FlagshipBinding;
|
|
175
|
+
/**
|
|
176
|
+
* Enable SDK-level logging.
|
|
177
|
+
* @default false
|
|
178
|
+
*/
|
|
179
|
+
logging?: boolean;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Options accepted by `FlagshipServerProvider`.
|
|
183
|
+
*
|
|
184
|
+
* Provide **either** HTTP configuration (`appId`/`endpoint` + credentials) **or**
|
|
185
|
+
* a wrangler `binding` — never both. The provider detects which mode to use
|
|
186
|
+
* based on the presence of the `binding` field.
|
|
187
|
+
*/
|
|
188
|
+
type FlagshipServerProviderOptions = FlagshipProviderOptions | FlagshipBindingProviderOptions;
|
|
189
|
+
/**
|
|
190
|
+
* Type guard: returns `true` when the options use binding mode.
|
|
191
|
+
*/
|
|
192
|
+
declare function isBindingOptions(options: FlagshipServerProviderOptions): options is FlagshipBindingProviderOptions;
|
|
133
193
|
/**
|
|
134
194
|
* Internal error codes produced by `FlagshipClient`.
|
|
135
195
|
* These are mapped to OpenFeature `ErrorCode` values by the providers.
|
|
@@ -230,4 +290,4 @@ declare class FlagshipClient {
|
|
|
230
290
|
private fetchWithTimeout;
|
|
231
291
|
}
|
|
232
292
|
//#endregion
|
|
233
|
-
export {
|
|
293
|
+
export { FlagshipBinding as a, FlagshipClientProviderOptions as c, FlagshipEvaluationResponse as d, FlagshipProviderOptions as f, FLAGSHIP_DEFAULT_BASE_URL as i, FlagshipError as l, isBindingOptions as m, ContextTransformer as n, FlagshipBindingEvaluationDetails as o, FlagshipServerProviderOptions as p, CachedFlag as r, FlagshipBindingProviderOptions as s, FlagshipClient as t, FlagshipErrorCode as u };
|
|
@@ -33,8 +33,10 @@ interface FlagshipProviderOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
accountId?: string;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Evaluation endpoint. Mutually exclusive with `appId`.
|
|
37
|
+
* Accepts an absolute URL or a root-relative path. Relative paths are
|
|
38
|
+
* only supported by `FlagshipClientProvider` and are resolved against
|
|
39
|
+
* `window.location.origin`.
|
|
38
40
|
*/
|
|
39
41
|
endpoint?: string;
|
|
40
42
|
/**
|
|
@@ -130,6 +132,64 @@ interface FlagshipEvaluationResponse {
|
|
|
130
132
|
*/
|
|
131
133
|
reason: 'TARGETING_MATCH' | 'DEFAULT' | 'DISABLED' | 'SPLIT';
|
|
132
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Shape of the Flagship wrangler binding exposed on `env` in Cloudflare Workers.
|
|
137
|
+
*
|
|
138
|
+
* This is an alias for the `Flagship` class from `@cloudflare/workers-types`.
|
|
139
|
+
* The binding communicates directly with the Flagship service via workerd RPC —
|
|
140
|
+
* no HTTP overhead, no auth tokens required. Configure it in `wrangler.json`:
|
|
141
|
+
*
|
|
142
|
+
* ```jsonc
|
|
143
|
+
* {
|
|
144
|
+
* "flagship": [
|
|
145
|
+
* { "binding": "FLAGS", "app_id": "<your-app-id>" }
|
|
146
|
+
* ]
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
type FlagshipBinding = Flagship;
|
|
151
|
+
/**
|
|
152
|
+
* Evaluation details returned by the Flagship binding's `*Details` methods.
|
|
153
|
+
* Contains the resolved value along with metadata about why that value was
|
|
154
|
+
* chosen.
|
|
155
|
+
*
|
|
156
|
+
* This is an alias for the `FlagshipEvaluationDetails` interface from
|
|
157
|
+
* `@cloudflare/workers-types`.
|
|
158
|
+
*/
|
|
159
|
+
type FlagshipBindingEvaluationDetails<T> = FlagshipEvaluationDetails<T>;
|
|
160
|
+
/**
|
|
161
|
+
* Configuration for `FlagshipServerProvider` when using a wrangler binding.
|
|
162
|
+
*
|
|
163
|
+
* In this mode the provider delegates all evaluations to the Flagship binding
|
|
164
|
+
* on `env`, bypassing HTTP entirely. No `appId`, `accountId`, or `authToken`
|
|
165
|
+
* is required — the binding handles authentication and routing.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* new FlagshipServerProvider({ binding: env.FLAGS })
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
interface FlagshipBindingProviderOptions {
|
|
173
|
+
/** The Flagship binding from the Worker's `env` object. */
|
|
174
|
+
binding: FlagshipBinding;
|
|
175
|
+
/**
|
|
176
|
+
* Enable SDK-level logging.
|
|
177
|
+
* @default false
|
|
178
|
+
*/
|
|
179
|
+
logging?: boolean;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Options accepted by `FlagshipServerProvider`.
|
|
183
|
+
*
|
|
184
|
+
* Provide **either** HTTP configuration (`appId`/`endpoint` + credentials) **or**
|
|
185
|
+
* a wrangler `binding` — never both. The provider detects which mode to use
|
|
186
|
+
* based on the presence of the `binding` field.
|
|
187
|
+
*/
|
|
188
|
+
type FlagshipServerProviderOptions = FlagshipProviderOptions | FlagshipBindingProviderOptions;
|
|
189
|
+
/**
|
|
190
|
+
* Type guard: returns `true` when the options use binding mode.
|
|
191
|
+
*/
|
|
192
|
+
declare function isBindingOptions(options: FlagshipServerProviderOptions): options is FlagshipBindingProviderOptions;
|
|
133
193
|
/**
|
|
134
194
|
* Internal error codes produced by `FlagshipClient`.
|
|
135
195
|
* These are mapped to OpenFeature `ErrorCode` values by the providers.
|
|
@@ -230,4 +290,4 @@ declare class FlagshipClient {
|
|
|
230
290
|
private fetchWithTimeout;
|
|
231
291
|
}
|
|
232
292
|
//#endregion
|
|
233
|
-
export {
|
|
293
|
+
export { FlagshipBinding as a, FlagshipClientProviderOptions as c, FlagshipEvaluationResponse as d, FlagshipProviderOptions as f, FLAGSHIP_DEFAULT_BASE_URL as i, FlagshipError as l, isBindingOptions as m, ContextTransformer as n, FlagshipBindingEvaluationDetails as o, FlagshipServerProviderOptions as p, CachedFlag as r, FlagshipBindingProviderOptions as s, FlagshipClient as t, FlagshipErrorCode as u };
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,
|
|
2
|
-
const require_src = require("./src-De-abNIr.cjs");
|
|
3
|
-
exports.ContextTransformer = require_src.ContextTransformer;
|
|
4
|
-
exports.FLAGSHIP_DEFAULT_BASE_URL = require_src.FLAGSHIP_DEFAULT_BASE_URL;
|
|
5
|
-
exports.FlagshipClient = require_src.FlagshipClient;
|
|
6
|
-
exports.FlagshipError = require_src.FlagshipError;
|
|
7
|
-
exports.FlagshipErrorCode = require_src.FlagshipErrorCode;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./src-QEIBSid3.cjs`);exports.ContextTransformer=e.n,exports.FLAGSHIP_DEFAULT_BASE_URL=e.r,exports.FlagshipClient=e.t,exports.FlagshipError=e.i,exports.FlagshipErrorCode=e.a,exports.isBindingOptions=e.o;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { CachedFlag, ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipClient, FlagshipClientProviderOptions, FlagshipError, FlagshipErrorCode, FlagshipEvaluationResponse, FlagshipProviderOptions };
|
|
1
|
+
import { a as FlagshipBinding, c as FlagshipClientProviderOptions, d as FlagshipEvaluationResponse, f as FlagshipProviderOptions, i as FLAGSHIP_DEFAULT_BASE_URL, l as FlagshipError, m as isBindingOptions, n as ContextTransformer, o as FlagshipBindingEvaluationDetails, p as FlagshipServerProviderOptions, r as CachedFlag, s as FlagshipBindingProviderOptions, t as FlagshipClient, u as FlagshipErrorCode } from "./index-BsvVosek.cjs";
|
|
2
|
+
export { CachedFlag, ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipBinding, FlagshipBindingEvaluationDetails, FlagshipBindingProviderOptions, FlagshipClient, FlagshipClientProviderOptions, FlagshipError, FlagshipErrorCode, FlagshipEvaluationResponse, FlagshipProviderOptions, FlagshipServerProviderOptions, isBindingOptions };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { CachedFlag, ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipClient, FlagshipClientProviderOptions, FlagshipError, FlagshipErrorCode, FlagshipEvaluationResponse, FlagshipProviderOptions };
|
|
1
|
+
import { a as FlagshipBinding, c as FlagshipClientProviderOptions, d as FlagshipEvaluationResponse, f as FlagshipProviderOptions, i as FLAGSHIP_DEFAULT_BASE_URL, l as FlagshipError, m as isBindingOptions, n as ContextTransformer, o as FlagshipBindingEvaluationDetails, p as FlagshipServerProviderOptions, r as CachedFlag, s as FlagshipBindingProviderOptions, t as FlagshipClient, u as FlagshipErrorCode } from "./index-BMGtqsWU.mjs";
|
|
2
|
+
export { CachedFlag, ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipBinding, FlagshipBindingEvaluationDetails, FlagshipBindingProviderOptions, FlagshipClient, FlagshipClientProviderOptions, FlagshipError, FlagshipErrorCode, FlagshipEvaluationResponse, FlagshipProviderOptions, FlagshipServerProviderOptions, isBindingOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export { ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipClient, FlagshipError, FlagshipErrorCode };
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,t as a}from"./src-DW2QohY9.mjs";export{n as ContextTransformer,i as FLAGSHIP_DEFAULT_BASE_URL,a as FlagshipClient,t as FlagshipError,e as FlagshipErrorCode,r as isBindingOptions};
|
package/dist/server.cjs
CHANGED
|
@@ -1,298 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,
|
|
2
|
-
const require_src = require("./src-De-abNIr.cjs");
|
|
3
|
-
let _openfeature_server_sdk = require("@openfeature/server-sdk");
|
|
4
|
-
//#region src/server-provider.ts
|
|
5
|
-
const _noop = () => {};
|
|
6
|
-
/**
|
|
7
|
-
* OpenFeature provider for Flagship (server-side / dynamic context).
|
|
8
|
-
*
|
|
9
|
-
* Use this provider with `@openfeature/server-sdk` for Node.js,
|
|
10
|
-
* Cloudflare Workers, and other server-side JavaScript environments.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
15
|
-
* import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
16
|
-
*
|
|
17
|
-
* await OpenFeature.setProviderAndWait(
|
|
18
|
-
* new FlagshipServerProvider({
|
|
19
|
-
* appId: 'app-abc123',
|
|
20
|
-
* accountId: 'your-account-id',
|
|
21
|
-
* authToken: 'your-token',
|
|
22
|
-
* })
|
|
23
|
-
* );
|
|
24
|
-
*
|
|
25
|
-
* const client = OpenFeature.getClient();
|
|
26
|
-
* const value = await client.getBooleanValue('my-flag', false, {
|
|
27
|
-
* targetingKey: 'user-123',
|
|
28
|
-
* email: 'user@example.com',
|
|
29
|
-
* });
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
var FlagshipServerProvider = class {
|
|
33
|
-
constructor(options) {
|
|
34
|
-
this.runsOn = "server";
|
|
35
|
-
this.events = new _openfeature_server_sdk.OpenFeatureEventEmitter();
|
|
36
|
-
this.currentStatus = _openfeature_server_sdk.ProviderStatus.NOT_READY;
|
|
37
|
-
this.metadata = { name: "Flagship Server Provider" };
|
|
38
|
-
this.client = new require_src.FlagshipClient(options);
|
|
39
|
-
this.logging = options.logging ?? false;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Returns the provided logger when logging is enabled, or a no-op logger
|
|
43
|
-
* when `logging` is `false`. Using this in every resolve method ensures
|
|
44
|
-
* the SDK produces no console output unless the caller opts in.
|
|
45
|
-
*/
|
|
46
|
-
logger(logger) {
|
|
47
|
-
if (this.logging) return logger;
|
|
48
|
-
return {
|
|
49
|
-
debug: _noop,
|
|
50
|
-
info: _noop,
|
|
51
|
-
warn: _noop,
|
|
52
|
-
error: _noop
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Probes the evaluation endpoint with a health-check request. A 404 response
|
|
57
|
-
* is treated as success — it means the endpoint is reachable but the
|
|
58
|
-
* health-check flag simply doesn't exist, which is expected. Any network
|
|
59
|
-
* failure or timeout sets the status to ERROR.
|
|
60
|
-
*/
|
|
61
|
-
async initialize(_context) {
|
|
62
|
-
try {
|
|
63
|
-
await this.client.evaluate("_flagship_health_check", {});
|
|
64
|
-
this.currentStatus = _openfeature_server_sdk.ProviderStatus.READY;
|
|
65
|
-
this.events.emit(_openfeature_server_sdk.ProviderEvents.Ready);
|
|
66
|
-
} catch (error) {
|
|
67
|
-
if (error instanceof require_src.FlagshipError && error.cause instanceof Response && error.cause.status === 404) {
|
|
68
|
-
this.currentStatus = _openfeature_server_sdk.ProviderStatus.READY;
|
|
69
|
-
this.events.emit(_openfeature_server_sdk.ProviderEvents.Ready);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
this.currentStatus = _openfeature_server_sdk.ProviderStatus.ERROR;
|
|
73
|
-
this.events.emit(_openfeature_server_sdk.ProviderEvents.Error, { message: error instanceof Error ? error.message : String(error) });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
async onClose() {
|
|
77
|
-
this.currentStatus = _openfeature_server_sdk.ProviderStatus.NOT_READY;
|
|
78
|
-
}
|
|
79
|
-
get status() {
|
|
80
|
-
return this.currentStatus;
|
|
81
|
-
}
|
|
82
|
-
async resolveBooleanEvaluation(flagKey, defaultValue, context, logger) {
|
|
83
|
-
return this.resolve(flagKey, defaultValue, context, "boolean", logger);
|
|
84
|
-
}
|
|
85
|
-
async resolveStringEvaluation(flagKey, defaultValue, context, logger) {
|
|
86
|
-
return this.resolve(flagKey, defaultValue, context, "string", logger);
|
|
87
|
-
}
|
|
88
|
-
async resolveNumberEvaluation(flagKey, defaultValue, context, logger) {
|
|
89
|
-
return this.resolve(flagKey, defaultValue, context, "number", logger);
|
|
90
|
-
}
|
|
91
|
-
async resolveObjectEvaluation(flagKey, defaultValue, context, logger) {
|
|
92
|
-
return this.resolve(flagKey, defaultValue, context, "object", logger);
|
|
93
|
-
}
|
|
94
|
-
async resolve(flagKey, defaultValue, context, expectedType, logger) {
|
|
95
|
-
const log = this.logger(logger);
|
|
96
|
-
try {
|
|
97
|
-
log.debug(`[Flagship] Evaluating flag "${flagKey}" (expected: ${expectedType})`);
|
|
98
|
-
const result = await this.client.evaluate(flagKey, context);
|
|
99
|
-
const actualType = this.getValueType(result.value);
|
|
100
|
-
if (actualType !== expectedType) {
|
|
101
|
-
const msg = `Flag "${flagKey}" type mismatch: expected ${expectedType}, got ${actualType}`;
|
|
102
|
-
log.warn(`[Flagship] ${msg}`);
|
|
103
|
-
return {
|
|
104
|
-
value: defaultValue,
|
|
105
|
-
errorCode: _openfeature_server_sdk.ErrorCode.TYPE_MISMATCH,
|
|
106
|
-
errorMessage: msg,
|
|
107
|
-
reason: "ERROR"
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
log.debug(`[Flagship] Flag "${flagKey}" resolved: value=${String(result.value)} reason=${result.reason} variant=${result.variant}`);
|
|
111
|
-
return {
|
|
112
|
-
value: result.value,
|
|
113
|
-
variant: result.variant,
|
|
114
|
-
reason: result.reason,
|
|
115
|
-
flagMetadata: {}
|
|
116
|
-
};
|
|
117
|
-
} catch (error) {
|
|
118
|
-
return this.handleError(flagKey, defaultValue, error, log);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Maps a runtime value to one of the four OpenFeature flag types.
|
|
123
|
-
* `null` maps to `'object'` (typeof null === 'object'), treating it as a
|
|
124
|
-
* JSON null which belongs to the object/structure category.
|
|
125
|
-
*/
|
|
126
|
-
getValueType(value) {
|
|
127
|
-
if (typeof value === "boolean") return "boolean";
|
|
128
|
-
if (typeof value === "string") return "string";
|
|
129
|
-
if (typeof value === "number") return "number";
|
|
130
|
-
return "object";
|
|
131
|
-
}
|
|
132
|
-
handleError(flagKey, defaultValue, error, logger) {
|
|
133
|
-
if (error instanceof require_src.FlagshipError) {
|
|
134
|
-
let errorCode;
|
|
135
|
-
switch (error.code) {
|
|
136
|
-
case require_src.FlagshipErrorCode.NETWORK_ERROR:
|
|
137
|
-
errorCode = error.cause instanceof Response && error.cause.status === 404 ? _openfeature_server_sdk.ErrorCode.FLAG_NOT_FOUND : _openfeature_server_sdk.ErrorCode.GENERAL;
|
|
138
|
-
break;
|
|
139
|
-
case require_src.FlagshipErrorCode.TIMEOUT_ERROR:
|
|
140
|
-
errorCode = _openfeature_server_sdk.ErrorCode.GENERAL;
|
|
141
|
-
break;
|
|
142
|
-
case require_src.FlagshipErrorCode.PARSE_ERROR:
|
|
143
|
-
errorCode = _openfeature_server_sdk.ErrorCode.PARSE_ERROR;
|
|
144
|
-
break;
|
|
145
|
-
case require_src.FlagshipErrorCode.INVALID_CONTEXT:
|
|
146
|
-
errorCode = _openfeature_server_sdk.ErrorCode.INVALID_CONTEXT;
|
|
147
|
-
break;
|
|
148
|
-
default: errorCode = _openfeature_server_sdk.ErrorCode.GENERAL;
|
|
149
|
-
}
|
|
150
|
-
logger.error(`[Flagship] Flag "${flagKey}" evaluation failed (${errorCode}): ${error.message}`);
|
|
151
|
-
return {
|
|
152
|
-
value: defaultValue,
|
|
153
|
-
errorCode,
|
|
154
|
-
errorMessage: error.message,
|
|
155
|
-
reason: "ERROR"
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
const errorMessage = String(error);
|
|
159
|
-
logger.error(`[Flagship] Flag "${flagKey}" evaluation failed (GENERAL): ${errorMessage}`);
|
|
160
|
-
return {
|
|
161
|
-
value: defaultValue,
|
|
162
|
-
errorCode: _openfeature_server_sdk.ErrorCode.GENERAL,
|
|
163
|
-
errorMessage,
|
|
164
|
-
reason: "ERROR"
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
//#endregion
|
|
169
|
-
//#region src/hooks/logging-hook.ts
|
|
170
|
-
/**
|
|
171
|
-
* Logging hook for debugging flag evaluations
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```typescript
|
|
175
|
-
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
176
|
-
* import { FlagshipServerProvider, LoggingHook } from '@cloudflare/flagship/server';
|
|
177
|
-
*
|
|
178
|
-
* const provider = new FlagshipServerProvider({ appId: 'your-app-id', accountId: 'your-account-id' });
|
|
179
|
-
* await OpenFeature.setProviderAndWait(provider);
|
|
180
|
-
*
|
|
181
|
-
* // Add logging hook
|
|
182
|
-
* OpenFeature.addHooks(new LoggingHook());
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
var LoggingHook = class {
|
|
186
|
-
constructor(logger = console.log) {
|
|
187
|
-
this.logger = logger;
|
|
188
|
-
}
|
|
189
|
-
before(hookContext, _hookHints) {
|
|
190
|
-
this.logger(`[Flagship] Evaluating flag: ${hookContext.flagKey}`, {
|
|
191
|
-
defaultValue: hookContext.defaultValue,
|
|
192
|
-
context: hookContext.context
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
after(hookContext, evaluationDetails, _hookHints) {
|
|
196
|
-
this.logger(`[Flagship] Flag ${hookContext.flagKey} evaluated`, {
|
|
197
|
-
value: evaluationDetails.value,
|
|
198
|
-
reason: evaluationDetails.reason,
|
|
199
|
-
variant: evaluationDetails.variant,
|
|
200
|
-
errorCode: evaluationDetails.errorCode
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
error(hookContext, error, _hookHints) {
|
|
204
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
205
|
-
this.logger(`[Flagship] Error evaluating flag ${hookContext.flagKey}:`, message);
|
|
206
|
-
}
|
|
207
|
-
finally(_hookContext, _evaluationDetails, _hookHints) {}
|
|
208
|
-
};
|
|
209
|
-
//#endregion
|
|
210
|
-
//#region src/hooks/telemetry-hook.ts
|
|
211
|
-
/**
|
|
212
|
-
* Telemetry hook for tracking flag evaluations
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* ```typescript
|
|
216
|
-
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
217
|
-
* import { FlagshipServerProvider, TelemetryHook } from '@cloudflare/flagship/server';
|
|
218
|
-
*
|
|
219
|
-
* const telemetryHook = new TelemetryHook((event) => {
|
|
220
|
-
* // Send to your analytics service
|
|
221
|
-
* analytics.track('flag_evaluated', event);
|
|
222
|
-
* });
|
|
223
|
-
*
|
|
224
|
-
* OpenFeature.addHooks(telemetryHook);
|
|
225
|
-
* ```
|
|
226
|
-
*/
|
|
227
|
-
var TelemetryHook = class {
|
|
228
|
-
constructor(onEvent) {
|
|
229
|
-
this.startTimes = /* @__PURE__ */ new Map();
|
|
230
|
-
this.contextKeys = /* @__PURE__ */ new WeakMap();
|
|
231
|
-
this.hints = /* @__PURE__ */ new WeakMap();
|
|
232
|
-
this.onEvent = onEvent;
|
|
233
|
-
}
|
|
234
|
-
before(hookContext, hookHints) {
|
|
235
|
-
const now = Date.now();
|
|
236
|
-
const key = `${hookContext.flagKey}-${now}-${Math.random()}`;
|
|
237
|
-
this.startTimes.set(key, now);
|
|
238
|
-
this.contextKeys.set(hookContext, key);
|
|
239
|
-
if (hookHints !== void 0) this.hints.set(hookContext, hookHints);
|
|
240
|
-
}
|
|
241
|
-
after(hookContext, evaluationDetails, _hookHints) {
|
|
242
|
-
const telemetryKey = this.contextKeys.get(hookContext);
|
|
243
|
-
const startTime = telemetryKey ? this.startTimes.get(telemetryKey) : void 0;
|
|
244
|
-
const duration = startTime !== void 0 ? Date.now() - startTime : void 0;
|
|
245
|
-
if (telemetryKey !== void 0) {
|
|
246
|
-
this.startTimes.delete(telemetryKey);
|
|
247
|
-
this.contextKeys.delete(hookContext);
|
|
248
|
-
}
|
|
249
|
-
this.onEvent({
|
|
250
|
-
type: "evaluation",
|
|
251
|
-
flagKey: hookContext.flagKey,
|
|
252
|
-
timestamp: Date.now(),
|
|
253
|
-
duration,
|
|
254
|
-
value: evaluationDetails.value,
|
|
255
|
-
reason: evaluationDetails.reason,
|
|
256
|
-
variant: evaluationDetails.variant,
|
|
257
|
-
errorCode: evaluationDetails.errorCode,
|
|
258
|
-
context: hookContext.context,
|
|
259
|
-
hints: this.hints.get(hookContext)
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
error(hookContext, error, _hookHints) {
|
|
263
|
-
const telemetryKey = this.contextKeys.get(hookContext);
|
|
264
|
-
const startTime = telemetryKey ? this.startTimes.get(telemetryKey) : void 0;
|
|
265
|
-
const duration = startTime !== void 0 ? Date.now() - startTime : void 0;
|
|
266
|
-
if (telemetryKey !== void 0) {
|
|
267
|
-
this.startTimes.delete(telemetryKey);
|
|
268
|
-
this.contextKeys.delete(hookContext);
|
|
269
|
-
}
|
|
270
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
271
|
-
this.onEvent({
|
|
272
|
-
type: "error",
|
|
273
|
-
flagKey: hookContext.flagKey,
|
|
274
|
-
timestamp: Date.now(),
|
|
275
|
-
duration,
|
|
276
|
-
errorMessage,
|
|
277
|
-
context: hookContext.context,
|
|
278
|
-
hints: this.hints.get(hookContext)
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
finally(hookContext, _evaluationDetails, _hookHints) {
|
|
282
|
-
const telemetryKey = this.contextKeys.get(hookContext);
|
|
283
|
-
if (telemetryKey !== void 0) {
|
|
284
|
-
this.startTimes.delete(telemetryKey);
|
|
285
|
-
this.contextKeys.delete(hookContext);
|
|
286
|
-
}
|
|
287
|
-
this.hints.delete(hookContext);
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
//#endregion
|
|
291
|
-
exports.ContextTransformer = require_src.ContextTransformer;
|
|
292
|
-
exports.FLAGSHIP_DEFAULT_BASE_URL = require_src.FLAGSHIP_DEFAULT_BASE_URL;
|
|
293
|
-
exports.FlagshipClient = require_src.FlagshipClient;
|
|
294
|
-
exports.FlagshipError = require_src.FlagshipError;
|
|
295
|
-
exports.FlagshipErrorCode = require_src.FlagshipErrorCode;
|
|
296
|
-
exports.FlagshipServerProvider = FlagshipServerProvider;
|
|
297
|
-
exports.LoggingHook = LoggingHook;
|
|
298
|
-
exports.TelemetryHook = TelemetryHook;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./src-QEIBSid3.cjs`);let t=require(`@openfeature/server-sdk`);const n=()=>{},r=[`appId`,`endpoint`,`accountId`,`authToken`,`baseUrl`,`fetchOptions`,`timeout`,`retries`,`retryDelay`];var i=class{constructor(n){if(this.runsOn=`server`,this.events=new t.OpenFeatureEventEmitter,this.currentStatus=t.ProviderStatus.NOT_READY,this.metadata={name:`Flagship Server Provider`},this.logging=n.logging??!1,e.o(n)){let e=r.filter(e=>e in n);if(e.length>0)throw Error(`Flagship: when using a binding, the following HTTP-specific options must not be provided: ${e.join(`, `)}. Provide either a binding or HTTP configuration, not both.`);this.binding=n.binding,this.client=void 0,this.resolve=this.resolveViaBinding.bind(this)}else this.client=new e.t(n),this.binding=void 0,this.resolve=this.resolveViaHttp.bind(this)}logger(e){return this.logging?e:{debug:n,info:n,warn:n,error:n}}async initialize(n){if(this.binding){this.currentStatus=t.ProviderStatus.READY,this.events.emit(t.ProviderEvents.Ready);return}try{await this.client.evaluate(`_flagship_health_check`,{}),this.currentStatus=t.ProviderStatus.READY,this.events.emit(t.ProviderEvents.Ready)}catch(n){if(n instanceof e.i&&n.cause instanceof Response&&n.cause.status===404){this.currentStatus=t.ProviderStatus.READY,this.events.emit(t.ProviderEvents.Ready);return}this.currentStatus=t.ProviderStatus.ERROR,this.events.emit(t.ProviderEvents.Error,{message:n instanceof Error?n.message:String(n)})}}async onClose(){this.currentStatus=t.ProviderStatus.NOT_READY}get status(){return this.currentStatus}async resolveBooleanEvaluation(e,t,n,r){return this.resolve(e,t,n,`boolean`,r)}async resolveStringEvaluation(e,t,n,r){return this.resolve(e,t,n,`string`,r)}async resolveNumberEvaluation(e,t,n,r){return this.resolve(e,t,n,`number`,r)}async resolveObjectEvaluation(e,t,n,r){return this.resolve(e,t,n,`object`,r)}async resolveViaHttp(e,n,r,i,o){let s=this.logger(o);try{s.debug(`[Flagship] Evaluating flag "${e}" (expected: ${i})`);let o=await this.client.evaluate(e,r),c=a(o.value);if(c!==i){let r=`Flag "${e}" type mismatch: expected ${i}, got ${c}`;return s.warn(`[Flagship] ${r}`),{value:n,errorCode:t.ErrorCode.TYPE_MISMATCH,errorMessage:r,reason:`ERROR`}}return s.debug(`[Flagship] Flag "${e}" resolved: value=${String(o.value)} reason=${o.reason} variant=${o.variant}`),{value:o.value,variant:o.variant,reason:o.reason,flagMetadata:{}}}catch(t){return this.handleHttpError(e,n,t,s)}}handleHttpError(n,r,i,a){if(i instanceof e.i){let o;switch(i.code){case e.a.NETWORK_ERROR:o=i.cause instanceof Response&&i.cause.status===404?t.ErrorCode.FLAG_NOT_FOUND:t.ErrorCode.GENERAL;break;case e.a.TIMEOUT_ERROR:o=t.ErrorCode.GENERAL;break;case e.a.PARSE_ERROR:o=t.ErrorCode.PARSE_ERROR;break;case e.a.INVALID_CONTEXT:o=t.ErrorCode.INVALID_CONTEXT;break;default:o=t.ErrorCode.GENERAL}return a.error(`[Flagship] Flag "${n}" evaluation failed (${o}): ${i.message}`),{value:r,errorCode:o,errorMessage:i.message,reason:`ERROR`}}let o=String(i);return a.error(`[Flagship] Flag "${n}" evaluation failed (GENERAL): ${o}`),{value:r,errorCode:t.ErrorCode.GENERAL,errorMessage:o,reason:`ERROR`}}async resolveViaBinding(e,n,r,i,c){let l=this.logger(c);try{l.debug(`[Flagship] Evaluating flag "${e}" via binding (expected: ${i})`);let c=o(r,l),u=await this.evaluateBinding(e,n,i,c);if(u.errorCode){let t=s(u.errorCode),r=u.errorMessage??`Binding error: ${u.errorCode}`;return l.error(`[Flagship] Flag "${e}" evaluation failed (${t}): ${r}`),{value:n,errorCode:t,errorMessage:r,reason:u.reason??`ERROR`}}let d=a(u.value);if(d!==i){let r=`Flag "${e}" type mismatch: expected ${i}, got ${d}`;return l.warn(`[Flagship] ${r}`),{value:n,errorCode:t.ErrorCode.TYPE_MISMATCH,errorMessage:r,reason:`ERROR`}}return l.debug(`[Flagship] Flag "${e}" resolved via binding: value=${String(u.value)} reason=${u.reason} variant=${u.variant}`),{value:u.value,variant:u.variant,reason:u.reason,flagMetadata:{}}}catch(r){let i=r instanceof Error?r.message:String(r);return l.error(`[Flagship] Flag "${e}" binding evaluation failed (GENERAL): ${i}`),{value:n,errorCode:t.ErrorCode.GENERAL,errorMessage:i,reason:`ERROR`}}}async evaluateBinding(e,t,n,r){let i=this.binding;switch(n){case`boolean`:return i.getBooleanDetails(e,t,r);case`string`:return i.getStringDetails(e,t,r);case`number`:return i.getNumberDetails(e,t,r);case`object`:return i.getObjectDetails(e,t,r)}}};function a(e){return typeof e==`boolean`?`boolean`:typeof e==`string`?`string`:typeof e==`number`?`number`:`object`}function o(e,t){let n={};for(let[r,i]of Object.entries(e))if(i!=null){if(i instanceof Date){n[r]=i.toISOString();continue}if(typeof i==`string`||typeof i==`number`||typeof i==`boolean`){n[r]=i;continue}if(typeof i==`object`){t.warn(`[Flagship] Context key "${r}" is a complex object/array and cannot be passed to the binding. This value will be ignored.`);continue}}return n}function s(e){switch(e){case`FLAG_NOT_FOUND`:return t.ErrorCode.FLAG_NOT_FOUND;case`PARSE_ERROR`:return t.ErrorCode.PARSE_ERROR;case`TYPE_MISMATCH`:return t.ErrorCode.TYPE_MISMATCH;case`INVALID_CONTEXT`:return t.ErrorCode.INVALID_CONTEXT;default:return t.ErrorCode.GENERAL}}var c=class{constructor(e=console.log){this.logger=e}before(e,t){this.logger(`[Flagship] Evaluating flag: ${e.flagKey}`,{defaultValue:e.defaultValue,context:e.context})}after(e,t,n){this.logger(`[Flagship] Flag ${e.flagKey} evaluated`,{value:t.value,reason:t.reason,variant:t.variant,errorCode:t.errorCode})}error(e,t,n){let r=t instanceof Error?t.message:String(t);this.logger(`[Flagship] Error evaluating flag ${e.flagKey}:`,r)}finally(e,t,n){}},l=class{constructor(e){this.startTimes=new Map,this.contextKeys=new WeakMap,this.hints=new WeakMap,this.onEvent=e}before(e,t){let n=Date.now(),r=`${e.flagKey}-${n}-${Math.random()}`;this.startTimes.set(r,n),this.contextKeys.set(e,r),t!==void 0&&this.hints.set(e,t)}after(e,t,n){let r=this.contextKeys.get(e),i=r?this.startTimes.get(r):void 0,a=i===void 0?void 0:Date.now()-i;r!==void 0&&(this.startTimes.delete(r),this.contextKeys.delete(e)),this.onEvent({type:`evaluation`,flagKey:e.flagKey,timestamp:Date.now(),duration:a,value:t.value,reason:t.reason,variant:t.variant,errorCode:t.errorCode,context:e.context,hints:this.hints.get(e)})}error(e,t,n){let r=this.contextKeys.get(e),i=r?this.startTimes.get(r):void 0,a=i===void 0?void 0:Date.now()-i;r!==void 0&&(this.startTimes.delete(r),this.contextKeys.delete(e));let o=t instanceof Error?t.message:String(t);this.onEvent({type:`error`,flagKey:e.flagKey,timestamp:Date.now(),duration:a,errorMessage:o,context:e.context,hints:this.hints.get(e)})}finally(e,t,n){let r=this.contextKeys.get(e);r!==void 0&&(this.startTimes.delete(r),this.contextKeys.delete(e)),this.hints.delete(e)}};exports.ContextTransformer=e.n,exports.FLAGSHIP_DEFAULT_BASE_URL=e.r,exports.FlagshipClient=e.t,exports.FlagshipError=e.i,exports.FlagshipErrorCode=e.a,exports.FlagshipServerProvider=i,exports.LoggingHook=c,exports.TelemetryHook=l,exports.isBindingOptions=e.o;
|