@servicetitan/launchdarkly-service 36.1.1 → 36.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/datadog-rum/create-datadog-inspector.d.ts +6 -0
- package/dist/datadog-rum/create-datadog-inspector.d.ts.map +1 -0
- package/dist/datadog-rum/create-datadog-inspector.js +13 -0
- package/dist/datadog-rum/create-datadog-inspector.js.map +1 -0
- package/dist/datadog-rum/index.d.ts +2 -0
- package/dist/datadog-rum/index.d.ts.map +1 -0
- package/dist/datadog-rum/index.js +3 -0
- package/dist/datadog-rum/index.js.map +1 -0
- package/dist/ld-provider.d.ts.map +1 -1
- package/dist/ld-provider.js +14 -4
- package/dist/ld-provider.js.map +1 -1
- package/dist/ld-service-provider.d.ts +2 -0
- package/dist/ld-service-provider.d.ts.map +1 -1
- package/dist/ld-service-provider.js +7 -1
- package/dist/ld-service-provider.js.map +1 -1
- package/dist/tests/ld-provider.test.js +14 -1
- package/dist/tests/ld-provider.test.js.map +1 -1
- package/dist/tests/ld-service-provider.test.js +96 -0
- package/dist/tests/ld-service-provider.test.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +23 -6
- package/src/datadog-rum/__tests__/create-datadog-inspector.test.ts +46 -0
- package/src/datadog-rum/create-datadog-inspector.ts +18 -0
- package/src/datadog-rum/index.ts +1 -0
- package/src/ld-provider.tsx +14 -5
- package/src/ld-service-provider.tsx +9 -1
- package/src/tests/ld-provider.test.tsx +17 -1
- package/src/tests/ld-service-provider.test.tsx +113 -0
- package/src/types.ts +1 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
|
|
2
|
+
export interface CreateDatadogInspectorOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function createDatadogInspector(options?: CreateDatadogInspectorOptions): LDInspectionFlagUsedHandler;
|
|
6
|
+
//# sourceMappingURL=create-datadog-inspector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-datadog-inspector.d.ts","sourceRoot":"","sources":["../../src/datadog-rum/create-datadog-inspector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAE9E,MAAM,WAAW,6BAA6B;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,sBAAsB,CAClC,OAAO,GAAE,6BAAkC,GAC5C,2BAA2B,CAQ7B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { datadogRum } from '@datadog/browser-rum';
|
|
2
|
+
export function createDatadogInspector(options = {}) {
|
|
3
|
+
var _options_name;
|
|
4
|
+
return {
|
|
5
|
+
type: 'flag-used',
|
|
6
|
+
name: (_options_name = options.name) !== null && _options_name !== void 0 ? _options_name : 'datadog-feature-flag-tracking',
|
|
7
|
+
method: (key, detail)=>{
|
|
8
|
+
datadogRum.addFeatureFlagEvaluation(key, detail.value);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=create-datadog-inspector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/datadog-rum/create-datadog-inspector.ts"],"sourcesContent":["import { datadogRum } from '@datadog/browser-rum';\nimport type { LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';\n\nexport interface CreateDatadogInspectorOptions {\n name?: string;\n}\n\nexport function createDatadogInspector(\n options: CreateDatadogInspectorOptions = {}\n): LDInspectionFlagUsedHandler {\n return {\n type: 'flag-used',\n name: options.name ?? 'datadog-feature-flag-tracking',\n method: (key, detail) => {\n datadogRum.addFeatureFlagEvaluation(key, detail.value);\n },\n };\n}\n"],"names":["datadogRum","createDatadogInspector","options","type","name","method","key","detail","addFeatureFlagEvaluation","value"],"mappings":"AAAA,SAASA,UAAU,QAAQ,uBAAuB;AAOlD,OAAO,SAASC,uBACZC,UAAyC,CAAC,CAAC;QAIjCA;IAFV,OAAO;QACHC,MAAM;QACNC,IAAI,GAAEF,gBAAAA,QAAQE,IAAI,cAAZF,2BAAAA,gBAAgB;QACtBG,QAAQ,CAACC,KAAKC;YACVP,WAAWQ,wBAAwB,CAACF,KAAKC,OAAOE,KAAK;QACzD;IACJ;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/datadog-rum/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/datadog-rum/index.ts"],"sourcesContent":["export * from './create-datadog-inspector';\n"],"names":[],"mappings":"AAAA,cAAc,6BAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ld-provider.d.ts","sourceRoot":"","sources":["../src/ld-provider.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEH,cAAc,IAAI,kBAAkB,EACvC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAmC,MAAM,OAAO,CAAC;AAM/E,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,EAAE,cAAc,GAAG,SAAS,CAAC,GAAG;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;
|
|
1
|
+
{"version":3,"file":"ld-provider.d.ts","sourceRoot":"","sources":["../src/ld-provider.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEH,cAAc,IAAI,kBAAkB,EACvC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAmC,MAAM,OAAO,CAAC;AAM/E,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,EAAE,cAAc,GAAG,SAAS,CAAC,GAAG;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAaF,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAgD3D,CAAC"}
|
package/dist/ld-provider.js
CHANGED
|
@@ -7,6 +7,18 @@ import { FeatureFlagProvider } from './feature-flag-provider';
|
|
|
7
7
|
import { FeatureFlagStore } from './feature-flag-store';
|
|
8
8
|
import { getLDClient } from './get-ld-client';
|
|
9
9
|
import { LD_SERVICE_TOKEN } from './types';
|
|
10
|
+
function mergeOptions(ldService) {
|
|
11
|
+
const { application, options } = ldService !== null && ldService !== void 0 ? ldService : {};
|
|
12
|
+
if (!options && application === undefined) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
...options,
|
|
17
|
+
...application !== undefined ? {
|
|
18
|
+
application
|
|
19
|
+
} : {}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
10
22
|
export const LDProvider = provide({
|
|
11
23
|
singletons: [
|
|
12
24
|
FeatureFlagStore
|
|
@@ -19,9 +31,7 @@ export const LDProvider = provide({
|
|
|
19
31
|
useEffect(()=>{
|
|
20
32
|
(async function initializeClient() {
|
|
21
33
|
var _ref;
|
|
22
|
-
const options = (ldService
|
|
23
|
-
application: ldService.application
|
|
24
|
-
} : undefined;
|
|
34
|
+
const options = mergeOptions(ldService);
|
|
25
35
|
const config = {
|
|
26
36
|
clientSideID,
|
|
27
37
|
context,
|
|
@@ -36,7 +46,7 @@ export const LDProvider = provide({
|
|
|
36
46
|
category: 'LaunchDarkly',
|
|
37
47
|
code: 'InitializationFailed',
|
|
38
48
|
error,
|
|
39
|
-
message:
|
|
49
|
+
message: `${projectName} client failed to initialize`
|
|
40
50
|
});
|
|
41
51
|
}
|
|
42
52
|
}
|
package/dist/ld-provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ld-provider.tsx"],"sourcesContent":["import { Log } from '@servicetitan/log-service';\nimport { provide, useOptionalDependencies } from '@servicetitan/react-ioc';\nimport { LDClient } from 'launchdarkly-js-client-sdk';\nimport {\n LDProvider as BaseLDProvider,\n ProviderConfig as BaseProviderConfig,\n} from 'launchdarkly-react-client-sdk';\nimport { FC, PropsWithChildren, useEffect, useReducer, useState } from 'react';\nimport { FeatureFlagProvider } from './feature-flag-provider';\nimport { FeatureFlagStore } from './feature-flag-store';\nimport { getLDClient } from './get-ld-client';\nimport { LD_SERVICE_TOKEN } from './types';\n\nexport type ProviderConfig = Pick<BaseProviderConfig, 'clientSideID' | 'context'> & {\n projectName: string;\n waitForInitialization?: boolean;\n};\n\nexport const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({\n singletons: [FeatureFlagStore],\n})(props => {\n const { children, clientSideID, context, projectName, waitForInitialization = true } = props;\n const [ldClient, setLDClient] = useState<LDClient>();\n const [ldService, log] = useOptionalDependencies(LD_SERVICE_TOKEN, Log);\n const [counter, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n useEffect(() => {\n (async function initializeClient() {\n const options = ldService
|
|
1
|
+
{"version":3,"sources":["../src/ld-provider.tsx"],"sourcesContent":["import { Log } from '@servicetitan/log-service';\nimport { provide, useOptionalDependencies } from '@servicetitan/react-ioc';\nimport { LDClient } from 'launchdarkly-js-client-sdk';\nimport {\n LDProvider as BaseLDProvider,\n ProviderConfig as BaseProviderConfig,\n} from 'launchdarkly-react-client-sdk';\nimport { FC, PropsWithChildren, useEffect, useReducer, useState } from 'react';\nimport { FeatureFlagProvider } from './feature-flag-provider';\nimport { FeatureFlagStore } from './feature-flag-store';\nimport { getLDClient } from './get-ld-client';\nimport { LDService, LD_SERVICE_TOKEN } from './types';\n\nexport type ProviderConfig = Pick<BaseProviderConfig, 'clientSideID' | 'context'> & {\n projectName: string;\n waitForInitialization?: boolean;\n};\n\nfunction mergeOptions(ldService: LDService | undefined): BaseProviderConfig['options'] {\n const { application, options } = ldService ?? {};\n if (!options && application === undefined) {\n return undefined;\n }\n return {\n ...options,\n ...(application !== undefined ? { application } : {}),\n };\n}\n\nexport const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({\n singletons: [FeatureFlagStore],\n})(props => {\n const { children, clientSideID, context, projectName, waitForInitialization = true } = props;\n const [ldClient, setLDClient] = useState<LDClient>();\n const [ldService, log] = useOptionalDependencies(LD_SERVICE_TOKEN, Log);\n const [counter, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n useEffect(() => {\n (async function initializeClient() {\n const options = mergeOptions(ldService);\n const config: BaseProviderConfig = { clientSideID, context, options };\n const client = ldService?.getClient(config) ?? getLDClient(config);\n if (waitForInitialization) {\n try {\n await client.waitForInitialization(5);\n } catch (error) {\n log?.error({\n category: 'LaunchDarkly',\n code: 'InitializationFailed',\n error,\n message: `${projectName} client failed to initialize`,\n });\n }\n }\n setLDClient(client);\n })();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientSideID]); // only reload if clientSideID changes\n\n useEffect(() => {\n if (!waitForInitialization && ldClient) {\n ldClient.on('initialized', forceUpdate);\n return () => {\n ldClient.off('initialized', forceUpdate);\n };\n }\n }, [ldClient, waitForInitialization]);\n\n if (!ldClient) {\n return null;\n }\n\n return (\n <BaseLDProvider key={counter.toString()} clientSideID={clientSideID} ldClient={ldClient}>\n <FeatureFlagProvider>{children}</FeatureFlagProvider>\n </BaseLDProvider>\n );\n});\n"],"names":["Log","provide","useOptionalDependencies","LDProvider","BaseLDProvider","useEffect","useReducer","useState","FeatureFlagProvider","FeatureFlagStore","getLDClient","LD_SERVICE_TOKEN","mergeOptions","ldService","application","options","undefined","singletons","props","children","clientSideID","context","projectName","waitForInitialization","ldClient","setLDClient","log","counter","forceUpdate","x","initializeClient","config","client","getClient","error","category","code","message","on","off","toString"],"mappings":";AAAA,SAASA,GAAG,QAAQ,4BAA4B;AAChD,SAASC,OAAO,EAAEC,uBAAuB,QAAQ,0BAA0B;AAE3E,SACIC,cAAcC,cAAc,QAEzB,gCAAgC;AACvC,SAAgCC,SAAS,EAAEC,UAAU,EAAEC,QAAQ,QAAQ,QAAQ;AAC/E,SAASC,mBAAmB,QAAQ,0BAA0B;AAC9D,SAASC,gBAAgB,QAAQ,uBAAuB;AACxD,SAASC,WAAW,QAAQ,kBAAkB;AAC9C,SAAoBC,gBAAgB,QAAQ,UAAU;AAOtD,SAASC,aAAaC,SAAgC;IAClD,MAAM,EAAEC,WAAW,EAAEC,OAAO,EAAE,GAAGF,sBAAAA,uBAAAA,YAAa,CAAC;IAC/C,IAAI,CAACE,WAAWD,gBAAgBE,WAAW;QACvC,OAAOA;IACX;IACA,OAAO;QACH,GAAGD,OAAO;QACV,GAAID,gBAAgBE,YAAY;YAAEF;QAAY,IAAI,CAAC,CAAC;IACxD;AACJ;AAEA,OAAO,MAAMX,aAAoDF,QAAQ;IACrEgB,YAAY;QAACR;KAAiB;AAClC,GAAGS,CAAAA;IACC,MAAM,EAAEC,QAAQ,EAAEC,YAAY,EAAEC,OAAO,EAAEC,WAAW,EAAEC,wBAAwB,IAAI,EAAE,GAAGL;IACvF,MAAM,CAACM,UAAUC,YAAY,GAAGlB;IAChC,MAAM,CAACM,WAAWa,IAAI,GAAGxB,wBAAwBS,kBAAkBX;IACnE,MAAM,CAAC2B,SAASC,YAAY,GAAGtB,WAAW,CAACuB,IAAcA,IAAI,GAAG;IAEhExB,UAAU;QACL,CAAA,eAAeyB;;YACZ,MAAMf,UAAUH,aAAaC;YAC7B,MAAMkB,SAA6B;gBAAEX;gBAAcC;gBAASN;YAAQ;YACpE,MAAMiB,iBAASnB,sBAAAA,gCAAAA,UAAWoB,SAAS,CAACF,8CAAWrB,YAAYqB;YAC3D,IAAIR,uBAAuB;gBACvB,IAAI;oBACA,MAAMS,OAAOT,qBAAqB,CAAC;gBACvC,EAAE,OAAOW,OAAO;oBACZR,gBAAAA,0BAAAA,IAAKQ,KAAK,CAAC;wBACPC,UAAU;wBACVC,MAAM;wBACNF;wBACAG,SAAS,GAAGf,YAAY,4BAA4B,CAAC;oBACzD;gBACJ;YACJ;YACAG,YAAYO;QAChB,CAAA;IACA,uDAAuD;IAC3D,GAAG;QAACZ;KAAa,GAAG,sCAAsC;IAE1Df,UAAU;QACN,IAAI,CAACkB,yBAAyBC,UAAU;YACpCA,SAASc,EAAE,CAAC,eAAeV;YAC3B,OAAO;gBACHJ,SAASe,GAAG,CAAC,eAAeX;YAChC;QACJ;IACJ,GAAG;QAACJ;QAAUD;KAAsB;IAEpC,IAAI,CAACC,UAAU;QACX,OAAO;IACX;IAEA,qBACI,KAACpB;QAAwCgB,cAAcA;QAAcI,UAAUA;kBAC3E,cAAA,KAAChB;sBAAqBW;;OADLQ,QAAQa,QAAQ;AAI7C,GAAG"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { LDOptions } from 'launchdarkly-js-client-sdk';
|
|
1
2
|
import { FC, PropsWithChildren } from 'react';
|
|
2
3
|
import { LDApplication } from './types';
|
|
3
4
|
interface LDServiceProviderProps {
|
|
4
5
|
application?: LDApplication;
|
|
6
|
+
options?: Omit<LDOptions, 'application'>;
|
|
5
7
|
}
|
|
6
8
|
export declare const LDServiceProvider: FC<PropsWithChildren<LDServiceProviderProps>>;
|
|
7
9
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ld-service-provider.d.ts","sourceRoot":"","sources":["../src/ld-service-provider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAqB,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAoB,aAAa,EAAa,MAAM,SAAS,CAAC;AAMrE,UAAU,sBAAsB;IAC5B,WAAW,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ld-service-provider.d.ts","sourceRoot":"","sources":["../src/ld-service-provider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAqB,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAoB,aAAa,EAAa,MAAM,SAAS,CAAC;AAMrE,UAAU,sBAAsB;IAC5B,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;CAC5C;AAED,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAwB1E,CAAC"}
|
|
@@ -13,16 +13,22 @@ export const LDServiceProvider = provide({
|
|
|
13
13
|
inherit: true
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
|
-
})(({ children, application })=>{
|
|
16
|
+
})(({ children, application, options })=>{
|
|
17
17
|
const [providedLDService] = useDependencies(LD_SERVICE_TOKEN);
|
|
18
18
|
const prevApplication = useRef(providedLDService.application);
|
|
19
|
+
const prevOptions = useRef(providedLDService.options);
|
|
19
20
|
if (providedLDService === ldService || !providedLDService.application) {
|
|
20
21
|
providedLDService.application = application;
|
|
21
22
|
}
|
|
23
|
+
if (providedLDService === ldService || !providedLDService.options) {
|
|
24
|
+
providedLDService.options = options;
|
|
25
|
+
}
|
|
22
26
|
useEffect(()=>{
|
|
23
27
|
return ()=>{
|
|
24
28
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
29
|
providedLDService.application = prevApplication.current;
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
providedLDService.options = prevOptions.current;
|
|
26
32
|
};
|
|
27
33
|
}, [
|
|
28
34
|
providedLDService
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ld-service-provider.tsx"],"sourcesContent":["import { provide, useDependencies } from '@servicetitan/react-ioc';\nimport { FC, PropsWithChildren, useEffect, useRef } from 'react';\nimport { getLDClient } from './get-ld-client';\nimport { LD_SERVICE_TOKEN, LDApplication, LDService } from './types';\n\nconst ldService: LDService = {\n getClient: getLDClient,\n};\n\ninterface LDServiceProviderProps {\n application?: LDApplication;\n}\n\nexport const LDServiceProvider: FC<PropsWithChildren<LDServiceProviderProps>> = provide({\n singletons: [{ provide: LD_SERVICE_TOKEN, useValue: ldService, inherit: true }],\n})(({ children, application }) => {\n const [providedLDService] = useDependencies(LD_SERVICE_TOKEN);\n const prevApplication = useRef(providedLDService.application);\n\n if (providedLDService === ldService || !providedLDService.application) {\n providedLDService.application = application;\n }\n\n useEffect(() => {\n return () => {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n providedLDService.application = prevApplication.current;\n };\n }, [providedLDService]);\n\n return children;\n});\n"],"names":["provide","useDependencies","useEffect","useRef","getLDClient","LD_SERVICE_TOKEN","ldService","getClient","LDServiceProvider","singletons","useValue","inherit","children","application","providedLDService","prevApplication","current"],"mappings":"AAAA,SAASA,OAAO,EAAEC,eAAe,QAAQ,0BAA0B;
|
|
1
|
+
{"version":3,"sources":["../src/ld-service-provider.tsx"],"sourcesContent":["import { provide, useDependencies } from '@servicetitan/react-ioc';\nimport { LDOptions } from 'launchdarkly-js-client-sdk';\nimport { FC, PropsWithChildren, useEffect, useRef } from 'react';\nimport { getLDClient } from './get-ld-client';\nimport { LD_SERVICE_TOKEN, LDApplication, LDService } from './types';\n\nconst ldService: LDService = {\n getClient: getLDClient,\n};\n\ninterface LDServiceProviderProps {\n application?: LDApplication;\n options?: Omit<LDOptions, 'application'>;\n}\n\nexport const LDServiceProvider: FC<PropsWithChildren<LDServiceProviderProps>> = provide({\n singletons: [{ provide: LD_SERVICE_TOKEN, useValue: ldService, inherit: true }],\n})(({ children, application, options }) => {\n const [providedLDService] = useDependencies(LD_SERVICE_TOKEN);\n const prevApplication = useRef(providedLDService.application);\n const prevOptions = useRef(providedLDService.options);\n\n if (providedLDService === ldService || !providedLDService.application) {\n providedLDService.application = application;\n }\n if (providedLDService === ldService || !providedLDService.options) {\n providedLDService.options = options;\n }\n\n useEffect(() => {\n return () => {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n providedLDService.application = prevApplication.current;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n providedLDService.options = prevOptions.current;\n };\n }, [providedLDService]);\n\n return children;\n});\n"],"names":["provide","useDependencies","useEffect","useRef","getLDClient","LD_SERVICE_TOKEN","ldService","getClient","LDServiceProvider","singletons","useValue","inherit","children","application","options","providedLDService","prevApplication","prevOptions","current"],"mappings":"AAAA,SAASA,OAAO,EAAEC,eAAe,QAAQ,0BAA0B;AAEnE,SAAgCC,SAAS,EAAEC,MAAM,QAAQ,QAAQ;AACjE,SAASC,WAAW,QAAQ,kBAAkB;AAC9C,SAASC,gBAAgB,QAAkC,UAAU;AAErE,MAAMC,YAAuB;IACzBC,WAAWH;AACf;AAOA,OAAO,MAAMI,oBAAmER,QAAQ;IACpFS,YAAY;QAAC;YAAET,SAASK;YAAkBK,UAAUJ;YAAWK,SAAS;QAAK;KAAE;AACnF,GAAG,CAAC,EAAEC,QAAQ,EAAEC,WAAW,EAAEC,OAAO,EAAE;IAClC,MAAM,CAACC,kBAAkB,GAAGd,gBAAgBI;IAC5C,MAAMW,kBAAkBb,OAAOY,kBAAkBF,WAAW;IAC5D,MAAMI,cAAcd,OAAOY,kBAAkBD,OAAO;IAEpD,IAAIC,sBAAsBT,aAAa,CAACS,kBAAkBF,WAAW,EAAE;QACnEE,kBAAkBF,WAAW,GAAGA;IACpC;IACA,IAAIE,sBAAsBT,aAAa,CAACS,kBAAkBD,OAAO,EAAE;QAC/DC,kBAAkBD,OAAO,GAAGA;IAChC;IAEAZ,UAAU;QACN,OAAO;YACH,uDAAuD;YACvDa,kBAAkBF,WAAW,GAAGG,gBAAgBE,OAAO;YACvD,uDAAuD;YACvDH,kBAAkBD,OAAO,GAAGG,YAAYC,OAAO;QACnD;IACJ,GAAG;QAACH;KAAkB;IAEtB,OAAOH;AACX,GAAG"}
|
|
@@ -148,7 +148,7 @@ describe(`[launchdarkly-service] ${LDProvider.name}`, ()=>{
|
|
|
148
148
|
category: 'LaunchDarkly',
|
|
149
149
|
code: 'InitializationFailed',
|
|
150
150
|
error: 'Oops!',
|
|
151
|
-
message:
|
|
151
|
+
message: `${props.projectName} client failed to initialize`
|
|
152
152
|
});
|
|
153
153
|
});
|
|
154
154
|
});
|
|
@@ -177,6 +177,19 @@ describe(`[launchdarkly-service] ${LDProvider.name}`, ()=>{
|
|
|
177
177
|
}));
|
|
178
178
|
expect(ldServiceClient.waitForInitialization).toHaveBeenCalled();
|
|
179
179
|
});
|
|
180
|
+
describe('when LDService has options', ()=>{
|
|
181
|
+
const options = {
|
|
182
|
+
hash: 'abc123'
|
|
183
|
+
};
|
|
184
|
+
beforeEach(()=>ldService.options = options);
|
|
185
|
+
test('forwards options to LDClient', async ()=>{
|
|
186
|
+
subject();
|
|
187
|
+
await waitFor(()=>expectLDProvider());
|
|
188
|
+
expect(ldService.getClient).toHaveBeenCalledWith(expect.objectContaining({
|
|
189
|
+
options
|
|
190
|
+
}));
|
|
191
|
+
});
|
|
192
|
+
});
|
|
180
193
|
describe('when application is provided', ()=>{
|
|
181
194
|
const application = {
|
|
182
195
|
id: 'some-application',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tests/ld-provider.test.tsx"],"sourcesContent":["import { Log } from '@servicetitan/log-service';\nimport { Provider, useDependencies } from '@servicetitan/react-ioc';\nimport '@testing-library/jest-dom';\nimport { render, screen, waitFor } from '@testing-library/react';\nimport { LDClient } from 'launchdarkly-js-client-sdk';\nimport { act, FC, Fragment } from 'react';\nimport { FeatureFlagStore } from '../feature-flag-store';\nimport { getLDClient } from '../get-ld-client';\nimport { LDProvider } from '../ld-provider';\nimport { LD_SERVICE_TOKEN, LDService } from '../types';\n\nfunction formatProps(props: Record<string, any>) {\n return Object.entries(props).map(([key, value]) => {\n return `${key}=${value.toString()}`;\n });\n}\n\nfunction mockComponent(name: string) {\n return {\n [name]: ({ children, ...props }: any) => (\n <Fragment>\n <div>{`<${name} ${formatProps(props)} />`}</div>\n {children}\n </Fragment>\n ),\n };\n}\n\njest.mock('launchdarkly-react-client-sdk', () => ({\n ...mockComponent('LDProvider'),\n}));\njest.mock('../feature-flag-provider', () => ({\n ...mockComponent('FeatureFlagProvider'),\n}));\njest.mock('../get-ld-client', () => ({\n getLDClient: jest.fn(),\n}));\n\ndescribe(`[launchdarkly-service] ${LDProvider.name}`, () => {\n const log: Partial<Log> = {\n error: jest.fn(),\n };\n const ldClient: Partial<LDClient> & { toString: () => string } = {\n on: jest.fn(),\n off: jest.fn(),\n waitUntilReady: jest.fn().mockResolvedValue(undefined),\n waitForInitialization: jest.fn().mockResolvedValue(undefined),\n toString: () => 'ldClient',\n };\n let singletons: Required<Parameters<typeof Provider>[0]>['singletons'];\n let props: Parameters<typeof LDProvider>[0];\n let providedFeatureFlagStore: FeatureFlagStore;\n let renderCount: number;\n\n const Component: FC = () => {\n [providedFeatureFlagStore] = useDependencies(FeatureFlagStore);\n renderCount += 1;\n return <div>{'<Component />'}</div>;\n };\n\n beforeEach(() => {\n jest.clearAllMocks();\n jest.mocked(getLDClient).mockReturnValue(ldClient as LDClient);\n renderCount = 0;\n singletons = [];\n props = { clientSideID: 'foo', projectName: 'bar' };\n });\n\n const subject = () => {\n return render(\n <Provider singletons={singletons}>\n <LDProvider {...props}>\n <Component />\n </LDProvider>\n </Provider>\n );\n };\n\n function expectLDProvider(props?: Record<string, any>) {\n if (props) {\n expect(screen.getByText(`<LDProvider ${formatProps(props)} />`)).toBeInTheDocument();\n } else {\n expect(screen.getByText(/<LDProvider .* \\/>/)).toBeInTheDocument();\n }\n }\n\n test('renders base LDProvider and FeatureFlagProvider after client is ready and initialized', async () => {\n subject();\n\n await waitFor(() => expectLDProvider({ clientSideID: props.clientSideID, ldClient }));\n\n expect(screen.getByText('<FeatureFlagProvider />')).toBeInTheDocument();\n expect(screen.getByText('<Component />')).toBeInTheDocument();\n expect(ldClient.waitForInitialization).toHaveBeenCalled();\n });\n\n test('provides FeatureFlagStore', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(providedFeatureFlagStore).toBeDefined();\n });\n\n describe('when waitForInitialization is false', () => {\n let initializedCallback: Function;\n\n beforeEach(() => {\n props.waitForInitialization = false;\n jest.mocked(ldClient.on)?.mockImplementation((event, callback) => {\n if (event === 'initialized') {\n initializedCallback = callback;\n }\n });\n });\n\n test('does not wait for client to initialize', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(ldClient.waitForInitialization).not.toHaveBeenCalled();\n });\n\n test('re-renders when client is initialized', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n expect(renderCount).toBe(1);\n\n act(() => initializedCallback?.());\n expect(renderCount).toBe(2);\n });\n\n test('deregisters listener when component is unmounted', async () => {\n const { unmount } = subject();\n\n await waitFor(() => expectLDProvider());\n\n unmount();\n\n expect(jest.mocked(ldClient.off)).toHaveBeenCalledWith(\n 'initialized',\n initializedCallback\n );\n });\n });\n\n describe('when initialization fails', () => {\n beforeEach(() => {\n jest.mocked(ldClient.waitForInitialization)?.mockRejectedValue('Oops!');\n });\n\n test('renders base LDProvider', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n });\n\n describe('when log service is provided', () => {\n beforeEach(() => singletons.push({ provide: Log, useValue: log }));\n\n test('logs error', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(log.error).toHaveBeenCalledWith({\n category: 'LaunchDarkly',\n code: 'InitializationFailed',\n error: 'Oops!',\n message: `\"${props.projectName}\" client failed to initialize`,\n });\n });\n });\n });\n\n describe('when LDService is provided', () => {\n const ldServiceClient: Partial<LDClient> & { toString: () => string } = {\n waitUntilReady: jest.fn().mockResolvedValue(undefined),\n waitForInitialization: jest.fn().mockResolvedValue(undefined),\n toString: () => 'ldServiceClient',\n };\n let ldService: LDService;\n\n beforeEach(() => {\n ldService = { getClient: jest.fn().mockReturnValue(ldServiceClient) };\n singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: ldService });\n });\n\n test('uses LDService client', async () => {\n subject();\n\n await waitFor(() =>\n expectLDProvider({ clientSideID: props.clientSideID, ldClient: ldServiceClient })\n );\n\n expect(ldServiceClient.waitForInitialization).toHaveBeenCalled();\n });\n\n describe('when application is provided', () => {\n const application = { id: 'some-application', version: '1.2.3' };\n\n beforeEach(() => (ldService.application = application));\n\n test('passes application to LDClient', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(ldService.getClient).toHaveBeenCalledWith(\n expect.objectContaining({\n options: { application },\n })\n );\n });\n });\n });\n});\n"],"names":["Log","Provider","useDependencies","render","screen","waitFor","act","Fragment","FeatureFlagStore","getLDClient","LDProvider","LD_SERVICE_TOKEN","formatProps","props","Object","entries","map","key","value","toString","mockComponent","name","children","div","jest","mock","fn","describe","log","error","ldClient","on","off","waitUntilReady","mockResolvedValue","undefined","waitForInitialization","singletons","providedFeatureFlagStore","renderCount","Component","beforeEach","clearAllMocks","mocked","mockReturnValue","clientSideID","projectName","subject","expectLDProvider","expect","getByText","toBeInTheDocument","test","toHaveBeenCalled","toBeDefined","initializedCallback","mockImplementation","event","callback","not","toBe","unmount","toHaveBeenCalledWith","mockRejectedValue","push","provide","useValue","category","code","message","ldServiceClient","ldService","getClient","application","id","version","objectContaining","options"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/tests/ld-provider.test.tsx"],"sourcesContent":["import { Log } from '@servicetitan/log-service';\nimport { Provider, useDependencies } from '@servicetitan/react-ioc';\nimport '@testing-library/jest-dom';\nimport { render, screen, waitFor } from '@testing-library/react';\nimport { LDClient } from 'launchdarkly-js-client-sdk';\nimport { act, FC, Fragment } from 'react';\nimport { FeatureFlagStore } from '../feature-flag-store';\nimport { getLDClient } from '../get-ld-client';\nimport { LDProvider } from '../ld-provider';\nimport { LD_SERVICE_TOKEN, LDService } from '../types';\n\nfunction formatProps(props: Record<string, any>) {\n return Object.entries(props).map(([key, value]) => {\n return `${key}=${value.toString()}`;\n });\n}\n\nfunction mockComponent(name: string) {\n return {\n [name]: ({ children, ...props }: any) => (\n <Fragment>\n <div>{`<${name} ${formatProps(props)} />`}</div>\n {children}\n </Fragment>\n ),\n };\n}\n\njest.mock('launchdarkly-react-client-sdk', () => ({\n ...mockComponent('LDProvider'),\n}));\njest.mock('../feature-flag-provider', () => ({\n ...mockComponent('FeatureFlagProvider'),\n}));\njest.mock('../get-ld-client', () => ({\n getLDClient: jest.fn(),\n}));\n\ndescribe(`[launchdarkly-service] ${LDProvider.name}`, () => {\n const log: Partial<Log> = {\n error: jest.fn(),\n };\n const ldClient: Partial<LDClient> & { toString: () => string } = {\n on: jest.fn(),\n off: jest.fn(),\n waitUntilReady: jest.fn().mockResolvedValue(undefined),\n waitForInitialization: jest.fn().mockResolvedValue(undefined),\n toString: () => 'ldClient',\n };\n let singletons: Required<Parameters<typeof Provider>[0]>['singletons'];\n let props: Parameters<typeof LDProvider>[0];\n let providedFeatureFlagStore: FeatureFlagStore;\n let renderCount: number;\n\n const Component: FC = () => {\n [providedFeatureFlagStore] = useDependencies(FeatureFlagStore);\n renderCount += 1;\n return <div>{'<Component />'}</div>;\n };\n\n beforeEach(() => {\n jest.clearAllMocks();\n jest.mocked(getLDClient).mockReturnValue(ldClient as LDClient);\n renderCount = 0;\n singletons = [];\n props = { clientSideID: 'foo', projectName: 'bar' };\n });\n\n const subject = () => {\n return render(\n <Provider singletons={singletons}>\n <LDProvider {...props}>\n <Component />\n </LDProvider>\n </Provider>\n );\n };\n\n function expectLDProvider(props?: Record<string, any>) {\n if (props) {\n expect(screen.getByText(`<LDProvider ${formatProps(props)} />`)).toBeInTheDocument();\n } else {\n expect(screen.getByText(/<LDProvider .* \\/>/)).toBeInTheDocument();\n }\n }\n\n test('renders base LDProvider and FeatureFlagProvider after client is ready and initialized', async () => {\n subject();\n\n await waitFor(() => expectLDProvider({ clientSideID: props.clientSideID, ldClient }));\n\n expect(screen.getByText('<FeatureFlagProvider />')).toBeInTheDocument();\n expect(screen.getByText('<Component />')).toBeInTheDocument();\n expect(ldClient.waitForInitialization).toHaveBeenCalled();\n });\n\n test('provides FeatureFlagStore', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(providedFeatureFlagStore).toBeDefined();\n });\n\n describe('when waitForInitialization is false', () => {\n let initializedCallback: Function;\n\n beforeEach(() => {\n props.waitForInitialization = false;\n jest.mocked(ldClient.on)?.mockImplementation((event, callback) => {\n if (event === 'initialized') {\n initializedCallback = callback;\n }\n });\n });\n\n test('does not wait for client to initialize', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(ldClient.waitForInitialization).not.toHaveBeenCalled();\n });\n\n test('re-renders when client is initialized', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n expect(renderCount).toBe(1);\n\n act(() => initializedCallback?.());\n expect(renderCount).toBe(2);\n });\n\n test('deregisters listener when component is unmounted', async () => {\n const { unmount } = subject();\n\n await waitFor(() => expectLDProvider());\n\n unmount();\n\n expect(jest.mocked(ldClient.off)).toHaveBeenCalledWith(\n 'initialized',\n initializedCallback\n );\n });\n });\n\n describe('when initialization fails', () => {\n beforeEach(() => {\n jest.mocked(ldClient.waitForInitialization)?.mockRejectedValue('Oops!');\n });\n\n test('renders base LDProvider', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n });\n\n describe('when log service is provided', () => {\n beforeEach(() => singletons.push({ provide: Log, useValue: log }));\n\n test('logs error', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(log.error).toHaveBeenCalledWith({\n category: 'LaunchDarkly',\n code: 'InitializationFailed',\n error: 'Oops!',\n message: `${props.projectName} client failed to initialize`,\n });\n });\n });\n });\n\n describe('when LDService is provided', () => {\n const ldServiceClient: Partial<LDClient> & { toString: () => string } = {\n waitUntilReady: jest.fn().mockResolvedValue(undefined),\n waitForInitialization: jest.fn().mockResolvedValue(undefined),\n toString: () => 'ldServiceClient',\n };\n let ldService: LDService;\n\n beforeEach(() => {\n ldService = { getClient: jest.fn().mockReturnValue(ldServiceClient) };\n singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: ldService });\n });\n\n test('uses LDService client', async () => {\n subject();\n\n await waitFor(() =>\n expectLDProvider({ clientSideID: props.clientSideID, ldClient: ldServiceClient })\n );\n\n expect(ldServiceClient.waitForInitialization).toHaveBeenCalled();\n });\n\n describe('when LDService has options', () => {\n const options: LDService['options'] = { hash: 'abc123' };\n\n beforeEach(() => (ldService.options = options));\n\n test('forwards options to LDClient', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(ldService.getClient).toHaveBeenCalledWith(\n expect.objectContaining({ options })\n );\n });\n });\n\n describe('when application is provided', () => {\n const application = { id: 'some-application', version: '1.2.3' };\n\n beforeEach(() => (ldService.application = application));\n\n test('passes application to LDClient', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(ldService.getClient).toHaveBeenCalledWith(\n expect.objectContaining({\n options: { application },\n })\n );\n });\n });\n });\n});\n"],"names":["Log","Provider","useDependencies","render","screen","waitFor","act","Fragment","FeatureFlagStore","getLDClient","LDProvider","LD_SERVICE_TOKEN","formatProps","props","Object","entries","map","key","value","toString","mockComponent","name","children","div","jest","mock","fn","describe","log","error","ldClient","on","off","waitUntilReady","mockResolvedValue","undefined","waitForInitialization","singletons","providedFeatureFlagStore","renderCount","Component","beforeEach","clearAllMocks","mocked","mockReturnValue","clientSideID","projectName","subject","expectLDProvider","expect","getByText","toBeInTheDocument","test","toHaveBeenCalled","toBeDefined","initializedCallback","mockImplementation","event","callback","not","toBe","unmount","toHaveBeenCalledWith","mockRejectedValue","push","provide","useValue","category","code","message","ldServiceClient","ldService","getClient","options","hash","objectContaining","application","id","version"],"mappings":"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"}
|
|
@@ -6,12 +6,14 @@ import { LDServiceProvider } from '../ld-service-provider';
|
|
|
6
6
|
import { LD_SERVICE_TOKEN } from '../types';
|
|
7
7
|
describe(`[launchdarkly-service] ${LDServiceProvider.name}`, ()=>{
|
|
8
8
|
let providedApplication;
|
|
9
|
+
let providedOptions;
|
|
9
10
|
let providedLDService;
|
|
10
11
|
let singletons;
|
|
11
12
|
let props;
|
|
12
13
|
const Component = ()=>{
|
|
13
14
|
[providedLDService] = useDependencies(LD_SERVICE_TOKEN);
|
|
14
15
|
providedApplication = providedLDService.application;
|
|
16
|
+
providedOptions = providedLDService.options;
|
|
15
17
|
return null;
|
|
16
18
|
};
|
|
17
19
|
beforeEach(()=>{
|
|
@@ -37,6 +39,10 @@ describe(`[launchdarkly-service] ${LDServiceProvider.name}`, ()=>{
|
|
|
37
39
|
subject();
|
|
38
40
|
expect(providedApplication).toBeUndefined();
|
|
39
41
|
});
|
|
42
|
+
test('provides no options', ()=>{
|
|
43
|
+
subject();
|
|
44
|
+
expect(providedOptions).toBeUndefined();
|
|
45
|
+
});
|
|
40
46
|
describe('when LDService is already provided', ()=>{
|
|
41
47
|
const ldService = {
|
|
42
48
|
getClient: jest.fn()
|
|
@@ -124,6 +130,96 @@ describe(`[launchdarkly-service] ${LDServiceProvider.name}`, ()=>{
|
|
|
124
130
|
});
|
|
125
131
|
});
|
|
126
132
|
});
|
|
133
|
+
describe('with "options" prop', ()=>{
|
|
134
|
+
const options = {
|
|
135
|
+
hash: 'abc123'
|
|
136
|
+
};
|
|
137
|
+
beforeEach(()=>props.options = options);
|
|
138
|
+
function itProvidesOptions() {
|
|
139
|
+
test('provides options', ()=>{
|
|
140
|
+
subject();
|
|
141
|
+
expect(providedOptions).toBe(options);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
itProvidesOptions();
|
|
145
|
+
describe('when rerendered with new options', ()=>{
|
|
146
|
+
const newOptions = {
|
|
147
|
+
evaluationReasons: true
|
|
148
|
+
};
|
|
149
|
+
beforeEach(()=>{
|
|
150
|
+
const { rerender } = subject();
|
|
151
|
+
rerender(/*#__PURE__*/ _jsx(LDServiceProvider, {
|
|
152
|
+
options: newOptions,
|
|
153
|
+
children: /*#__PURE__*/ _jsx(Component, {})
|
|
154
|
+
}));
|
|
155
|
+
});
|
|
156
|
+
test('provides new options', ()=>{
|
|
157
|
+
expect(providedOptions).toBe(newOptions);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('when rerendered without options', ()=>{
|
|
161
|
+
beforeEach(()=>{
|
|
162
|
+
const { rerender } = subject();
|
|
163
|
+
rerender(/*#__PURE__*/ _jsx(LDServiceProvider, {
|
|
164
|
+
children: /*#__PURE__*/ _jsx(Component, {})
|
|
165
|
+
}));
|
|
166
|
+
});
|
|
167
|
+
test('drops options', ()=>{
|
|
168
|
+
expect(providedOptions).toBeUndefined();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('when LDService is already provided', ()=>{
|
|
172
|
+
let parentLDService;
|
|
173
|
+
beforeEach(()=>{
|
|
174
|
+
parentLDService = {
|
|
175
|
+
getClient: jest.fn()
|
|
176
|
+
};
|
|
177
|
+
singletons === null || singletons === void 0 ? void 0 : singletons.push({
|
|
178
|
+
provide: LD_SERVICE_TOKEN,
|
|
179
|
+
useValue: parentLDService
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
itProvidesOptions();
|
|
183
|
+
test('unmount resets LDService.options', ()=>{
|
|
184
|
+
const { unmount } = subject();
|
|
185
|
+
unmount();
|
|
186
|
+
expect(parentLDService.options).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
describe('when provided LDService has options', ()=>{
|
|
189
|
+
const otherOptions = {
|
|
190
|
+
evaluationReasons: true
|
|
191
|
+
};
|
|
192
|
+
beforeEach(()=>parentLDService.options = otherOptions);
|
|
193
|
+
test('provides inherited options', ()=>{
|
|
194
|
+
subject();
|
|
195
|
+
expect(providedOptions).toBe(otherOptions);
|
|
196
|
+
});
|
|
197
|
+
test('unmount preserves LDService.options', ()=>{
|
|
198
|
+
const { unmount } = subject();
|
|
199
|
+
unmount();
|
|
200
|
+
expect(parentLDService.options).toBe(otherOptions);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('with both "application" and "options" props', ()=>{
|
|
206
|
+
const application = {
|
|
207
|
+
id: 'some-application',
|
|
208
|
+
version: '1.0.0'
|
|
209
|
+
};
|
|
210
|
+
const options = {
|
|
211
|
+
hash: 'abc123'
|
|
212
|
+
};
|
|
213
|
+
beforeEach(()=>{
|
|
214
|
+
props.application = application;
|
|
215
|
+
props.options = options;
|
|
216
|
+
});
|
|
217
|
+
test('provides both application and options', ()=>{
|
|
218
|
+
subject();
|
|
219
|
+
expect(providedApplication).toBe(application);
|
|
220
|
+
expect(providedOptions).toBe(options);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
127
223
|
});
|
|
128
224
|
|
|
129
225
|
//# sourceMappingURL=ld-service-provider.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tests/ld-service-provider.test.tsx"],"sourcesContent":["import { Provider, useDependencies } from '@servicetitan/react-ioc';\nimport { render } from '@testing-library/react';\nimport { ComponentProps, FC } from 'react';\nimport { getLDClient } from '../get-ld-client';\nimport { LDServiceProvider } from '../ld-service-provider';\nimport { LD_SERVICE_TOKEN, LDApplication, LDService } from '../types';\n\ndescribe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {\n let providedApplication: LDApplication | undefined;\n let providedLDService: LDService;\n let singletons: Parameters<typeof Provider>[0]['singletons'];\n let props: ComponentProps<typeof LDServiceProvider>;\n\n const Component: FC = () => {\n [providedLDService] = useDependencies(LD_SERVICE_TOKEN);\n providedApplication = providedLDService.application;\n return null;\n };\n\n beforeEach(() => {\n singletons = [];\n props = {};\n });\n\n const subject = () => {\n return render(\n <Provider singletons={singletons}>\n <LDServiceProvider {...props}>\n <Component />\n </LDServiceProvider>\n </Provider>\n );\n };\n\n test('provides LDService instance', () => {\n subject();\n\n expect(providedLDService).toEqual({ getClient: getLDClient });\n });\n\n test('provides no application', () => {\n subject();\n\n expect(providedApplication).toBeUndefined();\n });\n\n describe('when LDService is already provided', () => {\n const ldService: LDService = { getClient: jest.fn() };\n\n beforeEach(() => singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: ldService }));\n\n test('provides existing LDService', () => {\n subject();\n\n expect(providedLDService).toBe(ldService);\n });\n });\n\n describe('with \"application\" prop', () => {\n const application = { id: 'some-application', version: '1.0.0' };\n\n beforeEach(() => (props.application = application));\n\n function itProvidesApplication() {\n test('provides application', () => {\n subject();\n\n expect(providedApplication).toBe(application);\n });\n }\n\n itProvidesApplication();\n\n describe('when rerendered with new application', () => {\n const newApplication = { id: 'foo' };\n\n beforeEach(() => {\n const { rerender } = subject();\n\n rerender(\n <LDServiceProvider application={newApplication}>\n <Component />\n </LDServiceProvider>\n );\n });\n\n test('provides new application', () => {\n expect(providedApplication).toBe(newApplication);\n });\n });\n\n describe('when rerendered without application', () => {\n beforeEach(() => {\n const { rerender } = subject();\n\n rerender(\n <LDServiceProvider>\n <Component />\n </LDServiceProvider>\n );\n });\n\n test('drops application', () => {\n expect(providedApplication).toBeUndefined();\n });\n });\n\n describe('when LDService is already provided', () => {\n let providedLDService: LDService;\n\n beforeEach(() => {\n providedLDService = { getClient: jest.fn() };\n singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: providedLDService });\n });\n\n itProvidesApplication();\n\n test('unmount resets LDService.application', () => {\n const { unmount } = subject();\n unmount();\n\n expect(providedLDService.application).toBeUndefined();\n });\n\n describe('when provided LDService has application', () => {\n const otherApplication = { id: 'other-application', version: '2.0.0' };\n\n beforeEach(() => (providedLDService.application = otherApplication));\n\n test('provides inherited application', () => {\n subject();\n\n expect(providedApplication).toBe(otherApplication);\n });\n\n test('unmount preserves LDService.application', () => {\n const { unmount } = subject();\n unmount();\n\n expect(providedLDService.application).toBe(otherApplication);\n });\n });\n });\n });\n});\n"],"names":["Provider","useDependencies","render","getLDClient","LDServiceProvider","LD_SERVICE_TOKEN","describe","name","providedApplication","providedLDService","singletons","props","Component","application","beforeEach","subject","test","expect","toEqual","getClient","toBeUndefined","ldService","jest","fn","push","provide","useValue","toBe","id","version","itProvidesApplication","newApplication","rerender","unmount","otherApplication"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/tests/ld-service-provider.test.tsx"],"sourcesContent":["import { Provider, useDependencies } from '@servicetitan/react-ioc';\nimport { render } from '@testing-library/react';\nimport { LDOptions } from 'launchdarkly-js-client-sdk';\nimport { ComponentProps, FC } from 'react';\nimport { getLDClient } from '../get-ld-client';\nimport { LDServiceProvider } from '../ld-service-provider';\nimport { LD_SERVICE_TOKEN, LDApplication, LDService } from '../types';\n\ndescribe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {\n let providedApplication: LDApplication | undefined;\n let providedOptions: LDOptions | undefined;\n let providedLDService: LDService;\n let singletons: Parameters<typeof Provider>[0]['singletons'];\n let props: ComponentProps<typeof LDServiceProvider>;\n\n const Component: FC = () => {\n [providedLDService] = useDependencies(LD_SERVICE_TOKEN);\n providedApplication = providedLDService.application;\n providedOptions = providedLDService.options;\n return null;\n };\n\n beforeEach(() => {\n singletons = [];\n props = {};\n });\n\n const subject = () => {\n return render(\n <Provider singletons={singletons}>\n <LDServiceProvider {...props}>\n <Component />\n </LDServiceProvider>\n </Provider>\n );\n };\n\n test('provides LDService instance', () => {\n subject();\n\n expect(providedLDService).toEqual({ getClient: getLDClient });\n });\n\n test('provides no application', () => {\n subject();\n\n expect(providedApplication).toBeUndefined();\n });\n\n test('provides no options', () => {\n subject();\n\n expect(providedOptions).toBeUndefined();\n });\n\n describe('when LDService is already provided', () => {\n const ldService: LDService = { getClient: jest.fn() };\n\n beforeEach(() => singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: ldService }));\n\n test('provides existing LDService', () => {\n subject();\n\n expect(providedLDService).toBe(ldService);\n });\n });\n\n describe('with \"application\" prop', () => {\n const application = { id: 'some-application', version: '1.0.0' };\n\n beforeEach(() => (props.application = application));\n\n function itProvidesApplication() {\n test('provides application', () => {\n subject();\n\n expect(providedApplication).toBe(application);\n });\n }\n\n itProvidesApplication();\n\n describe('when rerendered with new application', () => {\n const newApplication = { id: 'foo' };\n\n beforeEach(() => {\n const { rerender } = subject();\n\n rerender(\n <LDServiceProvider application={newApplication}>\n <Component />\n </LDServiceProvider>\n );\n });\n\n test('provides new application', () => {\n expect(providedApplication).toBe(newApplication);\n });\n });\n\n describe('when rerendered without application', () => {\n beforeEach(() => {\n const { rerender } = subject();\n\n rerender(\n <LDServiceProvider>\n <Component />\n </LDServiceProvider>\n );\n });\n\n test('drops application', () => {\n expect(providedApplication).toBeUndefined();\n });\n });\n\n describe('when LDService is already provided', () => {\n let providedLDService: LDService;\n\n beforeEach(() => {\n providedLDService = { getClient: jest.fn() };\n singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: providedLDService });\n });\n\n itProvidesApplication();\n\n test('unmount resets LDService.application', () => {\n const { unmount } = subject();\n unmount();\n\n expect(providedLDService.application).toBeUndefined();\n });\n\n describe('when provided LDService has application', () => {\n const otherApplication = { id: 'other-application', version: '2.0.0' };\n\n beforeEach(() => (providedLDService.application = otherApplication));\n\n test('provides inherited application', () => {\n subject();\n\n expect(providedApplication).toBe(otherApplication);\n });\n\n test('unmount preserves LDService.application', () => {\n const { unmount } = subject();\n unmount();\n\n expect(providedLDService.application).toBe(otherApplication);\n });\n });\n });\n });\n\n describe('with \"options\" prop', () => {\n const options: LDOptions = { hash: 'abc123' };\n\n beforeEach(() => (props.options = options));\n\n function itProvidesOptions() {\n test('provides options', () => {\n subject();\n\n expect(providedOptions).toBe(options);\n });\n }\n\n itProvidesOptions();\n\n describe('when rerendered with new options', () => {\n const newOptions: LDOptions = { evaluationReasons: true };\n\n beforeEach(() => {\n const { rerender } = subject();\n\n rerender(\n <LDServiceProvider options={newOptions}>\n <Component />\n </LDServiceProvider>\n );\n });\n\n test('provides new options', () => {\n expect(providedOptions).toBe(newOptions);\n });\n });\n\n describe('when rerendered without options', () => {\n beforeEach(() => {\n const { rerender } = subject();\n\n rerender(\n <LDServiceProvider>\n <Component />\n </LDServiceProvider>\n );\n });\n\n test('drops options', () => {\n expect(providedOptions).toBeUndefined();\n });\n });\n\n describe('when LDService is already provided', () => {\n let parentLDService: LDService;\n\n beforeEach(() => {\n parentLDService = { getClient: jest.fn() };\n singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: parentLDService });\n });\n\n itProvidesOptions();\n\n test('unmount resets LDService.options', () => {\n const { unmount } = subject();\n unmount();\n\n expect(parentLDService.options).toBeUndefined();\n });\n\n describe('when provided LDService has options', () => {\n const otherOptions: LDOptions = { evaluationReasons: true };\n\n beforeEach(() => (parentLDService.options = otherOptions));\n\n test('provides inherited options', () => {\n subject();\n\n expect(providedOptions).toBe(otherOptions);\n });\n\n test('unmount preserves LDService.options', () => {\n const { unmount } = subject();\n unmount();\n\n expect(parentLDService.options).toBe(otherOptions);\n });\n });\n });\n });\n\n describe('with both \"application\" and \"options\" props', () => {\n const application = { id: 'some-application', version: '1.0.0' };\n const options: LDOptions = { hash: 'abc123' };\n\n beforeEach(() => {\n props.application = application;\n props.options = options;\n });\n\n test('provides both application and options', () => {\n subject();\n\n expect(providedApplication).toBe(application);\n expect(providedOptions).toBe(options);\n });\n });\n});\n"],"names":["Provider","useDependencies","render","getLDClient","LDServiceProvider","LD_SERVICE_TOKEN","describe","name","providedApplication","providedOptions","providedLDService","singletons","props","Component","application","options","beforeEach","subject","test","expect","toEqual","getClient","toBeUndefined","ldService","jest","fn","push","provide","useValue","toBe","id","version","itProvidesApplication","newApplication","rerender","unmount","otherApplication","hash","itProvidesOptions","newOptions","evaluationReasons","parentLDService","otherOptions"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface ClientConfig {
|
|
|
9
9
|
export interface LDService {
|
|
10
10
|
getClient: (config: ClientConfig) => LDClient;
|
|
11
11
|
application?: LDApplication;
|
|
12
|
+
options?: Omit<LDOptions, 'application'>;
|
|
12
13
|
}
|
|
13
14
|
export type FlagKey<S, K extends keyof S> = K & {
|
|
14
15
|
typeRef?: S[K];
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEjF,eAAO,MAAM,gBAAgB,0DAA6C,CAAC;AAE3E,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,QAAQ,CAAC;IAC9C,WAAW,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEjF,eAAO,MAAM,gBAAgB,0DAA6C,CAAC;AAE3E,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;AAErD,MAAM,WAAW,YAAY;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,QAAQ,CAAC;IAC9C,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;CAC5C;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG;IAC5C,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;CAClB,CAAC"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { symbolToken } from '@servicetitan/react-ioc';\nimport type { LDClient, LDContext, LDOptions } from 'launchdarkly-js-client-sdk';\n\nexport const LD_SERVICE_TOKEN = symbolToken<LDService>('LD_SERVICE_TOKEN');\n\nexport type LDApplication = LDOptions['application'];\n\nexport interface ClientConfig {\n clientSideID: string;\n context?: LDContext;\n options?: LDOptions;\n}\n\nexport interface LDService {\n getClient: (config: ClientConfig) => LDClient;\n application?: LDApplication;\n}\n\nexport type FlagKey<S, K extends keyof S> = K & {\n typeRef?: S[K];\n};\n"],"names":["symbolToken","LD_SERVICE_TOKEN"],"mappings":"AAAA,SAASA,WAAW,QAAQ,0BAA0B;AAGtD,OAAO,MAAMC,mBAAmBD,YAAuB,oBAAoB"}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { symbolToken } from '@servicetitan/react-ioc';\nimport type { LDClient, LDContext, LDOptions } from 'launchdarkly-js-client-sdk';\n\nexport const LD_SERVICE_TOKEN = symbolToken<LDService>('LD_SERVICE_TOKEN');\n\nexport type LDApplication = LDOptions['application'];\n\nexport interface ClientConfig {\n clientSideID: string;\n context?: LDContext;\n options?: LDOptions;\n}\n\nexport interface LDService {\n getClient: (config: ClientConfig) => LDClient;\n application?: LDApplication;\n options?: Omit<LDOptions, 'application'>;\n}\n\nexport type FlagKey<S, K extends keyof S> = K & {\n typeRef?: S[K];\n};\n"],"names":["symbolToken","LD_SERVICE_TOKEN"],"mappings":"AAAA,SAASA,WAAW,QAAQ,0BAA0B;AAGtD,OAAO,MAAMC,mBAAmBD,YAAuB,oBAAoB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/launchdarkly-service",
|
|
3
|
-
"version": "36.
|
|
3
|
+
"version": "36.2.0",
|
|
4
4
|
"description": "Service for creating and reusing client connections for LaunchDarkly",
|
|
5
5
|
"homepage": "https://docs.st.dev/docs/frontend/uikit/launchdarkly-service",
|
|
6
6
|
"repository": {
|
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
"url": "https://github.com/servicetitan/uikit.git",
|
|
9
9
|
"directory": "packages/launchdarkly-service"
|
|
10
10
|
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./datadog-rum": {
|
|
17
|
+
"types": "./dist/datadog-rum/index.d.ts",
|
|
18
|
+
"default": "./dist/datadog-rum/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
11
21
|
"main": "./dist/index.js",
|
|
12
22
|
"typings": "./dist/index.d.ts",
|
|
13
23
|
"sideEffects": false,
|
|
@@ -16,8 +26,9 @@
|
|
|
16
26
|
"src"
|
|
17
27
|
],
|
|
18
28
|
"devDependencies": {
|
|
19
|
-
"@
|
|
20
|
-
"@servicetitan/
|
|
29
|
+
"@datadog/browser-rum": "^6.32.0",
|
|
30
|
+
"@servicetitan/log-service": "36.2.0",
|
|
31
|
+
"@servicetitan/react-ioc": "36.2.0",
|
|
21
32
|
"@testing-library/dom": "^10.4.1",
|
|
22
33
|
"@testing-library/jest-dom": "^6.9.1",
|
|
23
34
|
"@testing-library/react": "^16.3.2",
|
|
@@ -28,19 +39,25 @@
|
|
|
28
39
|
"react": "~18.3.1"
|
|
29
40
|
},
|
|
30
41
|
"peerDependencies": {
|
|
31
|
-
"@
|
|
32
|
-
"@servicetitan/
|
|
42
|
+
"@datadog/browser-rum": ">=6.19.0",
|
|
43
|
+
"@servicetitan/log-service": "36.2.0",
|
|
44
|
+
"@servicetitan/react-ioc": "36.2.0",
|
|
33
45
|
"launchdarkly-js-client-sdk": "3.5.0",
|
|
34
46
|
"launchdarkly-react-client-sdk": "3.6.1",
|
|
35
47
|
"mobx": ">= 6.13.5",
|
|
36
48
|
"mobx-react": ">=9.2.0",
|
|
37
49
|
"react": ">=17.0.2"
|
|
38
50
|
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"@datadog/browser-rum": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
39
56
|
"publishConfig": {
|
|
40
57
|
"access": "public"
|
|
41
58
|
},
|
|
42
59
|
"cli": {
|
|
43
60
|
"webpack": false
|
|
44
61
|
},
|
|
45
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "87254b2caf60b20eb46c33b40ffb4dd05ad3e0f7"
|
|
46
63
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { datadogRum } from '@datadog/browser-rum';
|
|
2
|
+
import type { LDContext, LDEvaluationDetail } from 'launchdarkly-js-client-sdk';
|
|
3
|
+
import { createDatadogInspector } from '../create-datadog-inspector';
|
|
4
|
+
|
|
5
|
+
jest.mock('@datadog/browser-rum', () => ({
|
|
6
|
+
datadogRum: { addFeatureFlagEvaluation: jest.fn() },
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
describe('[launchdarkly-service] createDatadogInspector', () => {
|
|
10
|
+
let options: Parameters<typeof createDatadogInspector>[0];
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
options = undefined;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const subject = () => createDatadogInspector(options);
|
|
18
|
+
|
|
19
|
+
test('returns a flag-used inspector', () => {
|
|
20
|
+
expect(subject().type).toBe('flag-used');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('uses default name', () => {
|
|
24
|
+
expect(subject().name).toBe('datadog-feature-flag-tracking');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('when name is provided', () => {
|
|
28
|
+
beforeEach(() => (options = { name: 'foo' }));
|
|
29
|
+
|
|
30
|
+
test('uses provided name', () => {
|
|
31
|
+
expect(subject().name).toBe('foo');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('method', () => {
|
|
36
|
+
const flagKey = 'foo';
|
|
37
|
+
const detail: LDEvaluationDetail = { value: true, variationIndex: 1 };
|
|
38
|
+
const context: LDContext = {};
|
|
39
|
+
|
|
40
|
+
test('forwards key and value to datadogRum.addFeatureFlagEvaluation', () => {
|
|
41
|
+
subject().method(flagKey, detail, context);
|
|
42
|
+
|
|
43
|
+
expect(datadogRum.addFeatureFlagEvaluation).toHaveBeenCalledWith(flagKey, detail.value);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { datadogRum } from '@datadog/browser-rum';
|
|
2
|
+
import type { LDInspectionFlagUsedHandler } from 'launchdarkly-js-client-sdk';
|
|
3
|
+
|
|
4
|
+
export interface CreateDatadogInspectorOptions {
|
|
5
|
+
name?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function createDatadogInspector(
|
|
9
|
+
options: CreateDatadogInspectorOptions = {}
|
|
10
|
+
): LDInspectionFlagUsedHandler {
|
|
11
|
+
return {
|
|
12
|
+
type: 'flag-used',
|
|
13
|
+
name: options.name ?? 'datadog-feature-flag-tracking',
|
|
14
|
+
method: (key, detail) => {
|
|
15
|
+
datadogRum.addFeatureFlagEvaluation(key, detail.value);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './create-datadog-inspector';
|
package/src/ld-provider.tsx
CHANGED
|
@@ -9,13 +9,24 @@ import { FC, PropsWithChildren, useEffect, useReducer, useState } from 'react';
|
|
|
9
9
|
import { FeatureFlagProvider } from './feature-flag-provider';
|
|
10
10
|
import { FeatureFlagStore } from './feature-flag-store';
|
|
11
11
|
import { getLDClient } from './get-ld-client';
|
|
12
|
-
import { LD_SERVICE_TOKEN } from './types';
|
|
12
|
+
import { LDService, LD_SERVICE_TOKEN } from './types';
|
|
13
13
|
|
|
14
14
|
export type ProviderConfig = Pick<BaseProviderConfig, 'clientSideID' | 'context'> & {
|
|
15
15
|
projectName: string;
|
|
16
16
|
waitForInitialization?: boolean;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
function mergeOptions(ldService: LDService | undefined): BaseProviderConfig['options'] {
|
|
20
|
+
const { application, options } = ldService ?? {};
|
|
21
|
+
if (!options && application === undefined) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
...options,
|
|
26
|
+
...(application !== undefined ? { application } : {}),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
export const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({
|
|
20
31
|
singletons: [FeatureFlagStore],
|
|
21
32
|
})(props => {
|
|
@@ -26,9 +37,7 @@ export const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({
|
|
|
26
37
|
|
|
27
38
|
useEffect(() => {
|
|
28
39
|
(async function initializeClient() {
|
|
29
|
-
const options = ldService
|
|
30
|
-
? { application: ldService.application }
|
|
31
|
-
: undefined;
|
|
40
|
+
const options = mergeOptions(ldService);
|
|
32
41
|
const config: BaseProviderConfig = { clientSideID, context, options };
|
|
33
42
|
const client = ldService?.getClient(config) ?? getLDClient(config);
|
|
34
43
|
if (waitForInitialization) {
|
|
@@ -39,7 +48,7 @@ export const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({
|
|
|
39
48
|
category: 'LaunchDarkly',
|
|
40
49
|
code: 'InitializationFailed',
|
|
41
50
|
error,
|
|
42
|
-
message:
|
|
51
|
+
message: `${projectName} client failed to initialize`,
|
|
43
52
|
});
|
|
44
53
|
}
|
|
45
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { provide, useDependencies } from '@servicetitan/react-ioc';
|
|
2
|
+
import { LDOptions } from 'launchdarkly-js-client-sdk';
|
|
2
3
|
import { FC, PropsWithChildren, useEffect, useRef } from 'react';
|
|
3
4
|
import { getLDClient } from './get-ld-client';
|
|
4
5
|
import { LD_SERVICE_TOKEN, LDApplication, LDService } from './types';
|
|
@@ -9,22 +10,29 @@ const ldService: LDService = {
|
|
|
9
10
|
|
|
10
11
|
interface LDServiceProviderProps {
|
|
11
12
|
application?: LDApplication;
|
|
13
|
+
options?: Omit<LDOptions, 'application'>;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const LDServiceProvider: FC<PropsWithChildren<LDServiceProviderProps>> = provide({
|
|
15
17
|
singletons: [{ provide: LD_SERVICE_TOKEN, useValue: ldService, inherit: true }],
|
|
16
|
-
})(({ children, application }) => {
|
|
18
|
+
})(({ children, application, options }) => {
|
|
17
19
|
const [providedLDService] = useDependencies(LD_SERVICE_TOKEN);
|
|
18
20
|
const prevApplication = useRef(providedLDService.application);
|
|
21
|
+
const prevOptions = useRef(providedLDService.options);
|
|
19
22
|
|
|
20
23
|
if (providedLDService === ldService || !providedLDService.application) {
|
|
21
24
|
providedLDService.application = application;
|
|
22
25
|
}
|
|
26
|
+
if (providedLDService === ldService || !providedLDService.options) {
|
|
27
|
+
providedLDService.options = options;
|
|
28
|
+
}
|
|
23
29
|
|
|
24
30
|
useEffect(() => {
|
|
25
31
|
return () => {
|
|
26
32
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
33
|
providedLDService.application = prevApplication.current;
|
|
34
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
+
providedLDService.options = prevOptions.current;
|
|
28
36
|
};
|
|
29
37
|
}, [providedLDService]);
|
|
30
38
|
|
|
@@ -169,7 +169,7 @@ describe(`[launchdarkly-service] ${LDProvider.name}`, () => {
|
|
|
169
169
|
category: 'LaunchDarkly',
|
|
170
170
|
code: 'InitializationFailed',
|
|
171
171
|
error: 'Oops!',
|
|
172
|
-
message:
|
|
172
|
+
message: `${props.projectName} client failed to initialize`,
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
175
|
});
|
|
@@ -198,6 +198,22 @@ describe(`[launchdarkly-service] ${LDProvider.name}`, () => {
|
|
|
198
198
|
expect(ldServiceClient.waitForInitialization).toHaveBeenCalled();
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
+
describe('when LDService has options', () => {
|
|
202
|
+
const options: LDService['options'] = { hash: 'abc123' };
|
|
203
|
+
|
|
204
|
+
beforeEach(() => (ldService.options = options));
|
|
205
|
+
|
|
206
|
+
test('forwards options to LDClient', async () => {
|
|
207
|
+
subject();
|
|
208
|
+
|
|
209
|
+
await waitFor(() => expectLDProvider());
|
|
210
|
+
|
|
211
|
+
expect(ldService.getClient).toHaveBeenCalledWith(
|
|
212
|
+
expect.objectContaining({ options })
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
201
217
|
describe('when application is provided', () => {
|
|
202
218
|
const application = { id: 'some-application', version: '1.2.3' };
|
|
203
219
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Provider, useDependencies } from '@servicetitan/react-ioc';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
|
+
import { LDOptions } from 'launchdarkly-js-client-sdk';
|
|
3
4
|
import { ComponentProps, FC } from 'react';
|
|
4
5
|
import { getLDClient } from '../get-ld-client';
|
|
5
6
|
import { LDServiceProvider } from '../ld-service-provider';
|
|
@@ -7,6 +8,7 @@ import { LD_SERVICE_TOKEN, LDApplication, LDService } from '../types';
|
|
|
7
8
|
|
|
8
9
|
describe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {
|
|
9
10
|
let providedApplication: LDApplication | undefined;
|
|
11
|
+
let providedOptions: LDOptions | undefined;
|
|
10
12
|
let providedLDService: LDService;
|
|
11
13
|
let singletons: Parameters<typeof Provider>[0]['singletons'];
|
|
12
14
|
let props: ComponentProps<typeof LDServiceProvider>;
|
|
@@ -14,6 +16,7 @@ describe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {
|
|
|
14
16
|
const Component: FC = () => {
|
|
15
17
|
[providedLDService] = useDependencies(LD_SERVICE_TOKEN);
|
|
16
18
|
providedApplication = providedLDService.application;
|
|
19
|
+
providedOptions = providedLDService.options;
|
|
17
20
|
return null;
|
|
18
21
|
};
|
|
19
22
|
|
|
@@ -44,6 +47,12 @@ describe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {
|
|
|
44
47
|
expect(providedApplication).toBeUndefined();
|
|
45
48
|
});
|
|
46
49
|
|
|
50
|
+
test('provides no options', () => {
|
|
51
|
+
subject();
|
|
52
|
+
|
|
53
|
+
expect(providedOptions).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
47
56
|
describe('when LDService is already provided', () => {
|
|
48
57
|
const ldService: LDService = { getClient: jest.fn() };
|
|
49
58
|
|
|
@@ -142,4 +151,108 @@ describe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {
|
|
|
142
151
|
});
|
|
143
152
|
});
|
|
144
153
|
});
|
|
154
|
+
|
|
155
|
+
describe('with "options" prop', () => {
|
|
156
|
+
const options: LDOptions = { hash: 'abc123' };
|
|
157
|
+
|
|
158
|
+
beforeEach(() => (props.options = options));
|
|
159
|
+
|
|
160
|
+
function itProvidesOptions() {
|
|
161
|
+
test('provides options', () => {
|
|
162
|
+
subject();
|
|
163
|
+
|
|
164
|
+
expect(providedOptions).toBe(options);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
itProvidesOptions();
|
|
169
|
+
|
|
170
|
+
describe('when rerendered with new options', () => {
|
|
171
|
+
const newOptions: LDOptions = { evaluationReasons: true };
|
|
172
|
+
|
|
173
|
+
beforeEach(() => {
|
|
174
|
+
const { rerender } = subject();
|
|
175
|
+
|
|
176
|
+
rerender(
|
|
177
|
+
<LDServiceProvider options={newOptions}>
|
|
178
|
+
<Component />
|
|
179
|
+
</LDServiceProvider>
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('provides new options', () => {
|
|
184
|
+
expect(providedOptions).toBe(newOptions);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('when rerendered without options', () => {
|
|
189
|
+
beforeEach(() => {
|
|
190
|
+
const { rerender } = subject();
|
|
191
|
+
|
|
192
|
+
rerender(
|
|
193
|
+
<LDServiceProvider>
|
|
194
|
+
<Component />
|
|
195
|
+
</LDServiceProvider>
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('drops options', () => {
|
|
200
|
+
expect(providedOptions).toBeUndefined();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('when LDService is already provided', () => {
|
|
205
|
+
let parentLDService: LDService;
|
|
206
|
+
|
|
207
|
+
beforeEach(() => {
|
|
208
|
+
parentLDService = { getClient: jest.fn() };
|
|
209
|
+
singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: parentLDService });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
itProvidesOptions();
|
|
213
|
+
|
|
214
|
+
test('unmount resets LDService.options', () => {
|
|
215
|
+
const { unmount } = subject();
|
|
216
|
+
unmount();
|
|
217
|
+
|
|
218
|
+
expect(parentLDService.options).toBeUndefined();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('when provided LDService has options', () => {
|
|
222
|
+
const otherOptions: LDOptions = { evaluationReasons: true };
|
|
223
|
+
|
|
224
|
+
beforeEach(() => (parentLDService.options = otherOptions));
|
|
225
|
+
|
|
226
|
+
test('provides inherited options', () => {
|
|
227
|
+
subject();
|
|
228
|
+
|
|
229
|
+
expect(providedOptions).toBe(otherOptions);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('unmount preserves LDService.options', () => {
|
|
233
|
+
const { unmount } = subject();
|
|
234
|
+
unmount();
|
|
235
|
+
|
|
236
|
+
expect(parentLDService.options).toBe(otherOptions);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('with both "application" and "options" props', () => {
|
|
243
|
+
const application = { id: 'some-application', version: '1.0.0' };
|
|
244
|
+
const options: LDOptions = { hash: 'abc123' };
|
|
245
|
+
|
|
246
|
+
beforeEach(() => {
|
|
247
|
+
props.application = application;
|
|
248
|
+
props.options = options;
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('provides both application and options', () => {
|
|
252
|
+
subject();
|
|
253
|
+
|
|
254
|
+
expect(providedApplication).toBe(application);
|
|
255
|
+
expect(providedOptions).toBe(options);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
145
258
|
});
|
package/src/types.ts
CHANGED