@servicetitan/launchdarkly-service 39.3.0 → 40.1.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.
Files changed (68) hide show
  1. package/dist/create-ld-client.d.ts +4 -0
  2. package/dist/create-ld-client.d.ts.map +1 -0
  3. package/dist/create-ld-client.js +12 -0
  4. package/dist/create-ld-client.js.map +1 -0
  5. package/dist/get-client.d.ts +4 -0
  6. package/dist/get-client.d.ts.map +1 -0
  7. package/dist/get-client.js +13 -0
  8. package/dist/get-client.js.map +1 -0
  9. package/dist/get-ld-client.d.ts +1 -1
  10. package/dist/get-ld-client.d.ts.map +1 -1
  11. package/dist/get-ld-client.js +8 -15
  12. package/dist/get-ld-client.js.map +1 -1
  13. package/dist/get-sdk-version.d.ts +2 -0
  14. package/dist/get-sdk-version.d.ts.map +1 -0
  15. package/dist/get-sdk-version.js +11 -0
  16. package/dist/get-sdk-version.js.map +1 -0
  17. package/dist/get-version-client.d.ts +4 -0
  18. package/dist/get-version-client.d.ts.map +1 -0
  19. package/dist/get-version-client.js +12 -0
  20. package/dist/get-version-client.js.map +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/ld-provider.js +2 -3
  26. package/dist/ld-provider.js.map +1 -1
  27. package/dist/ld-service-provider.d.ts.map +1 -1
  28. package/dist/ld-service-provider.js +3 -1
  29. package/dist/ld-service-provider.js.map +1 -1
  30. package/dist/tests/create-ld-client.test.d.ts +2 -0
  31. package/dist/tests/create-ld-client.test.d.ts.map +1 -0
  32. package/dist/tests/create-ld-client.test.js +58 -0
  33. package/dist/tests/create-ld-client.test.js.map +1 -0
  34. package/dist/tests/get-client.test.d.ts +2 -0
  35. package/dist/tests/get-client.test.d.ts.map +1 -0
  36. package/dist/tests/get-client.test.js +81 -0
  37. package/dist/tests/get-client.test.js.map +1 -0
  38. package/dist/tests/get-ld-client.test.js +24 -41
  39. package/dist/tests/get-ld-client.test.js.map +1 -1
  40. package/dist/tests/get-sdk-version.test.d.ts +2 -0
  41. package/dist/tests/get-sdk-version.test.d.ts.map +1 -0
  42. package/dist/tests/get-sdk-version.test.js +41 -0
  43. package/dist/tests/get-sdk-version.test.js.map +1 -0
  44. package/dist/tests/get-version-client.test.d.ts +2 -0
  45. package/dist/tests/get-version-client.test.d.ts.map +1 -0
  46. package/dist/tests/get-version-client.test.js +63 -0
  47. package/dist/tests/get-version-client.test.js.map +1 -0
  48. package/dist/tests/ld-service-provider.test.js +3 -1
  49. package/dist/tests/ld-service-provider.test.js.map +1 -1
  50. package/dist/types.d.ts +7 -0
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js.map +1 -1
  53. package/package.json +11 -11
  54. package/src/create-ld-client.ts +8 -0
  55. package/src/get-client.ts +13 -0
  56. package/src/get-ld-client.ts +8 -11
  57. package/src/get-sdk-version.ts +10 -0
  58. package/src/get-version-client.ts +18 -0
  59. package/src/index.ts +2 -0
  60. package/src/ld-provider.tsx +2 -2
  61. package/src/ld-service-provider.tsx +2 -0
  62. package/src/tests/create-ld-client.test.ts +64 -0
  63. package/src/tests/get-client.test.ts +98 -0
  64. package/src/tests/get-ld-client.test.ts +30 -44
  65. package/src/tests/get-sdk-version.test.ts +48 -0
  66. package/src/tests/get-version-client.test.ts +70 -0
  67. package/src/tests/ld-service-provider.test.tsx +2 -1
  68. package/src/types.ts +11 -0
@@ -0,0 +1,41 @@
1
+ import { getSDKVersion } from '../get-sdk-version';
2
+ let mockSdkVersion;
3
+ let mockPackageJsonVersion;
4
+ jest.mock('launchdarkly-js-client-sdk', ()=>({
5
+ get version () {
6
+ return mockSdkVersion;
7
+ }
8
+ }));
9
+ jest.mock('launchdarkly-js-client-sdk/package.json', ()=>({
10
+ get version () {
11
+ return mockPackageJsonVersion;
12
+ }
13
+ }));
14
+ describe(`[launchdarkly-service] ${getSDKVersion.name}`, ()=>{
15
+ beforeEach(()=>{
16
+ mockSdkVersion = '3.9.5';
17
+ mockPackageJsonVersion = '3.9.6';
18
+ });
19
+ const subject = ()=>getSDKVersion();
20
+ test('returns the version exported by the running SDK', ()=>{
21
+ expect(subject()).toBe(mockSdkVersion);
22
+ });
23
+ describe('when the running SDK does not export a version', ()=>{
24
+ beforeEach(()=>{
25
+ mockSdkVersion = undefined;
26
+ });
27
+ test('returns the version from package.json instead', ()=>{
28
+ expect(subject()).toBe(mockPackageJsonVersion);
29
+ });
30
+ describe('when package.json also has no version', ()=>{
31
+ beforeEach(()=>{
32
+ mockPackageJsonVersion = undefined;
33
+ });
34
+ test('returns undefined', ()=>{
35
+ expect(subject()).toBeUndefined();
36
+ });
37
+ });
38
+ });
39
+ });
40
+
41
+ //# sourceMappingURL=get-sdk-version.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tests/get-sdk-version.test.ts"],"sourcesContent":["import { getSDKVersion } from '../get-sdk-version';\n\nlet mockSdkVersion: string | undefined;\nlet mockPackageJsonVersion: string | undefined;\n\njest.mock('launchdarkly-js-client-sdk', () => ({\n get version() {\n return mockSdkVersion;\n },\n}));\njest.mock('launchdarkly-js-client-sdk/package.json', () => ({\n get version() {\n return mockPackageJsonVersion;\n },\n}));\n\ndescribe(`[launchdarkly-service] ${getSDKVersion.name}`, () => {\n beforeEach(() => {\n mockSdkVersion = '3.9.5';\n mockPackageJsonVersion = '3.9.6';\n });\n\n const subject = () => getSDKVersion();\n\n test('returns the version exported by the running SDK', () => {\n expect(subject()).toBe(mockSdkVersion);\n });\n\n describe('when the running SDK does not export a version', () => {\n beforeEach(() => {\n mockSdkVersion = undefined;\n });\n\n test('returns the version from package.json instead', () => {\n expect(subject()).toBe(mockPackageJsonVersion);\n });\n\n describe('when package.json also has no version', () => {\n beforeEach(() => {\n mockPackageJsonVersion = undefined;\n });\n\n test('returns undefined', () => {\n expect(subject()).toBeUndefined();\n });\n });\n });\n});\n"],"names":["getSDKVersion","mockSdkVersion","mockPackageJsonVersion","jest","mock","version","describe","name","beforeEach","subject","test","expect","toBe","undefined","toBeUndefined"],"mappings":"AAAA,SAASA,aAAa,QAAQ,qBAAqB;AAEnD,IAAIC;AACJ,IAAIC;AAEJC,KAAKC,IAAI,CAAC,8BAA8B,IAAO,CAAA;QAC3C,IAAIC;YACA,OAAOJ;QACX;IACJ,CAAA;AACAE,KAAKC,IAAI,CAAC,2CAA2C,IAAO,CAAA;QACxD,IAAIC;YACA,OAAOH;QACX;IACJ,CAAA;AAEAI,SAAS,CAAC,uBAAuB,EAAEN,cAAcO,IAAI,EAAE,EAAE;IACrDC,WAAW;QACPP,iBAAiB;QACjBC,yBAAyB;IAC7B;IAEA,MAAMO,UAAU,IAAMT;IAEtBU,KAAK,mDAAmD;QACpDC,OAAOF,WAAWG,IAAI,CAACX;IAC3B;IAEAK,SAAS,kDAAkD;QACvDE,WAAW;YACPP,iBAAiBY;QACrB;QAEAH,KAAK,iDAAiD;YAClDC,OAAOF,WAAWG,IAAI,CAACV;QAC3B;QAEAI,SAAS,yCAAyC;YAC9CE,WAAW;gBACPN,yBAAyBW;YAC7B;YAEAH,KAAK,qBAAqB;gBACtBC,OAAOF,WAAWK,aAAa;YACnC;QACJ;IACJ;AACJ"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=get-version-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-version-client.test.d.ts","sourceRoot":"","sources":["../../src/tests/get-version-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,63 @@
1
+ import { getVersionClient } from '../get-version-client';
2
+ describe(`[launchdarkly-service] ${getVersionClient.name}`, ()=>{
3
+ let version;
4
+ let config;
5
+ let createClient;
6
+ let client;
7
+ const subject = ()=>getVersionClient(version, config, createClient);
8
+ beforeEach(()=>{
9
+ version = '3.9.5';
10
+ config = {
11
+ clientSideID: makeId(),
12
+ context: {
13
+ kind: 'multi'
14
+ }
15
+ };
16
+ client = Symbol('client');
17
+ createClient = jest.fn().mockReturnValue(client);
18
+ });
19
+ test('returns the client created by createClient', ()=>{
20
+ expect(subject()).toBe(client);
21
+ });
22
+ test('creates the client using the given config', ()=>{
23
+ subject();
24
+ expect(createClient).toHaveBeenCalledWith(config);
25
+ });
26
+ describe('when a client already exists', ()=>{
27
+ const newClient = Symbol('newClient');
28
+ beforeEach(()=>{
29
+ subject();
30
+ createClient.mockClear();
31
+ createClient.mockReturnValue(newClient);
32
+ });
33
+ test('returns the existing client', ()=>{
34
+ expect(subject()).toBe(client);
35
+ expect(createClient).not.toHaveBeenCalled();
36
+ });
37
+ function itCreatesNewClient() {
38
+ test('creates a new client', ()=>{
39
+ expect(subject()).toBe(newClient);
40
+ });
41
+ }
42
+ describe('when clientSideID differs', ()=>{
43
+ beforeEach(()=>{
44
+ config = {
45
+ ...config,
46
+ clientSideID: makeId()
47
+ };
48
+ });
49
+ itCreatesNewClient();
50
+ });
51
+ describe('when version differs', ()=>{
52
+ beforeEach(()=>{
53
+ version = '3.5.0';
54
+ });
55
+ itCreatesNewClient();
56
+ });
57
+ });
58
+ });
59
+ function makeId() {
60
+ return Math.round(1e6 * Math.random()).toString(36);
61
+ }
62
+
63
+ //# sourceMappingURL=get-version-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tests/get-version-client.test.ts"],"sourcesContent":["import { LDClient } from 'launchdarkly-js-client-sdk';\nimport { getVersionClient } from '../get-version-client';\nimport { ClientConfig } from '../types';\n\ndescribe(`[launchdarkly-service] ${getVersionClient.name}`, () => {\n let version: string;\n let config: ClientConfig;\n let createClient: jest.Mock<LDClient, [ClientConfig]>;\n let client: LDClient;\n\n const subject = () => getVersionClient(version, config, createClient);\n\n beforeEach(() => {\n version = '3.9.5';\n config = { clientSideID: makeId(), context: { kind: 'multi' } };\n client = Symbol('client') as unknown as LDClient;\n createClient = jest.fn().mockReturnValue(client);\n });\n\n test('returns the client created by createClient', () => {\n expect(subject()).toBe(client);\n });\n\n test('creates the client using the given config', () => {\n subject();\n\n expect(createClient).toHaveBeenCalledWith(config);\n });\n\n describe('when a client already exists', () => {\n const newClient = Symbol('newClient') as unknown as LDClient;\n\n beforeEach(() => {\n subject();\n createClient.mockClear();\n createClient.mockReturnValue(newClient);\n });\n\n test('returns the existing client', () => {\n expect(subject()).toBe(client);\n expect(createClient).not.toHaveBeenCalled();\n });\n\n function itCreatesNewClient() {\n test('creates a new client', () => {\n expect(subject()).toBe(newClient);\n });\n }\n\n describe('when clientSideID differs', () => {\n beforeEach(() => {\n config = { ...config, clientSideID: makeId() };\n });\n\n itCreatesNewClient();\n });\n\n describe('when version differs', () => {\n beforeEach(() => {\n version = '3.5.0';\n });\n\n itCreatesNewClient();\n });\n });\n});\n\nfunction makeId() {\n return Math.round(1e6 * Math.random()).toString(36);\n}\n"],"names":["getVersionClient","describe","name","version","config","createClient","client","subject","beforeEach","clientSideID","makeId","context","kind","Symbol","jest","fn","mockReturnValue","test","expect","toBe","toHaveBeenCalledWith","newClient","mockClear","not","toHaveBeenCalled","itCreatesNewClient","Math","round","random","toString"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,wBAAwB;AAGzDC,SAAS,CAAC,uBAAuB,EAAED,iBAAiBE,IAAI,EAAE,EAAE;IACxD,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJ,MAAMC,UAAU,IAAMP,iBAAiBG,SAASC,QAAQC;IAExDG,WAAW;QACPL,UAAU;QACVC,SAAS;YAAEK,cAAcC;YAAUC,SAAS;gBAAEC,MAAM;YAAQ;QAAE;QAC9DN,SAASO,OAAO;QAChBR,eAAeS,KAAKC,EAAE,GAAGC,eAAe,CAACV;IAC7C;IAEAW,KAAK,8CAA8C;QAC/CC,OAAOX,WAAWY,IAAI,CAACb;IAC3B;IAEAW,KAAK,6CAA6C;QAC9CV;QAEAW,OAAOb,cAAce,oBAAoB,CAAChB;IAC9C;IAEAH,SAAS,gCAAgC;QACrC,MAAMoB,YAAYR,OAAO;QAEzBL,WAAW;YACPD;YACAF,aAAaiB,SAAS;YACtBjB,aAAaW,eAAe,CAACK;QACjC;QAEAJ,KAAK,+BAA+B;YAChCC,OAAOX,WAAWY,IAAI,CAACb;YACvBY,OAAOb,cAAckB,GAAG,CAACC,gBAAgB;QAC7C;QAEA,SAASC;YACLR,KAAK,wBAAwB;gBACzBC,OAAOX,WAAWY,IAAI,CAACE;YAC3B;QACJ;QAEApB,SAAS,6BAA6B;YAClCO,WAAW;gBACPJ,SAAS;oBAAE,GAAGA,MAAM;oBAAEK,cAAcC;gBAAS;YACjD;YAEAe;QACJ;QAEAxB,SAAS,wBAAwB;YAC7BO,WAAW;gBACPL,UAAU;YACd;YAEAsB;QACJ;IACJ;AACJ;AAEA,SAASf;IACL,OAAOgB,KAAKC,KAAK,CAAC,MAAMD,KAAKE,MAAM,IAAIC,QAAQ,CAAC;AACpD"}
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Provider, useDependencies } from '@servicetitan/react-ioc';
3
3
  import { render } from '@testing-library/react';
4
4
  import { getLDClient } from '../get-ld-client';
5
+ import { getVersionClient } from '../get-version-client';
5
6
  import { LDServiceProvider } from '../ld-service-provider';
6
7
  import { LD_SERVICE_TOKEN } from '../types';
7
8
  describe(`[launchdarkly-service] ${LDServiceProvider.name}`, ()=>{
@@ -32,7 +33,8 @@ describe(`[launchdarkly-service] ${LDServiceProvider.name}`, ()=>{
32
33
  test('provides LDService instance', ()=>{
33
34
  subject();
34
35
  expect(providedLDService).toEqual({
35
- getClient: getLDClient
36
+ getClient: getLDClient,
37
+ getVersionClient
36
38
  });
37
39
  });
38
40
  test('provides no application', ()=>{
@@ -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 { 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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"}
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 { getVersionClient } from '../get-version-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, getVersionClient });\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","getVersionClient","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":";AAAA,SAASA,QAAQ,EAAEC,eAAe,QAAQ,0BAA0B;AACpE,SAASC,MAAM,QAAQ,yBAAyB;AAGhD,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,gBAAgB,QAAQ,wBAAwB;AACzD,SAASC,iBAAiB,QAAQ,yBAAyB;AAC3D,SAASC,gBAAgB,QAAkC,WAAW;AAEtEC,SAAS,CAAC,uBAAuB,EAAEF,kBAAkBG,IAAI,EAAE,EAAE;IACzD,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJ,MAAMC,YAAgB;QAClB,CAACH,kBAAkB,GAAGV,gBAAgBK;QACtCG,sBAAsBE,kBAAkBI,WAAW;QACnDL,kBAAkBC,kBAAkBK,OAAO;QAC3C,OAAO;IACX;IAEAC,WAAW;QACPL,aAAa,EAAE;QACfC,QAAQ,CAAC;IACb;IAEA,MAAMK,UAAU;QACZ,OAAOhB,qBACH,KAACF;YAASY,YAAYA;sBAClB,cAAA,KAACP;gBAAmB,GAAGQ,KAAK;0BACxB,cAAA,KAACC;;;IAIjB;IAEAK,KAAK,+BAA+B;QAChCD;QAEAE,OAAOT,mBAAmBU,OAAO,CAAC;YAAEC,WAAWnB;YAAaC;QAAiB;IACjF;IAEAe,KAAK,2BAA2B;QAC5BD;QAEAE,OAAOX,qBAAqBc,aAAa;IAC7C;IAEAJ,KAAK,uBAAuB;QACxBD;QAEAE,OAAOV,iBAAiBa,aAAa;IACzC;IAEAhB,SAAS,sCAAsC;QAC3C,MAAMiB,YAAuB;YAAEF,WAAWG,KAAKC,EAAE;QAAG;QAEpDT,WAAW,IAAML,uBAAAA,iCAAAA,WAAYe,IAAI,CAAC;gBAAEC,SAAStB;gBAAkBuB,UAAUL;YAAU;QAEnFL,KAAK,+BAA+B;YAChCD;YAEAE,OAAOT,mBAAmBmB,IAAI,CAACN;QACnC;IACJ;IAEAjB,SAAS,2BAA2B;QAChC,MAAMQ,cAAc;YAAEgB,IAAI;YAAoBC,SAAS;QAAQ;QAE/Df,WAAW,IAAOJ,MAAME,WAAW,GAAGA;QAEtC,SAASkB;YACLd,KAAK,wBAAwB;gBACzBD;gBAEAE,OAAOX,qBAAqBqB,IAAI,CAACf;YACrC;QACJ;QAEAkB;QAEA1B,SAAS,wCAAwC;YAC7C,MAAM2B,iBAAiB;gBAAEH,IAAI;YAAM;YAEnCd,WAAW;gBACP,MAAM,EAAEkB,QAAQ,EAAE,GAAGjB;gBAErBiB,uBACI,KAAC9B;oBAAkBU,aAAamB;8BAC5B,cAAA,KAACpB;;YAGb;YAEAK,KAAK,4BAA4B;gBAC7BC,OAAOX,qBAAqBqB,IAAI,CAACI;YACrC;QACJ;QAEA3B,SAAS,uCAAuC;YAC5CU,WAAW;gBACP,MAAM,EAAEkB,QAAQ,EAAE,GAAGjB;gBAErBiB,uBACI,KAAC9B;8BACG,cAAA,KAACS;;YAGb;YAEAK,KAAK,qBAAqB;gBACtBC,OAAOX,qBAAqBc,aAAa;YAC7C;QACJ;QAEAhB,SAAS,sCAAsC;YAC3C,IAAII;YAEJM,WAAW;gBACPN,oBAAoB;oBAAEW,WAAWG,KAAKC,EAAE;gBAAG;gBAC3Cd,uBAAAA,iCAAAA,WAAYe,IAAI,CAAC;oBAAEC,SAAStB;oBAAkBuB,UAAUlB;gBAAkB;YAC9E;YAEAsB;YAEAd,KAAK,wCAAwC;gBACzC,MAAM,EAAEiB,OAAO,EAAE,GAAGlB;gBACpBkB;gBAEAhB,OAAOT,kBAAkBI,WAAW,EAAEQ,aAAa;YACvD;YAEAhB,SAAS,2CAA2C;gBAChD,MAAM8B,mBAAmB;oBAAEN,IAAI;oBAAqBC,SAAS;gBAAQ;gBAErEf,WAAW,IAAON,kBAAkBI,WAAW,GAAGsB;gBAElDlB,KAAK,kCAAkC;oBACnCD;oBAEAE,OAAOX,qBAAqBqB,IAAI,CAACO;gBACrC;gBAEAlB,KAAK,2CAA2C;oBAC5C,MAAM,EAAEiB,OAAO,EAAE,GAAGlB;oBACpBkB;oBAEAhB,OAAOT,kBAAkBI,WAAW,EAAEe,IAAI,CAACO;gBAC/C;YACJ;QACJ;IACJ;IAEA9B,SAAS,uBAAuB;QAC5B,MAAMS,UAAqB;YAAEsB,MAAM;QAAS;QAE5CrB,WAAW,IAAOJ,MAAMG,OAAO,GAAGA;QAElC,SAASuB;YACLpB,KAAK,oBAAoB;gBACrBD;gBAEAE,OAAOV,iBAAiBoB,IAAI,CAACd;YACjC;QACJ;QAEAuB;QAEAhC,SAAS,oCAAoC;YACzC,MAAMiC,aAAwB;gBAAEC,mBAAmB;YAAK;YAExDxB,WAAW;gBACP,MAAM,EAAEkB,QAAQ,EAAE,GAAGjB;gBAErBiB,uBACI,KAAC9B;oBAAkBW,SAASwB;8BACxB,cAAA,KAAC1B;;YAGb;YAEAK,KAAK,wBAAwB;gBACzBC,OAAOV,iBAAiBoB,IAAI,CAACU;YACjC;QACJ;QAEAjC,SAAS,mCAAmC;YACxCU,WAAW;gBACP,MAAM,EAAEkB,QAAQ,EAAE,GAAGjB;gBAErBiB,uBACI,KAAC9B;8BACG,cAAA,KAACS;;YAGb;YAEAK,KAAK,iBAAiB;gBAClBC,OAAOV,iBAAiBa,aAAa;YACzC;QACJ;QAEAhB,SAAS,sCAAsC;YAC3C,IAAImC;YAEJzB,WAAW;gBACPyB,kBAAkB;oBAAEpB,WAAWG,KAAKC,EAAE;gBAAG;gBACzCd,uBAAAA,iCAAAA,WAAYe,IAAI,CAAC;oBAAEC,SAAStB;oBAAkBuB,UAAUa;gBAAgB;YAC5E;YAEAH;YAEApB,KAAK,oCAAoC;gBACrC,MAAM,EAAEiB,OAAO,EAAE,GAAGlB;gBACpBkB;gBAEAhB,OAAOsB,gBAAgB1B,OAAO,EAAEO,aAAa;YACjD;YAEAhB,SAAS,uCAAuC;gBAC5C,MAAMoC,eAA0B;oBAAEF,mBAAmB;gBAAK;gBAE1DxB,WAAW,IAAOyB,gBAAgB1B,OAAO,GAAG2B;gBAE5CxB,KAAK,8BAA8B;oBAC/BD;oBAEAE,OAAOV,iBAAiBoB,IAAI,CAACa;gBACjC;gBAEAxB,KAAK,uCAAuC;oBACxC,MAAM,EAAEiB,OAAO,EAAE,GAAGlB;oBACpBkB;oBAEAhB,OAAOsB,gBAAgB1B,OAAO,EAAEc,IAAI,CAACa;gBACzC;YACJ;QACJ;IACJ;IAEApC,SAAS,+CAA+C;QACpD,MAAMQ,cAAc;YAAEgB,IAAI;YAAoBC,SAAS;QAAQ;QAC/D,MAAMhB,UAAqB;YAAEsB,MAAM;QAAS;QAE5CrB,WAAW;YACPJ,MAAME,WAAW,GAAGA;YACpBF,MAAMG,OAAO,GAAGA;QACpB;QAEAG,KAAK,yCAAyC;YAC1CD;YAEAE,OAAOX,qBAAqBqB,IAAI,CAACf;YACjCK,OAAOV,iBAAiBoB,IAAI,CAACd;QACjC;IACJ;AACJ"}
package/dist/types.d.ts CHANGED
@@ -8,6 +8,13 @@ export interface ClientConfig {
8
8
  }
9
9
  export interface LDService {
10
10
  getClient: (config: ClientConfig) => LDClient;
11
+ /**
12
+ * Like `getClient`, but shares the client with any other caller using the same `version` and
13
+ * `clientSideID`, regardless of which side's SDK build constructed it. Callers supply
14
+ * `createClient` because the provider may not have the SDK version needed to build a client
15
+ * for a `version` it doesn't recognize.
16
+ */
17
+ getVersionClient?: (version: string, config: ClientConfig, createClient: (config: ClientConfig) => LDClient) => LDClient;
11
18
  application?: LDApplication;
12
19
  options?: Omit<LDOptions, 'application'>;
13
20
  }
@@ -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,EAAE,SAAS,CAAC;IACnB,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"}
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,EAAE,SAAS,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,QAAQ,CAAC;IAC9C;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,QAAQ,KAC/C,QAAQ,CAAC;IACd,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 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"}
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 /**\n * Like `getClient`, but shares the client with any other caller using the same `version` and\n * `clientSideID`, regardless of which side's SDK build constructed it. Callers supply\n * `createClient` because the provider may not have the SDK version needed to build a client\n * for a `version` it doesn't recognize.\n */\n getVersionClient?: (\n version: string,\n config: ClientConfig,\n createClient: (config: ClientConfig) => LDClient\n ) => 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": "39.3.0",
3
+ "version": "40.1.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": {
@@ -26,24 +26,24 @@
26
26
  "src"
27
27
  ],
28
28
  "devDependencies": {
29
- "@datadog/browser-rum": "^7.9.0",
30
- "@servicetitan/log-service": "39.3.0",
31
- "@servicetitan/react-ioc": "39.3.0",
32
- "@testing-library/dom": "^10.4.1",
29
+ "@datadog/browser-rum": "^7.13.0",
30
+ "@servicetitan/log-service": "40.1.0",
31
+ "@servicetitan/react-ioc": "40.1.0",
32
+ "@testing-library/dom": "^10.4.2",
33
33
  "@testing-library/jest-dom": "^7.0.1",
34
34
  "@testing-library/react": "^16.3.3",
35
- "launchdarkly-js-client-sdk": "3.5.0",
36
- "launchdarkly-react-client-sdk": "3.6.1",
35
+ "launchdarkly-js-client-sdk": "3.9.5",
36
+ "launchdarkly-react-client-sdk": "3.9.4",
37
37
  "mobx": "~6.16.1",
38
38
  "mobx-react": "~9.2.2",
39
39
  "react": "~18.3.1"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@datadog/browser-rum": ">=6.19.0",
43
- "@servicetitan/log-service": "39.3.0",
44
- "@servicetitan/react-ioc": "39.3.0",
45
- "launchdarkly-js-client-sdk": "3.5.0",
46
- "launchdarkly-react-client-sdk": "3.6.1",
43
+ "@servicetitan/log-service": "40.1.0",
44
+ "@servicetitan/react-ioc": "40.1.0",
45
+ "launchdarkly-js-client-sdk": "3.9.5",
46
+ "launchdarkly-react-client-sdk": "3.9.4",
47
47
  "mobx": ">= 6.13.5",
48
48
  "mobx-react": ">=9.2.0",
49
49
  "react": ">=17.0.2"
@@ -0,0 +1,8 @@
1
+ import { LDClient, LDOptions, initialize } from 'launchdarkly-js-client-sdk';
2
+ import { ClientConfig } from './types';
3
+
4
+ const defaultOptions: LDOptions = { sendEventsOnlyForVariation: true };
5
+
6
+ export function createLDClient({ clientSideID, context, options }: ClientConfig): LDClient {
7
+ return initialize(clientSideID, context, { ...defaultOptions, ...options });
8
+ }
@@ -0,0 +1,13 @@
1
+ import { LDClient } from 'launchdarkly-js-client-sdk';
2
+ import { createLDClient } from './create-ld-client';
3
+ import { getLDClient } from './get-ld-client';
4
+ import { getSDKVersion } from './get-sdk-version';
5
+ import { ClientConfig, LDService } from './types';
6
+
7
+ export function getClient(ldService: LDService | undefined, config: ClientConfig): LDClient {
8
+ const version = getSDKVersion();
9
+ if (version && ldService?.getVersionClient) {
10
+ return ldService.getVersionClient(version, config, createLDClient);
11
+ }
12
+ return ldService?.getClient(config) ?? getLDClient(config);
13
+ }
@@ -1,15 +1,12 @@
1
- import { LDClient, LDOptions, initialize } from 'launchdarkly-js-client-sdk';
1
+ import { LDClient } from 'launchdarkly-js-client-sdk';
2
+ import { createLDClient } from './create-ld-client';
3
+ import { getSDKVersion } from './get-sdk-version';
4
+ import { getVersionClient } from './get-version-client';
2
5
  import { ClientConfig } from './types';
3
6
 
4
- const ldClients: Map<string, LDClient> = new Map();
7
+ // Ensures different bundles that fail version detection won't share an incompatible client.
8
+ const UNKNOWN_VERSION = `0.0.0-unknown.${Math.round(1e6 * Math.random()).toString(36)}`;
5
9
 
6
- const defaultOptions: LDOptions = { sendEventsOnlyForVariation: true };
7
-
8
- export function getLDClient({ clientSideID, context, options }: ClientConfig): LDClient {
9
- let client = ldClients.get(clientSideID);
10
- if (!client) {
11
- client = initialize(clientSideID, context, { ...defaultOptions, ...options });
12
- ldClients.set(clientSideID, client);
13
- }
14
- return client;
10
+ export function getLDClient(config: ClientConfig): LDClient {
11
+ return getVersionClient(getSDKVersion() ?? UNKNOWN_VERSION, config, createLDClient);
15
12
  }
@@ -0,0 +1,10 @@
1
+ import * as LDClientSDK from 'launchdarkly-js-client-sdk';
2
+ import ldClientPackageJson from 'launchdarkly-js-client-sdk/package.json';
3
+
4
+ /*
5
+ * "version" isn't declared in the SDK's typings, but is present at runtime. Fall back to
6
+ * package.json's version in case a future SDK release removes the runtime export.
7
+ */
8
+ export function getSDKVersion(): string | undefined {
9
+ return (LDClientSDK as unknown as { version?: string }).version ?? ldClientPackageJson.version;
10
+ }
@@ -0,0 +1,18 @@
1
+ import { LDClient } from 'launchdarkly-js-client-sdk';
2
+ import { ClientConfig } from './types';
3
+
4
+ const clients: Map<string, LDClient> = new Map();
5
+
6
+ export function getVersionClient(
7
+ version: string,
8
+ config: ClientConfig,
9
+ createClient: (config: ClientConfig) => LDClient
10
+ ): LDClient {
11
+ const key = `${version}:${config.clientSideID}`;
12
+ let client = clients.get(key);
13
+ if (!client) {
14
+ client = createClient(config);
15
+ clients.set(key, client);
16
+ }
17
+ return client;
18
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './create-flag-keys';
2
+ export * from './create-ld-client';
2
3
  export * from './feature-flag-store';
4
+ export * from './get-client';
3
5
  export * from './get-ld-client';
4
6
  export * from './ld-provider';
5
7
  export * from './ld-service-provider';
@@ -8,7 +8,7 @@ import {
8
8
  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
- import { getLDClient } from './get-ld-client';
11
+ import { getClient } from './get-client';
12
12
  import { ClientConfig, LDService, LD_SERVICE_TOKEN } from './types';
13
13
 
14
14
  export type ProviderConfig = Pick<BaseProviderConfig, 'clientSideID' | 'flags'> & {
@@ -55,7 +55,7 @@ export const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({
55
55
  (async function initializeClient() {
56
56
  const options = mergeOptions(ldService);
57
57
  const config: ClientConfig = { clientSideID, context, options };
58
- const client = ldService?.getClient(config) ?? getLDClient(config);
58
+ const client = getClient(ldService, config);
59
59
  if (waitForInitialization) {
60
60
  try {
61
61
  await client.waitForInitialization(5);
@@ -2,10 +2,12 @@ import { provide, useDependencies } from '@servicetitan/react-ioc';
2
2
  import { LDOptions } from 'launchdarkly-js-client-sdk';
3
3
  import { FC, PropsWithChildren, useEffect, useRef } from 'react';
4
4
  import { getLDClient } from './get-ld-client';
5
+ import { getVersionClient } from './get-version-client';
5
6
  import { LD_SERVICE_TOKEN, LDApplication, LDService } from './types';
6
7
 
7
8
  const ldService: LDService = {
8
9
  getClient: getLDClient,
10
+ getVersionClient,
9
11
  };
10
12
 
11
13
  interface LDServiceProviderProps {
@@ -0,0 +1,64 @@
1
+ import { initialize } from 'launchdarkly-js-client-sdk';
2
+ import { createLDClient } from '../create-ld-client';
3
+ import { ClientConfig } from '../types';
4
+
5
+ jest.mock('launchdarkly-js-client-sdk', () => ({
6
+ initialize: jest.fn(),
7
+ }));
8
+
9
+ describe(`[launchdarkly-service] ${createLDClient.name}`, () => {
10
+ const mockClient: any = Symbol('mockClient');
11
+ const defaultOptions = { sendEventsOnlyForVariation: true };
12
+ let config: ClientConfig;
13
+
14
+ const subject = () => createLDClient(config);
15
+
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+ jest.mocked(initialize).mockImplementation(() => mockClient);
19
+ config = { clientSideID: makeId(), context: { kind: 'multi' } };
20
+ });
21
+
22
+ test('initializes client with the given context and default options', () => {
23
+ subject();
24
+
25
+ expect(initialize).toHaveBeenCalledWith(
26
+ config.clientSideID,
27
+ config.context,
28
+ defaultOptions
29
+ );
30
+ });
31
+
32
+ test('returns the client', () => {
33
+ expect(subject()).toBe(mockClient);
34
+ });
35
+
36
+ describe('when given options', () => {
37
+ beforeEach(() => (config.options = { hash: 'foo' }));
38
+
39
+ test('passes options through alongside the defaults', () => {
40
+ subject();
41
+
42
+ expect(initialize).toHaveBeenCalledWith(config.clientSideID, config.context, {
43
+ ...defaultOptions,
44
+ ...config.options,
45
+ });
46
+ });
47
+ });
48
+
49
+ describe('when given sendEventsOnlyForVariation', () => {
50
+ beforeEach(() => (config.options = { sendEventsOnlyForVariation: false }));
51
+
52
+ test('lets the caller override the default', () => {
53
+ subject();
54
+
55
+ expect(initialize).toHaveBeenCalledWith(config.clientSideID, config.context, {
56
+ sendEventsOnlyForVariation: false,
57
+ });
58
+ });
59
+ });
60
+ });
61
+
62
+ function makeId() {
63
+ return Math.round(1e6 * Math.random()).toString(36);
64
+ }
@@ -0,0 +1,98 @@
1
+ import { createLDClient } from '../create-ld-client';
2
+ import { getClient } from '../get-client';
3
+ import { getLDClient } from '../get-ld-client';
4
+ import { getSDKVersion } from '../get-sdk-version';
5
+ import { ClientConfig, LDService } from '../types';
6
+
7
+ jest.mock('../create-ld-client', () => ({
8
+ createLDClient: jest.fn(),
9
+ }));
10
+ jest.mock('../get-ld-client', () => ({
11
+ getLDClient: jest.fn(),
12
+ }));
13
+ jest.mock('../get-sdk-version', () => ({
14
+ getSDKVersion: jest.fn(),
15
+ }));
16
+
17
+ describe(`[launchdarkly-service] ${getClient.name}`, () => {
18
+ const client: any = Symbol('client');
19
+ const version = '3.9.5';
20
+ let ldService: LDService | undefined;
21
+ let config: ClientConfig;
22
+
23
+ const subject = () => getClient(ldService, config);
24
+
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+ jest.mocked(getLDClient).mockReturnValue(client);
28
+ jest.mocked(getSDKVersion).mockReturnValue(version);
29
+ config = { clientSideID: makeId(), context: { kind: 'multi' } };
30
+ ldService = undefined;
31
+ });
32
+
33
+ test('returns the locally created client', () => {
34
+ expect(subject()).toBe(client);
35
+ });
36
+
37
+ describe('when ldService is provided', () => {
38
+ const ldServiceClient: any = Symbol('ldServiceClient');
39
+
40
+ beforeEach(() => {
41
+ ldService = { getClient: jest.fn().mockReturnValue(ldServiceClient) };
42
+ });
43
+
44
+ function itReturnsLDServiceClient() {
45
+ test('returns the LDService client', () => {
46
+ expect(subject()).toBe(ldServiceClient);
47
+ });
48
+ }
49
+
50
+ itReturnsLDServiceClient();
51
+
52
+ test('requests it using the config', () => {
53
+ subject();
54
+
55
+ expect(ldService?.getClient).toHaveBeenCalledWith(config);
56
+ });
57
+
58
+ describe('when LDService includes getVersionClient', () => {
59
+ const versionedClient: any = Symbol('versionedClient');
60
+
61
+ beforeEach(() => {
62
+ ldService!.getVersionClient = jest.fn().mockReturnValue(versionedClient);
63
+ });
64
+
65
+ test('returns the version-aware client', () => {
66
+ expect(subject()).toBe(versionedClient);
67
+ });
68
+
69
+ test('requests it using the running SDK version, the config, and the client factory', () => {
70
+ subject();
71
+
72
+ expect(ldService!.getVersionClient).toHaveBeenCalledWith(
73
+ version,
74
+ config,
75
+ createLDClient
76
+ );
77
+ });
78
+
79
+ describe('when the running SDK version is unknown', () => {
80
+ beforeEach(() => {
81
+ jest.mocked(getSDKVersion).mockReturnValue(undefined);
82
+ });
83
+
84
+ itReturnsLDServiceClient();
85
+
86
+ test('does not call getVersionClient', () => {
87
+ subject();
88
+
89
+ expect(ldService!.getVersionClient).not.toHaveBeenCalled();
90
+ });
91
+ });
92
+ });
93
+ });
94
+ });
95
+
96
+ function makeId() {
97
+ return Math.round(1e6 * Math.random()).toString(36);
98
+ }