@spotify-confidence/openfeature-server-provider-local 0.4.0 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.4.0...openfeature-provider-js-v0.5.0) (2025-12-19)
4
+
5
+
6
+ ### Features
7
+
8
+ * pluggable materialization ([#211](https://github.com/spotify/confidence-resolver/issues/211)) ([96aea72](https://github.com/spotify/confidence-resolver/commit/96aea72a2dce2e02fdfa4374e4fa245f0efc177b))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * rust-guest bumped from 0.1.11 to 0.1.12
16
+
3
17
  ## [0.4.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.3.0...openfeature-provider-js-v0.4.0) (2025-12-11)
4
18
 
5
19
 
package/README.md CHANGED
@@ -138,15 +138,72 @@ The provider periodically:
138
138
 
139
139
  ---
140
140
 
141
- ## Sticky Assignments
141
+ ## Materialization Stores
142
142
 
143
- The provider supports **Sticky Assignments** for consistent variant assignments across flag evaluations.
143
+ Materialization stores provide persistent storage for sticky variant assignments and custom targeting segments. This enables two key use cases:
144
144
 
145
- **📖 See the [Integration Guide: Sticky Assignments](../INTEGRATION_GUIDE.md#sticky-assignments)** for:
146
- - How sticky assignments work
147
- - Server-managed storage (zero configuration)
148
- - Latency considerations
149
- - Custom storage options (currently Java-only, coming soon to JavaScript)
145
+ 1. **Sticky Assignments**: Maintain consistent variant assignments across evaluations even when targeting attributes change. This enables pausing intake (stopping new users from entering an experiment) while keeping existing users in their assigned variants.
146
+
147
+ 2. **Custom Targeting via Materialized Segments**: Precomputed sets of identifiers from datasets that should be targeted. Instead of evaluating complex targeting rules at runtime, materializations allow efficient lookup of whether a unit (user, session, etc.) is included in a target segment.
148
+
149
+ ### Default Behavior
150
+
151
+ ⚠️ Warning: If your flags rely on sticky assignments or materialized segments, the default SDK behaviour will prevent those rules from being applied and your evaluations will fall back to default values. For production workloads that need sticky behavior or segment lookups, implement and configure a real `MaterializationStore` (e.g., Redis, DynamoDB, or a key-value store) to avoid unexpected fallbacks and ensure consistent variant assignment.
152
+
153
+ ### Remote Materialization Store
154
+
155
+ For quick setup without managing your own storage infrastructure, enable the built-in remote materialization store:
156
+
157
+ ```ts
158
+ const provider = createConfidenceServerProvider({
159
+ flagClientSecret: process.env.CONFIDENCE_FLAG_CLIENT_SECRET!,
160
+ materializationStore: 'CONFIDENCE_REMOTE_STORE',
161
+ });
162
+ ```
163
+
164
+ **When to use**:
165
+ - You need sticky assignments or materialized segments but don't want to manage storage infrastructure
166
+ - Quick prototyping or getting started
167
+ - Lower-volume applications where network latency is acceptable
168
+
169
+ **Trade-offs**:
170
+ - Additional network calls during flag resolution (adds latency)
171
+ - Lower performance compared to local storage implementations (Redis, DynamoDB, etc.)
172
+
173
+ ### Custom Implementations
174
+
175
+ For improved latency and reduced network calls, implement the `MaterializationStore` interface to store materialization data in your infrastructure:
176
+
177
+ ```ts
178
+ import { MaterializationStore } from '@spotify-confidence/openfeature-server-provider-local';
179
+
180
+ class MyRedisStore implements MaterializationStore {
181
+ async readMaterializations(readOps: MaterializationStore.ReadOp[]): Promise<MaterializationStore.ReadResult[]> {
182
+ // Load materialization data from Redis
183
+ }
184
+
185
+ async writeMaterializations(writeOps: MaterializationStore.WriteOp[]): Promise<void> {
186
+ // Store materialization data to Redis
187
+ }
188
+ }
189
+
190
+ const provider = createConfidenceServerProvider({
191
+ flagClientSecret: process.env.CONFIDENCE_FLAG_CLIENT_SECRET!,
192
+ materializationStore: new MyRedisStore(),
193
+ });
194
+ ```
195
+
196
+ For read-only stores (e.g., pre-populated materialized segments without sticky assignment writes), omit the `writeMaterializations` method.
197
+
198
+ ### When to Use Materialization Stores
199
+
200
+ Consider implementing a materialization store if:
201
+ - You need to support sticky variant assignments for experiments
202
+ - You use materialized segments for custom targeting
203
+ - You want to minimize network latency during flag resolution
204
+ - You have high-volume flag evaluations
205
+
206
+ If you don't use sticky assignments or materialized segments, the default behavior is sufficient.
150
207
 
151
208
  ---
152
209
 
Binary file
@@ -1,8 +1,85 @@
1
1
  import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
2
2
  import { EvaluationContext, JsonValue, Provider, ProviderMetadata, ProviderStatus, ResolutionDetails } from "@openfeature/server-sdk";
3
3
 
4
+ //#region src/proto/confidence/flags/types/v1/types.d.ts
5
+ /** Schema for the value of a flag. */
6
+ interface FlagSchema {
7
+ /** Schema if this is a struct */
8
+ structSchema?: FlagSchema_StructFlagSchema | undefined;
9
+ /** Schema if this is a list */
10
+ listSchema?: FlagSchema_ListFlagSchema | undefined;
11
+ /** Schema if this is an int */
12
+ intSchema?: FlagSchema_IntFlagSchema | undefined;
13
+ /** Schema if this is a double */
14
+ doubleSchema?: FlagSchema_DoubleFlagSchema | undefined;
15
+ /** Schema if this is a string */
16
+ stringSchema?: FlagSchema_StringFlagSchema | undefined;
17
+ /** Schema if this is a bool */
18
+ boolSchema?: FlagSchema_BoolFlagSchema | undefined;
19
+ }
20
+ /** A schema of nested fields. */
21
+ interface FlagSchema_StructFlagSchema {
22
+ /** Map of field name to the schema for the field */
23
+ schema: {
24
+ [key: string]: FlagSchema;
25
+ };
26
+ }
27
+ /** A number that has a decimal place. */
28
+ interface FlagSchema_DoubleFlagSchema {}
29
+ /** A whole number without a decimal point. */
30
+ interface FlagSchema_IntFlagSchema {}
31
+ /** A string. */
32
+ interface FlagSchema_StringFlagSchema {}
33
+ /** A boolean: true or false. */
34
+ interface FlagSchema_BoolFlagSchema {}
35
+ /** A list of values. */
36
+ interface FlagSchema_ListFlagSchema {
37
+ /** The schema for the elements in the list */
38
+ elementSchema?: FlagSchema | undefined;
39
+ }
40
+ declare const FlagSchema: MessageFns$5<FlagSchema>;
41
+ declare const FlagSchema_StructFlagSchema: MessageFns$5<FlagSchema_StructFlagSchema>;
42
+ declare const FlagSchema_DoubleFlagSchema: MessageFns$5<FlagSchema_DoubleFlagSchema>;
43
+ declare const FlagSchema_IntFlagSchema: MessageFns$5<FlagSchema_IntFlagSchema>;
44
+ declare const FlagSchema_StringFlagSchema: MessageFns$5<FlagSchema_StringFlagSchema>;
45
+ declare const FlagSchema_BoolFlagSchema: MessageFns$5<FlagSchema_BoolFlagSchema>;
46
+ declare const FlagSchema_ListFlagSchema: MessageFns$5<FlagSchema_ListFlagSchema>;
47
+ type Builtin$5 = Date | Function | Uint8Array | string | number | boolean | undefined;
48
+ type DeepPartial$5<T> = T extends Builtin$5 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$5<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$5<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$5<T[K]> } : Partial<T>;
49
+ type KeysOfUnion$5<T> = T extends T ? keyof T : never;
50
+ type Exact$5<P, I extends P> = P extends Builtin$5 ? P : P & { [K in keyof P]: Exact$5<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$5<P>>]: never };
51
+ interface MessageFns$5<T> {
52
+ encode(message: T, writer?: BinaryWriter): BinaryWriter;
53
+ decode(input: BinaryReader | Uint8Array, length?: number): T;
54
+ fromJSON(object: any): T;
55
+ toJSON(message: T): unknown;
56
+ create<I extends Exact$5<DeepPartial$5<T>, I>>(base?: I): T;
57
+ fromPartial<I extends Exact$5<DeepPartial$5<T>, I>>(object: I): T;
58
+ }
59
+ //#endregion
4
60
  //#region src/proto/confidence/flags/resolver/v1/types.d.ts
5
-
61
+ declare enum ResolveReason {
62
+ /** RESOLVE_REASON_UNSPECIFIED - Unspecified enum. */
63
+ RESOLVE_REASON_UNSPECIFIED = 0,
64
+ /** RESOLVE_REASON_MATCH - The flag was successfully resolved because one rule matched. */
65
+ RESOLVE_REASON_MATCH = 1,
66
+ /** RESOLVE_REASON_NO_SEGMENT_MATCH - The flag could not be resolved because no rule matched. */
67
+ RESOLVE_REASON_NO_SEGMENT_MATCH = 2,
68
+ /**
69
+ * RESOLVE_REASON_NO_TREATMENT_MATCH - The flag could not be resolved because the matching rule had no variant
70
+ * that could be assigned.
71
+ *
72
+ * @deprecated
73
+ */
74
+ RESOLVE_REASON_NO_TREATMENT_MATCH = 3,
75
+ /** RESOLVE_REASON_FLAG_ARCHIVED - The flag could not be resolved because it was archived. */
76
+ RESOLVE_REASON_FLAG_ARCHIVED = 4,
77
+ /** RESOLVE_REASON_TARGETING_KEY_ERROR - The flag could not be resolved because the targeting key field was invalid */
78
+ RESOLVE_REASON_TARGETING_KEY_ERROR = 5,
79
+ /** RESOLVE_REASON_ERROR - Unknown error occurred during the resolve */
80
+ RESOLVE_REASON_ERROR = 6,
81
+ UNRECOGNIZED = -1,
82
+ }
6
83
  declare enum SdkId {
7
84
  SDK_ID_UNSPECIFIED = 0,
8
85
  SDK_ID_JAVA_PROVIDER = 1,
@@ -29,10 +106,6 @@ declare enum SdkId {
29
106
  SDK_ID_JS_LOCAL_SERVER_PROVIDER = 22,
30
107
  UNRECOGNIZED = -1,
31
108
  }
32
- /**
33
- * (-- api-linter: core::0123::resource-annotation=disabled
34
- * aip.dev/not-precedent: SDKs are not internal Confidence resources. --)
35
- */
36
109
  interface Sdk {
37
110
  /** Name of a Confidence SDKs. */
38
111
  id?: SdkId | undefined;
@@ -41,45 +114,21 @@ interface Sdk {
41
114
  /** Version of the SDK. */
42
115
  version: string;
43
116
  }
44
- declare const Sdk: MessageFns$2<Sdk>;
45
- type Builtin$2 = Date | Function | Uint8Array | string | number | boolean | undefined;
46
- type DeepPartial$2<T> = T extends Builtin$2 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$2<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$2<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$2<T[K]> } : Partial<T>;
47
- type KeysOfUnion$2<T> = T extends T ? keyof T : never;
48
- type Exact$2<P, I extends P> = P extends Builtin$2 ? P : P & { [K in keyof P]: Exact$2<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$2<P>>]: never };
49
- interface MessageFns$2<T> {
117
+ declare const Sdk: MessageFns$4<Sdk>;
118
+ type Builtin$4 = Date | Function | Uint8Array | string | number | boolean | undefined;
119
+ type DeepPartial$4<T> = T extends Builtin$4 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$4<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$4<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$4<T[K]> } : Partial<T>;
120
+ type KeysOfUnion$4<T> = T extends T ? keyof T : never;
121
+ type Exact$4<P, I extends P> = P extends Builtin$4 ? P : P & { [K in keyof P]: Exact$4<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$4<P>>]: never };
122
+ interface MessageFns$4<T> {
50
123
  encode(message: T, writer?: BinaryWriter): BinaryWriter;
51
124
  decode(input: BinaryReader | Uint8Array, length?: number): T;
52
125
  fromJSON(object: any): T;
53
126
  toJSON(message: T): unknown;
54
- create<I extends Exact$2<DeepPartial$2<T>, I>>(base?: I): T;
55
- fromPartial<I extends Exact$2<DeepPartial$2<T>, I>>(object: I): T;
56
- }
57
- //#endregion
58
- //#region src/proto/types.d.ts
59
- declare enum ResolveReason {
60
- /** RESOLVE_REASON_UNSPECIFIED - Unspecified enum. */
61
- RESOLVE_REASON_UNSPECIFIED = 0,
62
- /** RESOLVE_REASON_MATCH - The flag was successfully resolved because one rule matched. */
63
- RESOLVE_REASON_MATCH = 1,
64
- /** RESOLVE_REASON_NO_SEGMENT_MATCH - The flag could not be resolved because no rule matched. */
65
- RESOLVE_REASON_NO_SEGMENT_MATCH = 2,
66
- /**
67
- * RESOLVE_REASON_NO_TREATMENT_MATCH - The flag could not be resolved because the matching rule had no variant
68
- * that could be assigned.
69
- *
70
- * @deprecated
71
- */
72
- RESOLVE_REASON_NO_TREATMENT_MATCH = 3,
73
- /** RESOLVE_REASON_FLAG_ARCHIVED - The flag could not be resolved because it was archived. */
74
- RESOLVE_REASON_FLAG_ARCHIVED = 4,
75
- /** RESOLVE_REASON_TARGETING_KEY_ERROR - The flag could not be resolved because the targeting key field was invalid */
76
- RESOLVE_REASON_TARGETING_KEY_ERROR = 5,
77
- /** RESOLVE_REASON_ERROR - Unknown error occurred during the resolve */
78
- RESOLVE_REASON_ERROR = 6,
79
- UNRECOGNIZED = -1,
127
+ create<I extends Exact$4<DeepPartial$4<T>, I>>(base?: I): T;
128
+ fromPartial<I extends Exact$4<DeepPartial$4<T>, I>>(object: I): T;
80
129
  }
81
130
  //#endregion
82
- //#region src/proto/resolver/api.d.ts
131
+ //#region src/proto/confidence/flags/resolver/v1/api.d.ts
83
132
  interface ResolveFlagsRequest {
84
133
  /**
85
134
  * If non-empty, the specific flags are resolved, otherwise all flags
@@ -104,6 +153,7 @@ interface ResolveFlagsRequest {
104
153
  * resolve, or delayed until `ApplyFlag` is called. A flag is typically
105
154
  * applied when it is used, if this occurs much later than the resolve, then
106
155
  * `apply` should likely be set to false.
156
+ * In a local resolve case it is encouraged to set this to `true`
107
157
  */
108
158
  apply: boolean;
109
159
  /** Information about the SDK used to initiate the request. */
@@ -135,79 +185,105 @@ interface ResolvedFlag {
135
185
  value?: {
136
186
  [key: string]: any;
137
187
  } | undefined;
188
+ /** The schema of the value that was returned. */
189
+ flagSchema?: FlagSchema_StructFlagSchema | undefined;
138
190
  /** The reason to why the flag could be resolved or not. */
139
191
  reason: ResolveReason;
140
192
  }
193
+ declare const ResolveFlagsRequest: MessageFns$3<ResolveFlagsRequest>;
194
+ declare const ResolveFlagsResponse: MessageFns$3<ResolveFlagsResponse>;
195
+ declare const ResolvedFlag: MessageFns$3<ResolvedFlag>;
196
+ type Builtin$3 = Date | Function | Uint8Array | string | number | boolean | undefined;
197
+ 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>;
198
+ type KeysOfUnion$3<T> = T extends T ? keyof T : never;
199
+ type Exact$3<P, I extends P> = P extends Builtin$3 ? P : P & { [K in keyof P]: Exact$3<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$3<P>>]: never };
200
+ interface MessageFns$3<T> {
201
+ encode(message: T, writer?: BinaryWriter): BinaryWriter;
202
+ decode(input: BinaryReader | Uint8Array, length?: number): T;
203
+ fromJSON(object: any): T;
204
+ toJSON(message: T): unknown;
205
+ create<I extends Exact$3<DeepPartial$3<T>, I>>(base?: I): T;
206
+ fromPartial<I extends Exact$3<DeepPartial$3<T>, I>>(object: I): T;
207
+ }
208
+ //#endregion
209
+ //#region src/proto/confidence/flags/resolver/v1/internal_api.d.ts
210
+ interface VariantReadOp {
211
+ unit: string;
212
+ materialization: string;
213
+ rule: string;
214
+ }
215
+ interface InclusionReadOp {
216
+ unit: string;
217
+ materialization: string;
218
+ }
219
+ interface ReadOp$1 {
220
+ variantReadOp?: VariantReadOp | undefined;
221
+ inclusionReadOp?: InclusionReadOp | undefined;
222
+ }
223
+ interface ReadOperationsRequest {
224
+ ops: ReadOp$1[];
225
+ }
226
+ interface VariantData {
227
+ unit: string;
228
+ materialization: string;
229
+ rule: string;
230
+ variant: string;
231
+ }
232
+ interface InclusionData {
233
+ unit: string;
234
+ materialization: string;
235
+ isIncluded: boolean;
236
+ }
237
+ interface ReadResult {
238
+ variantResult?: VariantData | undefined;
239
+ inclusionResult?: InclusionData | undefined;
240
+ }
241
+ declare const VariantReadOp: MessageFns$2<VariantReadOp>;
242
+ declare const InclusionReadOp: MessageFns$2<InclusionReadOp>;
243
+ declare const ReadOp$1: MessageFns$2<ReadOp$1>;
244
+ declare const ReadOperationsRequest: MessageFns$2<ReadOperationsRequest>;
245
+ declare const VariantData: MessageFns$2<VariantData>;
246
+ declare const InclusionData: MessageFns$2<InclusionData>;
247
+ declare const ReadResult: MessageFns$2<ReadResult>;
248
+ type Builtin$2 = Date | Function | Uint8Array | string | number | boolean | undefined;
249
+ type DeepPartial$2<T> = T extends Builtin$2 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$2<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$2<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$2<T[K]> } : Partial<T>;
250
+ type KeysOfUnion$2<T> = T extends T ? keyof T : never;
251
+ type Exact$2<P, I extends P> = P extends Builtin$2 ? P : P & { [K in keyof P]: Exact$2<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion$2<P>>]: never };
252
+ interface MessageFns$2<T> {
253
+ encode(message: T, writer?: BinaryWriter): BinaryWriter;
254
+ decode(input: BinaryReader | Uint8Array, length?: number): T;
255
+ fromJSON(object: any): T;
256
+ toJSON(message: T): unknown;
257
+ create<I extends Exact$2<DeepPartial$2<T>, I>>(base?: I): T;
258
+ fromPartial<I extends Exact$2<DeepPartial$2<T>, I>>(object: I): T;
259
+ }
260
+ //#endregion
261
+ //#region src/proto/confidence/wasm/wasm_api.d.ts
141
262
  /** Request for resolving flags with sticky (materialized) assignments */
142
263
  interface ResolveWithStickyRequest {
143
264
  /** The standard resolve request */
144
265
  resolveRequest?: ResolveFlagsRequest | undefined;
145
- /**
146
- * Context about the materialization required for the resolve
147
- * Map of targeting key (unit) to their materialization data
148
- */
149
- materializationsPerUnit: {
150
- [key: string]: MaterializationMap;
151
- };
152
- /** If a materialization info is missing, return to the caller immediately */
266
+ /** if a materialization info is missing, we want tor return to the caller immediately */
153
267
  failFastOnSticky: boolean;
154
- }
155
- /** Map of materialization IDs to their info for a specific unit */
156
- interface MaterializationMap {
157
- /** Map of materialization name to info */
158
- infoMap: {
159
- [key: string]: MaterializationInfo;
160
- };
161
- }
162
- /** Information about a materialization for a specific unit */
163
- interface MaterializationInfo {
164
- /** Whether the unit is in this materialization/rollout */
165
- unitInInfo: boolean;
166
- /** Map of rule name to variant name for this unit */
167
- ruleToVariant: {
168
- [key: string]: string;
169
- };
268
+ /** if we should support sticky or completely skip the flag if they had sticky rules */
269
+ notProcessSticky: boolean;
270
+ /** Context about the materialization required for the resolve */
271
+ materializations: ReadResult[];
170
272
  }
171
273
  /** Response from resolving with sticky assignments */
172
274
  interface ResolveWithStickyResponse {
173
275
  success?: ResolveWithStickyResponse_Success | undefined;
174
- missingMaterializations?: ResolveWithStickyResponse_MissingMaterializations | undefined;
276
+ readOpsRequest?: ReadOperationsRequest | undefined;
175
277
  }
176
278
  /** Successful resolution with materialization updates */
177
279
  interface ResolveWithStickyResponse_Success {
178
- /** The resolved flags response */
179
280
  response?: ResolveFlagsResponse | undefined;
180
281
  /** New assignments that should be stored */
181
- updates: ResolveWithStickyResponse_MaterializationUpdate[];
182
- }
183
- /** Information about missing materializations */
184
- interface ResolveWithStickyResponse_MissingMaterializations {
185
- items: ResolveWithStickyResponse_MissingMaterializationItem[];
186
- }
187
- /** A missing materialization item */
188
- interface ResolveWithStickyResponse_MissingMaterializationItem {
189
- unit: string;
190
- rule: string;
191
- readMaterialization: string;
192
- }
193
- /** A materialization update to be stored */
194
- interface ResolveWithStickyResponse_MaterializationUpdate {
195
- unit: string;
196
- writeMaterialization: string;
197
- rule: string;
198
- variant: string;
282
+ materializationUpdates: VariantData[];
199
283
  }
200
- declare const ResolveFlagsRequest: MessageFns$1<ResolveFlagsRequest>;
201
- declare const ResolveFlagsResponse: MessageFns$1<ResolveFlagsResponse>;
202
- declare const ResolvedFlag: MessageFns$1<ResolvedFlag>;
203
284
  declare const ResolveWithStickyRequest: MessageFns$1<ResolveWithStickyRequest>;
204
- declare const MaterializationMap: MessageFns$1<MaterializationMap>;
205
- declare const MaterializationInfo: MessageFns$1<MaterializationInfo>;
206
285
  declare const ResolveWithStickyResponse: MessageFns$1<ResolveWithStickyResponse>;
207
286
  declare const ResolveWithStickyResponse_Success: MessageFns$1<ResolveWithStickyResponse_Success>;
208
- declare const ResolveWithStickyResponse_MissingMaterializations: MessageFns$1<ResolveWithStickyResponse_MissingMaterializations>;
209
- declare const ResolveWithStickyResponse_MissingMaterializationItem: MessageFns$1<ResolveWithStickyResponse_MissingMaterializationItem>;
210
- declare const ResolveWithStickyResponse_MaterializationUpdate: MessageFns$1<ResolveWithStickyResponse_MaterializationUpdate>;
211
287
  type Builtin$1 = Date | Function | Uint8Array | string | number | boolean | undefined;
212
288
  type DeepPartial$1<T> = T extends Builtin$1 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$1<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$1<U>> : T extends {} ? { [K in keyof T]?: DeepPartial$1<T[K]> } : Partial<T>;
213
289
  type KeysOfUnion$1<T> = T extends T ? keyof T : never;
@@ -221,7 +297,7 @@ interface MessageFns$1<T> {
221
297
  fromPartial<I extends Exact$1<DeepPartial$1<T>, I>>(object: I): T;
222
298
  }
223
299
  //#endregion
224
- //#region src/proto/messages.d.ts
300
+ //#region src/proto/confidence/wasm/messages.d.ts
225
301
  interface SetResolverStateRequest {
226
302
  state: Uint8Array;
227
303
  accountId: string;
@@ -248,12 +324,87 @@ interface LocalResolver {
248
324
  flushAssigned(): Uint8Array;
249
325
  }
250
326
  //#endregion
327
+ //#region src/materialization.d.ts
328
+ declare namespace MaterializationStore {
329
+ namespace ReadOp {
330
+ interface Variant {
331
+ readonly op: "variant";
332
+ readonly unit: string;
333
+ readonly materialization: string;
334
+ readonly rule: string;
335
+ }
336
+ interface Inclusion {
337
+ readonly op: "inclusion";
338
+ readonly unit: string;
339
+ readonly materialization: string;
340
+ }
341
+ }
342
+ type ReadOp = ReadOp.Variant | ReadOp.Inclusion;
343
+ namespace ReadResult {
344
+ interface Variant {
345
+ readonly op: "variant";
346
+ readonly unit: string;
347
+ readonly materialization: string;
348
+ readonly rule: string;
349
+ readonly variant?: string;
350
+ }
351
+ interface Inclusion {
352
+ readonly op: "inclusion";
353
+ readonly unit: string;
354
+ readonly materialization: string;
355
+ readonly included: boolean;
356
+ }
357
+ }
358
+ type ReadResult = ReadResult.Inclusion | ReadResult.Variant;
359
+ namespace WriteOp {
360
+ interface Variant {
361
+ readonly op: "variant";
362
+ readonly unit: string;
363
+ readonly materialization: string;
364
+ readonly rule: string;
365
+ readonly variant: string;
366
+ }
367
+ }
368
+ type WriteOp = WriteOp.Variant;
369
+ }
370
+ /**
371
+ * Interface for storing and retrieving materialization data.
372
+ *
373
+ * Implementations can use any storage backend (e.g., Redis, BigTable, Cassandra, DynamoDB,
374
+ * or simple key-value stores) as long as they fulfill this contract.
375
+ *
376
+ * @see The README for conceptual documentation on sticky assignments and materialized segments.
377
+ */
378
+ interface MaterializationStore {
379
+ /**
380
+ * Reads materialization data for the given operations.
381
+ *
382
+ * For `variant` operations, returns the stored variant assignment (if any) for a unit/materialization/rule combination.
383
+ * For `inclusion` operations, returns whether a unit is included in a materialized segment.
384
+ *
385
+ * @param readOps - The read operations to perform.
386
+ * @returns Results for each operation. The order of results does not need to match the order of operations;
387
+ * callers match results to operations using the `unit`, `materialization`, and `op` fields.
388
+ */
389
+ readMaterializations(readOps: MaterializationStore.ReadOp[]): Promise<MaterializationStore.ReadResult[]>;
390
+ /**
391
+ * Persists variant assignments for sticky bucketing.
392
+ *
393
+ * This method is optional. Omit it for read-only stores that only serve pre-populated
394
+ * materialized segments without supporting runtime sticky assignment writes.
395
+ *
396
+ * @param writeOps - The variant assignments to persist.
397
+ */
398
+ writeMaterializations?(writeOps: MaterializationStore.WriteOp[]): Promise<void>;
399
+ }
400
+ //#endregion
251
401
  //#region src/ConfidenceServerProviderLocal.d.ts
252
402
  interface ProviderOptions {
253
403
  flagClientSecret: string;
254
404
  initializeTimeout?: number;
255
405
  flushInterval?: number;
256
406
  fetch?: typeof fetch;
407
+ materializationStore?: MaterializationStore | "CONFIDENCE_REMOTE_STORE";
257
408
  }
258
409
  /**
259
410
  * OpenFeature Provider for Confidence Server SDK (Local Mode)
@@ -269,18 +420,13 @@ declare class ConfidenceServerProviderLocal implements Provider {
269
420
  private readonly main;
270
421
  private readonly fetch;
271
422
  private readonly flushInterval;
423
+ private readonly materializationStore;
272
424
  private stateEtag;
273
425
  constructor(resolver: LocalResolver, options: ProviderOptions);
274
426
  initialize(context?: EvaluationContext): Promise<void>;
275
427
  onClose(): Promise<void>;
276
428
  evaluate<T>(flagKey: string, defaultValue: T, context: EvaluationContext): Promise<ResolutionDetails<T>>;
277
- /**
278
- * Internal recursive method for resolving with sticky assignments.
279
- *
280
- * @private
281
- */
282
- private resolveWithStickyInternal;
283
- private remoteResolve;
429
+ private resolveWithSticky;
284
430
  /**
285
431
  * Extract and validate the value from a resolved flag.
286
432
  */
@@ -289,6 +435,8 @@ declare class ConfidenceServerProviderLocal implements Provider {
289
435
  flush(signal?: AbortSignal): Promise<void>;
290
436
  private flushAssigned;
291
437
  private sendFlagLogs;
438
+ private readMaterializations;
439
+ private writeMaterializations;
292
440
  private static convertReason;
293
441
  private static convertEvaluationContext;
294
442
  /** Resolves with an evaluation of a Boolean flag */
@@ -304,4 +452,4 @@ declare class ConfidenceServerProviderLocal implements Provider {
304
452
  //#region src/index.browser.d.ts
305
453
  declare function createConfidenceServerProvider(options: ProviderOptions): ConfidenceServerProviderLocal;
306
454
  //#endregion
307
- export { createConfidenceServerProvider };
455
+ export { type MaterializationStore, createConfidenceServerProvider };