@hyphen/sdk 1.3.0 → 1.5.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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  # Hyphen Node.js SDK
9
9
 
10
- The Hyphen Node.js SDK is a JavaScript library that allows developers to easily integrate Hyphen's feature flagging and experimentation capabilities into their Node.js applications. With this SDK, you can manage feature flags more effectively, enabling you to control the rollout of new features and conduct A/B testing with ease.
10
+ The Hyphen Node.js SDK is a JavaScript library that allows developers to easily integrate Hyphen's feature flag service [Toggle](https://hyphen.ai/toggle) into their Node.js applications. In addition to Toggle, the SDK also provides ENV management capabilities such as loading environment variables from `.env` files, managing environment variables.
11
11
 
12
12
  # Table of Contents
13
13
  - [Installation](#installation)
@@ -18,6 +18,7 @@ The Hyphen Node.js SDK is a JavaScript library that allows developers to easily
18
18
  - [Toggle Hooks](#toggle-hooks)
19
19
  - [Toggle Error Handling](#toggle-error-handling)
20
20
  - [Toggle Caching](#toggle-caching)
21
+ - [Toggle Environment Variables](#toggle-environment-variables)
21
22
  - [Toggle Self-Hosted](#toggle-self-hosted)
22
23
  - [Contributing](#contributing)
23
24
  - [Testing Your Changes](#testing-your-changes)
@@ -395,6 +396,17 @@ const result = await toggle.getBoolean('hyphen-sdk-boolean', false);
395
396
  console.log('Boolean toggle value:', result); // true
396
397
  ```
397
398
 
399
+ # Toggle Environment Variables
400
+
401
+ You can also use environment variables to set the `publicApiKey` and `applicationId`. This is useful for keeping your API keys secure and not hardcoding them in your code. To do this just set your `.env` file with the following variables:
402
+
403
+ ```bash
404
+ HYPHEN_PUBLIC_API_KEY=your_public_api_key
405
+ HYPHEN_APPLICATION_ID=your_application_id
406
+ ```
407
+
408
+ On initialization of the `Toggle` class, the SDK will automatically check for these environment variables and use them if they are set. If they are not set, you will need to provide them in the constructor.
409
+
398
410
  ## Toggle Self-Hosted
399
411
 
400
412
  Toggle uses [Horizon](https://hyphen.ai/horizon) to fetch the feature flags. If you are using a self-hosted version of Hyphen you can use the `uris` option in the constructor to set the url of your self-hosted version:
@@ -472,6 +484,36 @@ const result = await toggle.getBoolean('hyphen-sdk-boolean', false);
472
484
  console.log('Boolean toggle value:', result); // true
473
485
  ```
474
486
 
487
+ # ENV
488
+
489
+ Hyphens secret management service known as [ENV](https://hyphen.ai/env) allows you to manage your environment variables in a secure way. The Hyphen Node.js SDK provides a simple way to access your environment variables.
490
+
491
+ ## Loading Environment Variables
492
+ To load your environment variables, you can use the `loadEnv` function from the SDK. This function will automatically load your environment variables from the `.env` file and then override them with the environment based environment file if it exists (ex: `.env.development`). This is useful for managing different environments such as development, staging, and production.
493
+
494
+ ```javascript
495
+ import { loadEnv } from '@hyphen/sdk';
496
+
497
+ //load your default environment variables and envrionment variables
498
+ loadEnv();
499
+ ```
500
+
501
+ If your environment variables are not stored in the root of your project you can specify the path to your `.env` file:
502
+
503
+ ```javascript
504
+ import { loadEnv } from '@hyphen/sdk';
505
+ //load your default environment variables and envrionment variables
506
+ loadEnv({ path: '/path/to/your/env/files/' });
507
+ ```
508
+
509
+ You can also specify the environment variables to load by passing an array of variable names:
510
+
511
+ ```javascript
512
+ import { loadEnv } from '@hyphen/sdk';
513
+ //load your default environment variables and envrionment variables
514
+ loadEnv({ environment: 'development' });
515
+ ```
516
+
475
517
  # Contributing
476
518
 
477
519
  We welcome contributions to the Hyphen Node.js SDK! If you have an idea for a new feature, bug fix, or improvement, please follow these steps:
package/dist/index.cjs CHANGED
@@ -32,15 +32,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
34
  Toggle: () => Toggle,
35
- ToggleHooks: () => ToggleHooks
35
+ ToggleHooks: () => ToggleHooks,
36
+ loadEnv: () => loadEnv
36
37
  });
37
38
  module.exports = __toCommonJS(index_exports);
38
39
 
39
40
  // src/toggle.ts
40
41
  var import_node_process = __toESM(require("process"), 1);
41
42
  var import_hookified = require("hookified");
43
+ var import_dotenv = __toESM(require("dotenv"), 1);
42
44
  var import_server_sdk = require("@openfeature/server-sdk");
43
45
  var import_openfeature_server_provider = require("@hyphen/openfeature-server-provider");
46
+ import_dotenv.default.config();
44
47
  var ToggleHooks = /* @__PURE__ */ function(ToggleHooks2) {
45
48
  ToggleHooks2["beforeGetBoolean"] = "beforeGetBoolean";
46
49
  ToggleHooks2["afterGetBoolean"] = "afterGetBoolean";
@@ -56,8 +59,8 @@ var Toggle = class extends import_hookified.Hookified {
56
59
  static {
57
60
  __name(this, "Toggle");
58
61
  }
59
- _applicationId;
60
- _publicApiKey = "";
62
+ _applicationId = import_node_process.default.env.HYPHEN_APPLICATION_ID;
63
+ _publicApiKey = import_node_process.default.env.HYPHEN_PUBLIC_API_KEY;
61
64
  _environment;
62
65
  _client;
63
66
  _context;
@@ -70,24 +73,26 @@ var Toggle = class extends import_hookified.Hookified {
70
73
  */
71
74
  constructor(options) {
72
75
  super();
73
- this._applicationId = options.applicationId;
74
- this.setPublicApiKey(options.publicApiKey);
75
- this._environment = options.environment ?? import_node_process.default.env.NODE_ENV ?? "development";
76
- this._context = options.context;
77
- this._throwErrors = options.throwErrors ?? false;
78
- this._uris = options.uris;
79
- this._caching = options.caching;
76
+ this._throwErrors = options?.throwErrors ?? false;
77
+ this._applicationId = options?.applicationId;
78
+ if (options?.publicApiKey) {
79
+ this.setPublicApiKey(options.publicApiKey);
80
+ }
81
+ this._environment = options?.environment ?? import_node_process.default.env.NODE_ENV ?? "development";
82
+ this._context = options?.context;
83
+ this._uris = options?.uris;
84
+ this._caching = options?.caching;
80
85
  }
81
86
  /**
82
87
  * Get the application ID
83
- * @returns {string}
88
+ * @returns {string | undefined}
84
89
  */
85
90
  get applicationId() {
86
91
  return this._applicationId;
87
92
  }
88
93
  /**
89
94
  * Set the application ID
90
- * @param {string} value
95
+ * @param {string | undefined} value
91
96
  */
92
97
  set applicationId(value) {
93
98
  this._applicationId = value;
@@ -104,6 +109,11 @@ var Toggle = class extends import_hookified.Hookified {
104
109
  * @param {string} value
105
110
  */
106
111
  set publicApiKey(value) {
112
+ if (!value) {
113
+ this._publicApiKey = void 0;
114
+ this._client = void 0;
115
+ return;
116
+ }
107
117
  this.setPublicApiKey(value);
108
118
  }
109
119
  /**
@@ -210,13 +220,27 @@ var Toggle = class extends import_hookified.Hookified {
210
220
  */
211
221
  async getClient() {
212
222
  if (!this._client) {
223
+ if (this._applicationId === void 0 || this._applicationId.length === 0) {
224
+ const errorMessage = "Application ID is not set. You must set it before using the client or have the HYPHEN_APPLICATION_ID environment variable set.";
225
+ this.emit("error", new Error(errorMessage));
226
+ if (this._throwErrors) {
227
+ throw new Error(errorMessage);
228
+ }
229
+ }
213
230
  const options = {
214
231
  application: this._applicationId,
215
232
  environment: this._environment,
216
233
  horizonUrls: this._uris,
217
234
  cache: this._caching
218
235
  };
219
- await import_server_sdk.OpenFeature.setProviderAndWait(new import_openfeature_server_provider.HyphenProvider(this._publicApiKey, options));
236
+ if (this._publicApiKey && this._publicApiKey.length > 0) {
237
+ await import_server_sdk.OpenFeature.setProviderAndWait(new import_openfeature_server_provider.HyphenProvider(this._publicApiKey, options));
238
+ } else {
239
+ this.emit("error", new Error("Public API key is not set. You must set it before using the client or have the HYPHEN_PUBLIC_API_KEY environment variable set."));
240
+ if (this._throwErrors) {
241
+ throw new Error("Public API key is not set");
242
+ }
243
+ }
220
244
  this._client = import_server_sdk.OpenFeature.getClient(this._context);
221
245
  }
222
246
  return this._client;
@@ -373,8 +397,35 @@ var Toggle = class extends import_hookified.Hookified {
373
397
  return defaultValue;
374
398
  }
375
399
  };
400
+
401
+ // src/env.ts
402
+ var import_node_process2 = __toESM(require("process"), 1);
403
+ var import_node_fs = __toESM(require("fs"), 1);
404
+ var import_node_path = __toESM(require("path"), 1);
405
+ var import_dotenv2 = require("dotenv");
406
+ function loadEnv(options) {
407
+ const currentWorkingDirectory = options?.path ?? import_node_process2.default.cwd();
408
+ const envPath = import_node_path.default.resolve(currentWorkingDirectory, ".env");
409
+ if (import_node_fs.default.existsSync(envPath)) {
410
+ (0, import_dotenv2.config)({
411
+ path: envPath
412
+ });
413
+ }
414
+ const environment = options?.environment ?? import_node_process2.default.env.NODE_ENV;
415
+ if (environment) {
416
+ const envSpecificPath = import_node_path.default.resolve(currentWorkingDirectory, `.env.${environment}`);
417
+ if (import_node_fs.default.existsSync(envSpecificPath)) {
418
+ (0, import_dotenv2.config)({
419
+ path: envSpecificPath,
420
+ override: true
421
+ });
422
+ }
423
+ }
424
+ }
425
+ __name(loadEnv, "loadEnv");
376
426
  // Annotate the CommonJS export names for ESM import in node:
377
427
  0 && (module.exports = {
378
428
  Toggle,
379
- ToggleHooks
429
+ ToggleHooks,
430
+ loadEnv
380
431
  });
package/dist/index.d.cts CHANGED
@@ -18,15 +18,15 @@ type ToggleCachingOptions = {
18
18
  };
19
19
  type ToggleOptions = {
20
20
  /**
21
- * Your application name
21
+ * Your application name. If this is not set it will look for the HYPHEN_APPLICATION_ID environment variable.
22
22
  * @type {string}
23
23
  */
24
- applicationId: string;
24
+ applicationId?: string;
25
25
  /**
26
- * Your Hyphen Public API key
26
+ * Your Hyphen Public API key. If this is not set it will look for the HYPHEN_PUBLIC_API_KEY environment variable.
27
27
  * @type {string}
28
28
  */
29
- publicApiKey: string;
29
+ publicApiKey?: string;
30
30
  /**
31
31
  * Your environment name such as development, production. Default is what is set at NODE_ENV
32
32
  * @type {string}
@@ -73,27 +73,27 @@ declare class Toggle extends Hookified {
73
73
  private _throwErrors;
74
74
  private _uris;
75
75
  private _caching;
76
- constructor(options: ToggleOptions);
76
+ constructor(options?: ToggleOptions);
77
77
  /**
78
78
  * Get the application ID
79
- * @returns {string}
79
+ * @returns {string | undefined}
80
80
  */
81
- get applicationId(): string;
81
+ get applicationId(): string | undefined;
82
82
  /**
83
83
  * Set the application ID
84
- * @param {string} value
84
+ * @param {string | undefined} value
85
85
  */
86
- set applicationId(value: string);
86
+ set applicationId(value: string | undefined);
87
87
  /**
88
88
  * Get the public API key
89
89
  * @returns {string}
90
90
  */
91
- get publicApiKey(): string;
91
+ get publicApiKey(): string | undefined;
92
92
  /**
93
93
  * Set the public API key
94
94
  * @param {string} value
95
95
  */
96
- set publicApiKey(value: string);
96
+ set publicApiKey(value: string | undefined);
97
97
  /**
98
98
  * Get the environment
99
99
  * @returns {string}
@@ -202,4 +202,19 @@ declare class Toggle extends Hookified {
202
202
  getObject<T>(key: string, defaultValue: T, options?: ToggleGetOptions): Promise<T>;
203
203
  }
204
204
 
205
- export { Toggle, type ToggleCachingOptions, type ToggleContext, type ToggleGetOptions, ToggleHooks, type ToggleOptions };
205
+ type LoadEnvOptions = {
206
+ path?: string;
207
+ environment?: string;
208
+ };
209
+ /**
210
+ * @description Helper function to load your environment variables based on your default .env file
211
+ * and the current environment.
212
+ * @param {LoadEnvOptions} [options] - Options to customize the loading of environment variables.
213
+ * @returns {void}
214
+ * @example
215
+ * import { loadEnv } from '@hyphen/sdk';
216
+ * loadEnv();
217
+ */
218
+ declare function loadEnv(options?: LoadEnvOptions): void;
219
+
220
+ export { Toggle, type ToggleCachingOptions, type ToggleContext, type ToggleGetOptions, ToggleHooks, type ToggleOptions, loadEnv };
package/dist/index.d.ts CHANGED
@@ -18,15 +18,15 @@ type ToggleCachingOptions = {
18
18
  };
19
19
  type ToggleOptions = {
20
20
  /**
21
- * Your application name
21
+ * Your application name. If this is not set it will look for the HYPHEN_APPLICATION_ID environment variable.
22
22
  * @type {string}
23
23
  */
24
- applicationId: string;
24
+ applicationId?: string;
25
25
  /**
26
- * Your Hyphen Public API key
26
+ * Your Hyphen Public API key. If this is not set it will look for the HYPHEN_PUBLIC_API_KEY environment variable.
27
27
  * @type {string}
28
28
  */
29
- publicApiKey: string;
29
+ publicApiKey?: string;
30
30
  /**
31
31
  * Your environment name such as development, production. Default is what is set at NODE_ENV
32
32
  * @type {string}
@@ -73,27 +73,27 @@ declare class Toggle extends Hookified {
73
73
  private _throwErrors;
74
74
  private _uris;
75
75
  private _caching;
76
- constructor(options: ToggleOptions);
76
+ constructor(options?: ToggleOptions);
77
77
  /**
78
78
  * Get the application ID
79
- * @returns {string}
79
+ * @returns {string | undefined}
80
80
  */
81
- get applicationId(): string;
81
+ get applicationId(): string | undefined;
82
82
  /**
83
83
  * Set the application ID
84
- * @param {string} value
84
+ * @param {string | undefined} value
85
85
  */
86
- set applicationId(value: string);
86
+ set applicationId(value: string | undefined);
87
87
  /**
88
88
  * Get the public API key
89
89
  * @returns {string}
90
90
  */
91
- get publicApiKey(): string;
91
+ get publicApiKey(): string | undefined;
92
92
  /**
93
93
  * Set the public API key
94
94
  * @param {string} value
95
95
  */
96
- set publicApiKey(value: string);
96
+ set publicApiKey(value: string | undefined);
97
97
  /**
98
98
  * Get the environment
99
99
  * @returns {string}
@@ -202,4 +202,19 @@ declare class Toggle extends Hookified {
202
202
  getObject<T>(key: string, defaultValue: T, options?: ToggleGetOptions): Promise<T>;
203
203
  }
204
204
 
205
- export { Toggle, type ToggleCachingOptions, type ToggleContext, type ToggleGetOptions, ToggleHooks, type ToggleOptions };
205
+ type LoadEnvOptions = {
206
+ path?: string;
207
+ environment?: string;
208
+ };
209
+ /**
210
+ * @description Helper function to load your environment variables based on your default .env file
211
+ * and the current environment.
212
+ * @param {LoadEnvOptions} [options] - Options to customize the loading of environment variables.
213
+ * @returns {void}
214
+ * @example
215
+ * import { loadEnv } from '@hyphen/sdk';
216
+ * loadEnv();
217
+ */
218
+ declare function loadEnv(options?: LoadEnvOptions): void;
219
+
220
+ export { Toggle, type ToggleCachingOptions, type ToggleContext, type ToggleGetOptions, ToggleHooks, type ToggleOptions, loadEnv };
package/dist/index.js CHANGED
@@ -4,8 +4,10 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
4
4
  // src/toggle.ts
5
5
  import process from "process";
6
6
  import { Hookified } from "hookified";
7
+ import dotenv from "dotenv";
7
8
  import { OpenFeature } from "@openfeature/server-sdk";
8
9
  import { HyphenProvider } from "@hyphen/openfeature-server-provider";
10
+ dotenv.config();
9
11
  var ToggleHooks = /* @__PURE__ */ function(ToggleHooks2) {
10
12
  ToggleHooks2["beforeGetBoolean"] = "beforeGetBoolean";
11
13
  ToggleHooks2["afterGetBoolean"] = "afterGetBoolean";
@@ -21,8 +23,8 @@ var Toggle = class extends Hookified {
21
23
  static {
22
24
  __name(this, "Toggle");
23
25
  }
24
- _applicationId;
25
- _publicApiKey = "";
26
+ _applicationId = process.env.HYPHEN_APPLICATION_ID;
27
+ _publicApiKey = process.env.HYPHEN_PUBLIC_API_KEY;
26
28
  _environment;
27
29
  _client;
28
30
  _context;
@@ -35,24 +37,26 @@ var Toggle = class extends Hookified {
35
37
  */
36
38
  constructor(options) {
37
39
  super();
38
- this._applicationId = options.applicationId;
39
- this.setPublicApiKey(options.publicApiKey);
40
- this._environment = options.environment ?? process.env.NODE_ENV ?? "development";
41
- this._context = options.context;
42
- this._throwErrors = options.throwErrors ?? false;
43
- this._uris = options.uris;
44
- this._caching = options.caching;
40
+ this._throwErrors = options?.throwErrors ?? false;
41
+ this._applicationId = options?.applicationId;
42
+ if (options?.publicApiKey) {
43
+ this.setPublicApiKey(options.publicApiKey);
44
+ }
45
+ this._environment = options?.environment ?? process.env.NODE_ENV ?? "development";
46
+ this._context = options?.context;
47
+ this._uris = options?.uris;
48
+ this._caching = options?.caching;
45
49
  }
46
50
  /**
47
51
  * Get the application ID
48
- * @returns {string}
52
+ * @returns {string | undefined}
49
53
  */
50
54
  get applicationId() {
51
55
  return this._applicationId;
52
56
  }
53
57
  /**
54
58
  * Set the application ID
55
- * @param {string} value
59
+ * @param {string | undefined} value
56
60
  */
57
61
  set applicationId(value) {
58
62
  this._applicationId = value;
@@ -69,6 +73,11 @@ var Toggle = class extends Hookified {
69
73
  * @param {string} value
70
74
  */
71
75
  set publicApiKey(value) {
76
+ if (!value) {
77
+ this._publicApiKey = void 0;
78
+ this._client = void 0;
79
+ return;
80
+ }
72
81
  this.setPublicApiKey(value);
73
82
  }
74
83
  /**
@@ -175,13 +184,27 @@ var Toggle = class extends Hookified {
175
184
  */
176
185
  async getClient() {
177
186
  if (!this._client) {
187
+ if (this._applicationId === void 0 || this._applicationId.length === 0) {
188
+ const errorMessage = "Application ID is not set. You must set it before using the client or have the HYPHEN_APPLICATION_ID environment variable set.";
189
+ this.emit("error", new Error(errorMessage));
190
+ if (this._throwErrors) {
191
+ throw new Error(errorMessage);
192
+ }
193
+ }
178
194
  const options = {
179
195
  application: this._applicationId,
180
196
  environment: this._environment,
181
197
  horizonUrls: this._uris,
182
198
  cache: this._caching
183
199
  };
184
- await OpenFeature.setProviderAndWait(new HyphenProvider(this._publicApiKey, options));
200
+ if (this._publicApiKey && this._publicApiKey.length > 0) {
201
+ await OpenFeature.setProviderAndWait(new HyphenProvider(this._publicApiKey, options));
202
+ } else {
203
+ this.emit("error", new Error("Public API key is not set. You must set it before using the client or have the HYPHEN_PUBLIC_API_KEY environment variable set."));
204
+ if (this._throwErrors) {
205
+ throw new Error("Public API key is not set");
206
+ }
207
+ }
185
208
  this._client = OpenFeature.getClient(this._context);
186
209
  }
187
210
  return this._client;
@@ -338,7 +361,34 @@ var Toggle = class extends Hookified {
338
361
  return defaultValue;
339
362
  }
340
363
  };
364
+
365
+ // src/env.ts
366
+ import process2 from "process";
367
+ import fs from "fs";
368
+ import path from "path";
369
+ import { config } from "dotenv";
370
+ function loadEnv(options) {
371
+ const currentWorkingDirectory = options?.path ?? process2.cwd();
372
+ const envPath = path.resolve(currentWorkingDirectory, ".env");
373
+ if (fs.existsSync(envPath)) {
374
+ config({
375
+ path: envPath
376
+ });
377
+ }
378
+ const environment = options?.environment ?? process2.env.NODE_ENV;
379
+ if (environment) {
380
+ const envSpecificPath = path.resolve(currentWorkingDirectory, `.env.${environment}`);
381
+ if (fs.existsSync(envSpecificPath)) {
382
+ config({
383
+ path: envSpecificPath,
384
+ override: true
385
+ });
386
+ }
387
+ }
388
+ }
389
+ __name(loadEnv, "loadEnv");
341
390
  export {
342
391
  Toggle,
343
- ToggleHooks
392
+ ToggleHooks,
393
+ loadEnv
344
394
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyphen/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Hyphen SDK for Node.js",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -11,12 +11,12 @@
11
11
  "require": "./dist/index.cjs"
12
12
  }
13
13
  },
14
- "types": "dist/node/index.d.ts",
14
+ "types": "dist/index.d.ts",
15
15
  "scripts": {
16
16
  "test": "xo --fix && vitest run --coverage",
17
17
  "test:ci": "xo && vitest run --coverage",
18
18
  "build": "rimraf ./dist && tsup src/index.ts --format esm,cjs --dts --clean",
19
- "clean": "rimraf ./dist",
19
+ "clean": "rimraf ./dist pnpm-lock.yaml node_modules coverage",
20
20
  "prepublishOnly": "rimraf ./dist && tsup src/index.ts --format esm,cjs --dts --clean"
21
21
  },
22
22
  "keywords": [
@@ -37,7 +37,7 @@
37
37
  "tsup": "^8.5.0",
38
38
  "typescript": "^5.8.3",
39
39
  "vitest": "^3.1.4",
40
- "xo": "^1.0.0"
40
+ "xo": "^1.1.0"
41
41
  },
42
42
  "files": [
43
43
  "dist",