Giter Site home page Giter Site logo

unleash / unleash-proxy-client-js Goto Github PK

View Code? Open in Web Editor NEW
43.0 3.0 44.0 1.98 MB

A browser client that can be used together with the unleash-proxy.

License: Apache License 2.0

TypeScript 97.28% JavaScript 1.59% HTML 1.14%
javascript feature-toggles unleash hacktoberfest

unleash-proxy-client-js's Introduction

Unleash Proxy Client for the browser (JS)

The JavaScript proxy client is a tiny Unleash client written in JavaScript without any external dependencies (except from browser APIs). This client stores toggles relevant for the current user in localStorage and synchronizes with Unleash (the Unleash front-end API or the Unleash proxy) in the background. Because toggles are stored in the user's browser, the client can use them to bootstrap itself the next time the user visits the same web page.

This client expect fetch to be available. If you need to support older browsers you should probably use the fetch polyfill.

Frameworks supported

This package is not tied to any framework, but can be used together most popular frameworks, examples:

How to use the client

Step 1: Installation

npm install unleash-proxy-client

Step 2: Initialize the SDK


๐Ÿ’ก TIP: As a client-side SDK, this SDK requires you to connect to either an Unleash proxy or to the Unleash front-end API. Refer to the connection options section for more information.


Configure the client according to your needs. The following example provides only the required options. Refer to the section on available options for the full list.

import { UnleashClient } from 'unleash-proxy-client';

const unleash = new UnleashClient({
    url: 'https://<your-unleash-instance>/api/frontend',
    clientKey: '<your-client-side-token>',
    appName: 'my-webapp',
});

// Start the background polling
unleash.start();

Connection options

To connect this SDK to your Unleash instance's front-end API, use the URL to your Unleash instance's front-end API (<unleash-url>/api/frontend) as the url parameter. For the clientKey parameter, use a FRONTEND token generated from your Unleash instance. Refer to the how to create API tokens guide for the necessary steps.

To connect this SDK to the Unleash proxy, use the proxy's URL and a proxy client key. The configuration section of the Unleash proxy docs contains more info on how to configure client keys for your proxy.

Step 3: Let the client synchronize

You should wait for the client's ready or initialized events before you start working with it. Before it's ready, the client might not report the correct state for your features.

unleash.on('ready', () => {
    if (unleash.isEnabled('proxy.demo')) {
        console.log('proxy.demo is enabled');
    } else {
        console.log('proxy.demo is disabled');
    }
});

The difference between the events is described in the section on available events.

Step 4: Check feature toggle states

Once the client is ready, you can start checking features in your application. Use the isEnabled method to check the state of any feature you want:

unleash.isEnabled('proxy.demo');

You can use the getVariant method to get the variant of an enabled feature that has variants. If the feature is disabled or if it has no variants, then you will get back the disabled variant

const variant = unleash.getVariant('proxy.demo');
if (variant.name === 'blue') {
    // something with variant blue...
}

Updating the Unleash context

The Unleash context is used to evaluate features against attributes of a the current user. To update and configure the Unleash context in this SDK, use the updateContext, setContextField and removeContextField methods.

The context you set in your app will be passed along to the Unleash proxy or the front-end API as query parameters for feature evaluation.

The updateContext method will replace the entire (mutable part) of the Unleash context with the data that you pass in.

The setContextField method only acts on the property that you choose. It does not affect any other properties of the Unleash context.

unleash.updateContext({ userId: '1233' });

unleash.setContextField('userId', '4141');

The removeContextField method only acts on the property that you choose. It does not affect any other properties of the Unleash context.

unleash.updateContext({ userId: '1233', sessionId: 'sessionNotAffected' });

unleash.removeContextField('userId');

Available options

The Unleash SDK takes the following options:

option required default description
url yes n/a The Unleash Proxy URL to connect to. E.g.: https://examples.com/proxy
clientKey yes n/a The Unleash Proxy Secret to be used
appName yes n/a The name of the application using this SDK. Will be used as part of the metrics sent to Unleash Proxy. Will also be part of the Unleash Context.
refreshInterval no 30 How often, in seconds, the SDK should check for updated toggle configuration. If set to 0 will disable checking for updates
disableRefresh no false If set to true, the client will not check for updated toggle configuration
metricsInterval no 60 How often, in seconds, the SDK should send usage metrics back to Unleash Proxy. It will be started after the initial metrics report, which is sent after the configured metricsIntervalInitial
metricsIntervalInitial no 2 How long the SDK should wait for the first metrics report back to the Unleash API. If you want to disable the initial metrics call you can set it to 0.
disableMetrics no false Set this option to true if you want to disable usage metrics
storageProvider no LocalStorageProvider in browser, InMemoryStorageProvider otherwise Allows you to inject a custom storeProvider
fetch no window.fetch or global fetch Allows you to override the fetch implementation to use. Useful in Node.js environments where you can inject node-fetch
bootstrap no [] Allows you to bootstrap the cached feature toggle configuration.
bootstrapOverride no true Should the bootstrap automatically override cached data in the local-storage. Will only be used if bootstrap is not an empty array.
headerName no Authorization Which header the SDK should use to authorize with Unleash / Unleash Proxy. The header will be given the clientKey as its value.
customHeaders no {} Additional headers to use when making HTTP requests to the Unleash proxy. In case of name collisions with the default headers, the customHeaders value will be used if it is not null or undefined. customHeaders values that are null or undefined will be ignored.
impressionDataAll no false Allows you to trigger "impression" events for all getToggle and getVariant invocations. This is particularly useful for "disabled" feature toggles that are not visible to frontend SDKs.
environment no default Sets the environment option of the Unleash context. This does not affect the SDK's Unleash environment.

Listen for updates via the EventEmitter

The client is also an event emitter. This means that your code can subscribe to updates from the client. This is a neat way to update a single page app when toggle state updates.

unleash.on('update', () => {
    const myToggle = unleash.isEnabled('proxy.demo');
    //do something useful
});

Available events:

  • error - emitted when an error occurs on init, or when fetch function fails, or when fetch receives a non-ok response object. The error object is sent as payload.
  • initialized - emitted after the SDK has read local cached data in the storageProvider.
  • ready - emitted after the SDK has successfully started and performed the initial fetch towards the Unleash Proxy.
  • update - emitted every time the Unleash Proxy return a new feature toggle configuration. The SDK will emit this event as part of the initial fetch from the SDK.
  • recovered - emitted when the SDK has recovered from an error. This event will only be emitted if the SDK has previously emitted an error.
  • sent - emitted when the SDK has successfully sent metrics to Unleash.

PS! Please remember that you should always register your event listeners before your call unleash.start(). If you register them after you have started the SDK you risk loosing important events.

SessionId - Important note!

You may provide a custom session id via the "context". If you do not provide a sessionId this SDK will create a random session id, which will also be stored in the provided storage (local storage). By always having a consistent sessionId available ensures that even "anonymous" users will get a consistent experience when feature toggles is evaluated, in combination with a gradual (percentage based) rollout.

Stop the SDK

You can stop the Unleash client by calling the stop method. Once the client has been stopped, it will no longer check for updates or send metrics to the server.

A stopped client can be restarted.

unleash.stop()

Custom store

This SDK can work with React Native storage @react-native-async-storage/async-storage or react-native-shared-preferences and many more to backup feature toggles locally. This is useful for bootstrapping the SDK the next time the user comes back to your application.

You can provide your own storage implementation.

Examples:

import SharedPreferences from 'react-native-shared-preferences';
import { UnleashClient } from 'unleash-proxy-client';

const unleash = new UnleashClient({
    url: 'https://eu.unleash-hosted.com/hosted/proxy',
    clientKey: 'your-proxy-key',
    appName: 'my-webapp',
    storageProvider: {
      save: (name: string, data: any) => SharedPreferences.setItem(name, data),
      get: (name: string) => SharedPreferences.getItem(name, (val) => val)
    },
});
import AsyncStorage from '@react-native-async-storage/async-storage';
import { UnleashClient } from 'unleash-proxy-client';

const PREFIX = 'unleash:repository';

const unleash = new UnleashClient({
    url: 'https://eu.unleash-hosted.com/hosted/proxy',
    clientKey: 'your-proxy-key',
    appName: 'my-webapp',
    storageProvider: {
       save: (name: string, data: any) => {
        const repo = JSON.stringify(data);
        const key = `${PREFIX}:${name}`;
        return AsyncStorage.setItem(key, repo);
      },
      get: (name: string) => {
        const key = `${PREFIX}:${name}`;
        const data = await AsyncStorage.getItem(key);
        return data ? JSON.parse(data) : undefined;
      }
    },
});

How to use in node.js

This SDK can also be used in node.js applications (from v1.4.0). Please note that you will need to provide a valid "fetch" implementation. Only ECMAScript modules is exported from this package.

import fetch from 'node-fetch';
import { UnleashClient, InMemoryStorageProvider } from 'unleash-proxy-client';

const unleash = new UnleashClient({
  url: 'https://app.unleash-hosted.com/demo/proxy',
  clientKey: 'proxy-123',
  appName: 'nodejs-proxy',
  storageProvider: new InMemoryStorageProvider(),
  fetch,
});

await unleash.start();
const isEnabled = unleash.isEnabled('proxy.demo');
console.log(isEnabled);

index.mjs

How to use the client via CDN.

<html>
<head>
    <script src="https://unpkg.com/unleash-proxy-client@latest/build/main.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        var config = {url: 'https://app.unleash-hosted.com/demo/proxy', clientKey: 'proxy-123', appName: 'web'};
        var client = new unleash.UnleashClient(config);
        client.updateContext({userId: '1233'})

        client.on('update', () => {
            console.log(client.isEnabled('proxy.demo'));
        });
        client.start();
    </script>
</head>
</html>

Bootstrap

Now it is possible to bootstrap the SDK with your own feature toggle configuration when you don't want to make an API call.

This is also useful if you require the toggles to be in a certain state immediately after initializing the SDK.

How to use it ?

Add a bootstrap attribute when create a new UnleashClient.
There's also a bootstrapOverride attribute which is by default is true.

import { UnleashClient } from 'unleash-proxy-client';

const unleash = new UnleashClient({
  url: 'https://app.unleash-hosted.com/demo/proxy',
  clientKey: 'proxy-123',
  appName: 'nodejs-proxy',
  bootstrap: [{
	"enabled": true,
	"name": "demoApp.step4",
	"variant": {
		"enabled": true,
		"name": "blue",
		"feature_enabled": true,
	}
  }],
  bootstrapOverride: false
});

NOTES: โš ๏ธ If bootstrapOverride is true (by default), any local cached data will be overridden with the bootstrap specified.
If bootstrapOverride is false any local cached data will not be overridden unless the local cache is empty.

unleash-proxy-client-js's People

Contributors

andreas-unleash avatar anttiviljami avatar badsgahhl avatar christian98 avatar cmalard avatar dependabot[bot] avatar dzixxx avatar fredrikoseberg avatar gabrielnastase avatar ivarconr avatar kei95 avatar killthekitten avatar kwasniew avatar mattmurph9 avatar mohsen-khalili avatar nunogois avatar olav avatar pvinis avatar rbtcollins avatar sighphyre avatar sjaanus avatar thomasheartman avatar travis-walsh avatar tymek avatar ykhedher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

unleash-proxy-client-js's Issues

Failed to parse source map Warning

Describe the bug

package version: 3.1.0
react version: 18.2.0
react scripts version: 5.0.1

Getting warnings in console after upgrading to latest version

Steps to reproduce the bug

install latest version using yarn

Expected behavior

should not throw warnings

Logs, error output, etc.

No response

Screenshots

image

Additional context

No response

Unleash version

3.1.0

Subscription type

None

Hosting type

None

SDK information (language and version)

No response

Unleash: Fetching feature toggles did not have an ok response

Describe the bug

See this error in the console constantly "Unleash: Fetching feature toggles did not have an ok response".

image

However I can see in my network traffic I am getting proper 304 response.

image

Steps to reproduce the bug

I am running React Query SDK version of your plugin hitting the frontend proxy. All the code is working fine but this error is constantly displayed over and over in the console.

Expected behavior

304 should not generate this error message

Logs, error output, etc.

{
  "log": {
    "version": "1.2",
    "creator": {
      "name": "WebInspector",
      "version": "537.36"
    },
    "pages": [],
    "entries": [
      {
        "_initiator": {
          "type": "script",
          "stack": {
            "callFrames": [
              {
                "functionName": "",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 115011,
                "columnNumber": 27
              },
              {
                "functionName": "d",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 114467,
                "columnNumber": 16
              },
              {
                "functionName": "",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 114411,
                "columnNumber": 15
              },
              {
                "functionName": "",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 114404,
                "columnNumber": 34
              },
              {
                "functionName": "c",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 114383,
                "columnNumber": 11
              },
              {
                "functionName": "f.fetchToggles",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 114994,
                "columnNumber": 13
              },
              {
                "functionName": "",
                "scriptId": "1226",
                "url": "http://localhost:3000/static/js/bundle.js",
                "lineNumber": 114942,
                "columnNumber": 25
              }
            ]
          }
        },
        "_priority": "High",
        "_resourceType": "fetch",
        "cache": {},
        "connection": "181364",
        "request": {
          "method": "GET",
          "url": "http://localhost:3002/proxy?sessionId=813167092&appName=default&environment=dev",
          "httpVersion": "HTTP/1.1",
          "headers": [
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "Accept-Encoding",
              "value": "gzip, deflate, br"
            },
            {
              "name": "Accept-Language",
              "value": "en-US,en;q=0.9,la;q=0.8"
            },
            {
              "name": "Authorization",
              "value": "proxy-client-key"
            },
            {
              "name": "Cache-Control",
              "value": "max-age=0"
            },
            {
              "name": "Connection",
              "value": "keep-alive"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Host",
              "value": "localhost:3002"
            },
            {
              "name": "If-None-Match",
              "value": "W/\"15c-+9UjpQ9/f67orBE1JPsf/FITb8o\""
            },
            {
              "name": "Origin",
              "value": "http://localhost:3000"
            },
            {
              "name": "Referer",
              "value": "http://localhost:3000/"
            },
            {
              "name": "Sec-Fetch-Dest",
              "value": "empty"
            },
            {
              "name": "Sec-Fetch-Mode",
              "value": "cors"
            },
            {
              "name": "Sec-Fetch-Site",
              "value": "same-site"
            },
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
            },
            {
              "name": "sec-ch-ua",
              "value": "\"Google Chrome\";v=\"105\", \"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"105\""
            },
            {
              "name": "sec-ch-ua-mobile",
              "value": "?0"
            },
            {
              "name": "sec-ch-ua-platform",
              "value": "\"Windows\""
            }
          ],
          "queryString": [
            {
              "name": "sessionId",
              "value": "813167092"
            },
            {
              "name": "appName",
              "value": "default"
            },
            {
              "name": "environment",
              "value": "dev"
            }
          ],
          "cookies": [],
          "headersSize": 757,
          "bodySize": 0
        },
        "response": {
          "status": 304,
          "statusText": "Not Modified",
          "httpVersion": "HTTP/1.1",
          "headers": [
            {
              "name": "Access-Control-Allow-Origin",
              "value": "*"
            },
            {
              "name": "Access-Control-Expose-Headers",
              "value": "ETag"
            },
            {
              "name": "Cache-control",
              "value": "public, max-age=2"
            },
            {
              "name": "Connection",
              "value": "keep-alive"
            },
            {
              "name": "Date",
              "value": "Wed, 21 Sep 2022 21:53:01 GMT"
            },
            {
              "name": "ETag",
              "value": "W/\"15c-+9UjpQ9/f67orBE1JPsf/FITb8o\""
            },
            {
              "name": "Keep-Alive",
              "value": "timeout=5"
            }
          ],
          "cookies": [],
          "content": {
            "size": 0,
            "mimeType": "x-unknown"
          },
          "redirectURL": "",
          "headersSize": 259,
          "bodySize": 0,
          "_transferSize": 259,
          "_error": "net::ERR_ABORTED"
        },
        "serverIPAddress": "[::1]",
        "startedDateTime": "2022-09-21T21:53:01.636Z",
        "time": 15.02799999434501,
        "timings": {
          "blocked": 9.417999921545386,
          "dns": -1,
          "ssl": -1,
          "connect": -1,
          "send": 0.08500000000000002,
          "wait": 2.04500000064075,
          "receive": 3.480000072158873,
          "_blocked_queueing": 9.009999921545386
        }
      },
  }
}

Screenshots

Provided above.

Additional context

No response

Unleash version

Latest

Subscription type

No response

Hosting type

No response

SDK information (language and version)

"@unleash/proxy-client-react": "^3.3.0",

ready is fired even if unleash proxy is not ready?

Describe the bug

Hey there,

it seems that the ready event is triggered on the proxy client even if we disable our unleash proxy (or enter a wrong url for that matter):

this.unleash = new UnleashClient(config);

    // delegate checking feature toggles to unleash if available
    this.unleash.on('ready', () => {
      console.log('FeatureConfigService: Unleash is ready...');
      this.unleashReady = true;
    });
    this.unleash.on('update', () => {
      console.log('FeatureConfigService: Updating feature toggles...');
      this.unleashReady = true;
    });
    this.unleash.on('error', () => {
      console.log('FeatureConfigService: Could not reach unleash proxy, using defaults as fallback...');
      this.unleashReady = false;
    });
    this.unleash.start();

we get an error event and afterwards and ready event which basically breaks our boolean logic for our fallback.
We are using the "unleash-proxy-client": "^2.0.1",

Please let me know if you need any further information.

Steps to reproduce the bug

No response

Expected behavior

Only be ready when you are really ready and do not fire a ready event in the case of an error, in particular after the error event.

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

No response

Subscription type

No response

Hosting type

No response

SDK information (language and version)

No response

Dynamic refresh interval

Describe the feature request

const wait = (sec) => new Promise(resolve => setTimeout(resolve, sec * 1000));

let i = 0;

const client = new unleash.UnleashClient({
  appName,
  url,
  clientKey,
  refreshInterval: () => wait(++i), // refresh after 1 sec, 2 sec, 3, 4...
});

https://github.com/Unleash/unleash-proxy-client-js/blob/d5f0c743/src/index.ts#L31

  interface IConfig extends IStaticContext {
      url: URL | string;
      clientKey: string;
      disableRefresh?: boolean;
-     refreshInterval?: number;
+     refreshInterval?: number | () => Promise<void>;
      metricsInterval?: number;
      disableMetrics?: boolean;
      storageProvider?: IStorageProvider;
      context?: IMutableContext;
      fetch?: any;
      bootstrap?: IToggle[];
      bootstrapOverride?: boolean;
      headerName?: string;
      customHeaders?: Record<string, string>;
      impressionDataAll?: boolean;
      usePOSTrequests?: boolean;
  }

Background

We want to refresh values more frequently when user uses the app actively (we will check the activity from keyboard & mouse event).
Currently we can't configure refresh rate dynamically so I propose new config interface setting next timeout interval.

Solution suggestions

No response

Update context does not fetch new flags

If the refreshInterval is set to 0 and updateContext function is called, the data does not update.

public async updateContext(context: IMutableContext): Promise<void> {
    // Give the user a nicer error message when including
    // static fields in the mutable context object
    // @ts-ignore
    if (context.appName || context.environment) {
        console.warn(
            "appName and environment are static. They can't be updated with updateContext."
        );
    }
    const staticContext = {
        environment: this.context.environment,
        appName: this.context.appName,
    };
    this.context = { ...staticContext, ...context };
    if (this.timerRef) { // <--------- here is the problem
        await this.fetchToggles();
    }
}

Gitlab - Feature Flags - 401 Unauthorized

I am using Feature Flags in Gitlab so in the documentation they mention to used unleash.

I went to Deployment > Feature Flags and created a flag called: live-chat-settings. Where also i got the URL that i will use in your library:

  • URL: https://gl.eweev.com/api/v4/feature_flags/unleash/***
  • clientKey (in gitlab it's called instanceId): Ljf3c6-**********MhR
  • appName: chat

Under the Deployment > Environments I got the app name I have two:

  • vuejs
  • chat

I downloaded:

 npm install unleash-proxy-client --save

Where in the code i added the following:

initUnleash() {
      const unleash = new UnleashClient({
        url: 'https://gl.eweev.com/api/v4/feature_flags/unleash/***',
        clientKey: 'Ljf3c6-**********MhR',
        appName: 'chat'
      })
      console.log('unleash: ', unleash)

      // Start the background polling
      unleash.start()
    },

Here is the console.log for the variable:

{
    "toggles": [],
    "etag": "",
    "url": "https://gl.eweev.com/api/v4/feature_flags/unleash/***",
    "clientKey": "Ljf3c6-**********MhR",
    "storage": {
        "prefix": "unleash:repository"
    },
    "refreshInterval": 30000,
    "context": {
        "appName": "chat",
        "environment": "default"
    },
    "metrics": {
        "disabled": false,
        "metricsInterval": 30000,
        "appName": "chat",
        "url": "https://gl.eweev.com/api/v4/feature_flags/unleash/***",
        "started": "2021-07-29T13:39:10.438Z",
        "clientKey": "Ljf3c6-**********MhR",
        "bucket": {
            "start": "2021-07-29T13:49:01.292Z",
            "stop": null,
            "toggles": {}
        },
        "timer": 74
    },
    "e": {},
    "timerRef": 21
}

I am getting the following console error on my browser:

GET https://gl.eweev.com/api/v4/feature_flags/unleash/***?appName=chat&environment=default 401

Where the response to the API call is the following:

{
    "message": "401 Unauthorized"
}

Anyone can help me?

[React-Native] triggers uuid crash when feature with impression data enabled is used with `isEnabled`

Describe the bug

Hi! It's a really specific issue. when a certain word is used for key and run isEnabled() with it, it causes a crash bug which is coming from uuid library. I know that it's a known issue with v4 uuid and there's a monkey patch fix for it, but I figured it's confusing for ones who are not familiar/new to React Native. I have no idea why that particular word triggers this behavior... but I hope this report helps someone come across the same issue. I fixed it by changing the word to pwd but I have a feeling that there are more keywords that triggers this issue.

The straightforward fix for this issue would be installing react-native-get-random-values but it doesn't feel right to bring it only for React Native since this library is for other frameworks too.

dependencies:

"unleash-proxy-client": "1.11.0",
"react-native": "0.66.3",
"react": "17.0.2",

Steps to reproduce the bug

  1. Create a key in unleash that contains the word "password"(e.g. [your-product-name].change-password.enable-screen)
  2. Enable the feature in your unleash console
  3. In your app, trigger isEnabled() function with the key just created

Expected behavior

isEnabled() should return true value.

Logs, error output, etc.

No response

Screenshots

Additional context

No response

Unleash version

1.11.0

Subscription type

Enterprise

Hosting type

No response

SDK information (language and version)

No response

Right-hand side of instanceof is not an object

Describe the bug

image
An error is reported when the url is passed in a string
Maybe you need Object.prototype.toString.call(url) === '[object URL]' ? url : new URL(url)

Steps to reproduce the bug

No response

Expected behavior

No response

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

No response

Subscription type

No response

Hosting type

No response

SDK information (language and version)

No response

Unleash: unable to fetch feature toggles TypeError: XXXX

Describe the bug

Currently i used unleash-client-proxry-js with vue2 js and i used sentry for monitoring error

I found some error that I cannot reproduce ?

  1. Unleash: unable to fetch feature toggles TypeError: Load failed
  • Observation I found sentry-platform found on IOS many by not found android browser.
  1. Unleash: unable to fetch feature toggles TypeError: Failed to fetch
  • Observation I found sentry-platform found on Android many by not found safari browser.

my application start unleash by example code below

const startApp = async () => {
  try {
    await unleash.start()
  } finally {
    new Vue({
      apolloProvider,
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
  }
}
startApp()

Steps to reproduce the bug

  1. Go to web-browser
  2. used network console blocking domain unleash (but on real use case i cannot reproduce) but i found error 2,000 - 11,000
  3. found error on console

Expected behavior

No response

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

3.1.0

Subscription type

Open source

Hosting type

Self-hosted

SDK information (language and version)

No response

When does the "update" event actually get triggered

I've been experimenting with the library and I'm not quite clear on when the "update" event actually get triggered. I have registered a listener for the "update" event but it doesn't seem to get triggered.

I was expecting it to get triggered on the next poll, whenever one (or more) of the toggles was changed since the last poll, is that how it should work? To tests it I was simply turning a toggle on and off but the event is not getting triggered.

Empty 'If-None-Match' header is send causing some firewalls to block the request

Describe the bug

Requests are made with headers always containing the If-None-Match header, even if there is no value.

if no etag is set, it defaults to '' and thus the If-None-Match header is set with an empty value.

Some firewalls, like "F5" are blocking requests with empty headers by default.

Steps to reproduce the bug

Don't set an etag

Expected behavior

No response

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

No response

Subscription type

None

Hosting type

None

SDK information (language and version)

No response

Uncaught error bubbles up to the app

If the proxy is unreachable when it's offline or blocked by the client, for example, this fetch will throw a runtime error that reaches the app.

await this.fetch(url, {
cache: 'no-cache',
method: 'POST',
headers: {
'Authorization': this.clientKey,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});

I suggest we implement a try/catch just like it's being done in index.ts.

if (this.fetch) {
try {
const context = this.context;
const urlWithQuery = new URL(this.url.toString());
// Add context information to url search params. If the properties
// object is included in the context, flatten it into the search params
// e.g. /?...&property.param1=param1Value&property.param2=param2Value
Object.entries(context).forEach(([contextKey, contextValue]) => {
if (contextKey === 'properties' && contextValue) {
Object.entries<string>(contextValue).forEach(([propertyKey, propertyValue]) =>
urlWithQuery.searchParams.append(`properties[${propertyKey}]`, propertyValue)
);
} else {
urlWithQuery.searchParams.append(contextKey, contextValue);
}
});
const response = await this.fetch(urlWithQuery.toString(), {
cache: 'no-cache',
headers: {
'Authorization': this.clientKey,
'Accept': 'application/json',
'Content-Type': 'application/json',
'If-None-Match': this.etag,
},
});
if (response.ok && response.status !== 304) {
this.etag = response.headers.get('ETag') || '';
const data = await response.json();
await this.storeToggles(data.toggles);
}
} catch (e) {
// tslint:disable-next-line
console.error('Unleash: unable to fetch feature toggles', e);
this.emit(EVENTS.ERROR, e);
}
}

As a side-note, on Next.js apps, this is very bothersome during development because it shows a modal displaying uncaught runtime errors.
image

`customHeaders` aren't included in metrics requests

Describe the bug

According to this slack thread and @kwasniew's digging:

Custom headers aren't included in metrics requests.

Steps to reproduce the bug

  1. Initialize the proxy client with custom headers
  2. Check the network tab and watch for metrics requests
  3. Confirm that the metrics requests don't contain the custom headers

Expected behavior

When I provide custom headers to the client, I'd expect it to use these headers for all communication with the upstream.

This is also what the readme suggests:

Additional headers to use when making HTTP requests to the Unleash proxy. In case of name collisions with the default headers, the customHeaders value will be used.

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

No response

Subscription type

None

Hosting type

None

SDK information (language and version)

2.4.2

Create a `useFlags` hook that enumerates all flags

Describe the feature request

We would like to report in the UI a dynamic list of all flags used
Currently we'd need to do something along the lines of

Background

const testEnabled= useFlag('rmm-test')
const test2Enabled= useFlag('rmm-test2)

const test3Enabled= useFlag('rmm-test3)

And im not sure its possibly to iterate through an array and dynamically make hooks.

Solution suggestions

whereas ideally it'd be good to return an array or record of key value pairs

const allFlags = useFlags() // {rmm-test1:true,...}

It appears

 const client = useUnleashClient()
  const a = client.getAllToggles()

does something similar so perhaps the hook can just call getAllToggles

Expand error reporting functionality for Http errors

Describe the feature request

I'd like the unleash-proxy client to emit error events on http errors in the 400-500 range.

Background

Currently, unleash-proxy-client depends on the fetch implementation to emit error events, and we've now had a couple of cases where users expected to be able to react errors from the client also for http errors in the 4-500 range.

Solution suggestions

On refreshing the toggles, the proxy client should check the status of the response, if it is in the 4-500 range it should emit an Error event allowing consumers of the unleash events to further react to what went wrong

Further improvement should also probably take into consideration that a 401 on initialization should lead to stopping poll.

instanceId in metrics

Describe the feature request

Hi, the instanceId currently is always set to browser.

What is the plan for this field? Can we have the context being stored there? So that usage metrics can be even more useful

Background

No response

Solution suggestions

How about store something meaningful in the instanceId e.g. user sessionID, or user email etc

How do I use my custom strategy?

Hello,

I spent a good part of yesterday pulling my hair because my test app wouldn't get flags enabled for my custom strategy (fy-workspace in the demo sandbox). Then I saw somewhere that for custom strategies the flags would always be false unless the client registers what custom strategies it wants to use. Since I don't quite know what I am doing I tried a lot of silly things. Here's two:

  • I tried to just provide a fy-workspace as a string to the strategies entry when creating the client. (I thought wth, worth a try ๐Ÿคท ).
  • I tried to import Strategy from the unleash-proxy-client and subclass it and such. (I didn't quite realize I was using the service via a proxy, because reasons)

Anyway, finally I realized I was using a proxy and that I need to run my own proxy in order to test my custom strategy. That's where I am now and I fail to get the proxy to start using my custom strategy. I am using the docker image and starting it like so:

docker run \
   -e UNLEASH_PROXY_SECRETS=some-secret \
   -e UNLEASH_URL=https://app.unleash-hosted.com/demo/api/ \
   -e UNLEASH_API_TOKEN=56907a2fa53c1d16101d509a10b78e36190b0f918d9f122d \
   -e UNLEASH_CUSTOM_STRATEGIES_FILE=/config/toggle-strategies.js \
   --mount type=bind,source="$(pwd)"/config,target=/config \
   -p 3333:3000 \
   unleashorg/unleash-proxy

And in my local directory in ./config/toggle-strategies.js I have:

const unleash = require('unleash-client');

class WorkspaceStrategy extends unleash.Strategy {
  constructor() {
    super('fy-workspace');
  }

  isEnabled(parameters, context) {
    return parameters['fy-workspace-id'].includes(context['fy-workspace-id']);
  }
}

module.exports = [new WorkspaceStrategy()];

I then get:

internal/modules/cjs/loader.js:892
  throw err;
  ^

Error: Cannot find module 'unleash-client'
Require stack:
- /config/toggle-strategies.js
- /unleash-proxy/dist/config.js

Side note: I first tried with ES6 syntax.

I think I am doing a lot of things wrong here, but I am out of wits.

Sorry if I am posting this in the wrong repo, it wasn't clear cut to me where it would go best. ๐Ÿ˜„

Remove react native dependency

The @react-native-async-storage/async-storage is big, and it's only used in the file https://github.com/Unleash/unleash-proxy-client-js/blob/v1.11.0/src/storage-provider-local.ts.

When installing the unleash proxy client in my package.json, it installs 695 packages and add 180MB to my node_modules folder, mainly because of the react dependencies. It seems to be way too big to only get and set values in the localStorage. Moreover, we don't even use it and use a custom storage implementation.

Would it be possible to remove the react native dependency and use another lighter library when using the localStorage ? Or maybe creating another package to provide the default storage implementations (unleash-proxy-client-js-local-store, etc) ?

Add support to provide namePrefix and tags for filtering

Describe the feature request

Thank you for your work Unleash team. Is it possible to port the functionality from Unleash/unleash-client-node#237 into this SDK too?

Background

I have a codebase with multiple projects that sometimes share feature toggles and sometimes not. I'd like to be able to use tags in the Unleash Admin to designate which project uses which toggles and then in each project fetch only those toggles that are relevant to it.

Solution suggestions

No response

`useFlag` breaks server side

Describe the bug

Our app is both SSR and client side rendered and as such we need to use both @unleash/proxy-client-react, as well as unleash-client unleash-proxy-client

TypeError: Cannot read properties of null (reading 'isEnabled')

To get this working we need to do something along these lines

import { useReactiveVar } from '@apollo/client'
import { useFlag } from '@unleash/proxy-client-react'
import { variables } from 'core/variables'
import { UnleashFlagKey } from 'types/UnleashFlagKey'

const useUnleashFlag = (flag: UnleashFlagKey) => {
  const serverFlag = useReactiveVar(variables.unleashFeatures)[flag]
  const clientFlag = useFlag(flag)
  return serverFlag || clientFlag
}

export default useUnleashFlag

However on initial server render it throws which would then mean we'd need to change this implement to have conditional hooks.

It would be prefable if it would return undefined rather than throwing

Steps to reproduce the bug

No response

Expected behavior

No response

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

No response

Subscription type

No response

Hosting type

No response

SDK information (language and version)

No response

feat: allow to define fallback functions

I am using Feature Flags in Gitlab and I like to keep the amount of feature flags as a minimum.
Therefore we configure feature flags only for production environment and like to change the default behavior for undefined toggles. So they are activated on other environments by default.

In the unleash-client you are allowed to pass a fallback function.

I'd like to have this sort of functionality for the proxy client too.

feat: generate a random sessionId when not provided.

Today the SDK does not set a default sessionId. The problem of not doing this is that we get a poor experience for "anonymous" users because evaluation happens on the server side. This could lead to a flickering behavior of feature flag state.

Today the user of this SDK can already generate their own sessionId and inject that as part of the unleashContext, but we see a lot of users that do not do this.

The purposed change is that this SDK should generate a random sessionId, if not provided by the user. This will allow the SDK to provide a more consistent behavior in a combination with a gradual rollout and anonymous users.

Missing `currentTime` in Unleash Context object

Describe the bug

The currentTime property https://docs.getunleash.io/reference/unleash-context seems to be missing.

https://github.com/Unleash/unleash-proxy-client-js/blob/main/src/index.ts#L9

Cheers!

Steps to reproduce the bug

Try to set the currentTime property on the context, it would result in noop.

Expected behavior

Take the currentTime property into account

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

3.2.0

Subscription type

Pro

Hosting type

Hosted by Unleash

SDK information (language and version)

"@unleash/proxy-client-react": "^4.1.1"

Support relative urls

Describe the feature request

Proxy client JS should support relative URLS.

For example, /features/proxy should be a valid URL, and it should rely on the browser behaviour to automatically proxy that through the current origin.

Background

No response

Solution suggestions

No response

Metrics implementation adds a trailing slash to app url

Describe the bug

On the metrics class constructor the url is being parsed to a URL if its not an instance of the URL JS class. If you instantiate a url without the base param it will append a slash (/) to the end of the URI. The problem with this is that on the sendMetrics method of the Metrics class you append a slash (/) followed by /client/metrics to the end of the initialized url const url = `${this.url}/client/metrics`;

Steps to reproduce the bug

Start the unleash client with an app url as a string:

export const config: IFlagProvider['config'] = {
url: 'https://my-unleash-endpoint',
clientKey: UNLEASH_CLIENT_KEY,
appName: UNLEASH_APP_NAME,
refreshInterval: Number(UNLEASH_REFRESH_INTERVAL),
metricsInterval: Number(UNLEASH_METRICS_INTERVAL),
storageProvider: {
save: (name: string, data: DataUnleash | unknown) =>
AsyncStorage.setItem(name, JSON.stringify(data)),
get: async (name: string) => {
const data = await AsyncStorage.getItem(name);
return data ? JSON.parse(data) : undefined;
},
},
};

And check the interaction with the metrics url. You should see this url: https://my-unleash-endpoint//client/metrics

Expected behavior

Metrics url should have a valid URL format when adding /client/metrics.

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

^2.5.0

Subscription type

None

Hosting type

None

SDK information (language and version)

No response

Use HTTP status codes from server to backoff when needed

Describe the feature request

In order to reduce load on servers, as well as save our users some bandwidth, I'd like for this SDK to react to http status codes, and not just keep requesting at the same frequency if the server is telling it to chill out.

Background

Part of a cross-SDK initiative to make all our SDKs respect http statuses in order to save ourselves and our users for bandwidth/cpu usage that adds no value to either the server or the client.

Solution suggestions

Unleash/unleash-client-node#537 follows the correct pattern. Use 404, 429 or 50x statuses to reduce polling frequency. On 401 or 403 log and don't keep polling - your user probably needs to update their key before there's any point in continuing to hit the server.

Add the ability to insert custom headers into the request

Need to ability to pass additional custom headers currently the behavior allows for changing the name of the Authorization header, but not to include additional headers.
new UnleashClient({
url: 'http://host/proxy',
clientKey: 'xxxxxxx',
environment: 'default',
customHeaders: {
'AccessId': 'xxxxxxxxxxxxx',
'AccessSecret: 'xxxxxxxxxxxx'
}
});

headers: {
[this.headerName]: this.clientKey,
Accept: 'application/json',
'Content-Type': 'application/json',
'If-None-Match': this.etag,
},

NPM error in react example.

npm build
echo PORT=3001 > .env
npm start
curl http://localhost:3001
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
    at validateString (internal/validators.js:120:11)
    at Object.join (path.js:1039:7)
    at noopServiceWorkerMiddleware (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/react-dev-utils/noopServiceWorkerMiddleware.js:14:26)
    at Layer.handle [as handle_request] (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:317:13)
    at /home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:335:12)
    at next (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:275:10)
    at launchEditorMiddleware (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/react-dev-utils/errorOverlayMiddleware.js:20:7)
    at Layer.handle [as handle_request] (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:317:13)
    at /home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:284:7
    at Function.process_params (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:335:12)
    at next (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/index.js:275:10)
    at handleWebpackInternalMiddleware (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/react-dev-utils/evalSourceMapMiddleware.js:42:7)
    at Layer.handle [as handle_request] (/home/robertc/src/unleash-proxy-client-js/examples/react-app/node_modules/express/lib/router/layer.js:95:5)

Not sure why this is happening - perhaps some bitrot with time?

feat: setContextField() method

Today a user of the SDK will need to provide the full context to update the Unleash Context being used. It would be useful if the SDK could provide a setContextField(name, value) method which will only replace the specified field on the context, and preserve the rest.

Add esm bundle

Describe the feature request

Currently the npm package doesn't provide an esm version of the bundle. For example the angular build process warns you about possible issues with the older formats.

Background

No response

Solution suggestions

Add an esm version.

CommonJS imports are broken in v3.0.0

Describe the bug

My server suddenly stopped working during startup as a result of attempting to import unleash-proxy-client v3.0.0. I believe this is because unleash-proxy-client package defines "type": "module" in its package.json which is not something you should do in NPM packages (this field is for applications to define, not libraries, more info here).

Steps to reproduce the bug

Create a new Node project, attempt to import unleash-proxy-client using require, and start the application. Notice the error, ex:

const unleash_proxy_client = require("unleash-proxy-client");

Expected behavior

Using CommonJS to import the package should not result in a runtime error.

Logs, error output, etc.

Error [ERR_REQUIRE_ESM]: require() of ES Module /home/samuel/WSL_Dev/Personal/dokku_app/node_modules/.pnpm/[email protected]/node_modules/unleash-proxy-client/build/cjs/index.js from /home/samuel/WSL_Dev/Personal/dokku_app/src/server/dist/shared/modules/stores/feature_flags_store.js not supported.
index.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /home/samuel/WSL_Dev/Personal/dokku_app/node_modules/.pnpm/[email protected]/node_modules/unleash-proxy-client/package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

    at Object.<anonymous> (/home/samuel/WSL_Dev/Personal/dokku_app/src/server/dist/shared/modules/stores/feature_flags_store.js:4:32)
    at Object.<anonymous> (/home/samuel/WSL_Dev/Personal/dokku_app/src/server/dist/server/src/boot_server.js:15:31)
    at /home/samuel/WSL_Dev/Personal/dokku_app/src/server/dist/server/src/main.js:29:43 {
  code: 'ERR_REQUIRE_ESM'
}

Screenshots

No response

Additional context

No response

Unleash version

3.0.0

Subscription type

None

Hosting type

None

SDK information (language and version)

No response

Document `fetchInterval` client setting

These facts are a bit hard to find in the docs/readme:

  • The client uses 30 seconds as default fetch interval
  • This default can be changed when creating the client using fetchInterval:

Align the refreshInterval to either milliseconds or seconds across all libraries/SDKs

Describe the feature request

When I was configuring my unleash proxies and SDKs, I noticed that in some places, the docs and code show that refreshInteval is in seconds, but in other SDKs it's in milliseconds.

This causes some confusion when there is unleash-proxy-client-js, unleash-proxy-node, unleash-proxy-react and using these client side or server side, it could easily be misconfigured.

Discussing on the slack group, it was asked that I raise the issue here so that it can be addressed, perhaps in the next major version.

Thanks for an amazing product and for releasing it to the open source community.

Background

Original discussion: https://unleash-community.slack.com/archives/CGN5JT69F/p1652975702243299

Solution suggestions

In the next major version, create a new variable name and make it milliseconds, in case anyone wants some some quick updates to happen.

CDN client fails for feature toggle with conditional activation strategy

Describe the bug

CDN client fails when trying obtain feature flag value with conditional activation strategy.
Simple standard activation strategies do not have problems.

Steps to reproduce the bug

  1. Set up unleash docker + unleash proxy as per documentation
  2. Create feature toggle with condition. In my case:

image

3. Create a simple html page with

<script src="https://unpkg.com/unleash-proxy-client@latest/build/main.min.js" type="text/javascript"></script>

and js code:

var config = {
      url: "http://localhost:3000/proxy",
      clientKey: "proxy-client-key",
      appName: "test"
  };
    
vat client = new unleash.UnleashClient(config);
client.updateContext({ hostName: "test.co" });
client.start();

function logFeatureFlag() {
    isTenantEnabled = client.isEnabled("per-tenant-feature");

    console.log({ isTenantEnabled: isTenantEnabled });
};

setInterval(logFeatureFlag, 5000);

Expected behavior

Feature Flag value obtained successfully

Logs, error output, etc.

main.min.js:1 Uncaught TypeError: q.randomFillSync is not a function
    at g (main.min.js:1:2075)
    at j (main.min.js:1:2508)
    at a.generateEventId (main.min.js:1:6730)
    at a.createBaseEvent (main.min.js:1:6944)
    at a.createImpressionEvent (main.min.js:1:6802)
    at f.isEnabled (main.min.js:1:9174)
    at logFeatureFlag (app.js? [sm]:328:1)

Screenshots

image

Additional context

No response

Unleash version

latest

Subscription type

Open source

Hosting type

Self-hosted

SDK information (language and version)

latest, javascript

updateContext and setContextField don't call fetchToggles until first fetchToggles completes

Describe the bug

After changing the context via updateContext, a new request for toggles doesn't trigger immediately. This looks like it's because the first fetchToggles and/or ready promise hasn't returned in start yet and timerRef isn't set.

Additionally, this means .start can be called multiple times until any chain of promises resolve.

Steps to reproduce the bug

  • Start a client.
  • Update the context after.
  • Observe no request for toggles with the latest context.

Expected behavior

When calling updateContext, a new request for fetchToggles should occur immediately if start has already been called.

Logs, error output, etc.

No response

Screenshots

No response

Additional context

Using @unleash/proxy-client-react and we are changing the context in a child component.

Unleash version

4.17.2

Subscription type

Pro

Hosting type

Hosted by Unleash

SDK information (language and version)

No response

Missing `IVariant` property `enabled`?

When i look a the unleash documentation and other client libraries of unleash, i think that this library is missing a property in the IVariant interface:

// current
interface IVariant {
    name: string;
    payload?: {
        type: string;
        value: string;
    };
}

// my understanding
interface IVariant {
    name: string;
    enabled: boolen;
    payload?: {
        type: string;
        value: string;
    };
}

The proxy-client-react is actually using this property here. That's why i'm wondering whether this is missing in this library?

Even when i dump a variant to the browsers console there is a enabled property in the returned object:
grafik

`undefined` userId considered in context

Describe the bug

In our React app, we had been updating the Unleash context's userId field in the following way:

useEffect(() => {
  updateContext({userId});
}, [userId])

until we noticed that there was an issue with the variant splitting for one of our feature flags - a flag with control and challenger variants, each with 50% weight.

Initially, the userId is undefined until an account is created, and seems like the undefined value is sent to the context as a string because the splitting issue is just for a single flag used before an account is created.

Steps to reproduce the bug

  1. Create a feature flag with 2 variants
  2. Assign 50% weight to each variant
  3. set undefined to the userId context field
  4. monitor how many times both variants have been assigned

Expected behavior

When a userId is undefined in the context, a sessionId should be used in order to distinguish the different users calling the proxy/server.

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

unleashorg/unleash-proxy:v0.11.7

Subscription type

Pro

Hosting type

Hosted by Unleash

SDK information (language and version)

@unleash/proxy-client-react v3.5.1, unleash-proxy-client v2.4.2

How do I init using instanceId?

The other SDKs (Node, Go, etc) all seem to have a way to initialize using instance ID. Is this possible here as well? Trying out the feature flag implementation of Gitlab but I'm having trouble setting it up

updateContext overrides the generated session id.

Describe the bug

Today updateContext wlll override the generated session id, which is unexpected.

Steps to reproduce the bug

No response

Expected behavior

updateContext should merge the existing sessionId and only override it if it is specified by the user.

Logs, error output, etc.

No response

Screenshots

No response

Additional context

No response

Unleash version

No response

Subscription type

No response

Hosting type

No response

SDK information (language and version)

No response

Duplicate Proxy Calls

Describe the bug

Proxy is making 2 calls every time you can see my network log here.

image

Steps to reproduce the bug

Just run React app with proxy and watch the F12 network traffic.

Expected behavior

Only 1 call every 15 seconds

Logs, error output, etc.

No response

Screenshots

No response

Additional context

Config:

const unleashInstance: IConfig = {
	url: process.env.REACT_APP_UNLEASH_SERVER!,
	clientKey: process.env.REACT_APP_UNLEASH_KEY!,
	refreshInterval: 15,
	appName: 'clm',
	environment: process.env.REACT_APP_UNLEASH_ENV!,
	disableMetrics: true
};

Unleash version

Latest

Subscription type

Open source

Hosting type

Self-hosted

SDK information (language and version)

"@unleash/proxy-client-react": "^3.3.0",

feat: bootstrap

Make it possible to bootstrap the SDK with feature toggle configuration.

Should have same format as returned from proxy:

[
  {
	"enabled": true,
	"name": "demoApp.step4",
	"variant": {
		"enabled": true,
		"name": "blue"
	}
  }
]

This is useful for multiple use-cases:

  • have sane defaults first time a user visit a webpage (empty localstorage)
  • easier local development and testing without dependency on unleash-proxy
  • serverside pre-render with sane defaults.

Silent errors for offline PWA

Describe the feature request

I am trying to get rid of the console error Unleash: unable to fetch feature toggles TypeError: Failed to fetch when our PWA is offline. We are using the React Client, is there a way to remove the errors while offline?

Background

https://unleash-community.slack.com/archives/C03GWTN7XMG/p1702460982222809

Solution suggestions

I think we could introduce "silent" mode or log verbosity. Here is the line responsible for console errors, but console.error is in use also in other places:

console.error('Unleash: unable to fetch feature toggles', e);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.