@spotify-confidence/openfeature-server-provider-local 0.7.0 → 0.8.1
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/CHANGELOG.md +24 -0
- package/README.md +15 -0
- package/dist/client.d.ts +129 -0
- package/dist/client.js +194 -0
- package/dist/confidence_resolver.wasm +0 -0
- package/dist/index.fetch.d.ts +69 -7
- package/dist/index.fetch.js +394 -126
- package/dist/index.inlined.d.ts +69 -7
- package/dist/index.inlined.js +395 -127
- package/dist/index.node.d.ts +69 -7
- package/dist/index.node.js +394 -126
- package/dist/server.d.ts +126 -0
- package/dist/server.js +114 -0
- package/package.json +27 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.1](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.8.0...openfeature-provider-js-v0.8.1) (2026-02-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Dependencies
|
|
7
|
+
|
|
8
|
+
* The following workspace dependencies were updated
|
|
9
|
+
* dependencies
|
|
10
|
+
* rust-guest bumped from 0.1.14 to 0.1.15
|
|
11
|
+
|
|
12
|
+
## [0.8.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.7.0...openfeature-provider-js-v0.8.0) (2026-01-27)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **js:** add React support with useFlag and useFlagDetails hooks ([#246](https://github.com/spotify/confidence-resolver/issues/246)) ([d579a4c](https://github.com/spotify/confidence-resolver/commit/d579a4c8fe493ee3a92539203f21d1c03758c58e))
|
|
18
|
+
* **wasm:** add wasm API to apply previously resolved flags ([#235](https://github.com/spotify/confidence-resolver/issues/235)) ([79048f6](https://github.com/spotify/confidence-resolver/commit/79048f63a8c771eb98ecf478cab0b654aa745374))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Dependencies
|
|
22
|
+
|
|
23
|
+
* The following workspace dependencies were updated
|
|
24
|
+
* dependencies
|
|
25
|
+
* rust-guest bumped from 0.1.13 to 0.1.14
|
|
26
|
+
|
|
3
27
|
## [0.7.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.6.0...openfeature-provider-js-v0.7.0) (2026-01-22)
|
|
4
28
|
|
|
5
29
|
|
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ OpenFeature provider for the Spotify Confidence resolver (local mode, powered by
|
|
|
8
8
|
- Automatic state refresh and batched flag log flushing
|
|
9
9
|
- Pluggable `fetch` with retries, timeouts and routing
|
|
10
10
|
- Optional logging using `debug`
|
|
11
|
+
- **[React integration](./README-REACT.md)** for Next.js with Server Components
|
|
11
12
|
|
|
12
13
|
## Requirements
|
|
13
14
|
|
|
@@ -194,6 +195,20 @@ const provider = createConfidenceServerProvider({
|
|
|
194
195
|
});
|
|
195
196
|
```
|
|
196
197
|
|
|
198
|
+
### `./react-server` and `./react-client` — React/Next.js Integration
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// Server Component
|
|
202
|
+
import { ConfidenceProvider, getFlag, getFlagDetails } from '@spotify-confidence/openfeature-server-provider-local/react-server';
|
|
203
|
+
|
|
204
|
+
// Client Component
|
|
205
|
+
import { useFlag, useFlagDetails } from '@spotify-confidence/openfeature-server-provider-local/react-client';
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
React hooks and components for Next.js App Router with Server Components. Flags are resolved on the server and provided to client components via React Context.
|
|
209
|
+
|
|
210
|
+
**See [README-REACT.md](./README-REACT.md) for full documentation.**
|
|
211
|
+
|
|
197
212
|
### A note on browser usage
|
|
198
213
|
|
|
199
214
|
While browsers are mentioned in this doc, this package is intended for server-side use only. Two concerns for browser usage:
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { EvaluationDetails, FlagValue } from "@openfeature/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "UNSPECIFIED";
|
|
5
|
+
declare enum ErrorCode {
|
|
6
|
+
PROVIDER_NOT_READY = "PROVIDER_NOT_READY",
|
|
7
|
+
PROVIDER_FATAL = "PROVIDER_FATAL",
|
|
8
|
+
FLAG_NOT_FOUND = "FLAG_NOT_FOUND",
|
|
9
|
+
TYPE_MISMATCH = "TYPE_MISMATCH",
|
|
10
|
+
GENERAL = "GENERAL",
|
|
11
|
+
}
|
|
12
|
+
interface ResolutionDetails<T> {
|
|
13
|
+
reason: ResolutionReason;
|
|
14
|
+
value: T;
|
|
15
|
+
variant?: string;
|
|
16
|
+
errorCode?: ErrorCode;
|
|
17
|
+
errorMessage?: string;
|
|
18
|
+
shouldApply: boolean;
|
|
19
|
+
}
|
|
20
|
+
type FlagPrimitive = null | boolean | string | number;
|
|
21
|
+
type FlagObject = {
|
|
22
|
+
[key: string]: FlagValue$1;
|
|
23
|
+
};
|
|
24
|
+
type FlagValue$1 = FlagPrimitive | FlagObject;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/flag-bundle.d.ts
|
|
27
|
+
interface FlagBundle {
|
|
28
|
+
flags: Record<string, ResolutionDetails<FlagObject | null> | undefined>;
|
|
29
|
+
resolveId: string;
|
|
30
|
+
resolveToken: string;
|
|
31
|
+
errorCode?: ErrorCode;
|
|
32
|
+
errorMessage?: string;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/react/client.d.ts
|
|
36
|
+
type FlagBundle$1 = FlagBundle;
|
|
37
|
+
type ApplyFn = (flagName: string) => Promise<void>;
|
|
38
|
+
/** @internal */
|
|
39
|
+
interface ConfidenceClientProviderProps {
|
|
40
|
+
bundle: FlagBundle$1;
|
|
41
|
+
apply: ApplyFn;
|
|
42
|
+
children: React.ReactNode;
|
|
43
|
+
}
|
|
44
|
+
/** @internal */
|
|
45
|
+
declare function ConfidenceClientProvider({
|
|
46
|
+
bundle,
|
|
47
|
+
apply,
|
|
48
|
+
children
|
|
49
|
+
}: ConfidenceClientProviderProps): React.ReactElement;
|
|
50
|
+
interface UseFlagOptions {
|
|
51
|
+
/** Set to false for manual exposure control. Default is true (auto-expose on mount). */
|
|
52
|
+
expose?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Details returned by useFlagDetails hook.
|
|
56
|
+
* Always includes an expose function for manual exposure logging.
|
|
57
|
+
*/
|
|
58
|
+
interface ClientEvaluationDetails<T extends FlagValue> extends EvaluationDetails<T> {
|
|
59
|
+
/** Function to manually log exposure. No-op if already auto-exposed or if called multiple times. */
|
|
60
|
+
expose: () => void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* React hook for accessing Confidence feature flag values.
|
|
64
|
+
*
|
|
65
|
+
* Automatically logs exposure when the component mounts.
|
|
66
|
+
* Supports dot notation to access nested properties within a flag value.
|
|
67
|
+
*
|
|
68
|
+
* @param flagKey - The flag key, optionally with dot notation for nested access (e.g., 'my-flag.config.enabled')
|
|
69
|
+
* @param defaultValue - Default value if flag or nested property is not found
|
|
70
|
+
* @returns The flag value (or nested property value)
|
|
71
|
+
*
|
|
72
|
+
* @example Basic usage
|
|
73
|
+
* ```tsx
|
|
74
|
+
* const enabled = useFlag('my-feature', false);
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @example Dot notation for nested properties
|
|
78
|
+
* ```tsx
|
|
79
|
+
* // Flag value: { config: { maxItems: 10, enabled: true } }
|
|
80
|
+
* const maxItems = useFlag('my-feature.config.maxItems', 5);
|
|
81
|
+
* const enabled = useFlag('my-feature.config.enabled', false);
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @see useFlagDetails for manual exposure control
|
|
85
|
+
*/
|
|
86
|
+
declare function useFlag<T extends FlagValue>(flagKey: string, defaultValue: T): T;
|
|
87
|
+
/**
|
|
88
|
+
* React hook for accessing Confidence feature flag values with full details.
|
|
89
|
+
*
|
|
90
|
+
* Returns the flag value along with variant, reason, and error information.
|
|
91
|
+
* By default, automatically logs exposure when the component mounts.
|
|
92
|
+
* Use `{ expose: false }` for manual exposure control.
|
|
93
|
+
*
|
|
94
|
+
* Supports dot notation to access nested properties within a flag value.
|
|
95
|
+
*
|
|
96
|
+
* @param flagKey - The flag key, optionally with dot notation for nested access (e.g., 'my-flag.config.enabled')
|
|
97
|
+
* @param defaultValue - Default value if flag or nested property is not found
|
|
98
|
+
* @param options - Use `{ expose: false }` for manual exposure control
|
|
99
|
+
* @returns EvaluationDetails with value, flagKey, flagMetadata, variant, reason, errorCode, errorMessage, and expose function.
|
|
100
|
+
*
|
|
101
|
+
* @example Auto exposure with full details
|
|
102
|
+
* ```tsx
|
|
103
|
+
* const { value, variant, reason } = useFlagDetails('my-feature', false);
|
|
104
|
+
* console.log(`Got ${value} from variant ${variant}, reason: ${reason}`);
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example Manual exposure
|
|
108
|
+
* ```tsx
|
|
109
|
+
* const { value: enabled, expose } = useFlagDetails('my-feature', false, { expose: false });
|
|
110
|
+
*
|
|
111
|
+
* const handleClick = () => {
|
|
112
|
+
* if (enabled) {
|
|
113
|
+
* expose(); // Log exposure only when user interacts
|
|
114
|
+
* doSomething();
|
|
115
|
+
* }
|
|
116
|
+
* };
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @example Error handling
|
|
120
|
+
* ```tsx
|
|
121
|
+
* const { value, errorCode } = useFlagDetails('my-feature', false);
|
|
122
|
+
* if (errorCode === 'FLAG_NOT_FOUND') {
|
|
123
|
+
* console.warn('Flag not configured');
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function useFlagDetails<T extends FlagValue>(flagKey: string, defaultValue: T, options?: UseFlagOptions): ClientEvaluationDetails<T>;
|
|
128
|
+
//#endregion
|
|
129
|
+
export { ClientEvaluationDetails, ConfidenceClientProvider, ConfidenceClientProviderProps, useFlag, useFlagDetails };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useRef } from "react";
|
|
3
|
+
import "@bufbuild/protobuf/wire";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
const NOOP_LOG_FN = Object.assign(() => {}, { enabled: false });
|
|
6
|
+
const debugBackend = loadDebug();
|
|
7
|
+
const logger = new class LoggerImpl {
|
|
8
|
+
childLoggers = /* @__PURE__ */ new Map();
|
|
9
|
+
debug = NOOP_LOG_FN;
|
|
10
|
+
info = NOOP_LOG_FN;
|
|
11
|
+
warn = NOOP_LOG_FN;
|
|
12
|
+
error = NOOP_LOG_FN;
|
|
13
|
+
constructor(name) {
|
|
14
|
+
this.name = name;
|
|
15
|
+
this.configure();
|
|
16
|
+
}
|
|
17
|
+
async configure(backend = debugBackend) {
|
|
18
|
+
const debug = await backend;
|
|
19
|
+
if (!debug) return;
|
|
20
|
+
const debugFn = this.debug = (debug(this.name + ":debug"));
|
|
21
|
+
const infoFn = this.info = (debug(this.name + ":info"));
|
|
22
|
+
const warnFn = this.warn = (debug(this.name + ":warn"));
|
|
23
|
+
const errorFn = this.error = (debug(this.name + ":error"));
|
|
24
|
+
switch (true) {
|
|
25
|
+
case debugFn.enabled: infoFn.enabled = true;
|
|
26
|
+
case infoFn.enabled: warnFn.enabled = true;
|
|
27
|
+
case warnFn.enabled: errorFn.enabled = true;
|
|
28
|
+
}
|
|
29
|
+
for (const child of this.childLoggers.values()) child.configure(debug);
|
|
30
|
+
}
|
|
31
|
+
getLogger(name) {
|
|
32
|
+
let child = (this.childLoggers.get(name));
|
|
33
|
+
if (!child) {
|
|
34
|
+
child = new LoggerImpl(this.name + ":" + name);
|
|
35
|
+
this.childLoggers.set(name, child);
|
|
36
|
+
}
|
|
37
|
+
return child;
|
|
38
|
+
}
|
|
39
|
+
}("cnfd");
|
|
40
|
+
logger.getLogger.bind(logger);
|
|
41
|
+
async function loadDebug() {
|
|
42
|
+
try {
|
|
43
|
+
const { default: debug } = await import("debug");
|
|
44
|
+
if (typeof debug !== "function") return null;
|
|
45
|
+
return debug;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function devWarn(message) {
|
|
51
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") console.warn(message);
|
|
52
|
+
}
|
|
53
|
+
function hasKey(obj, key) {
|
|
54
|
+
return key in obj;
|
|
55
|
+
}
|
|
56
|
+
let ErrorCode = /* @__PURE__ */ function(ErrorCode$1) {
|
|
57
|
+
ErrorCode$1["PROVIDER_NOT_READY"] = "PROVIDER_NOT_READY";
|
|
58
|
+
ErrorCode$1["PROVIDER_FATAL"] = "PROVIDER_FATAL";
|
|
59
|
+
ErrorCode$1["FLAG_NOT_FOUND"] = "FLAG_NOT_FOUND";
|
|
60
|
+
ErrorCode$1["TYPE_MISMATCH"] = "TYPE_MISMATCH";
|
|
61
|
+
ErrorCode$1["GENERAL"] = "GENERAL";
|
|
62
|
+
return ErrorCode$1;
|
|
63
|
+
}({});
|
|
64
|
+
function error(errorCode, errorMessage) {
|
|
65
|
+
return {
|
|
66
|
+
flags: {},
|
|
67
|
+
resolveId: "",
|
|
68
|
+
resolveToken: "",
|
|
69
|
+
errorCode,
|
|
70
|
+
errorMessage
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function resolve(bundle, flagKey, defaultValue, logger$1) {
|
|
74
|
+
const [flagName, ...path] = flagKey.split(".");
|
|
75
|
+
const flag = bundle?.flags[flagName];
|
|
76
|
+
if (bundle?.errorCode) {
|
|
77
|
+
logger$1?.warn(`Flag evaluation for "%s" failed. %s %s`, flagKey, bundle.errorCode, bundle?.errorMessage);
|
|
78
|
+
return {
|
|
79
|
+
reason: "ERROR",
|
|
80
|
+
errorCode: bundle.errorCode,
|
|
81
|
+
errorMessage: bundle.errorMessage,
|
|
82
|
+
value: defaultValue,
|
|
83
|
+
shouldApply: false
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (!flag) {
|
|
87
|
+
logger$1?.warn(`Flag evaluation for '${flagKey}' failed: flag not found`);
|
|
88
|
+
return {
|
|
89
|
+
reason: "ERROR",
|
|
90
|
+
errorCode: ErrorCode.FLAG_NOT_FOUND,
|
|
91
|
+
value: defaultValue,
|
|
92
|
+
shouldApply: false
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
let value = flag.value;
|
|
96
|
+
for (let i = 0; i < path.length; i++) {
|
|
97
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return {
|
|
98
|
+
reason: "ERROR",
|
|
99
|
+
value: defaultValue,
|
|
100
|
+
errorCode: ErrorCode.TYPE_MISMATCH,
|
|
101
|
+
errorMessage: `resolved value is not an object at ${[flagName, ...path.slice(0, i)].join(".")}`,
|
|
102
|
+
shouldApply: false
|
|
103
|
+
};
|
|
104
|
+
value = value[path[i]];
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const validated = evaluateAssignment(value, defaultValue, [flagName, ...path]);
|
|
108
|
+
return {
|
|
109
|
+
...flag,
|
|
110
|
+
value: validated
|
|
111
|
+
};
|
|
112
|
+
} catch (e) {
|
|
113
|
+
return {
|
|
114
|
+
reason: "ERROR",
|
|
115
|
+
value: defaultValue,
|
|
116
|
+
errorCode: ErrorCode.TYPE_MISMATCH,
|
|
117
|
+
errorMessage: String(e),
|
|
118
|
+
shouldApply: false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function evaluateAssignment(resolvedValue, defaultValue, path) {
|
|
123
|
+
const resolvedType = typeof resolvedValue;
|
|
124
|
+
const defaultType = typeof defaultValue;
|
|
125
|
+
if (Array.isArray(defaultValue)) throw `arrays are not supported as flag values at ${path.join(".")}`;
|
|
126
|
+
if (defaultValue === null) return resolvedValue;
|
|
127
|
+
if (resolvedValue === null) return defaultValue;
|
|
128
|
+
if (resolvedType !== defaultType) throw `resolved value (${resolvedType}) isn't assignable to default type (${defaultType}) at ${path.join(".")}`;
|
|
129
|
+
if (typeof resolvedValue === "object") {
|
|
130
|
+
const result = { ...resolvedValue };
|
|
131
|
+
for (const [key, value] of Object.entries(defaultValue)) {
|
|
132
|
+
if (!hasKey(resolvedValue, key)) throw `resolved value is missing field "${key}" at ${path.join(".")}`;
|
|
133
|
+
result[key] = evaluateAssignment(resolvedValue[key], value, [...path, key]);
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
return resolvedValue;
|
|
138
|
+
}
|
|
139
|
+
const ConfidenceContext = createContext(null);
|
|
140
|
+
const warnedFlags = /* @__PURE__ */ new Set();
|
|
141
|
+
function ConfidenceClientProvider({ bundle, apply, children }) {
|
|
142
|
+
const appliedFlags = useRef(/* @__PURE__ */ new Set());
|
|
143
|
+
const filteredApply = useCallback((flagName) => {
|
|
144
|
+
if (appliedFlags.current.has(flagName)) return Promise.resolve();
|
|
145
|
+
appliedFlags.current.add(flagName);
|
|
146
|
+
return apply(flagName);
|
|
147
|
+
}, [apply]);
|
|
148
|
+
return /* @__PURE__ */ jsx(ConfidenceContext.Provider, {
|
|
149
|
+
value: {
|
|
150
|
+
bundle,
|
|
151
|
+
apply: filteredApply
|
|
152
|
+
},
|
|
153
|
+
children
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function useFlag(flagKey, defaultValue) {
|
|
157
|
+
return useFlagDetails(flagKey, defaultValue).value;
|
|
158
|
+
}
|
|
159
|
+
function useFlagDetails(flagKey, defaultValue, options) {
|
|
160
|
+
const ctx = useContext(ConfidenceContext);
|
|
161
|
+
if (!ctx && !warnedFlags.has(flagKey)) {
|
|
162
|
+
warnedFlags.add(flagKey);
|
|
163
|
+
devWarn(`[Confidence] useFlagDetails("${flagKey}") called without a parent ConfidenceProvider. Returning default value.`);
|
|
164
|
+
}
|
|
165
|
+
const bundle = ctx?.bundle ?? error(ErrorCode.GENERAL, "useFlagDetails called without a parent ConfidenceProvider");
|
|
166
|
+
const [baseFlagName] = flagKey.split(".", 1);
|
|
167
|
+
const resolution = resolve(bundle, flagKey, defaultValue);
|
|
168
|
+
const autoExpose = options?.expose !== false;
|
|
169
|
+
const doExpose = useCallback(() => {
|
|
170
|
+
if (resolution.shouldApply) ctx?.apply(baseFlagName);
|
|
171
|
+
}, [
|
|
172
|
+
ctx,
|
|
173
|
+
baseFlagName,
|
|
174
|
+
resolution.shouldApply
|
|
175
|
+
]);
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
if (autoExpose) doExpose();
|
|
178
|
+
}, [autoExpose, doExpose]);
|
|
179
|
+
const expose = useCallback(() => {
|
|
180
|
+
if (autoExpose) devWarn(`[Confidence] expose() called on "${flagKey}" but auto-exposure is enabled. Call is ignored.`);
|
|
181
|
+
else doExpose();
|
|
182
|
+
}, [
|
|
183
|
+
autoExpose,
|
|
184
|
+
doExpose,
|
|
185
|
+
flagKey
|
|
186
|
+
]);
|
|
187
|
+
return {
|
|
188
|
+
flagKey,
|
|
189
|
+
flagMetadata: {},
|
|
190
|
+
...resolution,
|
|
191
|
+
expose
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
export { ConfidenceClientProvider, useFlag, useFlagDetails };
|
|
Binary file
|
package/dist/index.fetch.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
2
|
-
import { EvaluationContext, JsonValue, Provider, ProviderMetadata, ProviderStatus
|
|
2
|
+
import { EvaluationContext, JsonValue, Provider, ProviderMetadata, ProviderStatus } from "@openfeature/server-sdk";
|
|
3
3
|
|
|
4
4
|
//#region src/proto/confidence/flags/types/v1/types.d.ts
|
|
5
5
|
/** Schema for the value of a flag. */
|
|
@@ -173,6 +173,27 @@ interface ResolveFlagsResponse {
|
|
|
173
173
|
/** Unique identifier for this particular resolve request. */
|
|
174
174
|
resolveId: string;
|
|
175
175
|
}
|
|
176
|
+
interface ApplyFlagsRequest {
|
|
177
|
+
/** The flags to apply and information about when they were applied. */
|
|
178
|
+
flags: AppliedFlag[];
|
|
179
|
+
/** Credentials for the client. */
|
|
180
|
+
clientSecret: string;
|
|
181
|
+
/** An opaque token that was returned from `ResolveFlags`; it must be set. */
|
|
182
|
+
resolveToken: Uint8Array;
|
|
183
|
+
/**
|
|
184
|
+
* The client time when the this request was sent, used for correcting
|
|
185
|
+
* clock skew from the client.
|
|
186
|
+
*/
|
|
187
|
+
sendTime?: Date | undefined;
|
|
188
|
+
/** Information about the SDK used to initiate the request. */
|
|
189
|
+
sdk?: Sdk | undefined;
|
|
190
|
+
}
|
|
191
|
+
interface AppliedFlag {
|
|
192
|
+
/** The id of the flag that should be applied, has the format `flags/*`. */
|
|
193
|
+
flag: string;
|
|
194
|
+
/** The client time when the flag was applied. */
|
|
195
|
+
applyTime?: Date | undefined;
|
|
196
|
+
}
|
|
176
197
|
interface ResolvedFlag {
|
|
177
198
|
/** The id of the flag that as resolved. */
|
|
178
199
|
flag: string;
|
|
@@ -189,9 +210,13 @@ interface ResolvedFlag {
|
|
|
189
210
|
flagSchema?: FlagSchema_StructFlagSchema | undefined;
|
|
190
211
|
/** The reason to why the flag could be resolved or not. */
|
|
191
212
|
reason: ResolveReason;
|
|
213
|
+
/** Determines whether the flag should be applied in the clients */
|
|
214
|
+
shouldApply: boolean;
|
|
192
215
|
}
|
|
193
216
|
declare const ResolveFlagsRequest: MessageFns$3<ResolveFlagsRequest>;
|
|
194
217
|
declare const ResolveFlagsResponse: MessageFns$3<ResolveFlagsResponse>;
|
|
218
|
+
declare const ApplyFlagsRequest: MessageFns$3<ApplyFlagsRequest>;
|
|
219
|
+
declare const AppliedFlag: MessageFns$3<AppliedFlag>;
|
|
195
220
|
declare const ResolvedFlag: MessageFns$3<ResolvedFlag>;
|
|
196
221
|
type Builtin$3 = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
197
222
|
type DeepPartial$3<T> = T extends Builtin$3 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$3<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$3<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$3<T[K]> } : Partial<T>;
|
|
@@ -322,6 +347,7 @@ interface LocalResolver {
|
|
|
322
347
|
setResolverState(request: SetResolverStateRequest): void;
|
|
323
348
|
flushLogs(): Uint8Array;
|
|
324
349
|
flushAssigned(): Uint8Array;
|
|
350
|
+
applyFlags(request: ApplyFlagsRequest): void;
|
|
325
351
|
}
|
|
326
352
|
//#endregion
|
|
327
353
|
//#region src/materialization.d.ts
|
|
@@ -398,7 +424,40 @@ interface MaterializationStore {
|
|
|
398
424
|
writeMaterializations?(writeOps: MaterializationStore.WriteOp[]): Promise<void>;
|
|
399
425
|
}
|
|
400
426
|
//#endregion
|
|
427
|
+
//#region src/types.d.ts
|
|
428
|
+
type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "UNSPECIFIED";
|
|
429
|
+
declare enum ErrorCode {
|
|
430
|
+
PROVIDER_NOT_READY = "PROVIDER_NOT_READY",
|
|
431
|
+
PROVIDER_FATAL = "PROVIDER_FATAL",
|
|
432
|
+
FLAG_NOT_FOUND = "FLAG_NOT_FOUND",
|
|
433
|
+
TYPE_MISMATCH = "TYPE_MISMATCH",
|
|
434
|
+
GENERAL = "GENERAL",
|
|
435
|
+
}
|
|
436
|
+
interface ResolutionDetails<T> {
|
|
437
|
+
reason: ResolutionReason;
|
|
438
|
+
value: T;
|
|
439
|
+
variant?: string;
|
|
440
|
+
errorCode?: ErrorCode;
|
|
441
|
+
errorMessage?: string;
|
|
442
|
+
shouldApply: boolean;
|
|
443
|
+
}
|
|
444
|
+
type FlagPrimitive = null | boolean | string | number;
|
|
445
|
+
type FlagObject = {
|
|
446
|
+
[key: string]: FlagValue;
|
|
447
|
+
};
|
|
448
|
+
type FlagValue = FlagPrimitive | FlagObject;
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region src/flag-bundle.d.ts
|
|
451
|
+
interface FlagBundle {
|
|
452
|
+
flags: Record<string, ResolutionDetails<FlagObject | null> | undefined>;
|
|
453
|
+
resolveId: string;
|
|
454
|
+
resolveToken: string;
|
|
455
|
+
errorCode?: ErrorCode;
|
|
456
|
+
errorMessage?: string;
|
|
457
|
+
}
|
|
458
|
+
//#endregion
|
|
401
459
|
//#region src/ConfidenceServerProviderLocal.d.ts
|
|
460
|
+
type FlagBundle$1 = FlagBundle;
|
|
402
461
|
interface ProviderOptions {
|
|
403
462
|
flagClientSecret: string;
|
|
404
463
|
initializeTimeout?: number;
|
|
@@ -430,19 +489,15 @@ declare class ConfidenceServerProviderLocal implements Provider {
|
|
|
430
489
|
constructor(resolverOrPromise: LocalResolver | Promise<LocalResolver>, options: ProviderOptions);
|
|
431
490
|
initialize(context?: EvaluationContext): Promise<void>;
|
|
432
491
|
onClose(): Promise<void>;
|
|
433
|
-
|
|
492
|
+
resolve(context: EvaluationContext, flagNames: string[], apply?: boolean): Promise<FlagBundle$1>;
|
|
493
|
+
evaluate<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext): Promise<ResolutionDetails<T>>;
|
|
434
494
|
private resolveWithSticky;
|
|
435
|
-
/**
|
|
436
|
-
* Extract and validate the value from a resolved flag.
|
|
437
|
-
*/
|
|
438
|
-
private extractValue;
|
|
439
495
|
updateState(signal?: AbortSignal): Promise<void>;
|
|
440
496
|
flush(signal?: AbortSignal): Promise<void>;
|
|
441
497
|
private flushAssigned;
|
|
442
498
|
private sendFlagLogs;
|
|
443
499
|
private readMaterializations;
|
|
444
500
|
private writeMaterializations;
|
|
445
|
-
private static convertReason;
|
|
446
501
|
private static convertEvaluationContext;
|
|
447
502
|
/** Resolves with an evaluation of a Boolean flag */
|
|
448
503
|
resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: EvaluationContext): Promise<ResolutionDetails<boolean>>;
|
|
@@ -452,6 +507,13 @@ declare class ConfidenceServerProviderLocal implements Provider {
|
|
|
452
507
|
resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext): Promise<ResolutionDetails<T>>;
|
|
453
508
|
/** Resolves with an evaluation of a String flag */
|
|
454
509
|
resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext): Promise<ResolutionDetails<string>>;
|
|
510
|
+
/**
|
|
511
|
+
* Applies a previously resolved flag, logging that it was used/exposed.
|
|
512
|
+
* Call this when a flag value is actually rendered or used in the client.
|
|
513
|
+
* @param resolveToken - Base64-encoded resolve token from the flag bundle
|
|
514
|
+
* @param flagName - Name of the flag to apply
|
|
515
|
+
*/
|
|
516
|
+
applyFlag(resolveToken: string, flagName: string): void;
|
|
455
517
|
}
|
|
456
518
|
//#endregion
|
|
457
519
|
//#region src/index.fetch.d.ts
|