diff --git a/README.md b/README.md index 727e893a1..a62a097f9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ To view the README for a specific SDK, navigate to that SDK inside the `sdk` dir [React Client SDK](sdk/react) -[NodeJS Server SDK](sdk/nodejs) +[Node.js Server SDK](sdk/nodejs) There are several examples included in this repository for various SDKs. If you want to run them, proceed to setup: diff --git a/examples/nodejs-cloud/js/README.md b/examples/nodejs-cloud/js/README.md index c2591aade..4673af837 100644 --- a/examples/nodejs-cloud/js/README.md +++ b/examples/nodejs-cloud/js/README.md @@ -1,6 +1,6 @@ -# DevCycle NodeJS Cloud Server SDK - Javascript Example App +# DevCycle Node.js Cloud Server SDK - Javascript Example App -Example app for using the DevCycle NodeJS Server SDK with nodeJS and javascript. +Example app for using the DevCycle Node.js Server SDK with node.js and javascript. ### Run diff --git a/examples/nodejs-cloud/typescript/README.md b/examples/nodejs-cloud/typescript/README.md index 4011e354a..77a2057dd 100644 --- a/examples/nodejs-cloud/typescript/README.md +++ b/examples/nodejs-cloud/typescript/README.md @@ -1,6 +1,6 @@ -# DevCycle NodeJS Cloud Server SDK - Typescript Example App +# DevCycle Node.js Cloud Server SDK - Typescript Example App -Example app for using the DevCycle NodeJS Server SDK with nodeJS and typescript. +Example app for using the DevCycle Node.js Server SDK with node.js and typescript. ### Build and Run diff --git a/examples/nodejs-local/js/README.md b/examples/nodejs-local/js/README.md index fdb2c04a6..a5ed60c83 100644 --- a/examples/nodejs-local/js/README.md +++ b/examples/nodejs-local/js/README.md @@ -1,6 +1,6 @@ -# DevCycle NodeJS Local Server SDK - Typescript Example App +# DevCycle Node.js Local Server SDK - Typescript Example App -Example app for using the DevCycle NodeJS Server SDK with nodeJS and javascript. +Example app for using the DevCycle Node.js Server SDK with node.js and javascript. ### Run diff --git a/examples/nodejs-local/typescript/README.md b/examples/nodejs-local/typescript/README.md index 6ba17737d..7b9e212be 100644 --- a/examples/nodejs-local/typescript/README.md +++ b/examples/nodejs-local/typescript/README.md @@ -1,6 +1,6 @@ -# DevCycle NodeJS Local Server SDK - Typescript Example App +# DevCycle Node.js Local Server SDK - Typescript Example App -Example app for using the DevCycle NodeJS Server SDK with nodeJS and typescript. +Example app for using the DevCycle Node.js Server SDK with node.js and typescript. ### Build and Run diff --git a/examples/openfeature-nodejs/README.md b/examples/openfeature-nodejs/README.md index 3c86fd096..5233ac7b3 100644 --- a/examples/openfeature-nodejs/README.md +++ b/examples/openfeature-nodejs/README.md @@ -1,6 +1,8 @@ # OpenFeature DevCycle Provider Example App -Example app for using the OpenFeature NodeJS SDK with the DevCycle Provider. + + +Example app for using the OpenFeature Node.js SDK with the DevCycle Provider. ### Build and Run diff --git a/examples/openfeature-nodejs/package.json b/examples/openfeature-nodejs/package.json index aec6952ba..8f67df164 100644 --- a/examples/openfeature-nodejs/package.json +++ b/examples/openfeature-nodejs/package.json @@ -2,5 +2,9 @@ "name": "@devcycle/openfeature-nodejs-example", "version": "1.7.1", "description": "DevCycle OpenFeature NodeJS Example App", - "license": "MIT" + "license": "MIT", + "dependencies": { + "@openfeature/core": "^0.0.25", + "@openfeature/server-sdk": "^1.11.0" + } } diff --git a/examples/openfeature-nodejs/src/main.ts b/examples/openfeature-nodejs/src/main.ts index d22786314..881faff75 100644 --- a/examples/openfeature-nodejs/src/main.ts +++ b/examples/openfeature-nodejs/src/main.ts @@ -8,7 +8,9 @@ let openFeatureClient: Client async function startDevCycle() { await OpenFeature.setProviderAndWait( - initializeDevCycle(DEVCYCLE_SERVER_SDK_KEY).getOpenFeatureProvider(), + await initializeDevCycle( + DEVCYCLE_SERVER_SDK_KEY, + ).getOpenFeatureProvider(), ) openFeatureClient = OpenFeature.getClient() diff --git a/lib/shared/config-manager/README.md b/lib/shared/config-manager/README.md index d7e67356b..36532db06 100644 --- a/lib/shared/config-manager/README.md +++ b/lib/shared/config-manager/README.md @@ -1,6 +1,6 @@ # config-manager -This library extracts the `EnvironmentConfigManager` Server SDK logic to be used across the NodeJS SDK +This library extracts the `EnvironmentConfigManager` Server SDK logic to be used across the Node.js SDK and Edge Worker SDKs. ## Building diff --git a/sdk/js-cloud-server/README.md b/sdk/js-cloud-server/README.md index 51794a616..ec9d66a67 100644 --- a/sdk/js-cloud-server/README.md +++ b/sdk/js-cloud-server/README.md @@ -1,6 +1,6 @@ # DevCycle Javascript Cloud Bucketing Server SDK -This SDK is used to integrate DevCycle with your Javascript server-side application, where a NodeJS runtime isn't available. +This SDK is used to integrate DevCycle with your Javascript server-side application, where a Node.js runtime isn't available. Example use-cases would be Cloudflare Workers and other edge-worker runtimes. This SDK makes API requests to DevCycle's [Bucketing API](https://docs.devcycle.com/bucketing-api/) to perform bucketing. diff --git a/sdk/nodejs/.test.env b/sdk/nodejs/.test.env new file mode 100644 index 000000000..3ec2c52cf --- /dev/null +++ b/sdk/nodejs/.test.env @@ -0,0 +1 @@ +NODE_OPTIONS=--experimental-vm-modules diff --git a/sdk/nodejs/README.md b/sdk/nodejs/README.md index cf8d8a5bb..14274ab0e 100644 --- a/sdk/nodejs/README.md +++ b/sdk/nodejs/README.md @@ -1,6 +1,6 @@ -# DevCycle NodeJS Server SDK +# DevCycle Node.js Server SDK -The NodeJS Server SDK for DevCycle. +The Node.js Server SDK for DevCycle. This SDK uses local bucketing to perform all user segmentation and bucketing locally in the SDK, providing immediate responses to variable and feature requests for a user. diff --git a/sdk/nodejs/__tests__/initialize.spec.ts b/sdk/nodejs/__tests__/initialize.spec.ts index 97e86f68a..55e9684cd 100644 --- a/sdk/nodejs/__tests__/initialize.spec.ts +++ b/sdk/nodejs/__tests__/initialize.spec.ts @@ -17,10 +17,10 @@ describe('NodeJS SDK Initialize', () => { }) it('successfully creates a OpenFeature provider - local', async () => { - const provider = - initializeDevCycle('dvc_server_token').getOpenFeatureProvider() + const provider = await initializeDevCycle( + 'dvc_server_token', + ).getOpenFeatureProvider() expect(provider).toBeDefined() - expect(provider.status).toBe('NOT_READY') await OpenFeature.setProviderAndWait(provider) expect(provider.status).toBe('READY') const client = OpenFeature.getClient() @@ -28,7 +28,7 @@ describe('NodeJS SDK Initialize', () => { }) it('successfully creates a OpenFeature provider - cloud', async () => { - const provider = initializeDevCycle('dvc_server_token', { + const provider = await initializeDevCycle('dvc_server_token', { enableCloudBucketing: true, }).getOpenFeatureProvider() expect(provider).toBeDefined() diff --git a/sdk/nodejs/__tests__/open-feature-provider/DevCycleProvider.test.ts b/sdk/nodejs/__tests__/open-feature-provider/DevCycleProvider.test.ts index 9feee845f..044284319 100644 --- a/sdk/nodejs/__tests__/open-feature-provider/DevCycleProvider.test.ts +++ b/sdk/nodejs/__tests__/open-feature-provider/DevCycleProvider.test.ts @@ -45,7 +45,7 @@ describe.each(['DevCycleClient', 'DevCycleCloudClient'])( }, ) await OpenFeature.setProviderAndWait( - dvcClient.getOpenFeatureProvider(), + await dvcClient.getOpenFeatureProvider(), ) const ofClient = OpenFeature.getClient() ofClient.setContext({ targetingKey: 'node_sdk_test' }) diff --git a/sdk/nodejs/package.json b/sdk/nodejs/package.json index 631c11140..b89d8fe51 100644 --- a/sdk/nodejs/package.json +++ b/sdk/nodejs/package.json @@ -14,7 +14,7 @@ "@openfeature/server-sdk": "^1.11.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, "main": "src/index.js", "types": "src/index.d.ts" diff --git a/sdk/nodejs/src/client.ts b/sdk/nodejs/src/client.ts index 2dcc7b149..2ac57aabe 100644 --- a/sdk/nodejs/src/client.ts +++ b/sdk/nodejs/src/client.ts @@ -31,7 +31,6 @@ import { DevCycleEvent, } from '@devcycle/js-cloud-server-sdk' import { DVCPopulatedUserFromDevCycleUser } from './models/populatedUserHelpers' -import DevCycleProvider from './open-feature-provider/DevCycleProvider' interface IPlatformData { platform: string @@ -48,6 +47,11 @@ const castIncomingUser = (user: DevCycleUser) => { return user } +// Dynamically import the OpenFeature Provider, as it's an optional peer dependency +type DevCycleProviderConstructor = + typeof import('./open-feature-provider/DevCycleProvider').DevCycleProvider +type DevCycleProvider = InstanceType + export class DevCycleClient { private sdkKey: string private configHelper: EnvironmentConfigManager @@ -125,10 +129,24 @@ export class DevCycleClient { }) } - getOpenFeatureProvider(): DevCycleProvider { + async getOpenFeatureProvider(): Promise { + let DevCycleProviderClass + + try { + const importedModule = await import( + './open-feature-provider/DevCycleProvider.js' + ) + DevCycleProviderClass = importedModule.DevCycleProvider + } catch (error) { + throw new Error( + 'Missing "@openfeature/server-sdk" and/or "@openfeature/core" ' + + 'peer dependencies to get OpenFeature Provider', + ) + } + if (this.openFeatureProvider) return this.openFeatureProvider - this.openFeatureProvider = new DevCycleProvider(this, { + this.openFeatureProvider = new DevCycleProviderClass(this, { logger: this.logger, }) return this.openFeatureProvider diff --git a/sdk/nodejs/src/index.ts b/sdk/nodejs/src/index.ts index bd330ce56..1c410a392 100644 --- a/sdk/nodejs/src/index.ts +++ b/sdk/nodejs/src/index.ts @@ -18,7 +18,11 @@ import { } from '@devcycle/js-cloud-server-sdk' import { DevCycleServerSDKOptions } from '@devcycle/types' import { getNodeJSPlatformDetails } from './utils/platformDetails' -import DevCycleProvider from './open-feature-provider/DevCycleProvider' + +// Dynamically import the OpenFeature Provider, as it's an optional peer dependency +type DevCycleProviderConstructor = + typeof import('./open-feature-provider/DevCycleProvider').DevCycleProvider +type DevCycleProvider = InstanceType class DevCycleCloudClient extends InternalDevCycleCloudClient { private openFeatureProvider: DevCycleProvider @@ -31,10 +35,24 @@ class DevCycleCloudClient extends InternalDevCycleCloudClient { super(sdkKey, options, platformDetails) } - getOpenFeatureProvider(): DevCycleProvider { + async getOpenFeatureProvider(): Promise { + let DevCycleProviderClass + + try { + const importedModule = await import( + './open-feature-provider/DevCycleProvider.js' + ) + DevCycleProviderClass = importedModule.DevCycleProvider + } catch (error) { + throw new Error( + 'Missing "@openfeature/server-sdk" and/or "@openfeature/core" ' + + 'peer dependencies to get OpenFeature Provider', + ) + } + if (this.openFeatureProvider) return this.openFeatureProvider - this.openFeatureProvider = new DevCycleProvider(this, { + this.openFeatureProvider = new DevCycleProviderClass(this, { logger: this.logger, }) return this.openFeatureProvider @@ -47,7 +65,6 @@ export { DevCycleUser, DevCycleServerSDKOptions as DevCycleOptions, DevCycleEvent, - DevCycleProvider, DVCVariableValue, JSON, DVCJSON, diff --git a/sdk/nodejs/src/open-feature-provider/DevCycleProvider.ts b/sdk/nodejs/src/open-feature-provider/DevCycleProvider.ts index ffd869327..d88cd2191 100644 --- a/sdk/nodejs/src/open-feature-provider/DevCycleProvider.ts +++ b/sdk/nodejs/src/open-feature-provider/DevCycleProvider.ts @@ -38,7 +38,7 @@ type EvaluationContextObject = { [key: string]: EvaluationContextValue } -export default class DevCycleProvider implements Provider { +export class DevCycleProvider implements Provider { readonly metadata: ProviderMetadata = { name: 'devcycle-nodejs-provider', } as const diff --git a/yarn.lock b/yarn.lock index f9e8ef372..51197a977 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5210,6 +5210,9 @@ __metadata: "@devcycle/openfeature-nodejs-example@workspace:examples/openfeature-nodejs": version: 0.0.0-use.local resolution: "@devcycle/openfeature-nodejs-example@workspace:examples/openfeature-nodejs" + dependencies: + "@openfeature/core": ^0.0.25 + "@openfeature/server-sdk": ^1.11.0 languageName: unknown linkType: soft