Code Monkey home page Code Monkey logo

js-sdk-common's Introduction

LaunchDarkly Javascript SDK Core Components

Actions Status

LaunchDarkly overview

LaunchDarkly is a feature management platform that serves trillions of feature flags daily to help teams build better software, faster. Get started using LaunchDarkly today!

Twitter Follow

Overview

This project provides core implementation components for all of the LaunchDarkly client-side SDKs that use JavaScript: the JS (browser) SDK, the React SDK, the client-side Node SDK, and the Electron SDK. Application code should never refer to the launchdarkly-js-sdk-common package directly.

The initialize function in index.js creates the basic client object that all of those SDKs are built upon. The SDK's own initialize function calls this function, providing a "platform" object that defines additional capabilities specific to that SDK, and then optionally decorates the client object with any other public methods or properties it should have. Inasmuch as possible, the SDK code contains only what is necessary to distinguish it from the other JavaScript-based SDKs. For instance, this project contains no browser-specific code; that is all in js-client-sdk.

It also provides TypeScript definitions in index.d.ts which are re-exported or extended by the SDKs, so the Typedoc documentation for the SDKs includes them.

Contributing

We encourage pull requests and other contributions from the community. Check out our contributing guidelines for instructions on how to contribute to this project.

About LaunchDarkly

  • LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
    • Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
    • Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
    • Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
    • Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan). Disable parts of your application to facilitate maintenance, without taking everything offline.
  • LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Read our documentation for a complete list.
  • Explore LaunchDarkly

js-sdk-common's People

Contributors

bdwain avatar bwoskow-ld avatar dependabot[bot] avatar edvinerikson avatar eli-darkly avatar ember-stevens avatar github-actions[bot] avatar kinyoklion avatar kparkinson-ld avatar launchdarklyci avatar launchdarklyreleasebot avatar ld-repository-standards[bot] avatar mateuszsikora avatar mikedidomizio avatar

Stargazers

 avatar  avatar  avatar

Watchers

 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

js-sdk-common's Issues

consider correcting typing of `LDOptions`'s `bootstrap`

Is this a support request?
No

Describe the bug
The actual bug and request for a change is a lil cryptic, but let me state it and then explain the details

Would it be possible to get a more accurate type for bootstrap in LDOptions to help guide folks to correctly interpret the output of the Python SDK (and I imagine other server-side SDK's) FeatureFlagsState?

LDOption's bootstrap has the type LDFlagSet. The docs for the Python SDK's FeatureFlagsState says its JSON format is suitable for passing into bootstrap. However, the types of bootstrap and FeatureFlagsState are actually incompatible in a way that effects customers trying to allow for local development. The LDFlagSet is type as, essentially, a keyed object, while FeatureFlagsState's JSON is an Array at its top with (possibly) multiple items in it.

They happen to work okay because the JS code here wriggles around the problem because LDFlagSet's type is loose enough to allow it.

However, when you go to solve problems like those raised by needing to write our camelCase'ing flag names to create fake LDClients to allow engineers to do local development without needing to share a single LaunchDarkly environment and project, you hit the problem.

We use an HTTP endpoint in our server in local dev that uses the PythonSDK's all_flags_state (that returns a FeatureFlagsState) to bootstrap from a local flag data file without needing to contact LaunchDarkly. That's fetch on loading of the React app to fill in the faked out Provider and Client we have. But when you try to create the LDFlagKeyMap type and others needed by those Providers, you hit the problem that the FeatureFlagsState's JSON format is actually an Array with an undefined accounting for each of the possible number of items in it.

I'm assuming that the first item is a LDFlagSet (we make the simplifying assumption that all the items in the array, but that's likely not true), but I'm not sure if that's true or not.

To reproduce
Try to use a Python (or similar server-side client's) all_flags_state data to create your own no-request-making Client (and react Provider) by handling camelCasing and all that.

When bootstrapping a non valid object, it still resolves immediately

Describe the bug
When bootstrapping but the server is offline a $valid: false property is set:
https://github.com/launchdarkly/js-sdk-common/blob/master/src/index.js#L105

And you warn about that. But despite it not being valid and being an empty object, you do not wait for speaking to launch darkly and waitForInitialization resolves early, leaving an empty feature flag set.

Expected behavior
I expect if the bootstrap is not valid to behave as if it is not set.

We workaround this by checking $valid ourselves - but I don't think we should have to.

SDK version
The client api - 2.17.0

Library does not catch errors when changing localstorage

Describe the bug
If the browser's localstorage is at capacity (e.g. in chrome localstorage is capped at 5MB), then attempting to set an item in it will fail with a QuotaExceededError. Some parts of the LaunchDarkly SDK catch and disregard this error:

js-sdk-common/src/index.js

Lines 507 to 509 in 6315c36

if (useLocalStorage && store) {
return store.saveFlags(flags).catch(() => null); // disregard errors
} else {

But the diagnostics section does not catch the error:

function saveProperties() {
if (platform.localStorage) {
const props = { ...acc.getProps() };
platform.localStorage.set(localStorageKey, JSON.stringify(props), () => {});
}
}

Which causes the browser to report an unhandled promise rejection. We noticed this because our frontend error tracking tool is filling up with unhandled promise rejection errors from the LaunchDarkly SDK.

Expected behavior
The diagnostics code should call .catch() on the promise, and disregard (or handle) any errors encountered while storing this information.

LDFlagValue TypeScript type should be `unknown`

Describe the bug
The LDFlagValue type is currently any, but shouldn't it be unknown?

-export type LDFlagValue = any;
+export type LDFlagValue = unknown;

Feel free to ignore if you disagree.

To reproduce N/A
Expected behavior N/A
Logs N/A
SDK version 2.13.0
Language version, developer tools TypeScript
OS/platform N/A
Additional context N/A

Mistake in type docs about omit user key

js-sdk-common/typings.d.ts

Lines 266 to 280 in 05a82f9

/**
* A LaunchDarkly user object.
*/
export interface LDUser {
/**
* A unique string identifying a user.
*
* If you omit this property, and also set `anonymous` to `true`, the SDK will generate a UUID string
* and use that as the key; it will attempt to persist that value in local storage if possible so the
* next anonymous user will get the same key, but if local storage is unavailable then it will
* generate a new key each time you specify the user.
*
* It is an error to omit the `key` property if `anonymous` is not set.
*/
key?: string;

Hi, I found this line of description was not very clear. It says If you omit this property, and also set anonymous to true it should work, and It is an error to omit the key property if anonymous is not set..

However, if you omit the key or set the key to null or undefined, it would always return an error, no matter the anonymous is set or not.

I don't know what is the expected behavior here.

Expose a hook to surface when fresh flags have been fetched

Problem

Our organization bootstraps our client using localstorage, so we get the "successful initialization" event before the requestor finishes fetching new flags.

We have some business-cases where we need to identify if our flags have been read from localstorage, or are fresh from the LD servers, but we have no hooks to determine if/when this has occurred.

Proposal

finishInit returns a promise that is swallowed, but it seems we could somewhat easily emit an event when that promise resolves to indicate we have fetched the most recent flags.

This could ultimately be exposed through a waitForFreshData function to be compatible with the existing API.

`events.slice` is not a function

Describe the bug
Intermittently, I'm seeing this line throw a type error bc events is not an array, both in the browser and during jest testing.

To reproduce
I could try to post a minimal reproducible repo if it would help, but I think I see what's going wrong in the code itself:

  • chunkEventsForUrl is expecting events to be an array
  • it's called by EventSender.sendEvents here which is also expecting events to be an array.
  • However, that's called by sendDiagnosticEvent here, which is passing in only a single event to the whole flow, which then triggers the events.slice is not a function error. I believe the argument here should be [event] - it's not clear to me what's sending the diagnostic event based on the traceback.

My use case is a bit involved so it would take time to narrow down to a minimal reproducible example, especially since the error triggers non-deterministically. However, I believe the code fix here appears to be straightforward enough without one.

Expected behavior
Launch Darkly sdk to run without erroring

Logs
Screenshot 2023-10-10 at 11 57 55 AM

SDK version
└─┬ [email protected]
└─┬ [email protected]
└── [email protected]

Language version, developer tools
npm -v
8.19.3
node -v
v16.19.0

OS/platform
Mac M1 Apple Silicon

Additional context
N/A

ldClient top-level hash value not updated by ldClient.identify()

Describe the bug

Calls to the ldClient.identify() method do not update the ldClient.initialize()-scoped hash variable value, nor the Stream()-scoped hash value.

This causes errors in event streaming after calling ldClient.identify() with a new hash value.

To reproduce

  1. Start with 2 users: user1 & user2.
  2. Call ldClient.initialize(CLIENT_ID, user1, { hash: user1_hash, streaming: true }. The event stream is successfully initialized.
  3. After initialization & readiness confirmed, call ldClient.identify(user2, user2_hash). The new user is successfully identified.
  4. Calls to https://clientstream.launchdarkly.com will begin to fail, however. They continue to use the original hash value of user1_hash from the initialize() call in their query string.

Under the hood

The Stream module is initialized in the ldClient.initialize() call with the initial options.hash value as a parameter. ldClient.identify() takes in a hash parameter, but it doesn't update the top-level variable value.

index.js:

export function initialize(env, user, specifiedOptions, platform, extraOptionDefs) {
    // ...
    const options = configuration.validate(specifiedOptions, emitter, extraOptionDefs, logger);
    const hash = options.hash;  // const hash cannot be modified
    // ...

    // Initializes Stream() with the initial `hash` value:
    const stream = Stream(platform, options, environment, diagnosticsAccumulator, hash);
    // ...

    // identify() takes `hash` as a parameter, but does not update the top-level variable
    function identify(user, hash, onDone) {
        // Does not update top-level `hash` value...
    }
    // ...
}

In Stream.js, the initial hash value is used in the openConnection() method, wherein it is appended to the query string:

// Takes a `hash` parameter that never changes
export default function Stream(platform, config, environment, diagnosticsAccumulator, hash) {
    // ...

    // openConnection() always uses the initial `hash` value
    function openConnection() {
        // ...
        query = 'h=' + hash;
        // ...
    }
}

Expected behaviour

The ldClient.initialize()-scoped hash value & the Stream()-scoped hash value need to be updated on a call to ldClient.identify().

An option under the hood

ldClient.identify() does call connectStream()

index.js:

// Current index.js:

  function identify(user, hash, onDone) {
    // ...
    return utils.wrapPromiseCallback(
      clearFirst
        // ...
        .then(flagValueMap => {
          if (streamActive) {
            connectStream();
          }
          // ...

which itself effects the Stream() module via a stream.connect() call –

index.js:

// Current index.js:

  function connectStream() {
    // ...
    stream.connect(ident.getUser(), {
      ping: function() {
      // ...

Given that stream.connect() updates the Stream-scoped user, perhaps it could also update the Stream-scoped hash.

Instead of (Stream.js):

// Current Stream.js:

export default function Stream(platform, config, environment, diagnosticsAccumulator, hash) {
  // ...
  let user = null;
  let handlers = null;

  stream.connect = function(newUser, newHandlers) {
    user = newUser;
    handlers = {};
     // ...
    tryConnect();
  };
  // ...
}

we could have:

// Potential Stream.js:

// EDIT: Remove `hash` parameter –
export default function Stream(platform, config, environment, diagnosticsAccumulator) {
  // ...
  let user = null;

  // EDIT: Introduce `hash` variable –
  let hash = null;

  let handlers = null;

  // EDIT: Add `newHash` parameter to .connect() –
  stream.connect = function(newUser, newHash, newHandlers) {
    user = newUser;

    // EDIT: Set `hash` value to `newHash` value
    hash = newHash;

    handlers = {};
     // ...
    tryConnect();
  };
  // ...
}

which would require going from, in index.js:

// Current index.js:

  function identify(user, hash, onDone) {
    // ...
    return utils.wrapPromiseCallback(
      clearFirst
        .then(() => userValidator.validateUser(user))
        .then(realUser =>
          requestor
            .fetchFlagSettings(realUser, hash)
            .then(requestedFlags => {
              const flagValueMap = utils.transformVersionedValuesToValues(requestedFlags);
              ident.setUser(realUser);
              // ...
            })
        )
        .then(flagValueMap => {
          if (streamActive) {
            connectStream();
          }
          // ...
  }
  // ...

  function connectStream() {
    streamActive = true;
    if (!ident.getUser()) {
      return;
    }
    stream.connect(ident.getUser(), {
      ping: function() {
      // ...
    }
  }

to:

// Potential index.js:

  // EDIT: Rename `hash` param to `newHash` –
  function identify(user, newHash, onDone) {
    // ...
    return utils.wrapPromiseCallback(
      clearFirst
        .then(() => userValidator.validateUser(user))
        .then(realUser =>
          requestor
            .fetchFlagSettings(realUser, hash)
            .then(requestedFlags => {
              const flagValueMap = utils.transformVersionedValuesToValues(requestedFlags);
              ident.setUser(realUser);

              // EDIT: Update hash value –
              hash = newHash;

              // ...
            })
        )
        .then(flagValueMap => {
          if (streamActive) {
            connectStream();
          }
          // ...
  }
  // ...

  function connectStream() {
    streamActive = true;
    if (!ident.getUser()) {
      return;
    }

    // EDIT: Pass `hash` to `stream.connect()` –
    stream.connect(ident.getUser(), hash, {
      ping: function() {
      // ...
    }
  }

SDK version

3.1.1

Package dependencies don't use semver & can cause double-bundling

Describe the bug
In package.json, the listed package dependencies use a fixed versions. This causes double-bundling of modules in our client.
See:

"uuid": "3.3.2"

Since launchdarkyl relies on [email protected] explicitly, any package/app that relies on a newer version (say ^3.3.3 or better) will have the uuid package bundled twice.
The same occurs for all the other dependencies - @babel/polyfill, base64-js, fast-deep-equal & uuid

To fix this, you can change all of your dependencies to use the semver caret (^).

To reproduce
Clone https://github.com/berickson1/Playground
cd launchDarklyExample
npm install

Expected behavior
node_modules should contain one (and only one) copy of the uuid package

Actual behavior
node_modules contains two copies of the uuid package:
node_modules/uuid
node_modules/launchdarkly-js-sdk-common/node_modules/uuid

SDK version
3.1.1

OS/platform
Platform agnostic

Additional context
Add any other context about the problem here.

uuid package needs upgrade to version 7+

Getting this warning when install launchdarkly/js-sdk-common because of an out-of-date uuid version

npm WARN deprecated [email protected]: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.

Re-bootstrap on identify

Is your feature request related to a problem? Please describe.

When calling identify and the (server-generated) all_flags_state for the new user is available, I'd like these flags to be made available immediately (through React hooks etc.), not wait for a server round-trip.

The same reason as why you support bootstrapping at client initialization, only later in the client lifecycle.

Currently the flag defaults are used until the round-trip to your server completes.

Describe the solution you'd like

A new (optional) argument flags to identify would be great.

Describe alternatives you've considered

I've tried hacking around this in various ways but couldn't find a solution.

Node circular dependency warning on 4.x branch

Describe the bug
Using node-client-sdk, running a file that imports LaunchDarkly displays circular dependency warnings related to 'messages' in the terminal.

To reproduce

  1. install [email protected] in a project
  2. run node on the following code snippet in a terminal (might need to add "type": "module" in package.json):
    import * as LaunchDarkly from 'launchdarkly-node-client-sdk';

Expected behavior
No warnings to display in the terminal

Logs

(node) Warning: Accessing non-existent property 'messages' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:748:11)
at Object.get (node:internal/modules/cjs/loader:764:5)
at Object. (/home/$USER/source/git_repo/hello-node-client/node_modules/launchdarkly-js-sdk-common/src/InspectorManager.js:1:9)
at Module._compile (node:internal/modules/cjs/loader:1155:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10)
at Module.load (node:internal/modules/cjs/loader:1033:32)
at Function.Module._load (node:internal/modules/cjs/loader:868:12)
at Module.require (node:internal/modules/cjs/loader:1057:19)
at require (node:internal/modules/cjs/helpers:103:18)
at Object. (/home/$USER/source/git_repo/hello-node-client/node_modules/launchdarkly-js-sdk-common/src/index.js:17:46)

(node) Warning: Accessing non-existent property 'messages' of module exports inside circular dependency
at emitCircularRequireWarning (node:internal/modules/cjs/loader:748:11)
at Object.get (node:internal/modules/cjs/loader:764:5)
at Object. (/home/$USER/source/git_repo/hello-node-client/node_modules/launchdarkly-js-sdk-common/src/SafeInspector.js:1:9)
at Module._compile (node:internal/modules/cjs/loader:1155:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10)
at Module.load (node:internal/modules/cjs/loader:1033:32)
at Function.Module._load (node:internal/modules/cjs/loader:868:12)
at Module.require (node:internal/modules/cjs/loader:1057:19)
at require (node:internal/modules/cjs/helpers:103:18)
at Object. (/home/$USER/source/git_repo/hello-node-client/node_modules/launchdarkly-js-sdk-common/src/InspectorManager.js:2:23)

SDK version
launchdarky-js-sdk-common: 4.3.2
launchdarkly-node-client-sdk: 2.2.1

Language version, developer tools
node: 16.18.1
npm: 8.19.2

OS/platform
Windows 10
Microsoft WSL2

Additional context

SyntaxError: Unexpected token H in JSON at position 4104

Describe the bug
We catch a lot (100s / day) of errors with the message as stated in the title:
SyntaxError: Unexpected token H in JSON at position 4104

The token and position is different every time.

The error comes from:
launchdarkly-js-sdk-common/dist/ldclient-common.es.js in Object.put at line 1:24974

and it always is a result of multiple retries on stream connection. I can tell that by seeing dozens of the following console errors preceding the JSON.parse error:

LD: [warn] Error on stream connection: {"isTrusted":true}, will continue retrying every 1000 milliseconds.

To reproduce
Unfortunately, I haven't been able to reproduce the problem locally yet. We receive the error in Sentry and I've provided all information I had.

Expected behavior
Maybe the JSON.parse should be handled in try/catch and proxied to the consumer in a more descriptive manner. Right now I can't tell if the error is a problem in LD itself or it's caused by a wrong flag value.

Logs

SDK version
We use launchdarkly-react-client-sdk version 2.22.3 which has the following dependency tree to the common SDK:

Language version, developer tools
React 16.9.0
Sentry SDK: sentry.javascript.react v6.12.0

OS/platform
Any browser

Network requests to fetch flags are not retried unless the client successfully retrieves flags from localStorage

Describe the bug
Using js-client-sdk, network requests to fetch flags are not retried unless the client successfully retrieves flags from localStorage.

To reproduce
The client retries to fetch flags when it succeeds to bootstrap from localStorage:

  1. Set bootstrap: "localStorage"
  2. Load the application to let the client to store flags in localStorage
  3. Configure to block requests to https://*.launchdarkly.com/* using request blocking of Chrome's request blocking
  4. Reload the application
  5. See that the requests to https://clientstream.launchdarkly.com/eval/* is repeated

Otherwise it does not retries the requests:

  1. Clear localStorage or unset bootstrap option
  2. Configure to block requests to https://*.launchdarkly.com/* using request blocking of Chrome's request blocking
  3. Reload the application
  4. See that the requests is not retried

Expected behavior
A client retries network requests even if bootstrap is not set or there is no flags in localStorage.

SDK version
js-sdk-common: 3.8.2
js-client-sdk: 2.24.2

Language version, developer tools
Chrome 106

OS/platform
macOS 12.4

Additional context
These lines of code seems to be related.

Store hash value not updated by ldClient.identify()

Describe the bug
Potentially an extension of #13. The Store instance is created with an initial hash argument but does not update the hash when ldClient.identify() is called. When launchdarkly-electron-client-sdk is being used, the electron-json-storage library frequently cannot handle the length of the hash generated from the LDUser provided by ident.getUser().

To reproduce

  1. Initialize launchdarkly-electron-client-sdk in the Electron main process, e.g. with user { key: '123', name: 'user', email: '[email protected]' }.
  2. Initialize the client in a renderer process.
  3. Update the identity of the user using identify(). Use an LDUser with a lot of custom props.
  4. When the client clears flags, an ENAMETOOLONG exception will be thrown if getFlagsKey creates a key that is too long from the JSON stringification of the user.

Expected behavior

  1. It is possible to supply a hash in the options provided to LDElectron.initializeInMain (this would be in https://github.com/launchdarkly/electron-client-sdk).
  2. When identify() is called, the hash argument provided updates the hash in the Store closure in case it wasn't provided when the Store was created (to avoid the ENAMETOOLONG error on subsequent calls to identify()).

Logs
Log from our app in which abTestingService acts as logger for the SDK and adds some extra logs.
log.log

SDK version
launchdarkly-electron-client-sdk 1.6.1
launchdarkly-eventsource 1.4.0
launchdarkly-js-client-sdk 2.19.1
launchdarkly-js-sdk-common 3.3.1

Language version, developer tools
Node.js v12.18.3
NPM 7.6.0
TypeScript 3.9.9
Webpack 4.46.0

OS/platform
macOS Big Sur 11.3.1
Electron 11.4.4

Additional context
Let me know if I can provide any more context!

Use `unknown` instead of `any`

Is your feature request related to a problem? Please describe.

Now the SDK has been converted to TypeScript, it should not use any anymore.

Use of any causes problems for library consumers that use TypeScript in strict mode, as well as errors from the recommended set of TypeScript ESLint rules like https://typescript-eslint.io/rules/no-explicit-any/.

any is an escape hatch that disables type checking and allows you to access arbitrary properties, even ones that don’t exist.

Describe the solution you'd like

Use unknown instead of any. This would be a breaking change so would require a major version bump following SemVer.

Type-safe feature flags

Is your feature request related to a problem? Please describe.
We currently do this in our code:

launchdarkly-js-common-sdk.d.ts :

declare module "launchdarkly-js-sdk-common" {
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
  export interface LDFlagSet {
    someFlag: boolean;
    anotherFlag: boolean;
  }
}

Which allows us to use interface merging in order to get autocompletion on feature flags.

However, since it only extends the existing interface ([key: string]: LDFlagValue), it's still just as easy to accidentally use a nonexistent flag in the code as any key returned is any. This means that when deleting a feature flag, its relatively easy to accidentally leave behind parts of code that still reference this key, as the typing still resolves to any.

Describe the solution you'd like
I would like there to be a convenient way to get type safety for feature flags by defining an override somewhere in the code. The best case scenario for this would be for LDFlagSet to be an empty interface by default, however that's obviously a huge breaking change. If that were the case though, it would make it so that we can strongly type our feature flags by doing the above declaration merging.

Describe alternatives you've considered
We've tried doing more complex typescript type resolution by excluding the builtin types and using our own, but I was never able to get it to work properly. There may be a way, but there a number of outstanding typescript issues here as well that don't give me hope e.g. microsoft/TypeScript#36146

Adding missing type definitions "inExperiment"

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch [email protected] for the project I'm working on.

We're using the result of inExperiment from LDEvaluationReason so we're patching it locally as it's not in the type definitions
https://docs.launchdarkly.com/sdk/concepts/evaluation-reasons

Here is the diff that solved my problem:

diff --git a/node_modules/launchdarkly-js-sdk-common/typings.d.ts b/node_modules/launchdarkly-js-sdk-common/typings.d.ts
index a3bd0d5..5fbdabb 100644
--- a/node_modules/launchdarkly-js-sdk-common/typings.d.ts
+++ b/node_modules/launchdarkly-js-sdk-common/typings.d.ts
@@ -589,6 +589,13 @@ declare module 'launchdarkly-js-sdk-common' {
      * The key of the failed prerequisite flag, if the kind was `'PREREQUISITE_FAILED'`.
      */
     prerequisiteKey?: string;
+
+    /**
+     *  inExperiment is an optional attribute on the reason object that indicates whether the context was evaluated as part of an experiment:
+     *  If inExperiment is true, LaunchDarkly includes the event in experimentation analysis
+     *  If inExperiment is false, LaunchDarkly does not include this attribute in the reason object
+     */
+    inExperiment?: boolean;
   }
 
   /**

This issue body was partially generated by patch-package.

LDContext type is incorrect and errors when accessing `kind` property

Describe the bug
If you have a variable of type LDContext and try to check/access the kind property, TS will error out at you saying that Property 'kind' does not exist on type 'LDUser'.

To reproduce
Trivial, but something like this should trigger the TS error:

const c: LDContext;
const test = c.kind === 'user';

Expected behavior
Accessing kind should not throw a TS error

SDK version
"launchdarkly-react-native-client-sdk": "^7.1.5"

Language version, developer tools
React Native

OS/platform
MacOS

Additional context
LDUser should probably extend LDSingleKindContext

JSON.Parse unhandled errors happening for a small % of our clients

Is this a support request?
I will also raise it with support.

Describe the bug
Unhandled errors are happening on our live instances e.g.:

JSON.parse: expected ':' after property name in object at line 1 column 13248 of the JSON data

they are all JSON.parse errors.

When I look up the stack trace I get this:

        Source: webpack://@saxobank/node_modules/launchdarkly-js-sdk-common/dist/ldclient-common.es.js
        Name: parse
        Line: 0
        Column: 27596

and

Source: webpack://@saxobank/node_modules/launchdarkly-js-sdk-common/dist/ldclient-common.es.js
        Name: n
        Line: 0
        Column: 15981

We get these reports from our live web apps so the locations are minified.

We use version 3.4.0 - we use the launchdarkly-js-client-sdk 2.20.0 which depends on this.

To reproduce
Unknown, we get these reports from live

Expected behavior
No unhandled json parse errors.

SDK version
3.4.0

OS/platform
The user agent is:

Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0 Chrome/91.0.4472.114

Node process hangs before exiting

Describe the bug
In the event of no Internet connection, the js-sdk-common module seems to prevent process.exit() from completing for several seconds (up to ~15s in my own testing). This seems to be related to pending async calls (and removing this module from the NodeJS app I have been developing fixes this problem completely).

To reproduce
Run the following code in NodeJS while disconnected from the Internet:

import { initialize } from "launchdarkly-node-client-sdk";

async function repro() {
  let client;
  try {
    console.log("Initializing LD client ...");
    client = initialize("...key...", {
      anonymous: true,
    });
  } catch (error) {
    console.log("ERROR: LD client initialization failed");
  }

  try {
    console.log("Calling LD client.waitForInitialization() ...");

    client.waitForInitialization();
  } catch (error) {
    console.log("ERROR: client.waitForInitialization failed");
  }

  console.log("Calling process.exit()");
  process.exit(0);
}

repro();

Expected behavior
The process would be terminated immediately.

Logs
The app prints the following, and then hangs for several seconds before exiting:

Initializing LD client ...
Calling LD client.waitForInitialization() ...
Calling process.exit()

SDK version
The latest version as of the current time:

Language version, developer tools
Node v20.2

OS/platform
MacOS 14.3

Additional context
I believe the problematic setTimeout calls are in these two locations:

periodicTimer = setTimeout(sendPeriodicEvent, periodicInterval);

processor.start = function() {
const flushTick = () => {
processor.flush();
flushTimer = setTimeout(flushTick, flushInterval);
};
flushTimer = setTimeout(flushTick, flushInterval);
};

TypeErrors in diagnosticEvents

Describe the bug
In our frontend logs, there seem to be a large number of errors of the variety TypeError: o.push is not a function at Object.recordStreamInit, which seems to be originating in js-sdk-common/src/diagnosticEvents.js](https://github.com/launchdarkly/js-sdk-common/blob/bee90f5d5f4acfb64def652c57e9e5d45e05439b/src/diagnosticEvents.js#L53-59).

Describe the solution you'd like
A guard in place to prevent pushing data into non-array elements to reduce this sort of error possibility.

The track method is inconsistent with the TypeScript typings.d.ts file

Is this a support request?
No

Describe the bug

There are only two arguments in the .track method in the TypeScript file:

track(key: string, data?: any): void;

But three in the actual code:

function track(key, data, metricValue) {

To reproduce
When using the JS SDK, I can't compile the code using TypeScript because of Expected 1-2 arguments, but got 3. error.

Expected behavior
Should have three arguments

Logs
N/A

SDK version
"launchdarkly-react-client-sdk": "^2.18.0"

Language version, developer tools
Python, Node, React

OS/platform
Mac OS 10.15

Additional context
N/A

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.