@spotify-confidence/openfeature-server-provider-local 0.10.0 → 0.11.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,35 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.10.1...openfeature-provider-js-v0.11.0) (2026-03-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * add register_resolve WASM export for SDK-side telemetry ([#354](https://github.com/spotify/confidence-resolver/issues/354)) ([31499bc](https://github.com/spotify/confidence-resolver/commit/31499bc911942ecd751b4a4a702f35ded41e9776))
9
+ * add resolver_version field to TelemetryData ([#346](https://github.com/spotify/confidence-resolver/issues/346)) ([10ab1c6](https://github.com/spotify/confidence-resolver/commit/10ab1c657cb830dc36f433d1dca0af73beecaa27))
10
+ * local prometheus sdk metrics ([#322](https://github.com/spotify/confidence-resolver/issues/322)) ([8b18119](https://github.com/spotify/confidence-resolver/commit/8b18119eae449afbe4a0815e8aab5f82888a8621))
11
+
12
+
13
+ ### Dependencies
14
+
15
+ * The following workspace dependencies were updated
16
+ * dependencies
17
+ * rust-guest bumped from 0.1.19 to 0.1.20
18
+
19
+ ## [0.10.1](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.10.0...openfeature-provider-js-v0.10.1) (2026-03-12)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * pass the sdk with the state to get it into telemetry ([#332](https://github.com/spotify/confidence-resolver/issues/332)) ([1f22c5f](https://github.com/spotify/confidence-resolver/commit/1f22c5fa38c4e8a7d56ea4616db94f9b991d41ab))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * rust-guest bumped from 0.1.18 to 0.1.19
32
+
3
33
  ## [0.10.0](https://github.com/spotify/confidence-resolver/compare/openfeature-provider-js-v0.9.1...openfeature-provider-js-v0.10.0) (2026-03-06)
4
34
 
5
35
 
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { EvaluationDetails, FlagValue } from "@openfeature/core";
2
2
 
3
3
  //#region src/types.d.ts
4
- type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "UNSPECIFIED";
4
+ type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "MATERIALIZATION_NOT_SUPPORTED" | "UNSPECIFIED";
5
5
  declare enum ErrorCode {
6
6
  PROVIDER_NOT_READY = "PROVIDER_NOT_READY",
7
7
  PROVIDER_FATAL = "PROVIDER_FATAL",
package/dist/client.js CHANGED
@@ -92,6 +92,16 @@ function resolve(bundle, flagKey, defaultValue, logger$1) {
92
92
  shouldApply: false
93
93
  };
94
94
  }
95
+ if (flag.reason === "MATERIALIZATION_NOT_SUPPORTED") {
96
+ logger$1?.warn("Flag '%s' requires materializations but no materialization store is configured. Pass a MaterializationStore implementation or: 'CONFIDENCE_REMOTE_STORE' to createConfidenceServerProvider()", flagName);
97
+ return {
98
+ reason: "ERROR",
99
+ errorCode: ErrorCode.GENERAL,
100
+ errorMessage: `Flag '${flagName}' requires materializations. Configure a materialization store.`,
101
+ value: defaultValue,
102
+ shouldApply: false
103
+ };
104
+ }
95
105
  let value = flag.value;
96
106
  for (let i = 0; i < path.length; i++) {
97
107
  if (value === null || typeof value !== "object" || Array.isArray(value)) return {
Binary file
@@ -82,6 +82,10 @@ declare enum ResolveReason {
82
82
  RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE = 7,
83
83
  /** RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED - The flag could not be resolved because a rule is using materialization */
84
84
  RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED = 8,
85
+ /** RESOLVE_REASON_TYPE_MISMATCH - The resolved value type does not match the type requested by the SDK */
86
+ RESOLVE_REASON_TYPE_MISMATCH = 9,
87
+ /** RESOLVE_REASON_FLAG_NOT_FOUND - The flag was not found in the resolve response */
88
+ RESOLVE_REASON_FLAG_NOT_FOUND = 10,
85
89
  UNRECOGNIZED = -1,
86
90
  }
87
91
  declare enum SdkId {
@@ -273,6 +277,10 @@ interface MaterializationRecord {
273
277
  rule: string;
274
278
  variant: string;
275
279
  }
280
+ interface RegisterResolveRequest {
281
+ reason: ResolveReason;
282
+ latencyUs: number;
283
+ }
276
284
  declare const ResolveProcessRequest: MessageFns$2<ResolveProcessRequest>;
277
285
  declare const ResolveProcessRequest_StaticMaterializations: MessageFns$2<ResolveProcessRequest_StaticMaterializations>;
278
286
  declare const ResolveProcessRequest_Resume: MessageFns$2<ResolveProcessRequest_Resume>;
@@ -280,6 +288,7 @@ declare const ResolveProcessResponse: MessageFns$2<ResolveProcessResponse>;
280
288
  declare const ResolveProcessResponse_Resolved: MessageFns$2<ResolveProcessResponse_Resolved>;
281
289
  declare const ResolveProcessResponse_Suspended: MessageFns$2<ResolveProcessResponse_Suspended>;
282
290
  declare const MaterializationRecord: MessageFns$2<MaterializationRecord>;
291
+ declare const RegisterResolveRequest: MessageFns$2<RegisterResolveRequest>;
283
292
  type Builtin$2 = Date | Function | Uint8Array | string | number | boolean | undefined;
284
293
  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>;
285
294
  type KeysOfUnion$2<T> = T extends T ? keyof T : never;
@@ -297,6 +306,7 @@ interface MessageFns$2<T> {
297
306
  interface SetResolverStateRequest {
298
307
  state: Uint8Array;
299
308
  accountId: string;
309
+ sdk?: Sdk | undefined;
300
310
  }
301
311
  declare const SetResolverStateRequest: MessageFns$1<SetResolverStateRequest>;
302
312
  type Builtin$1 = Date | Function | Uint8Array | string | number | boolean | undefined;
@@ -315,10 +325,12 @@ interface MessageFns$1<T> {
315
325
  //#region src/LocalResolver.d.ts
316
326
  interface LocalResolver {
317
327
  resolveProcess(request: ResolveProcessRequest): ResolveProcessResponse;
328
+ registerResolve(request: RegisterResolveRequest): void;
318
329
  setResolverState(request: SetResolverStateRequest): void;
319
330
  flushLogs(): Uint8Array;
320
331
  flushAssigned(): Uint8Array;
321
332
  applyFlags(request: ApplyFlagsRequest): void;
333
+ prometheusSnapshot(instance: string): string;
322
334
  }
323
335
  //#endregion
324
336
  //#region src/proto/confidence/flags/resolver/v1/internal_api.d.ts
@@ -428,7 +440,7 @@ interface MaterializationStore {
428
440
  }
429
441
  //#endregion
430
442
  //#region src/types.d.ts
431
- type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "UNSPECIFIED";
443
+ type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "MATERIALIZATION_NOT_SUPPORTED" | "UNSPECIFIED";
432
444
  declare enum ErrorCode {
433
445
  PROVIDER_NOT_READY = "PROVIDER_NOT_READY",
434
446
  PROVIDER_FATAL = "PROVIDER_FATAL",
@@ -461,6 +473,12 @@ interface FlagBundle {
461
473
  //#endregion
462
474
  //#region src/ConfidenceServerProviderLocal.d.ts
463
475
  type FlagBundle$1 = FlagBundle;
476
+ /**
477
+ * Configuration for {@link ConfidenceServerProviderLocal.getPrometheusMetrics}.
478
+ *
479
+ * @experimental This API is subject to change.
480
+ */
481
+ interface SnapshotConfig {}
464
482
  interface ProviderOptions {
465
483
  flagClientSecret: string;
466
484
  initializeTimeout?: number;
@@ -512,6 +530,12 @@ declare class ConfidenceServerProviderLocal implements Provider {
512
530
  /** Resolves with an evaluation of a String flag */
513
531
  resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext): Promise<ResolutionDetails<string>>;
514
532
  /**
533
+ * Returns a Prometheus metrics snapshot from the WASM resolver.
534
+ *
535
+ * @experimental This API is subject to change.
536
+ */
537
+ getPrometheusMetrics(_request?: SnapshotConfig): string;
538
+ /**
515
539
  * Applies a previously resolved flag, logging that it was used/exposed.
516
540
  * Call this when a flag value is actually rendered or used in the client.
517
541
  * @param resolveToken - Base64-encoded resolve token from the flag bundle
@@ -529,4 +553,4 @@ declare function createConfidenceServerProvider({
529
553
  ...options
530
554
  }: ProviderOptionsExt): ConfidenceServerProviderLocal;
531
555
  //#endregion
532
- export { type MaterializationStore, ProviderOptionsExt, createConfidenceServerProvider };
556
+ export { type MaterializationStore, ProviderOptionsExt, type SnapshotConfig, createConfidenceServerProvider };
@@ -774,6 +774,8 @@ let ResolveReason = /* @__PURE__ */ function(ResolveReason$1) {
774
774
  ResolveReason$1[ResolveReason$1["RESOLVE_REASON_ERROR"] = 6] = "RESOLVE_REASON_ERROR";
775
775
  ResolveReason$1[ResolveReason$1["RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE"] = 7] = "RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE";
776
776
  ResolveReason$1[ResolveReason$1["RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED"] = 8] = "RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED";
777
+ ResolveReason$1[ResolveReason$1["RESOLVE_REASON_TYPE_MISMATCH"] = 9] = "RESOLVE_REASON_TYPE_MISMATCH";
778
+ ResolveReason$1[ResolveReason$1["RESOLVE_REASON_FLAG_NOT_FOUND"] = 10] = "RESOLVE_REASON_FLAG_NOT_FOUND";
777
779
  ResolveReason$1[ResolveReason$1["UNRECOGNIZED"] = -1] = "UNRECOGNIZED";
778
780
  return ResolveReason$1;
779
781
  }({});
@@ -797,6 +799,10 @@ function resolveReasonFromJSON(object) {
797
799
  case "RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE": return ResolveReason.RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE;
798
800
  case 8:
799
801
  case "RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED": return ResolveReason.RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED;
802
+ case 9:
803
+ case "RESOLVE_REASON_TYPE_MISMATCH": return ResolveReason.RESOLVE_REASON_TYPE_MISMATCH;
804
+ case 10:
805
+ case "RESOLVE_REASON_FLAG_NOT_FOUND": return ResolveReason.RESOLVE_REASON_FLAG_NOT_FOUND;
800
806
  case -1:
801
807
  case "UNRECOGNIZED":
802
808
  default: return ResolveReason.UNRECOGNIZED;
@@ -813,6 +819,8 @@ function resolveReasonToJSON(object) {
813
819
  case ResolveReason.RESOLVE_REASON_ERROR: return "RESOLVE_REASON_ERROR";
814
820
  case ResolveReason.RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE: return "RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE";
815
821
  case ResolveReason.RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED: return "RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED";
822
+ case ResolveReason.RESOLVE_REASON_TYPE_MISMATCH: return "RESOLVE_REASON_TYPE_MISMATCH";
823
+ case ResolveReason.RESOLVE_REASON_FLAG_NOT_FOUND: return "RESOLVE_REASON_FLAG_NOT_FOUND";
816
824
  case ResolveReason.UNRECOGNIZED:
817
825
  default: return "UNRECOGNIZED";
818
826
  }
@@ -1410,7 +1418,7 @@ function isObject(value) {
1410
1418
  function isSet$3(value) {
1411
1419
  return value !== null && value !== void 0;
1412
1420
  }
1413
- const VERSION = "0.10.0";
1421
+ const VERSION = "0.11.0";
1414
1422
  const NOOP_LOG_FN = Object.assign(() => {}, { enabled: false });
1415
1423
  const debugBackend = loadDebug();
1416
1424
  const logger$2 = new class LoggerImpl {
@@ -2392,13 +2400,15 @@ const Void = {
2392
2400
  function createBaseSetResolverStateRequest() {
2393
2401
  return {
2394
2402
  state: new Uint8Array(0),
2395
- accountId: ""
2403
+ accountId: "",
2404
+ sdk: void 0
2396
2405
  };
2397
2406
  }
2398
2407
  const SetResolverStateRequest = {
2399
2408
  encode(message, writer = new BinaryWriter()) {
2400
2409
  if (message.state.length !== 0) writer.uint32(10).bytes(message.state);
2401
2410
  if (message.accountId !== "") writer.uint32(18).string(message.accountId);
2411
+ if (message.sdk !== void 0) Sdk.encode(message.sdk, writer.uint32(26).fork()).join();
2402
2412
  return writer;
2403
2413
  },
2404
2414
  decode(input, length) {
@@ -2416,6 +2426,10 @@ const SetResolverStateRequest = {
2416
2426
  if (tag !== 18) break;
2417
2427
  message.accountId = reader.string();
2418
2428
  continue;
2429
+ case 3:
2430
+ if (tag !== 26) break;
2431
+ message.sdk = Sdk.decode(reader, reader.uint32());
2432
+ continue;
2419
2433
  }
2420
2434
  if ((tag & 7) === 4 || tag === 0) break;
2421
2435
  reader.skip(tag & 7);
@@ -2425,13 +2439,15 @@ const SetResolverStateRequest = {
2425
2439
  fromJSON(object) {
2426
2440
  return {
2427
2441
  state: isSet$1(object.state) ? bytesFromBase64$2(object.state) : new Uint8Array(0),
2428
- accountId: isSet$1(object.accountId) ? globalThis.String(object.accountId) : ""
2442
+ accountId: isSet$1(object.accountId) ? globalThis.String(object.accountId) : "",
2443
+ sdk: isSet$1(object.sdk) ? Sdk.fromJSON(object.sdk) : void 0
2429
2444
  };
2430
2445
  },
2431
2446
  toJSON(message) {
2432
2447
  const obj = {};
2433
2448
  if (message.state.length !== 0) obj.state = base64FromBytes$2(message.state);
2434
2449
  if (message.accountId !== "") obj.accountId = message.accountId;
2450
+ if (message.sdk !== void 0) obj.sdk = Sdk.toJSON(message.sdk);
2435
2451
  return obj;
2436
2452
  },
2437
2453
  create(base) {
@@ -2441,6 +2457,7 @@ const SetResolverStateRequest = {
2441
2457
  const message = createBaseSetResolverStateRequest();
2442
2458
  message.state = object.state ?? new Uint8Array(0);
2443
2459
  message.accountId = object.accountId ?? "";
2460
+ message.sdk = object.sdk !== void 0 && object.sdk !== null ? Sdk.fromPartial(object.sdk) : void 0;
2444
2461
  return message;
2445
2462
  }
2446
2463
  };
@@ -2541,6 +2558,90 @@ const Response$1 = {
2541
2558
  return message;
2542
2559
  }
2543
2560
  };
2561
+ function createBasePrometheusSnapshotRequest() {
2562
+ return { instance: "" };
2563
+ }
2564
+ const PrometheusSnapshotRequest = {
2565
+ encode(message, writer = new BinaryWriter()) {
2566
+ if (message.instance !== "") writer.uint32(10).string(message.instance);
2567
+ return writer;
2568
+ },
2569
+ decode(input, length) {
2570
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
2571
+ const end = length === void 0 ? reader.len : reader.pos + length;
2572
+ const message = createBasePrometheusSnapshotRequest();
2573
+ while (reader.pos < end) {
2574
+ const tag = reader.uint32();
2575
+ switch (tag >>> 3) {
2576
+ case 1:
2577
+ if (tag !== 10) break;
2578
+ message.instance = reader.string();
2579
+ continue;
2580
+ }
2581
+ if ((tag & 7) === 4 || tag === 0) break;
2582
+ reader.skip(tag & 7);
2583
+ }
2584
+ return message;
2585
+ },
2586
+ fromJSON(object) {
2587
+ return { instance: isSet$1(object.instance) ? globalThis.String(object.instance) : "" };
2588
+ },
2589
+ toJSON(message) {
2590
+ const obj = {};
2591
+ if (message.instance !== "") obj.instance = message.instance;
2592
+ return obj;
2593
+ },
2594
+ create(base) {
2595
+ return PrometheusSnapshotRequest.fromPartial(base ?? {});
2596
+ },
2597
+ fromPartial(object) {
2598
+ const message = createBasePrometheusSnapshotRequest();
2599
+ message.instance = object.instance ?? "";
2600
+ return message;
2601
+ }
2602
+ };
2603
+ function createBasePrometheusSnapshotResponse() {
2604
+ return { text: "" };
2605
+ }
2606
+ const PrometheusSnapshotResponse = {
2607
+ encode(message, writer = new BinaryWriter()) {
2608
+ if (message.text !== "") writer.uint32(10).string(message.text);
2609
+ return writer;
2610
+ },
2611
+ decode(input, length) {
2612
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
2613
+ const end = length === void 0 ? reader.len : reader.pos + length;
2614
+ const message = createBasePrometheusSnapshotResponse();
2615
+ while (reader.pos < end) {
2616
+ const tag = reader.uint32();
2617
+ switch (tag >>> 3) {
2618
+ case 1:
2619
+ if (tag !== 10) break;
2620
+ message.text = reader.string();
2621
+ continue;
2622
+ }
2623
+ if ((tag & 7) === 4 || tag === 0) break;
2624
+ reader.skip(tag & 7);
2625
+ }
2626
+ return message;
2627
+ },
2628
+ fromJSON(object) {
2629
+ return { text: isSet$1(object.text) ? globalThis.String(object.text) : "" };
2630
+ },
2631
+ toJSON(message) {
2632
+ const obj = {};
2633
+ if (message.text !== "") obj.text = message.text;
2634
+ return obj;
2635
+ },
2636
+ create(base) {
2637
+ return PrometheusSnapshotResponse.fromPartial(base ?? {});
2638
+ },
2639
+ fromPartial(object) {
2640
+ const message = createBasePrometheusSnapshotResponse();
2641
+ message.text = object.text ?? "";
2642
+ return message;
2643
+ }
2644
+ };
2544
2645
  function bytesFromBase64$2(b64) {
2545
2646
  if (globalThis.Buffer) return Uint8Array.from(globalThis.Buffer.from(b64, "base64"));
2546
2647
  else {
@@ -2620,6 +2721,16 @@ function resolve(bundle, flagKey, defaultValue, logger$4) {
2620
2721
  shouldApply: false
2621
2722
  };
2622
2723
  }
2724
+ if (flag.reason === "MATERIALIZATION_NOT_SUPPORTED") {
2725
+ logger$4?.warn("Flag '%s' requires materializations but no materialization store is configured. Pass a MaterializationStore implementation or: 'CONFIDENCE_REMOTE_STORE' to createConfidenceServerProvider()", flagName);
2726
+ return {
2727
+ reason: "ERROR",
2728
+ errorCode: ErrorCode.GENERAL,
2729
+ errorMessage: `Flag '${flagName}' requires materializations. Configure a materialization store.`,
2730
+ value: defaultValue,
2731
+ shouldApply: false
2732
+ };
2733
+ }
2623
2734
  let value = flag.value;
2624
2735
  for (let i = 0; i < path.length; i++) {
2625
2736
  if (value === null || typeof value !== "object" || Array.isArray(value)) return {
@@ -2673,6 +2784,7 @@ function convertReason(reason) {
2673
2784
  case ResolveReason.RESOLVE_REASON_TARGETING_KEY_ERROR: return "TARGETING_KEY_ERROR";
2674
2785
  case ResolveReason.RESOLVE_REASON_NO_TREATMENT_MATCH: return "NO_TREATMENT_MATCH";
2675
2786
  case ResolveReason.RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE: return "ERROR";
2787
+ case ResolveReason.RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED: return "MATERIALIZATION_NOT_SUPPORTED";
2676
2788
  default: return "UNSPECIFIED";
2677
2789
  }
2678
2790
  }
@@ -2768,10 +2880,28 @@ var ConfidenceServerProviderLocal = class ConfidenceServerProviderLocal {
2768
2880
  }
2769
2881
  }
2770
2882
  async evaluate(flagKey, defaultValue, context) {
2883
+ const startMs = performance.now();
2771
2884
  try {
2772
2885
  const [flagName] = flagKey.split(".", 1);
2773
2886
  const resolution = await this.resolve(context, [flagName], true);
2774
- return resolve(resolution, flagKey, defaultValue, logger$1);
2887
+ const result = resolve(resolution, flagKey, defaultValue, logger$1);
2888
+ const latencyUs = Math.round((performance.now() - startMs) * 1e3);
2889
+ let reason;
2890
+ if (resolution.errorCode) reason = ResolveReason.RESOLVE_REASON_ERROR;
2891
+ else {
2892
+ const [flagNameForTelemetry] = flagKey.split(".", 1);
2893
+ if (resolution.flags[flagNameForTelemetry]?.reason === "MATERIALIZATION_NOT_SUPPORTED") reason = ResolveReason.RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED;
2894
+ else if (result.errorCode === ErrorCode.FLAG_NOT_FOUND) reason = ResolveReason.RESOLVE_REASON_FLAG_NOT_FOUND;
2895
+ else if (result.errorCode === ErrorCode.TYPE_MISMATCH) reason = ResolveReason.RESOLVE_REASON_TYPE_MISMATCH;
2896
+ else reason = reasonStringToEnum(result.reason);
2897
+ }
2898
+ try {
2899
+ this.resolver.registerResolve({
2900
+ reason,
2901
+ latencyUs
2902
+ });
2903
+ } catch {}
2904
+ return result;
2775
2905
  } finally {
2776
2906
  this.flushAssigned();
2777
2907
  }
@@ -2813,7 +2943,12 @@ var ConfidenceServerProviderLocal = class ConfidenceServerProviderLocal {
2813
2943
  if (!resp.ok) throw new Error(`Failed to fetch state: ${resp.status} ${resp.statusText}`);
2814
2944
  this.stateEtag = resp.headers.get("etag");
2815
2945
  const bytes = new Uint8Array(await resp.arrayBuffer());
2816
- this.resolver.setResolverState(SetResolverStateRequest.decode(bytes));
2946
+ const stateRequest = SetResolverStateRequest.decode(bytes);
2947
+ stateRequest.sdk = {
2948
+ id: SdkId.SDK_ID_JS_LOCAL_SERVER_PROVIDER,
2949
+ version: VERSION
2950
+ };
2951
+ this.resolver.setResolverState(stateRequest);
2817
2952
  }
2818
2953
  async flush(signal) {
2819
2954
  const writeFlagLogRequest = this.resolver.flushLogs();
@@ -2873,6 +3008,9 @@ var ConfidenceServerProviderLocal = class ConfidenceServerProviderLocal {
2873
3008
  resolveStringEvaluation(flagKey, defaultValue, context) {
2874
3009
  return Promise.resolve(this.evaluate(flagKey, defaultValue, context));
2875
3010
  }
3011
+ getPrometheusMetrics(_request) {
3012
+ return this.resolver.prometheusSnapshot("0");
3013
+ }
2876
3014
  applyFlag(resolveToken, flagName) {
2877
3015
  const request = {
2878
3016
  flags: [{
@@ -2890,6 +3028,17 @@ var ConfidenceServerProviderLocal = class ConfidenceServerProviderLocal {
2890
3028
  this.resolver.applyFlags(request);
2891
3029
  }
2892
3030
  };
3031
+ function reasonStringToEnum(reason) {
3032
+ switch (reason) {
3033
+ case "MATCH": return ResolveReason.RESOLVE_REASON_MATCH;
3034
+ case "NO_SEGMENT_MATCH": return ResolveReason.RESOLVE_REASON_NO_SEGMENT_MATCH;
3035
+ case "NO_TREATMENT_MATCH": return ResolveReason.RESOLVE_REASON_NO_TREATMENT_MATCH;
3036
+ case "FLAG_ARCHIVED": return ResolveReason.RESOLVE_REASON_FLAG_ARCHIVED;
3037
+ case "TARGETING_KEY_ERROR": return ResolveReason.RESOLVE_REASON_TARGETING_KEY_ERROR;
3038
+ case "ERROR": return ResolveReason.RESOLVE_REASON_ERROR;
3039
+ default: return ResolveReason.RESOLVE_REASON_UNSPECIFIED;
3040
+ }
3041
+ }
2893
3042
  function createBaseResolveProcessRequest() {
2894
3043
  return {
2895
3044
  deferredMaterializations: void 0,
@@ -3311,6 +3460,61 @@ const MaterializationRecord = {
3311
3460
  return message;
3312
3461
  }
3313
3462
  };
3463
+ function createBaseRegisterResolveRequest() {
3464
+ return {
3465
+ reason: 0,
3466
+ latencyUs: 0
3467
+ };
3468
+ }
3469
+ const RegisterResolveRequest = {
3470
+ encode(message, writer = new BinaryWriter()) {
3471
+ if (message.reason !== 0) writer.uint32(8).int32(message.reason);
3472
+ if (message.latencyUs !== 0) writer.uint32(16).uint32(message.latencyUs);
3473
+ return writer;
3474
+ },
3475
+ decode(input, length) {
3476
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
3477
+ const end = length === void 0 ? reader.len : reader.pos + length;
3478
+ const message = createBaseRegisterResolveRequest();
3479
+ while (reader.pos < end) {
3480
+ const tag = reader.uint32();
3481
+ switch (tag >>> 3) {
3482
+ case 1:
3483
+ if (tag !== 8) break;
3484
+ message.reason = reader.int32();
3485
+ continue;
3486
+ case 2:
3487
+ if (tag !== 16) break;
3488
+ message.latencyUs = reader.uint32();
3489
+ continue;
3490
+ }
3491
+ if ((tag & 7) === 4 || tag === 0) break;
3492
+ reader.skip(tag & 7);
3493
+ }
3494
+ return message;
3495
+ },
3496
+ fromJSON(object) {
3497
+ return {
3498
+ reason: isSet(object.reason) ? resolveReasonFromJSON(object.reason) : 0,
3499
+ latencyUs: isSet(object.latencyUs) ? globalThis.Number(object.latencyUs) : 0
3500
+ };
3501
+ },
3502
+ toJSON(message) {
3503
+ const obj = {};
3504
+ if (message.reason !== 0) obj.reason = resolveReasonToJSON(message.reason);
3505
+ if (message.latencyUs !== 0) obj.latencyUs = Math.round(message.latencyUs);
3506
+ return obj;
3507
+ },
3508
+ create(base) {
3509
+ return RegisterResolveRequest.fromPartial(base ?? {});
3510
+ },
3511
+ fromPartial(object) {
3512
+ const message = createBaseRegisterResolveRequest();
3513
+ message.reason = object.reason ?? 0;
3514
+ message.latencyUs = object.latencyUs ?? 0;
3515
+ return message;
3516
+ }
3517
+ };
3314
3518
  function bytesFromBase64(b64) {
3315
3519
  if (globalThis.Buffer) return Uint8Array.from(globalThis.Buffer.from(b64, "base64"));
3316
3520
  else {
@@ -3338,10 +3542,12 @@ const EXPORT_FN_NAMES = [
3338
3542
  "wasm_msg_alloc",
3339
3543
  "wasm_msg_free",
3340
3544
  "wasm_msg_guest_resolve_flags",
3545
+ "wasm_msg_guest_register_resolve",
3341
3546
  "wasm_msg_guest_set_resolver_state",
3342
3547
  "wasm_msg_guest_bounded_flush_logs",
3343
3548
  "wasm_msg_guest_bounded_flush_assign",
3344
- "wasm_msg_guest_apply_flags"
3549
+ "wasm_msg_guest_apply_flags",
3550
+ "wasm_msg_guest_prometheus_snapshot"
3345
3551
  ];
3346
3552
  function verifyExports(exports) {
3347
3553
  for (const fnName of EXPORT_FN_NAMES) if (typeof exports[fnName] !== "function") throw new Error(`Expected Function export "${fnName}" found ${exports[fnName]}`);
@@ -3368,6 +3574,11 @@ var UnsafeWasmResolver = class {
3368
3574
  const resPtr = this.exports.wasm_msg_guest_resolve_flags(reqPtr);
3369
3575
  return this.consumeResponse(resPtr, ResolveProcessResponse);
3370
3576
  }
3577
+ registerResolve(request) {
3578
+ const reqPtr = this.transferRequest(request, RegisterResolveRequest);
3579
+ const resPtr = this.exports.wasm_msg_guest_register_resolve(reqPtr);
3580
+ this.consumeResponse(resPtr, Void);
3581
+ }
3371
3582
  setResolverState(request) {
3372
3583
  const reqPtr = this.transferRequest(request, SetResolverStateRequest);
3373
3584
  const resPtr = this.exports.wasm_msg_guest_set_resolver_state(reqPtr);
@@ -3390,6 +3601,11 @@ var UnsafeWasmResolver = class {
3390
3601
  const resPtr = this.exports.wasm_msg_guest_apply_flags(reqPtr);
3391
3602
  this.consumeResponse(resPtr, Void);
3392
3603
  }
3604
+ prometheusSnapshot(instance) {
3605
+ const reqPtr = this.transferRequest({ instance }, PrometheusSnapshotRequest);
3606
+ const resPtr = this.exports.wasm_msg_guest_prometheus_snapshot(reqPtr);
3607
+ return this.consumeResponse(resPtr, PrometheusSnapshotResponse).text;
3608
+ }
3393
3609
  transferRequest(value, codec) {
3394
3610
  const data = codec.encode(value).finish();
3395
3611
  return this.transfer({ data }, Request);
@@ -3447,6 +3663,11 @@ var WasmResolver = class {
3447
3663
  throw error$1;
3448
3664
  }
3449
3665
  }
3666
+ registerResolve(request) {
3667
+ try {
3668
+ this.delegate.registerResolve(request);
3669
+ } catch {}
3670
+ }
3450
3671
  setResolverState(request) {
3451
3672
  this.currentState = request;
3452
3673
  try {
@@ -3484,6 +3705,14 @@ var WasmResolver = class {
3484
3705
  throw error$1;
3485
3706
  }
3486
3707
  }
3708
+ prometheusSnapshot(instance) {
3709
+ try {
3710
+ return this.delegate.prometheusSnapshot(instance);
3711
+ } catch (error$1) {
3712
+ logger.error("prometheus snapshot failed:", error$1);
3713
+ return "";
3714
+ }
3715
+ }
3487
3716
  };
3488
3717
  let resolver = null;
3489
3718
  function createConfidenceServerProvider({ wasmUrl,...options }) {
@@ -82,6 +82,10 @@ declare enum ResolveReason {
82
82
  RESOLVE_REASON_UNRECOGNIZED_TARGETING_RULE = 7,
83
83
  /** RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED - The flag could not be resolved because a rule is using materialization */
84
84
  RESOLVE_REASON_MATERIALIZATION_NOT_SUPPORTED = 8,
85
+ /** RESOLVE_REASON_TYPE_MISMATCH - The resolved value type does not match the type requested by the SDK */
86
+ RESOLVE_REASON_TYPE_MISMATCH = 9,
87
+ /** RESOLVE_REASON_FLAG_NOT_FOUND - The flag was not found in the resolve response */
88
+ RESOLVE_REASON_FLAG_NOT_FOUND = 10,
85
89
  UNRECOGNIZED = -1,
86
90
  }
87
91
  declare enum SdkId {
@@ -273,6 +277,10 @@ interface MaterializationRecord {
273
277
  rule: string;
274
278
  variant: string;
275
279
  }
280
+ interface RegisterResolveRequest {
281
+ reason: ResolveReason;
282
+ latencyUs: number;
283
+ }
276
284
  declare const ResolveProcessRequest: MessageFns$2<ResolveProcessRequest>;
277
285
  declare const ResolveProcessRequest_StaticMaterializations: MessageFns$2<ResolveProcessRequest_StaticMaterializations>;
278
286
  declare const ResolveProcessRequest_Resume: MessageFns$2<ResolveProcessRequest_Resume>;
@@ -280,6 +288,7 @@ declare const ResolveProcessResponse: MessageFns$2<ResolveProcessResponse>;
280
288
  declare const ResolveProcessResponse_Resolved: MessageFns$2<ResolveProcessResponse_Resolved>;
281
289
  declare const ResolveProcessResponse_Suspended: MessageFns$2<ResolveProcessResponse_Suspended>;
282
290
  declare const MaterializationRecord: MessageFns$2<MaterializationRecord>;
291
+ declare const RegisterResolveRequest: MessageFns$2<RegisterResolveRequest>;
283
292
  type Builtin$2 = Date | Function | Uint8Array | string | number | boolean | undefined;
284
293
  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>;
285
294
  type KeysOfUnion$2<T> = T extends T ? keyof T : never;
@@ -297,6 +306,7 @@ interface MessageFns$2<T> {
297
306
  interface SetResolverStateRequest {
298
307
  state: Uint8Array;
299
308
  accountId: string;
309
+ sdk?: Sdk | undefined;
300
310
  }
301
311
  declare const SetResolverStateRequest: MessageFns$1<SetResolverStateRequest>;
302
312
  type Builtin$1 = Date | Function | Uint8Array | string | number | boolean | undefined;
@@ -315,10 +325,12 @@ interface MessageFns$1<T> {
315
325
  //#region src/LocalResolver.d.ts
316
326
  interface LocalResolver {
317
327
  resolveProcess(request: ResolveProcessRequest): ResolveProcessResponse;
328
+ registerResolve(request: RegisterResolveRequest): void;
318
329
  setResolverState(request: SetResolverStateRequest): void;
319
330
  flushLogs(): Uint8Array;
320
331
  flushAssigned(): Uint8Array;
321
332
  applyFlags(request: ApplyFlagsRequest): void;
333
+ prometheusSnapshot(instance: string): string;
322
334
  }
323
335
  //#endregion
324
336
  //#region src/proto/confidence/flags/resolver/v1/internal_api.d.ts
@@ -428,7 +440,7 @@ interface MaterializationStore {
428
440
  }
429
441
  //#endregion
430
442
  //#region src/types.d.ts
431
- type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "UNSPECIFIED";
443
+ type ResolutionReason = "ERROR" | "FLAG_ARCHIVED" | "MATCH" | "NO_SEGMENT_MATCH" | "TARGETING_KEY_ERROR" | "NO_TREATMENT_MATCH" | "MATERIALIZATION_NOT_SUPPORTED" | "UNSPECIFIED";
432
444
  declare enum ErrorCode {
433
445
  PROVIDER_NOT_READY = "PROVIDER_NOT_READY",
434
446
  PROVIDER_FATAL = "PROVIDER_FATAL",
@@ -461,6 +473,12 @@ interface FlagBundle {
461
473
  //#endregion
462
474
  //#region src/ConfidenceServerProviderLocal.d.ts
463
475
  type FlagBundle$1 = FlagBundle;
476
+ /**
477
+ * Configuration for {@link ConfidenceServerProviderLocal.getPrometheusMetrics}.
478
+ *
479
+ * @experimental This API is subject to change.
480
+ */
481
+ interface SnapshotConfig {}
464
482
  interface ProviderOptions {
465
483
  flagClientSecret: string;
466
484
  initializeTimeout?: number;
@@ -512,6 +530,12 @@ declare class ConfidenceServerProviderLocal implements Provider {
512
530
  /** Resolves with an evaluation of a String flag */
513
531
  resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext): Promise<ResolutionDetails<string>>;
514
532
  /**
533
+ * Returns a Prometheus metrics snapshot from the WASM resolver.
534
+ *
535
+ * @experimental This API is subject to change.
536
+ */
537
+ getPrometheusMetrics(_request?: SnapshotConfig): string;
538
+ /**
515
539
  * Applies a previously resolved flag, logging that it was used/exposed.
516
540
  * Call this when a flag value is actually rendered or used in the client.
517
541
  * @param resolveToken - Base64-encoded resolve token from the flag bundle
@@ -524,4 +548,4 @@ declare class ConfidenceServerProviderLocal implements Provider {
524
548
  type ProviderOptionsExt = ProviderOptions;
525
549
  declare function createConfidenceServerProvider(options: ProviderOptions): ConfidenceServerProviderLocal;
526
550
  //#endregion
527
- export { type MaterializationStore, ProviderOptionsExt, createConfidenceServerProvider };
551
+ export { type MaterializationStore, ProviderOptionsExt, type SnapshotConfig, createConfidenceServerProvider };