superposition-provider 0.93.1 → 0.94.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.
@@ -1,4 +1,4 @@
1
- import { GroupType, Bucket, VariantType } from "superposition-sdk";
1
+ import { GroupType, VariantType } from "superposition-sdk";
2
2
  import { SuperpositionOptions, ExperimentationOptions } from "./types";
3
3
  export interface Variant {
4
4
  id: string;
@@ -13,6 +13,10 @@ export interface Experiment {
13
13
  variants: Variant[];
14
14
  traffic_percentage: number;
15
15
  }
16
+ export interface Bucket {
17
+ variant_id: string;
18
+ experiment_id: string;
19
+ }
16
20
  export interface ExperimentGroup {
17
21
  id: string;
18
22
  context: Record<string, string>;
package/dist/index.esm.js CHANGED
@@ -9900,6 +9900,7 @@ function requireAws_restJson1 () {
9900
9900
  [_v]: [, input[_v]],
9901
9901
  [_sr]: [() => input.show_reasoning !== void 0, () => (input[_sr].toString())],
9902
9902
  [_ci]: [, input[_ci]],
9903
+ [_rr]: [() => input.resolve_remote !== void 0, () => (input[_rr].toString())],
9903
9904
  });
9904
9905
  let body;
9905
9906
  body = JSON.stringify((0, smithy_client_1.take)(input, {
@@ -12617,6 +12618,7 @@ function requireAws_restJson1 () {
12617
12618
  };
12618
12619
  const de_DimensionInfo = (output, context) => {
12619
12620
  return (0, smithy_client_1.take)(output, {
12621
+ 'autocomplete_function_name': smithy_client_1.expectString,
12620
12622
  'dependency_graph': smithy_client_1._json,
12621
12623
  'dimension_type': (_) => (0, smithy_client_1._json)((0, core_1.awsExpectUnion)(_)),
12622
12624
  'position': smithy_client_1.expectInt32,
@@ -12933,6 +12935,7 @@ function requireAws_restJson1 () {
12933
12935
  const _p = "prefix";
12934
12936
  const _pa = "page";
12935
12937
  const _pl = "plaintext";
12938
+ const _rr = "resolve_remote";
12936
12939
  const _s = "status";
12937
12940
  const _sb = "sort_by";
12938
12941
  const _so = "sort_on";
@@ -15596,7 +15599,10 @@ class ExperimentationClient {
15596
15599
  member_experiment_ids: exp_group.member_experiment_ids || [],
15597
15600
  group_type: exp_group.group_type ||
15598
15601
  superpositionSdk.GroupType.USER_CREATED,
15599
- buckets: exp_group.buckets || [],
15602
+ buckets: exp_group.buckets?.map((bucket) => ({
15603
+ variant_id: bucket.variant_id || "",
15604
+ experiment_id: bucket.experiment_id || "",
15605
+ })) || [],
15600
15606
  });
15601
15607
  }
15602
15608
  return experimentGroups;
@@ -15741,10 +15747,10 @@ class ConfigurationClient {
15741
15747
  console.error("Failed to refresh configuration. Will continue to use the last known good configuration.", error);
15742
15748
  }
15743
15749
  }, strategy.interval);
15744
- if (experimentationOptions) {
15745
- this.experimentationOptions = experimentationOptions;
15746
- this.experimentationClient = new ExperimentationClient(config, experimentationOptions);
15747
- }
15750
+ }
15751
+ if (experimentationOptions) {
15752
+ this.experimentationOptions = experimentationOptions;
15753
+ this.experimentationClient = new ExperimentationClient(config, experimentationOptions);
15748
15754
  }
15749
15755
  this.smithyClient = new superpositionSdk.SuperpositionClient({
15750
15756
  endpoint: this.config.endpoint,
@@ -15763,8 +15769,10 @@ class ConfigurationClient {
15763
15769
  let experimentationArgs;
15764
15770
  if (this.experimentationClient && targetingKey) {
15765
15771
  const experiments = await this.experimentationClient.getExperiments();
15772
+ const experiment_groups = await this.experimentationClient.getExperimentGroups();
15766
15773
  experimentationArgs = {
15767
15774
  experiments,
15775
+ experiment_groups,
15768
15776
  targeting_key: targetingKey,
15769
15777
  };
15770
15778
  }
@@ -15806,6 +15814,7 @@ class ConfigurationClient {
15806
15814
  }
15807
15815
  }
15808
15816
  // TODO: defaultValue is taken but not used. Should it be used as a fallback?
15817
+ // TODO: Remove this function all together and use eval for getAllConfigValue as well
15809
15818
  async getAllConfigValue(defaultValue, context, targetingKey) {
15810
15819
  try {
15811
15820
  const configData = await this.fetchConfigData();
@@ -15897,11 +15906,9 @@ class NativeResolver {
15897
15906
  try {
15898
15907
  this.lib = koffi.load(libPath || this.getDefaultLibPath());
15899
15908
  // Define the core resolution functions with CORRECT 8 parameters each
15900
- this.lib.core_get_resolved_config = this.lib.func("char* core_get_resolved_config(const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*)");
15901
- this.lib.core_get_resolved_config_with_reasoning = this.lib.func("char* core_get_resolved_config_with_reasoning(const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*)");
15909
+ this.lib.core_get_resolved_config = this.lib.func("char* core_get_resolved_config(const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*)");
15910
+ this.lib.core_get_resolved_config_with_reasoning = this.lib.func("char* core_get_resolved_config_with_reasoning(const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*, const char*)");
15902
15911
  this.lib.core_free_string = this.lib.func("void core_free_string(char*)");
15903
- this.lib.core_last_error_message = this.lib.func("char* core_last_error_message()");
15904
- this.lib.core_last_error_length = this.lib.func("int core_last_error_length()");
15905
15912
  this.lib.core_get_applicable_variants = this.lib.func("char* core_get_applicable_variants(const char*, const char*, const char*, const char*, const char*)");
15906
15913
  this.lib.core_test_connection = this.lib.func("int core_test_connection()");
15907
15914
  this.isAvailable = true;
@@ -15953,7 +15960,9 @@ class NativeResolver {
15953
15960
  console.log(" queryData :", queryDataJson);
15954
15961
  console.log(" mergeStrategy:", mergeStrategy);
15955
15962
  console.log(" filterPrefixes:", filterPrefixes);
15956
- console.log(" experimentation:", experimentationJson);
15963
+ console.log(" experiment:", experimentation?.experiments?.length);
15964
+ console.log(" experiment groups:", experimentation?.experiment_groups?.length);
15965
+ console.log(" targetingKey:", experimentation?.targetingKey);
15957
15966
  if (!defaultConfigsJson ||
15958
15967
  defaultConfigsJson === "null" ||
15959
15968
  defaultConfigsJson === "undefined") {
@@ -15979,10 +15988,12 @@ class NativeResolver {
15979
15988
  queryDataJson === "undefined") {
15980
15989
  throw new Error("queryData serialization failed");
15981
15990
  }
15982
- const result = this.lib.core_get_resolved_config(defaultConfigsJson, contextsJson, overridesJson, dimensionsJson, queryDataJson, mergeStrategy, filterPrefixesJson, experimentationJson);
15991
+ const ebuf = Buffer$1.alloc(256);
15992
+ const result = this.lib.core_get_resolved_config(defaultConfigsJson, contextsJson, overridesJson, dimensionsJson, queryDataJson, mergeStrategy, filterPrefixesJson, experimentationJson, ebuf);
15983
15993
  console.log("🔧 FFI call completed, result:", result);
15984
- if (!result) {
15985
- this.throwLastError("Failed to resolve config");
15994
+ const err = ebuf.toString('utf8').split('\0')[0];
15995
+ if (err.length !== 0) {
15996
+ this.throwFFIError(err);
15986
15997
  }
15987
15998
  const configStr = typeof result === "string"
15988
15999
  ? result
@@ -16009,9 +16020,11 @@ class NativeResolver {
16009
16020
  const experimentationJson = experimentation
16010
16021
  ? JSON.stringify(experimentation)
16011
16022
  : null;
16012
- const result = this.lib.core_get_resolved_config_with_reasoning(JSON.stringify(defaultConfigs || {}), JSON.stringify(contexts), JSON.stringify(overrides), JSON.stringify(dimensions), JSON.stringify(queryData), mergeStrategy, filterPrefixesJson, experimentationJson);
16013
- if (!result) {
16014
- this.throwLastError("Failed to resolve config with reasoning");
16023
+ const ebuf = Buffer$1.alloc(256);
16024
+ const result = this.lib.core_get_resolved_config_with_reasoning(JSON.stringify(defaultConfigs || {}), JSON.stringify(contexts), JSON.stringify(overrides), JSON.stringify(dimensions), JSON.stringify(queryData), mergeStrategy, filterPrefixesJson, experimentationJson, ebuf);
16025
+ const err = ebuf.toString('utf8').split('\0')[0];
16026
+ if (err.length !== 0) {
16027
+ this.throwFFIError(err);
16015
16028
  }
16016
16029
  const configStr = typeof result === "string"
16017
16030
  ? result
@@ -16049,10 +16062,12 @@ class NativeResolver {
16049
16062
  console.log(" userContext:", userContext);
16050
16063
  console.log(" identifier:", identifier);
16051
16064
  console.log(" filterPrefixes:", filterPrefixes);
16065
+ const ebuf = Buffer$1.alloc(256);
16052
16066
  const result = this.lib.core_get_applicable_variants(experimentsJson, experimentGroupsJson, dimensionsJson, userContextJson, identifier, filterPrefixesJson);
16053
16067
  console.log("FFI getApplicableVariants call completed, result:", result);
16054
- if (!result) {
16055
- this.throwLastError("Failed to get applicable variants");
16068
+ const err = ebuf.toString('utf8').split('\0')[0];
16069
+ if (err.length !== 0) {
16070
+ this.throwFFIError(err);
16056
16071
  }
16057
16072
  const resultStr = typeof result === "string"
16058
16073
  ? result
@@ -16136,22 +16151,8 @@ class NativeResolver {
16136
16151
  return false;
16137
16152
  }
16138
16153
  }
16139
- throwLastError(prefix) {
16140
- if (!this.isAvailable) {
16141
- throw new Error(`${prefix}: Native resolver not available`);
16142
- }
16143
- const errorLength = this.lib.core_last_error_length();
16144
- if (errorLength > 0) {
16145
- const errorPtr = this.lib.core_last_error_message();
16146
- const errorMsg = typeof errorPtr === "string"
16147
- ? errorPtr
16148
- : this.lib.decode(errorPtr, "string");
16149
- if (typeof errorPtr !== "string") {
16150
- this.lib.core_free_string(errorPtr);
16151
- }
16152
- throw new Error(`${prefix}: ${errorMsg}`);
16153
- }
16154
- throw new Error(`${prefix}: Unknown error`);
16154
+ throwFFIError(err) {
16155
+ throw new Error("ffi: " + err);
16155
16156
  }
16156
16157
  }
16157
16158
 
@@ -16165,13 +16166,14 @@ class SuperpositionProvider {
16165
16166
  constructor(config) {
16166
16167
  this.config = config;
16167
16168
  this.metadata = {
16168
- name: 'SuperpositionProvider',
16169
- slug: 'superposition-provider'
16169
+ name: "SuperpositionProvider",
16170
+ slug: "superposition-provider",
16170
16171
  };
16171
16172
  this.events = new OpenFeatureEventEmitter();
16172
16173
  this.hooks = [];
16173
16174
  this.status = ProviderStatus.NOT_READY;
16174
16175
  // Cache for processed contexts
16176
+ // TODO: verify if this is at all needed
16175
16177
  this.processedContextCache = new WeakMap();
16176
16178
  this.client = new ConfigurationClient({
16177
16179
  endpoint: config.endpoint,
@@ -16188,14 +16190,22 @@ class SuperpositionProvider {
16188
16190
  this.status = ProviderStatus.NOT_READY;
16189
16191
  try {
16190
16192
  await this.client.initialize();
16193
+ // TODO: find why is this needed?
16191
16194
  await this.client.eval(context || {});
16192
16195
  this.status = ProviderStatus.READY;
16193
- this.events.emit(ProviderEvents.Ready, { message: 'Provider ready' });
16196
+ this.events.emit(ProviderEvents.Ready, {
16197
+ message: "Provider ready",
16198
+ });
16194
16199
  }
16195
16200
  catch (error) {
16196
16201
  this.status = ProviderStatus.ERROR;
16197
- const message = error instanceof Error ? error.message : 'Initialization failed';
16198
- this.events.emit(ProviderEvents.Error, { message, errorCode: ErrorCode.PROVIDER_NOT_READY });
16202
+ const message = error instanceof Error
16203
+ ? error.message
16204
+ : "Initialization failed";
16205
+ this.events.emit(ProviderEvents.Error, {
16206
+ message,
16207
+ errorCode: ErrorCode.PROVIDER_NOT_READY,
16208
+ });
16199
16209
  throw error;
16200
16210
  }
16201
16211
  }
@@ -16210,7 +16220,7 @@ class SuperpositionProvider {
16210
16220
  processedContext = this.filterContext(context);
16211
16221
  this.processedContextCache.set(context, processedContext);
16212
16222
  }
16213
- const config = await this.client.eval(processedContext);
16223
+ const config = await this.client.eval(processedContext, undefined, context.targetingKey);
16214
16224
  const value = getNestedValue(config, flagKey);
16215
16225
  const converter = TYPE_CONVERTERS[type];
16216
16226
  return converter(value, defaultValue);
@@ -16218,17 +16228,24 @@ class SuperpositionProvider {
16218
16228
  filterContext(context) {
16219
16229
  const filtered = {};
16220
16230
  for (const [key, value] of Object.entries(context)) {
16221
- if (key.startsWith('__') || key === 'targetingKey' || key === 'timestamp') {
16231
+ if (key.startsWith("__") ||
16232
+ key === "targetingKey" ||
16233
+ key === "timestamp") {
16222
16234
  continue;
16223
16235
  }
16224
16236
  // Only include simple, serializable types
16225
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
16237
+ if (typeof value === "string" ||
16238
+ typeof value === "number" ||
16239
+ typeof value === "boolean") {
16226
16240
  filtered[key] = value;
16227
16241
  }
16228
- else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
16242
+ else if (typeof value === "object" &&
16243
+ value !== null &&
16244
+ !Array.isArray(value)) {
16229
16245
  try {
16230
16246
  const serialized = JSON.stringify(value);
16231
- if (serialized.length < 1000 && Object.keys(value).length < 10) {
16247
+ if (serialized.length < 1000 &&
16248
+ Object.keys(value).length < 10) {
16232
16249
  filtered[key] = value;
16233
16250
  }
16234
16251
  }
@@ -16241,62 +16258,76 @@ class SuperpositionProvider {
16241
16258
  }
16242
16259
  createResolver(type) {
16243
16260
  return async (flagKey, defaultValue, context) => {
16244
- if (this.status !== ProviderStatus.READY && this.status !== ProviderStatus.STALE) {
16261
+ if (this.status !== ProviderStatus.READY &&
16262
+ this.status !== ProviderStatus.STALE) {
16245
16263
  return {
16246
16264
  value: defaultValue,
16247
- reason: 'ERROR',
16248
- errorCode: this.status === ProviderStatus.FATAL ? ErrorCode.PROVIDER_FATAL : ErrorCode.PROVIDER_NOT_READY,
16249
- errorMessage: `Provider status: ${this.status}`
16265
+ reason: "ERROR",
16266
+ errorCode: this.status === ProviderStatus.FATAL
16267
+ ? ErrorCode.PROVIDER_FATAL
16268
+ : ErrorCode.PROVIDER_NOT_READY,
16269
+ errorMessage: `Provider status: ${this.status}`,
16250
16270
  };
16251
16271
  }
16252
16272
  try {
16253
16273
  const value = await this.evaluateFlag(flagKey, defaultValue, context, type);
16254
16274
  return {
16255
16275
  value,
16256
- reason: this.status === ProviderStatus.STALE ? 'STALE' : 'TARGETING_MATCH',
16276
+ reason: this.status === ProviderStatus.STALE
16277
+ ? "STALE"
16278
+ : "TARGETING_MATCH",
16257
16279
  };
16258
16280
  }
16259
16281
  catch (error) {
16260
16282
  return {
16261
16283
  value: defaultValue,
16262
- reason: 'ERROR',
16284
+ reason: "ERROR",
16263
16285
  errorCode: ErrorCode.GENERAL,
16264
- errorMessage: error instanceof Error ? error.message : 'Evaluation failed'
16286
+ errorMessage: error instanceof Error
16287
+ ? error.message
16288
+ : "Evaluation failed",
16265
16289
  };
16266
16290
  }
16267
16291
  };
16268
16292
  }
16269
16293
  async resolveBooleanEvaluation(flagKey, defaultValue, context) {
16270
- return this.createResolver('boolean')(flagKey, defaultValue, context);
16294
+ return this.createResolver("boolean")(flagKey, defaultValue, context);
16271
16295
  }
16272
16296
  async resolveStringEvaluation(flagKey, defaultValue, context) {
16273
- return this.createResolver('string')(flagKey, defaultValue, context);
16297
+ return this.createResolver("string")(flagKey, defaultValue, context);
16274
16298
  }
16275
16299
  async resolveNumberEvaluation(flagKey, defaultValue, context) {
16276
- return this.createResolver('number')(flagKey, defaultValue, context);
16300
+ return this.createResolver("number")(flagKey, defaultValue, context);
16277
16301
  }
16278
16302
  async resolveObjectEvaluation(flagKey, defaultValue, context, logger) {
16279
- return this.createResolver('object')(flagKey, defaultValue, context);
16303
+ return this.createResolver("object")(flagKey, defaultValue, context);
16280
16304
  }
16281
16305
  async resolveAllConfigDetails(defaultValue, context) {
16282
- if (this.status !== ProviderStatus.READY && this.status !== ProviderStatus.STALE) {
16306
+ if (this.status !== ProviderStatus.READY &&
16307
+ this.status !== ProviderStatus.STALE) {
16283
16308
  return defaultValue;
16284
16309
  }
16285
16310
  try {
16286
- const processedContext = this.processedContextCache.get(context) || this.filterContext(context);
16311
+ const processedContext = this.processedContextCache.get(context) ||
16312
+ this.filterContext(context);
16287
16313
  if (!this.processedContextCache.has(context)) {
16288
16314
  this.processedContextCache.set(context, processedContext);
16289
16315
  }
16290
16316
  const targetingKey = context.targetingKey;
16317
+ // TODO: remove this function and use eval for getAllConfigValue as well
16291
16318
  return await this.client.getAllConfigValue(defaultValue, processedContext, targetingKey);
16292
16319
  }
16293
16320
  catch (error) {
16294
- console.error('Error resolving all config details:', error);
16321
+ console.error("Error resolving all config details:", error);
16295
16322
  return defaultValue;
16296
16323
  }
16297
16324
  }
16298
- getStatus() { return this.status; }
16299
- getConfigurationClient() { return this.client; }
16325
+ getStatus() {
16326
+ return this.status;
16327
+ }
16328
+ getConfigurationClient() {
16329
+ return this.client;
16330
+ }
16300
16331
  }
16301
16332
 
16302
16333
  function httpRequest(options) {