@cloudflare/flagship 0.0.2 → 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/dist/server.d.cts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as FlagshipBinding, 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, s as FlagshipBindingProviderOptions, t as FlagshipClient, u as FlagshipErrorCode } from "./index-BsvVosek.cjs";
|
|
2
2
|
import { BeforeHookContext, ErrorCode, EvaluationContext, EvaluationDetails, FlagValue, Hook, HookContext, HookHints, JsonValue, Logger, OpenFeatureEventEmitter, Provider, ProviderMetadata, ProviderStatus, ResolutionDetails } from "@openfeature/server-sdk";
|
|
3
3
|
|
|
4
4
|
//#region src/server-provider.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* OpenFeature provider for Flagship (server-side / dynamic context).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* Cloudflare Workers, and other server-side JavaScript environments.
|
|
8
|
+
* Supports two modes of operation:
|
|
10
9
|
*
|
|
11
|
-
*
|
|
10
|
+
* **HTTP mode** — evaluates flags via HTTP requests to the Flagship API.
|
|
11
|
+
* Requires `appId`/`endpoint`, `accountId`, and optionally `authToken`.
|
|
12
|
+
*
|
|
13
|
+
* **Binding mode** — evaluates flags via a Cloudflare Workers wrangler binding.
|
|
14
|
+
* Only requires the `binding` field (the `Flagship` object from `env`). No HTTP
|
|
15
|
+
* overhead, no auth tokens. This is the recommended approach for Workers.
|
|
16
|
+
*
|
|
17
|
+
* @example HTTP mode
|
|
12
18
|
* ```typescript
|
|
13
19
|
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
14
20
|
* import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
@@ -20,22 +26,37 @@ import { BeforeHookContext, ErrorCode, EvaluationContext, EvaluationDetails, Fla
|
|
|
20
26
|
* authToken: 'your-token',
|
|
21
27
|
* })
|
|
22
28
|
* );
|
|
29
|
+
* ```
|
|
23
30
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
31
|
+
* @example Binding mode (Cloudflare Workers)
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
34
|
+
* import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
35
|
+
*
|
|
36
|
+
* export default {
|
|
37
|
+
* async fetch(request: Request, env: { FLAGS: FlagshipBinding }) {
|
|
38
|
+
* await OpenFeature.setProviderAndWait(
|
|
39
|
+
* new FlagshipServerProvider({ binding: env.FLAGS })
|
|
40
|
+
* );
|
|
41
|
+
* const client = OpenFeature.getClient();
|
|
42
|
+
* const value = await client.getBooleanValue('my-flag', false);
|
|
43
|
+
* return new Response(JSON.stringify({ value }));
|
|
44
|
+
* },
|
|
45
|
+
* };
|
|
29
46
|
* ```
|
|
30
47
|
*/
|
|
31
48
|
declare class FlagshipServerProvider implements Provider {
|
|
32
49
|
readonly metadata: ProviderMetadata;
|
|
33
50
|
readonly runsOn: "server";
|
|
34
51
|
readonly events: OpenFeatureEventEmitter;
|
|
52
|
+
/** Set when operating in HTTP mode; `undefined` in binding mode. */
|
|
35
53
|
private readonly client;
|
|
54
|
+
/** Set when operating in binding mode; `undefined` in HTTP mode. */
|
|
55
|
+
private readonly binding;
|
|
36
56
|
private readonly logging;
|
|
37
57
|
private currentStatus;
|
|
38
|
-
|
|
58
|
+
private readonly resolve;
|
|
59
|
+
constructor(options: FlagshipServerProviderOptions);
|
|
39
60
|
/**
|
|
40
61
|
* Returns the provided logger when logging is enabled, or a no-op logger
|
|
41
62
|
* when `logging` is `false`. Using this in every resolve method ensures
|
|
@@ -43,10 +64,14 @@ declare class FlagshipServerProvider implements Provider {
|
|
|
43
64
|
*/
|
|
44
65
|
private logger;
|
|
45
66
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
67
|
+
* Initializes the provider.
|
|
68
|
+
*
|
|
69
|
+
* **HTTP mode**: probes the evaluation endpoint with a health-check request.
|
|
70
|
+
* A 404 response is treated as success — it means the endpoint is reachable
|
|
71
|
+
* but the health-check flag simply doesn't exist, which is expected.
|
|
72
|
+
*
|
|
73
|
+
* **Binding mode**: sets READY immediately — the binding is guaranteed to
|
|
74
|
+
* be available by the Workers runtime.
|
|
50
75
|
*/
|
|
51
76
|
initialize(_context?: EvaluationContext): Promise<void>;
|
|
52
77
|
onClose(): Promise<void>;
|
|
@@ -55,14 +80,14 @@ declare class FlagshipServerProvider implements Provider {
|
|
|
55
80
|
resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
|
|
56
81
|
resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>>;
|
|
57
82
|
resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>>;
|
|
58
|
-
private
|
|
83
|
+
private resolveViaHttp;
|
|
84
|
+
private handleHttpError;
|
|
85
|
+
private resolveViaBinding;
|
|
59
86
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* JSON null which belongs to the object/structure category.
|
|
87
|
+
* Calls the appropriate `*Details` method on the binding based on the
|
|
88
|
+
* expected type. Falls back to `get` + synthetic details for unknown types.
|
|
63
89
|
*/
|
|
64
|
-
private
|
|
65
|
-
private handleError;
|
|
90
|
+
private evaluateBinding;
|
|
66
91
|
}
|
|
67
92
|
//#endregion
|
|
68
93
|
//#region src/hooks/logging-hook.d.ts
|
|
@@ -141,4 +166,4 @@ declare class TelemetryHook implements Hook {
|
|
|
141
166
|
finally(hookContext: Readonly<HookContext>, _evaluationDetails: EvaluationDetails<FlagValue>, _hookHints?: HookHints): void;
|
|
142
167
|
}
|
|
143
168
|
//#endregion
|
|
144
|
-
export { ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipClient, FlagshipError, FlagshipErrorCode, type FlagshipEvaluationResponse, type FlagshipProviderOptions, FlagshipServerProvider, LoggingHook, type TelemetryEvent, TelemetryHook };
|
|
169
|
+
export { ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, type FlagshipBinding, type FlagshipBindingEvaluationDetails, type FlagshipBindingProviderOptions, FlagshipClient, FlagshipError, FlagshipErrorCode, type FlagshipEvaluationResponse, type FlagshipProviderOptions, FlagshipServerProvider, type FlagshipServerProviderOptions, LoggingHook, type TelemetryEvent, TelemetryHook, isBindingOptions };
|
package/dist/server.d.mts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as FlagshipBinding, 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, s as FlagshipBindingProviderOptions, t as FlagshipClient, u as FlagshipErrorCode } from "./index-BMGtqsWU.mjs";
|
|
2
2
|
import { BeforeHookContext, ErrorCode, EvaluationContext, EvaluationDetails, FlagValue, Hook, HookContext, HookHints, JsonValue, Logger, OpenFeatureEventEmitter, Provider, ProviderMetadata, ProviderStatus, ResolutionDetails } from "@openfeature/server-sdk";
|
|
3
3
|
|
|
4
4
|
//#region src/server-provider.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* OpenFeature provider for Flagship (server-side / dynamic context).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* Cloudflare Workers, and other server-side JavaScript environments.
|
|
8
|
+
* Supports two modes of operation:
|
|
10
9
|
*
|
|
11
|
-
*
|
|
10
|
+
* **HTTP mode** — evaluates flags via HTTP requests to the Flagship API.
|
|
11
|
+
* Requires `appId`/`endpoint`, `accountId`, and optionally `authToken`.
|
|
12
|
+
*
|
|
13
|
+
* **Binding mode** — evaluates flags via a Cloudflare Workers wrangler binding.
|
|
14
|
+
* Only requires the `binding` field (the `Flagship` object from `env`). No HTTP
|
|
15
|
+
* overhead, no auth tokens. This is the recommended approach for Workers.
|
|
16
|
+
*
|
|
17
|
+
* @example HTTP mode
|
|
12
18
|
* ```typescript
|
|
13
19
|
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
14
20
|
* import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
@@ -20,22 +26,37 @@ import { BeforeHookContext, ErrorCode, EvaluationContext, EvaluationDetails, Fla
|
|
|
20
26
|
* authToken: 'your-token',
|
|
21
27
|
* })
|
|
22
28
|
* );
|
|
29
|
+
* ```
|
|
23
30
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
31
|
+
* @example Binding mode (Cloudflare Workers)
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
34
|
+
* import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
35
|
+
*
|
|
36
|
+
* export default {
|
|
37
|
+
* async fetch(request: Request, env: { FLAGS: FlagshipBinding }) {
|
|
38
|
+
* await OpenFeature.setProviderAndWait(
|
|
39
|
+
* new FlagshipServerProvider({ binding: env.FLAGS })
|
|
40
|
+
* );
|
|
41
|
+
* const client = OpenFeature.getClient();
|
|
42
|
+
* const value = await client.getBooleanValue('my-flag', false);
|
|
43
|
+
* return new Response(JSON.stringify({ value }));
|
|
44
|
+
* },
|
|
45
|
+
* };
|
|
29
46
|
* ```
|
|
30
47
|
*/
|
|
31
48
|
declare class FlagshipServerProvider implements Provider {
|
|
32
49
|
readonly metadata: ProviderMetadata;
|
|
33
50
|
readonly runsOn: "server";
|
|
34
51
|
readonly events: OpenFeatureEventEmitter;
|
|
52
|
+
/** Set when operating in HTTP mode; `undefined` in binding mode. */
|
|
35
53
|
private readonly client;
|
|
54
|
+
/** Set when operating in binding mode; `undefined` in HTTP mode. */
|
|
55
|
+
private readonly binding;
|
|
36
56
|
private readonly logging;
|
|
37
57
|
private currentStatus;
|
|
38
|
-
|
|
58
|
+
private readonly resolve;
|
|
59
|
+
constructor(options: FlagshipServerProviderOptions);
|
|
39
60
|
/**
|
|
40
61
|
* Returns the provided logger when logging is enabled, or a no-op logger
|
|
41
62
|
* when `logging` is `false`. Using this in every resolve method ensures
|
|
@@ -43,10 +64,14 @@ declare class FlagshipServerProvider implements Provider {
|
|
|
43
64
|
*/
|
|
44
65
|
private logger;
|
|
45
66
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
67
|
+
* Initializes the provider.
|
|
68
|
+
*
|
|
69
|
+
* **HTTP mode**: probes the evaluation endpoint with a health-check request.
|
|
70
|
+
* A 404 response is treated as success — it means the endpoint is reachable
|
|
71
|
+
* but the health-check flag simply doesn't exist, which is expected.
|
|
72
|
+
*
|
|
73
|
+
* **Binding mode**: sets READY immediately — the binding is guaranteed to
|
|
74
|
+
* be available by the Workers runtime.
|
|
50
75
|
*/
|
|
51
76
|
initialize(_context?: EvaluationContext): Promise<void>;
|
|
52
77
|
onClose(): Promise<void>;
|
|
@@ -55,14 +80,14 @@ declare class FlagshipServerProvider implements Provider {
|
|
|
55
80
|
resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
|
|
56
81
|
resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>>;
|
|
57
82
|
resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>>;
|
|
58
|
-
private
|
|
83
|
+
private resolveViaHttp;
|
|
84
|
+
private handleHttpError;
|
|
85
|
+
private resolveViaBinding;
|
|
59
86
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* JSON null which belongs to the object/structure category.
|
|
87
|
+
* Calls the appropriate `*Details` method on the binding based on the
|
|
88
|
+
* expected type. Falls back to `get` + synthetic details for unknown types.
|
|
63
89
|
*/
|
|
64
|
-
private
|
|
65
|
-
private handleError;
|
|
90
|
+
private evaluateBinding;
|
|
66
91
|
}
|
|
67
92
|
//#endregion
|
|
68
93
|
//#region src/hooks/logging-hook.d.ts
|
|
@@ -141,4 +166,4 @@ declare class TelemetryHook implements Hook {
|
|
|
141
166
|
finally(hookContext: Readonly<HookContext>, _evaluationDetails: EvaluationDetails<FlagValue>, _hookHints?: HookHints): void;
|
|
142
167
|
}
|
|
143
168
|
//#endregion
|
|
144
|
-
export { ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipClient, FlagshipError, FlagshipErrorCode, type FlagshipEvaluationResponse, type FlagshipProviderOptions, FlagshipServerProvider, LoggingHook, type TelemetryEvent, TelemetryHook };
|
|
169
|
+
export { ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, type FlagshipBinding, type FlagshipBindingEvaluationDetails, type FlagshipBindingProviderOptions, FlagshipClient, FlagshipError, FlagshipErrorCode, type FlagshipEvaluationResponse, type FlagshipProviderOptions, FlagshipServerProvider, type FlagshipServerProviderOptions, LoggingHook, type TelemetryEvent, TelemetryHook, isBindingOptions };
|
package/dist/server.mjs
CHANGED
|
@@ -1,290 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ErrorCode, OpenFeatureEventEmitter, ProviderEvents, ProviderStatus } from "@openfeature/server-sdk";
|
|
3
|
-
//#region src/server-provider.ts
|
|
4
|
-
const _noop = () => {};
|
|
5
|
-
/**
|
|
6
|
-
* OpenFeature provider for Flagship (server-side / dynamic context).
|
|
7
|
-
*
|
|
8
|
-
* Use this provider with `@openfeature/server-sdk` for Node.js,
|
|
9
|
-
* Cloudflare Workers, and other server-side JavaScript environments.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
14
|
-
* import { FlagshipServerProvider } from '@cloudflare/flagship/server';
|
|
15
|
-
*
|
|
16
|
-
* await OpenFeature.setProviderAndWait(
|
|
17
|
-
* new FlagshipServerProvider({
|
|
18
|
-
* appId: 'app-abc123',
|
|
19
|
-
* accountId: 'your-account-id',
|
|
20
|
-
* authToken: 'your-token',
|
|
21
|
-
* })
|
|
22
|
-
* );
|
|
23
|
-
*
|
|
24
|
-
* const client = OpenFeature.getClient();
|
|
25
|
-
* const value = await client.getBooleanValue('my-flag', false, {
|
|
26
|
-
* targetingKey: 'user-123',
|
|
27
|
-
* email: 'user@example.com',
|
|
28
|
-
* });
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
var FlagshipServerProvider = class {
|
|
32
|
-
constructor(options) {
|
|
33
|
-
this.runsOn = "server";
|
|
34
|
-
this.events = new OpenFeatureEventEmitter();
|
|
35
|
-
this.currentStatus = ProviderStatus.NOT_READY;
|
|
36
|
-
this.metadata = { name: "Flagship Server Provider" };
|
|
37
|
-
this.client = new FlagshipClient(options);
|
|
38
|
-
this.logging = options.logging ?? false;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Returns the provided logger when logging is enabled, or a no-op logger
|
|
42
|
-
* when `logging` is `false`. Using this in every resolve method ensures
|
|
43
|
-
* the SDK produces no console output unless the caller opts in.
|
|
44
|
-
*/
|
|
45
|
-
logger(logger) {
|
|
46
|
-
if (this.logging) return logger;
|
|
47
|
-
return {
|
|
48
|
-
debug: _noop,
|
|
49
|
-
info: _noop,
|
|
50
|
-
warn: _noop,
|
|
51
|
-
error: _noop
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Probes the evaluation endpoint with a health-check request. A 404 response
|
|
56
|
-
* is treated as success — it means the endpoint is reachable but the
|
|
57
|
-
* health-check flag simply doesn't exist, which is expected. Any network
|
|
58
|
-
* failure or timeout sets the status to ERROR.
|
|
59
|
-
*/
|
|
60
|
-
async initialize(_context) {
|
|
61
|
-
try {
|
|
62
|
-
await this.client.evaluate("_flagship_health_check", {});
|
|
63
|
-
this.currentStatus = ProviderStatus.READY;
|
|
64
|
-
this.events.emit(ProviderEvents.Ready);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
if (error instanceof FlagshipError && error.cause instanceof Response && error.cause.status === 404) {
|
|
67
|
-
this.currentStatus = ProviderStatus.READY;
|
|
68
|
-
this.events.emit(ProviderEvents.Ready);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
this.currentStatus = ProviderStatus.ERROR;
|
|
72
|
-
this.events.emit(ProviderEvents.Error, { message: error instanceof Error ? error.message : String(error) });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
async onClose() {
|
|
76
|
-
this.currentStatus = ProviderStatus.NOT_READY;
|
|
77
|
-
}
|
|
78
|
-
get status() {
|
|
79
|
-
return this.currentStatus;
|
|
80
|
-
}
|
|
81
|
-
async resolveBooleanEvaluation(flagKey, defaultValue, context, logger) {
|
|
82
|
-
return this.resolve(flagKey, defaultValue, context, "boolean", logger);
|
|
83
|
-
}
|
|
84
|
-
async resolveStringEvaluation(flagKey, defaultValue, context, logger) {
|
|
85
|
-
return this.resolve(flagKey, defaultValue, context, "string", logger);
|
|
86
|
-
}
|
|
87
|
-
async resolveNumberEvaluation(flagKey, defaultValue, context, logger) {
|
|
88
|
-
return this.resolve(flagKey, defaultValue, context, "number", logger);
|
|
89
|
-
}
|
|
90
|
-
async resolveObjectEvaluation(flagKey, defaultValue, context, logger) {
|
|
91
|
-
return this.resolve(flagKey, defaultValue, context, "object", logger);
|
|
92
|
-
}
|
|
93
|
-
async resolve(flagKey, defaultValue, context, expectedType, logger) {
|
|
94
|
-
const log = this.logger(logger);
|
|
95
|
-
try {
|
|
96
|
-
log.debug(`[Flagship] Evaluating flag "${flagKey}" (expected: ${expectedType})`);
|
|
97
|
-
const result = await this.client.evaluate(flagKey, context);
|
|
98
|
-
const actualType = this.getValueType(result.value);
|
|
99
|
-
if (actualType !== expectedType) {
|
|
100
|
-
const msg = `Flag "${flagKey}" type mismatch: expected ${expectedType}, got ${actualType}`;
|
|
101
|
-
log.warn(`[Flagship] ${msg}`);
|
|
102
|
-
return {
|
|
103
|
-
value: defaultValue,
|
|
104
|
-
errorCode: ErrorCode.TYPE_MISMATCH,
|
|
105
|
-
errorMessage: msg,
|
|
106
|
-
reason: "ERROR"
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
log.debug(`[Flagship] Flag "${flagKey}" resolved: value=${String(result.value)} reason=${result.reason} variant=${result.variant}`);
|
|
110
|
-
return {
|
|
111
|
-
value: result.value,
|
|
112
|
-
variant: result.variant,
|
|
113
|
-
reason: result.reason,
|
|
114
|
-
flagMetadata: {}
|
|
115
|
-
};
|
|
116
|
-
} catch (error) {
|
|
117
|
-
return this.handleError(flagKey, defaultValue, error, log);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Maps a runtime value to one of the four OpenFeature flag types.
|
|
122
|
-
* `null` maps to `'object'` (typeof null === 'object'), treating it as a
|
|
123
|
-
* JSON null which belongs to the object/structure category.
|
|
124
|
-
*/
|
|
125
|
-
getValueType(value) {
|
|
126
|
-
if (typeof value === "boolean") return "boolean";
|
|
127
|
-
if (typeof value === "string") return "string";
|
|
128
|
-
if (typeof value === "number") return "number";
|
|
129
|
-
return "object";
|
|
130
|
-
}
|
|
131
|
-
handleError(flagKey, defaultValue, error, logger) {
|
|
132
|
-
if (error instanceof FlagshipError) {
|
|
133
|
-
let errorCode;
|
|
134
|
-
switch (error.code) {
|
|
135
|
-
case FlagshipErrorCode.NETWORK_ERROR:
|
|
136
|
-
errorCode = error.cause instanceof Response && error.cause.status === 404 ? ErrorCode.FLAG_NOT_FOUND : ErrorCode.GENERAL;
|
|
137
|
-
break;
|
|
138
|
-
case FlagshipErrorCode.TIMEOUT_ERROR:
|
|
139
|
-
errorCode = ErrorCode.GENERAL;
|
|
140
|
-
break;
|
|
141
|
-
case FlagshipErrorCode.PARSE_ERROR:
|
|
142
|
-
errorCode = ErrorCode.PARSE_ERROR;
|
|
143
|
-
break;
|
|
144
|
-
case FlagshipErrorCode.INVALID_CONTEXT:
|
|
145
|
-
errorCode = ErrorCode.INVALID_CONTEXT;
|
|
146
|
-
break;
|
|
147
|
-
default: errorCode = ErrorCode.GENERAL;
|
|
148
|
-
}
|
|
149
|
-
logger.error(`[Flagship] Flag "${flagKey}" evaluation failed (${errorCode}): ${error.message}`);
|
|
150
|
-
return {
|
|
151
|
-
value: defaultValue,
|
|
152
|
-
errorCode,
|
|
153
|
-
errorMessage: error.message,
|
|
154
|
-
reason: "ERROR"
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
const errorMessage = String(error);
|
|
158
|
-
logger.error(`[Flagship] Flag "${flagKey}" evaluation failed (GENERAL): ${errorMessage}`);
|
|
159
|
-
return {
|
|
160
|
-
value: defaultValue,
|
|
161
|
-
errorCode: ErrorCode.GENERAL,
|
|
162
|
-
errorMessage,
|
|
163
|
-
reason: "ERROR"
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
//#endregion
|
|
168
|
-
//#region src/hooks/logging-hook.ts
|
|
169
|
-
/**
|
|
170
|
-
* Logging hook for debugging flag evaluations
|
|
171
|
-
*
|
|
172
|
-
* @example
|
|
173
|
-
* ```typescript
|
|
174
|
-
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
175
|
-
* import { FlagshipServerProvider, LoggingHook } from '@cloudflare/flagship/server';
|
|
176
|
-
*
|
|
177
|
-
* const provider = new FlagshipServerProvider({ appId: 'your-app-id', accountId: 'your-account-id' });
|
|
178
|
-
* await OpenFeature.setProviderAndWait(provider);
|
|
179
|
-
*
|
|
180
|
-
* // Add logging hook
|
|
181
|
-
* OpenFeature.addHooks(new LoggingHook());
|
|
182
|
-
* ```
|
|
183
|
-
*/
|
|
184
|
-
var LoggingHook = class {
|
|
185
|
-
constructor(logger = console.log) {
|
|
186
|
-
this.logger = logger;
|
|
187
|
-
}
|
|
188
|
-
before(hookContext, _hookHints) {
|
|
189
|
-
this.logger(`[Flagship] Evaluating flag: ${hookContext.flagKey}`, {
|
|
190
|
-
defaultValue: hookContext.defaultValue,
|
|
191
|
-
context: hookContext.context
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
after(hookContext, evaluationDetails, _hookHints) {
|
|
195
|
-
this.logger(`[Flagship] Flag ${hookContext.flagKey} evaluated`, {
|
|
196
|
-
value: evaluationDetails.value,
|
|
197
|
-
reason: evaluationDetails.reason,
|
|
198
|
-
variant: evaluationDetails.variant,
|
|
199
|
-
errorCode: evaluationDetails.errorCode
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
error(hookContext, error, _hookHints) {
|
|
203
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
204
|
-
this.logger(`[Flagship] Error evaluating flag ${hookContext.flagKey}:`, message);
|
|
205
|
-
}
|
|
206
|
-
finally(_hookContext, _evaluationDetails, _hookHints) {}
|
|
207
|
-
};
|
|
208
|
-
//#endregion
|
|
209
|
-
//#region src/hooks/telemetry-hook.ts
|
|
210
|
-
/**
|
|
211
|
-
* Telemetry hook for tracking flag evaluations
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* ```typescript
|
|
215
|
-
* import { OpenFeature } from '@openfeature/server-sdk';
|
|
216
|
-
* import { FlagshipServerProvider, TelemetryHook } from '@cloudflare/flagship/server';
|
|
217
|
-
*
|
|
218
|
-
* const telemetryHook = new TelemetryHook((event) => {
|
|
219
|
-
* // Send to your analytics service
|
|
220
|
-
* analytics.track('flag_evaluated', event);
|
|
221
|
-
* });
|
|
222
|
-
*
|
|
223
|
-
* OpenFeature.addHooks(telemetryHook);
|
|
224
|
-
* ```
|
|
225
|
-
*/
|
|
226
|
-
var TelemetryHook = class {
|
|
227
|
-
constructor(onEvent) {
|
|
228
|
-
this.startTimes = /* @__PURE__ */ new Map();
|
|
229
|
-
this.contextKeys = /* @__PURE__ */ new WeakMap();
|
|
230
|
-
this.hints = /* @__PURE__ */ new WeakMap();
|
|
231
|
-
this.onEvent = onEvent;
|
|
232
|
-
}
|
|
233
|
-
before(hookContext, hookHints) {
|
|
234
|
-
const now = Date.now();
|
|
235
|
-
const key = `${hookContext.flagKey}-${now}-${Math.random()}`;
|
|
236
|
-
this.startTimes.set(key, now);
|
|
237
|
-
this.contextKeys.set(hookContext, key);
|
|
238
|
-
if (hookHints !== void 0) this.hints.set(hookContext, hookHints);
|
|
239
|
-
}
|
|
240
|
-
after(hookContext, evaluationDetails, _hookHints) {
|
|
241
|
-
const telemetryKey = this.contextKeys.get(hookContext);
|
|
242
|
-
const startTime = telemetryKey ? this.startTimes.get(telemetryKey) : void 0;
|
|
243
|
-
const duration = startTime !== void 0 ? Date.now() - startTime : void 0;
|
|
244
|
-
if (telemetryKey !== void 0) {
|
|
245
|
-
this.startTimes.delete(telemetryKey);
|
|
246
|
-
this.contextKeys.delete(hookContext);
|
|
247
|
-
}
|
|
248
|
-
this.onEvent({
|
|
249
|
-
type: "evaluation",
|
|
250
|
-
flagKey: hookContext.flagKey,
|
|
251
|
-
timestamp: Date.now(),
|
|
252
|
-
duration,
|
|
253
|
-
value: evaluationDetails.value,
|
|
254
|
-
reason: evaluationDetails.reason,
|
|
255
|
-
variant: evaluationDetails.variant,
|
|
256
|
-
errorCode: evaluationDetails.errorCode,
|
|
257
|
-
context: hookContext.context,
|
|
258
|
-
hints: this.hints.get(hookContext)
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
error(hookContext, error, _hookHints) {
|
|
262
|
-
const telemetryKey = this.contextKeys.get(hookContext);
|
|
263
|
-
const startTime = telemetryKey ? this.startTimes.get(telemetryKey) : void 0;
|
|
264
|
-
const duration = startTime !== void 0 ? Date.now() - startTime : void 0;
|
|
265
|
-
if (telemetryKey !== void 0) {
|
|
266
|
-
this.startTimes.delete(telemetryKey);
|
|
267
|
-
this.contextKeys.delete(hookContext);
|
|
268
|
-
}
|
|
269
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
270
|
-
this.onEvent({
|
|
271
|
-
type: "error",
|
|
272
|
-
flagKey: hookContext.flagKey,
|
|
273
|
-
timestamp: Date.now(),
|
|
274
|
-
duration,
|
|
275
|
-
errorMessage,
|
|
276
|
-
context: hookContext.context,
|
|
277
|
-
hints: this.hints.get(hookContext)
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
finally(hookContext, _evaluationDetails, _hookHints) {
|
|
281
|
-
const telemetryKey = this.contextKeys.get(hookContext);
|
|
282
|
-
if (telemetryKey !== void 0) {
|
|
283
|
-
this.startTimes.delete(telemetryKey);
|
|
284
|
-
this.contextKeys.delete(hookContext);
|
|
285
|
-
}
|
|
286
|
-
this.hints.delete(hookContext);
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
//#endregion
|
|
290
|
-
export { ContextTransformer, FLAGSHIP_DEFAULT_BASE_URL, FlagshipClient, FlagshipError, FlagshipErrorCode, FlagshipServerProvider, LoggingHook, TelemetryHook };
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,t as a}from"./src-DW2QohY9.mjs";import{ErrorCode as o,OpenFeatureEventEmitter as s,ProviderEvents as c,ProviderStatus as l}from"@openfeature/server-sdk";const u=()=>{},d=[`appId`,`endpoint`,`accountId`,`authToken`,`baseUrl`,`fetchOptions`,`timeout`,`retries`,`retryDelay`];var f=class{constructor(e){if(this.runsOn=`server`,this.events=new s,this.currentStatus=l.NOT_READY,this.metadata={name:`Flagship Server Provider`},this.logging=e.logging??!1,r(e)){let t=d.filter(t=>t in e);if(t.length>0)throw Error(`Flagship: when using a binding, the following HTTP-specific options must not be provided: ${t.join(`, `)}. Provide either a binding or HTTP configuration, not both.`);this.binding=e.binding,this.client=void 0,this.resolve=this.resolveViaBinding.bind(this)}else this.client=new a(e),this.binding=void 0,this.resolve=this.resolveViaHttp.bind(this)}logger(e){return this.logging?e:{debug:u,info:u,warn:u,error:u}}async initialize(e){if(this.binding){this.currentStatus=l.READY,this.events.emit(c.Ready);return}try{await this.client.evaluate(`_flagship_health_check`,{}),this.currentStatus=l.READY,this.events.emit(c.Ready)}catch(e){if(e instanceof t&&e.cause instanceof Response&&e.cause.status===404){this.currentStatus=l.READY,this.events.emit(c.Ready);return}this.currentStatus=l.ERROR,this.events.emit(c.Error,{message:e instanceof Error?e.message:String(e)})}}async onClose(){this.currentStatus=l.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,t,n,r,i){let a=this.logger(i);try{a.debug(`[Flagship] Evaluating flag "${e}" (expected: ${r})`);let i=await this.client.evaluate(e,n),s=p(i.value);if(s!==r){let n=`Flag "${e}" type mismatch: expected ${r}, got ${s}`;return a.warn(`[Flagship] ${n}`),{value:t,errorCode:o.TYPE_MISMATCH,errorMessage:n,reason:`ERROR`}}return a.debug(`[Flagship] Flag "${e}" resolved: value=${String(i.value)} reason=${i.reason} variant=${i.variant}`),{value:i.value,variant:i.variant,reason:i.reason,flagMetadata:{}}}catch(n){return this.handleHttpError(e,t,n,a)}}handleHttpError(n,r,i,a){if(i instanceof t){let t;switch(i.code){case e.NETWORK_ERROR:t=i.cause instanceof Response&&i.cause.status===404?o.FLAG_NOT_FOUND:o.GENERAL;break;case e.TIMEOUT_ERROR:t=o.GENERAL;break;case e.PARSE_ERROR:t=o.PARSE_ERROR;break;case e.INVALID_CONTEXT:t=o.INVALID_CONTEXT;break;default:t=o.GENERAL}return a.error(`[Flagship] Flag "${n}" evaluation failed (${t}): ${i.message}`),{value:r,errorCode:t,errorMessage:i.message,reason:`ERROR`}}let s=String(i);return a.error(`[Flagship] Flag "${n}" evaluation failed (GENERAL): ${s}`),{value:r,errorCode:o.GENERAL,errorMessage:s,reason:`ERROR`}}async resolveViaBinding(e,t,n,r,i){let a=this.logger(i);try{a.debug(`[Flagship] Evaluating flag "${e}" via binding (expected: ${r})`);let i=m(n,a),s=await this.evaluateBinding(e,t,r,i);if(s.errorCode){let n=h(s.errorCode),r=s.errorMessage??`Binding error: ${s.errorCode}`;return a.error(`[Flagship] Flag "${e}" evaluation failed (${n}): ${r}`),{value:t,errorCode:n,errorMessage:r,reason:s.reason??`ERROR`}}let c=p(s.value);if(c!==r){let n=`Flag "${e}" type mismatch: expected ${r}, got ${c}`;return a.warn(`[Flagship] ${n}`),{value:t,errorCode:o.TYPE_MISMATCH,errorMessage:n,reason:`ERROR`}}return a.debug(`[Flagship] Flag "${e}" resolved via binding: value=${String(s.value)} reason=${s.reason} variant=${s.variant}`),{value:s.value,variant:s.variant,reason:s.reason,flagMetadata:{}}}catch(n){let r=n instanceof Error?n.message:String(n);return a.error(`[Flagship] Flag "${e}" binding evaluation failed (GENERAL): ${r}`),{value:t,errorCode:o.GENERAL,errorMessage:r,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 p(e){return typeof e==`boolean`?`boolean`:typeof e==`string`?`string`:typeof e==`number`?`number`:`object`}function m(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 h(e){switch(e){case`FLAG_NOT_FOUND`:return o.FLAG_NOT_FOUND;case`PARSE_ERROR`:return o.PARSE_ERROR;case`TYPE_MISMATCH`:return o.TYPE_MISMATCH;case`INVALID_CONTEXT`:return o.INVALID_CONTEXT;default:return o.GENERAL}}var g=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){}},_=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)}};export{n as ContextTransformer,i as FLAGSHIP_DEFAULT_BASE_URL,a as FlagshipClient,t as FlagshipError,e as FlagshipErrorCode,f as FlagshipServerProvider,g as LoggingHook,_ as TelemetryHook,r as isBindingOptions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`https://api.cloudflare.com`;function t(e){return`binding`in e&&e.binding!==null&&e.binding!==void 0}let n=function(e){return e.NETWORK_ERROR=`NETWORK_ERROR`,e.TIMEOUT_ERROR=`TIMEOUT_ERROR`,e.PARSE_ERROR=`PARSE_ERROR`,e.INVALID_CONTEXT=`INVALID_CONTEXT`,e}({});var r=class extends Error{constructor(e,t,n){super(e),this.code=t,this.cause=n,this.name=`FlagshipError`,Object.setPrototypeOf(this,new.target.prototype)}},i=class{static toQueryParams(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]=String(i);continue}if(typeof i==`object`){t?t.push(r):console.warn(`[Flagship] Context key "${r}" is a complex object/array and cannot be serialized to a query parameter. This value will be ignored during flag evaluation.`);continue}}return n}static buildUrl(e,t,n,r){let i=new URL(e);i.searchParams.set(`flagKey`,t);let a=this.toQueryParams(n,r);for(let[e,t]of Object.entries(a))i.searchParams.set(e,t);return i.toString()}},a=class{constructor(e){this.options={endpoint:s(e),fetchOptions:o(e),timeout:e.timeout||5e3,retries:Math.min(e.retries===void 0?1:e.retries,10),retryDelay:Math.min(e.retryDelay===void 0?1e3:e.retryDelay,3e4)}}async evaluate(e,t){let a=[],o=i.buildUrl(this.options.endpoint,e,t,a);if(a.length>0)throw new r(`Evaluation context contains complex values that cannot be serialized for flag "${e}". Unsupported keys: ${a.join(`, `)}. Use primitive values (string, number, boolean) or Date objects.`,n.INVALID_CONTEXT);return this.fetchWithRetry(o,this.options.retries)}async fetchWithRetry(e,t){try{return await this.fetchWithTimeout(e,this.options.timeout)}catch(n){if(n instanceof r&&n.cause instanceof Response){let e=n.cause.status;if(e===404||e===400)throw n}if(t>0)return await new Promise(e=>setTimeout(e,this.options.retryDelay)),this.fetchWithRetry(e,t-1);throw n}}async fetchWithTimeout(e,t){let i=new AbortController,a=setTimeout(()=>i.abort(),t);try{let t=await fetch(e,{...this.options.fetchOptions,signal:i.signal});if(clearTimeout(a),!t.ok)throw new r(`HTTP ${t.status}: ${t.statusText}`,n.NETWORK_ERROR,t);let o=await t.json();if(!o||typeof o!=`object`||!(`flagKey`in o)||!(`value`in o))throw new r(`Invalid response format from Flagship API`,n.PARSE_ERROR);return o}catch(e){throw clearTimeout(a),e instanceof Error&&e.name===`AbortError`?new r(`Request timeout after ${t}ms`,n.TIMEOUT_ERROR,e):e instanceof r?e:new r(`Network error: ${e}`,n.NETWORK_ERROR,e)}}};function o(e){let{authToken:t,fetchOptions:n={}}=e;if(!t)return n;let r=new Headers(n.headers);return r.has(`Authorization`)||r.set(`Authorization`,`Bearer ${t}`),{...n,headers:r}}function s(e){let{appId:t,endpoint:n,baseUrl:r,accountId:i}=e;if(t&&n)throw Error(`Flagship: provide either "appId" or "endpoint", not both`);if(!t&&!n)throw Error(`Flagship: either "appId" or "endpoint" is required`);if(n){try{new URL(n)}catch{throw Error(`Flagship: invalid endpoint URL: ${n}`)}return n}if(!i)throw Error(`Flagship: "accountId" is required when using "appId"`);let a=`${(r||`https://api.cloudflare.com`).replace(/\/+$/,``)}/client/v4/accounts/${encodeURIComponent(i)}/flagship/apps/${encodeURIComponent(t)}/evaluate`;try{new URL(a)}catch{throw Error(`Flagship: resolved endpoint is not a valid URL: ${a}`)}return a}export{n as a,r as i,i as n,t as o,e as r,a as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return`binding`in e&&e.binding!==null&&e.binding!==void 0}let t=function(e){return e.NETWORK_ERROR=`NETWORK_ERROR`,e.TIMEOUT_ERROR=`TIMEOUT_ERROR`,e.PARSE_ERROR=`PARSE_ERROR`,e.INVALID_CONTEXT=`INVALID_CONTEXT`,e}({});var n=class extends Error{constructor(e,t,n){super(e),this.code=t,this.cause=n,this.name=`FlagshipError`,Object.setPrototypeOf(this,new.target.prototype)}},r=class{static toQueryParams(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]=String(i);continue}if(typeof i==`object`){t?t.push(r):console.warn(`[Flagship] Context key "${r}" is a complex object/array and cannot be serialized to a query parameter. This value will be ignored during flag evaluation.`);continue}}return n}static buildUrl(e,t,n,r){let i=new URL(e);i.searchParams.set(`flagKey`,t);let a=this.toQueryParams(n,r);for(let[e,t]of Object.entries(a))i.searchParams.set(e,t);return i.toString()}},i=class{constructor(e){this.options={endpoint:o(e),fetchOptions:a(e),timeout:e.timeout||5e3,retries:Math.min(e.retries===void 0?1:e.retries,10),retryDelay:Math.min(e.retryDelay===void 0?1e3:e.retryDelay,3e4)}}async evaluate(e,i){let a=[],o=r.buildUrl(this.options.endpoint,e,i,a);if(a.length>0)throw new n(`Evaluation context contains complex values that cannot be serialized for flag "${e}". Unsupported keys: ${a.join(`, `)}. Use primitive values (string, number, boolean) or Date objects.`,t.INVALID_CONTEXT);return this.fetchWithRetry(o,this.options.retries)}async fetchWithRetry(e,t){try{return await this.fetchWithTimeout(e,this.options.timeout)}catch(r){if(r instanceof n&&r.cause instanceof Response){let e=r.cause.status;if(e===404||e===400)throw r}if(t>0)return await new Promise(e=>setTimeout(e,this.options.retryDelay)),this.fetchWithRetry(e,t-1);throw r}}async fetchWithTimeout(e,r){let i=new AbortController,a=setTimeout(()=>i.abort(),r);try{let r=await fetch(e,{...this.options.fetchOptions,signal:i.signal});if(clearTimeout(a),!r.ok)throw new n(`HTTP ${r.status}: ${r.statusText}`,t.NETWORK_ERROR,r);let o=await r.json();if(!o||typeof o!=`object`||!(`flagKey`in o)||!(`value`in o))throw new n(`Invalid response format from Flagship API`,t.PARSE_ERROR);return o}catch(e){throw clearTimeout(a),e instanceof Error&&e.name===`AbortError`?new n(`Request timeout after ${r}ms`,t.TIMEOUT_ERROR,e):e instanceof n?e:new n(`Network error: ${e}`,t.NETWORK_ERROR,e)}}};function a(e){let{authToken:t,fetchOptions:n={}}=e;if(!t)return n;let r=new Headers(n.headers);return r.has(`Authorization`)||r.set(`Authorization`,`Bearer ${t}`),{...n,headers:r}}function o(e){let{appId:t,endpoint:n,baseUrl:r,accountId:i}=e;if(t&&n)throw Error(`Flagship: provide either "appId" or "endpoint", not both`);if(!t&&!n)throw Error(`Flagship: either "appId" or "endpoint" is required`);if(n){try{new URL(n)}catch{throw Error(`Flagship: invalid endpoint URL: ${n}`)}return n}if(!i)throw Error(`Flagship: "accountId" is required when using "appId"`);let a=`${(r||`https://api.cloudflare.com`).replace(/\/+$/,``)}/client/v4/accounts/${encodeURIComponent(i)}/flagship/apps/${encodeURIComponent(t)}/evaluate`;try{new URL(a)}catch{throw Error(`Flagship: resolved endpoint is not a valid URL: ${a}`)}return a}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return`https://api.cloudflare.com`}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return i}});
|