@hyphen/sdk 2.0.2 → 2.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/README.md +70 -0
- package/dist/index.cjs +78 -55
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +77 -55
- package/package.json +16 -14
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ The Hyphen Node.js SDK is a JavaScript library that allows developers to easily
|
|
|
24
24
|
- [ENV - Secret Management Service](#env---secret-management-service)
|
|
25
25
|
- [Loading Environment Variables](#loading-environment-variables)
|
|
26
26
|
- [Net Info - Geo Information Service](#net-info---geo-information-service)
|
|
27
|
+
- [Execution Context - API Key Validation](#execution-context---api-key-validation)
|
|
27
28
|
- [Link - Short Code Service](#link---short-code-service)
|
|
28
29
|
- [Creating a Short Code](#creating-a-short-code)
|
|
29
30
|
- [Updating a Short Code](#updating-a-short-code)
|
|
@@ -673,6 +674,75 @@ console.log('IP Infos:', ipInfos);
|
|
|
673
674
|
|
|
674
675
|
You can also set the API key using the `HYPHEN_API_KEY` environment variable. This is useful for keeping your API key secure and not hardcoding it in your code.
|
|
675
676
|
|
|
677
|
+
# Execution Context - API Key Validation
|
|
678
|
+
|
|
679
|
+
The `getExecutionContext` function validates an API key and returns information about the authenticated user, organization, and request context. This is useful for verifying API keys and getting user/organization details.
|
|
680
|
+
|
|
681
|
+
## Basic Usage
|
|
682
|
+
|
|
683
|
+
```javascript
|
|
684
|
+
import { getExecutionContext } from '@hyphen/sdk';
|
|
685
|
+
|
|
686
|
+
const context = await getExecutionContext('your-api-key');
|
|
687
|
+
|
|
688
|
+
console.log('User:', context.user?.name);
|
|
689
|
+
console.log('Organization:', context.member?.organization?.name);
|
|
690
|
+
console.log('IP Address:', context.ipAddress);
|
|
691
|
+
console.log('Location:', context.location?.city, context.location?.country);
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
## Options
|
|
695
|
+
|
|
696
|
+
| Option | Type | Description |
|
|
697
|
+
|--------|------|-------------|
|
|
698
|
+
| `organizationId` | `string` | Optional organization ID to scope the context request |
|
|
699
|
+
| `baseUri` | `string` | Custom API base URI (defaults to `https://api.hyphen.ai`) |
|
|
700
|
+
| `cache` | `Cacheable` | Cacheable instance for caching requests |
|
|
701
|
+
|
|
702
|
+
## With Organization ID
|
|
703
|
+
|
|
704
|
+
If you need to get context for a specific organization:
|
|
705
|
+
|
|
706
|
+
```javascript
|
|
707
|
+
import { getExecutionContext } from '@hyphen/sdk';
|
|
708
|
+
|
|
709
|
+
const context = await getExecutionContext('your-api-key', {
|
|
710
|
+
organizationId: 'org_123456789',
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
console.log('Organization:', context.organization?.name);
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
## With Caching
|
|
717
|
+
|
|
718
|
+
To enable caching of execution context requests:
|
|
719
|
+
|
|
720
|
+
```javascript
|
|
721
|
+
import { Cacheable } from 'cacheable';
|
|
722
|
+
import { getExecutionContext } from '@hyphen/sdk';
|
|
723
|
+
|
|
724
|
+
const cache = new Cacheable({ ttl: 60000 }); // Cache for 60 seconds
|
|
725
|
+
|
|
726
|
+
const context = await getExecutionContext('your-api-key', {
|
|
727
|
+
cache,
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
console.log('User:', context.user?.name);
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
## Return Type
|
|
734
|
+
|
|
735
|
+
The function returns an `ExecutionContext` object with the following properties:
|
|
736
|
+
|
|
737
|
+
| Property | Type | Description |
|
|
738
|
+
|----------|------|-------------|
|
|
739
|
+
| `request` | `object` | Request metadata (`id`, `causationId`, `correlationId`) |
|
|
740
|
+
| `user` | `object` | User info (`id`, `name`, `rules`, `type`) |
|
|
741
|
+
| `member` | `object` | Member info with nested `organization` |
|
|
742
|
+
| `organization` | `object` | Organization info (`id`, `name`) |
|
|
743
|
+
| `ipAddress` | `string` | The IP address of the request |
|
|
744
|
+
| `location` | `object` | Geo location (`country`, `region`, `city`, `lat`, `lng`, `postalCode`, `timezone`) |
|
|
745
|
+
|
|
676
746
|
# Link - Short Code Service
|
|
677
747
|
|
|
678
748
|
The Hyphen Node.js SDK also provides a `Link` class that allows you to create and manage short codes. This can be useful for generating short links for your application.
|
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
Hyphen: () => Hyphen,
|
|
35
35
|
Toggle: () => Toggle,
|
|
36
36
|
env: () => env,
|
|
37
|
+
getExecutionContext: () => getExecutionContext,
|
|
37
38
|
loadEnv: () => loadEnv
|
|
38
39
|
});
|
|
39
40
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -42,56 +43,72 @@ module.exports = __toCommonJS(index_exports);
|
|
|
42
43
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
43
44
|
var import_node_path = __toESM(require("path"), 1);
|
|
44
45
|
var import_node_process = __toESM(require("process"), 1);
|
|
45
|
-
var
|
|
46
|
+
var import_node_util = require("util");
|
|
47
|
+
function loadEnvFile(filePath, override) {
|
|
48
|
+
try {
|
|
49
|
+
const content = import_node_fs.default.readFileSync(filePath, "utf8");
|
|
50
|
+
const parsed = (0, import_node_util.parseEnv)(content);
|
|
51
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
52
|
+
if (override || import_node_process.default.env[key] === void 0) {
|
|
53
|
+
import_node_process.default.env[key] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
__name(loadEnvFile, "loadEnvFile");
|
|
46
60
|
function env(options) {
|
|
47
61
|
const local = options?.local ?? true;
|
|
48
62
|
const currentWorkingDirectory = options?.path ?? import_node_process.default.cwd();
|
|
49
63
|
const envPath = import_node_path.default.resolve(currentWorkingDirectory, ".env");
|
|
50
|
-
|
|
51
|
-
(0, import_dotenv.config)({
|
|
52
|
-
path: envPath,
|
|
53
|
-
quiet: true,
|
|
54
|
-
debug: false
|
|
55
|
-
});
|
|
56
|
-
}
|
|
64
|
+
loadEnvFile(envPath, false);
|
|
57
65
|
if (local) {
|
|
58
66
|
const localEnvPath = import_node_path.default.resolve(currentWorkingDirectory, ".env.local");
|
|
59
|
-
|
|
60
|
-
(0, import_dotenv.config)({
|
|
61
|
-
path: localEnvPath,
|
|
62
|
-
override: true,
|
|
63
|
-
quiet: true,
|
|
64
|
-
debug: false
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
+
loadEnvFile(localEnvPath, true);
|
|
67
68
|
}
|
|
68
69
|
const environment = options?.environment ?? import_node_process.default.env.NODE_ENV;
|
|
69
70
|
if (environment) {
|
|
70
71
|
const envSpecificPath = import_node_path.default.resolve(currentWorkingDirectory, `.env.${environment}`);
|
|
71
|
-
|
|
72
|
-
(0, import_dotenv.config)({
|
|
73
|
-
path: envSpecificPath,
|
|
74
|
-
override: true,
|
|
75
|
-
quiet: true,
|
|
76
|
-
debug: false
|
|
77
|
-
});
|
|
78
|
-
}
|
|
72
|
+
loadEnvFile(envSpecificPath, true);
|
|
79
73
|
if (local) {
|
|
80
74
|
const envLocalPath = import_node_path.default.resolve(currentWorkingDirectory, `.env.${environment}.local`);
|
|
81
|
-
|
|
82
|
-
(0, import_dotenv.config)({
|
|
83
|
-
path: envLocalPath,
|
|
84
|
-
override: true,
|
|
85
|
-
quiet: true,
|
|
86
|
-
debug: false
|
|
87
|
-
});
|
|
88
|
-
}
|
|
75
|
+
loadEnvFile(envLocalPath, true);
|
|
89
76
|
}
|
|
90
77
|
}
|
|
91
78
|
}
|
|
92
79
|
__name(env, "env");
|
|
93
80
|
var loadEnv = env;
|
|
94
81
|
|
|
82
|
+
// src/execution-context.ts
|
|
83
|
+
var import_net = require("@cacheable/net");
|
|
84
|
+
async function getExecutionContext(apiKey, options) {
|
|
85
|
+
if (!apiKey) {
|
|
86
|
+
throw new Error("API key is required");
|
|
87
|
+
}
|
|
88
|
+
const baseUri = options?.baseUri ?? "https://api.hyphen.ai";
|
|
89
|
+
let url = `${baseUri}/api/execution-context`;
|
|
90
|
+
if (options?.organizationId) {
|
|
91
|
+
url += `?organizationId=${encodeURIComponent(options.organizationId)}`;
|
|
92
|
+
}
|
|
93
|
+
const net = options?.cache ? new import_net.CacheableNet({
|
|
94
|
+
cache: options.cache
|
|
95
|
+
}) : new import_net.CacheableNet();
|
|
96
|
+
const caching = options?.cache !== void 0;
|
|
97
|
+
const response = await net.get(url, {
|
|
98
|
+
headers: {
|
|
99
|
+
"x-api-key": apiKey,
|
|
100
|
+
"content-type": "application/json",
|
|
101
|
+
accept: "application/json"
|
|
102
|
+
},
|
|
103
|
+
caching
|
|
104
|
+
});
|
|
105
|
+
if (response.response.status !== 200) {
|
|
106
|
+
throw new Error(`Failed to get execution context: ${response.response.statusText}`);
|
|
107
|
+
}
|
|
108
|
+
return response.data;
|
|
109
|
+
}
|
|
110
|
+
__name(getExecutionContext, "getExecutionContext");
|
|
111
|
+
|
|
95
112
|
// src/hyphen.ts
|
|
96
113
|
var import_hookified3 = require("hookified");
|
|
97
114
|
|
|
@@ -100,7 +117,7 @@ var import_node_buffer = require("buffer");
|
|
|
100
117
|
var import_node_process2 = __toESM(require("process"), 1);
|
|
101
118
|
|
|
102
119
|
// src/base-service.ts
|
|
103
|
-
var
|
|
120
|
+
var import_net2 = require("@cacheable/net");
|
|
104
121
|
var import_cacheable = require("cacheable");
|
|
105
122
|
var import_hookified = require("hookified");
|
|
106
123
|
var import_pino = __toESM(require("pino"), 1);
|
|
@@ -122,7 +139,7 @@ var BaseService = class extends import_hookified.Hookified {
|
|
|
122
139
|
if (options && options.throwErrors !== void 0) {
|
|
123
140
|
this._throwErrors = options.throwErrors;
|
|
124
141
|
}
|
|
125
|
-
this._net = new
|
|
142
|
+
this._net = new import_net2.CacheableNet({
|
|
126
143
|
cache: this._cache
|
|
127
144
|
});
|
|
128
145
|
}
|
|
@@ -160,53 +177,53 @@ var BaseService = class extends import_hookified.Hookified {
|
|
|
160
177
|
this._log.info(message, ...args);
|
|
161
178
|
this.emit("info", message, ...args);
|
|
162
179
|
}
|
|
163
|
-
async get(url,
|
|
180
|
+
async get(url, config) {
|
|
164
181
|
let finalUrl = url;
|
|
165
|
-
if (
|
|
166
|
-
const params = new URLSearchParams(
|
|
182
|
+
if (config?.params) {
|
|
183
|
+
const params = new URLSearchParams(config.params);
|
|
167
184
|
finalUrl = `${url}?${params.toString()}`;
|
|
168
185
|
}
|
|
169
|
-
const { params: _, ...fetchConfig } =
|
|
186
|
+
const { params: _, ...fetchConfig } = config || {};
|
|
170
187
|
const response = await this._net.get(finalUrl, fetchConfig);
|
|
171
188
|
return {
|
|
172
189
|
data: response.data,
|
|
173
190
|
status: response.response.status,
|
|
174
191
|
statusText: response.response.statusText,
|
|
175
192
|
headers: response.response.headers,
|
|
176
|
-
config
|
|
193
|
+
config,
|
|
177
194
|
request: void 0
|
|
178
195
|
};
|
|
179
196
|
}
|
|
180
|
-
async post(url, data,
|
|
181
|
-
const response = await this._net.post(url, data,
|
|
197
|
+
async post(url, data, config) {
|
|
198
|
+
const response = await this._net.post(url, data, config);
|
|
182
199
|
return {
|
|
183
200
|
data: response.data,
|
|
184
201
|
status: response.response.status,
|
|
185
202
|
statusText: response.response.statusText,
|
|
186
203
|
headers: response.response.headers,
|
|
187
|
-
config
|
|
204
|
+
config,
|
|
188
205
|
request: void 0
|
|
189
206
|
};
|
|
190
207
|
}
|
|
191
|
-
async put(url, data,
|
|
192
|
-
const response = await this._net.put(url, data,
|
|
208
|
+
async put(url, data, config) {
|
|
209
|
+
const response = await this._net.put(url, data, config);
|
|
193
210
|
return {
|
|
194
211
|
data: response.data,
|
|
195
212
|
status: response.response.status,
|
|
196
213
|
statusText: response.response.statusText,
|
|
197
214
|
headers: response.response.headers,
|
|
198
|
-
config
|
|
215
|
+
config,
|
|
199
216
|
request: void 0
|
|
200
217
|
};
|
|
201
218
|
}
|
|
202
|
-
async delete(url,
|
|
219
|
+
async delete(url, config) {
|
|
203
220
|
const headers = {
|
|
204
|
-
...
|
|
221
|
+
...config?.headers
|
|
205
222
|
};
|
|
206
223
|
if (headers) {
|
|
207
224
|
delete headers["content-type"];
|
|
208
225
|
}
|
|
209
|
-
const { data: configData, ...restConfig } =
|
|
226
|
+
const { data: configData, ...restConfig } = config || {};
|
|
210
227
|
let body;
|
|
211
228
|
if (configData) {
|
|
212
229
|
body = typeof configData === "string" ? configData : JSON.stringify(configData);
|
|
@@ -234,18 +251,18 @@ var BaseService = class extends import_hookified.Hookified {
|
|
|
234
251
|
status: response.status,
|
|
235
252
|
statusText: response.statusText,
|
|
236
253
|
headers: response.headers,
|
|
237
|
-
config
|
|
254
|
+
config,
|
|
238
255
|
request: void 0
|
|
239
256
|
};
|
|
240
257
|
}
|
|
241
|
-
async patch(url, data,
|
|
242
|
-
const response = await this._net.patch(url, data,
|
|
258
|
+
async patch(url, data, config) {
|
|
259
|
+
const response = await this._net.patch(url, data, config);
|
|
243
260
|
return {
|
|
244
261
|
data: response.data,
|
|
245
262
|
status: response.response.status,
|
|
246
263
|
statusText: response.response.statusText,
|
|
247
264
|
headers: response.response.headers,
|
|
248
|
-
config
|
|
265
|
+
config,
|
|
249
266
|
request: void 0
|
|
250
267
|
};
|
|
251
268
|
}
|
|
@@ -722,6 +739,7 @@ var NetInfo = class extends BaseService {
|
|
|
722
739
|
const errorResult = {
|
|
723
740
|
ip,
|
|
724
741
|
type: "error",
|
|
742
|
+
/* v8 ignore next -- @preserve */
|
|
725
743
|
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
726
744
|
};
|
|
727
745
|
return errorResult;
|
|
@@ -765,7 +783,7 @@ var NetInfo = class extends BaseService {
|
|
|
765
783
|
};
|
|
766
784
|
|
|
767
785
|
// src/toggle.ts
|
|
768
|
-
var
|
|
786
|
+
var import_net3 = require("@cacheable/net");
|
|
769
787
|
var import_hookified2 = require("hookified");
|
|
770
788
|
var Toggle = class extends import_hookified2.Hookified {
|
|
771
789
|
static {
|
|
@@ -776,7 +794,7 @@ var Toggle = class extends import_hookified2.Hookified {
|
|
|
776
794
|
_applicationId;
|
|
777
795
|
_environment;
|
|
778
796
|
_horizonUrls = [];
|
|
779
|
-
_net = new
|
|
797
|
+
_net = new import_net3.CacheableNet();
|
|
780
798
|
_defaultContext;
|
|
781
799
|
_defaultTargetingKey = `${Math.random().toString(36).substring(7)}`;
|
|
782
800
|
/**
|
|
@@ -1008,6 +1026,7 @@ var Toggle = class extends import_hookified2.Hookified {
|
|
|
1008
1026
|
try {
|
|
1009
1027
|
const context = {
|
|
1010
1028
|
application: this._applicationId ?? "",
|
|
1029
|
+
/* v8 ignore next -- @preserve */
|
|
1011
1030
|
environment: this._environment ?? "development"
|
|
1012
1031
|
};
|
|
1013
1032
|
if (options?.context) {
|
|
@@ -1198,7 +1217,10 @@ var Toggle = class extends import_hookified2.Hookified {
|
|
|
1198
1217
|
const data = await response.json();
|
|
1199
1218
|
return data;
|
|
1200
1219
|
} catch (error) {
|
|
1201
|
-
const fetchError =
|
|
1220
|
+
const fetchError = (
|
|
1221
|
+
/* v8 ignore next -- @preserve */
|
|
1222
|
+
error instanceof Error ? error : new Error("Unknown fetch error")
|
|
1223
|
+
);
|
|
1202
1224
|
const statusMatch = fetchError.message.match(/status (\d{3})/);
|
|
1203
1225
|
if (statusMatch) {
|
|
1204
1226
|
const status = statusMatch[1];
|
|
@@ -1467,6 +1489,7 @@ var Hyphen = class extends import_hookified3.Hookified {
|
|
|
1467
1489
|
Hyphen,
|
|
1468
1490
|
Toggle,
|
|
1469
1491
|
env,
|
|
1492
|
+
getExecutionContext,
|
|
1470
1493
|
loadEnv
|
|
1471
1494
|
});
|
|
1472
1495
|
/* v8 ignore next -- @preserve */
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Cacheable } from 'cacheable';
|
|
1
2
|
import { HookifiedOptions, Hookified } from 'hookified';
|
|
2
3
|
import { FetchRequestInit, FetchOptions } from '@cacheable/net';
|
|
3
|
-
import { Cacheable } from 'cacheable';
|
|
4
4
|
import pino from 'pino';
|
|
5
5
|
|
|
6
6
|
type EnvOptions = {
|
|
@@ -21,6 +21,84 @@ declare function env(options?: EnvOptions): void;
|
|
|
21
21
|
declare const loadEnv: typeof env;
|
|
22
22
|
type LoadEnvOptions = EnvOptions;
|
|
23
23
|
|
|
24
|
+
type ExecutionContextOptions = {
|
|
25
|
+
/**
|
|
26
|
+
* The organization ID for the Hyphen API.
|
|
27
|
+
*/
|
|
28
|
+
organizationId?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The base URI for the Hyphen API.
|
|
31
|
+
* @default "https://api.hyphen.ai"
|
|
32
|
+
*/
|
|
33
|
+
baseUri?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The Cacheable instance to use for caching requests.
|
|
36
|
+
*/
|
|
37
|
+
cache?: Cacheable;
|
|
38
|
+
};
|
|
39
|
+
type ExecutionContextRequest = {
|
|
40
|
+
id?: string;
|
|
41
|
+
causationId?: string;
|
|
42
|
+
correlationId?: string;
|
|
43
|
+
};
|
|
44
|
+
type ExecutionContextUser = {
|
|
45
|
+
id?: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
rules?: Record<string, unknown>[];
|
|
48
|
+
type?: string;
|
|
49
|
+
};
|
|
50
|
+
type ExecutionContextMember = {
|
|
51
|
+
id?: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
organization?: {
|
|
54
|
+
id?: string;
|
|
55
|
+
name?: string;
|
|
56
|
+
};
|
|
57
|
+
rules?: Record<string, unknown>[];
|
|
58
|
+
};
|
|
59
|
+
type ExecutionContextOrganization = {
|
|
60
|
+
id?: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
};
|
|
63
|
+
type ExecutionContextLocation = {
|
|
64
|
+
country?: string;
|
|
65
|
+
region?: string;
|
|
66
|
+
city?: string;
|
|
67
|
+
lat?: number;
|
|
68
|
+
lng?: number;
|
|
69
|
+
postalCode?: string;
|
|
70
|
+
timezone?: string;
|
|
71
|
+
};
|
|
72
|
+
type ExecutionContext = {
|
|
73
|
+
request?: ExecutionContextRequest;
|
|
74
|
+
user?: ExecutionContextUser;
|
|
75
|
+
member?: ExecutionContextMember;
|
|
76
|
+
organization?: ExecutionContextOrganization;
|
|
77
|
+
ipAddress?: string;
|
|
78
|
+
location?: ExecutionContextLocation;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Get the execution context for the provided API key.
|
|
82
|
+
* This validates the API key and returns information about the organization, user, and request context.
|
|
83
|
+
*
|
|
84
|
+
* @param apiKey - The API key for the Hyphen API.
|
|
85
|
+
* @param options - Additional options for the request.
|
|
86
|
+
* @returns The execution context.
|
|
87
|
+
* @throws Error if the API key is not provided or if the request fails.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import { getExecutionContext } from '@hyphen/sdk';
|
|
92
|
+
*
|
|
93
|
+
* const context = await getExecutionContext('your-api-key', {
|
|
94
|
+
* organizationId: 'optional-org-id',
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* console.log(context.organization?.name);
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function getExecutionContext(apiKey: string, options?: ExecutionContextOptions): Promise<ExecutionContext>;
|
|
101
|
+
|
|
24
102
|
interface HttpResponse<T = any> {
|
|
25
103
|
data: T;
|
|
26
104
|
status: number;
|
|
@@ -1049,4 +1127,4 @@ declare class Hyphen extends Hookified {
|
|
|
1049
1127
|
set apiKey(value: string | undefined);
|
|
1050
1128
|
}
|
|
1051
1129
|
|
|
1052
|
-
export { type EnvOptions, Hyphen, type HyphenOptions, type LoadEnvOptions, Toggle, type ToggleContext, type ToggleEvaluation, ToggleEvents, type ToggleOptions, type ToggleUser, env, loadEnv };
|
|
1130
|
+
export { type EnvOptions, type ExecutionContext, type ExecutionContextLocation, type ExecutionContextMember, type ExecutionContextOptions, type ExecutionContextOrganization, type ExecutionContextRequest, type ExecutionContextUser, Hyphen, type HyphenOptions, type LoadEnvOptions, Toggle, type ToggleContext, type ToggleEvaluation, ToggleEvents, type ToggleOptions, type ToggleUser, env, getExecutionContext, loadEnv };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Cacheable } from 'cacheable';
|
|
1
2
|
import { HookifiedOptions, Hookified } from 'hookified';
|
|
2
3
|
import { FetchRequestInit, FetchOptions } from '@cacheable/net';
|
|
3
|
-
import { Cacheable } from 'cacheable';
|
|
4
4
|
import pino from 'pino';
|
|
5
5
|
|
|
6
6
|
type EnvOptions = {
|
|
@@ -21,6 +21,84 @@ declare function env(options?: EnvOptions): void;
|
|
|
21
21
|
declare const loadEnv: typeof env;
|
|
22
22
|
type LoadEnvOptions = EnvOptions;
|
|
23
23
|
|
|
24
|
+
type ExecutionContextOptions = {
|
|
25
|
+
/**
|
|
26
|
+
* The organization ID for the Hyphen API.
|
|
27
|
+
*/
|
|
28
|
+
organizationId?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The base URI for the Hyphen API.
|
|
31
|
+
* @default "https://api.hyphen.ai"
|
|
32
|
+
*/
|
|
33
|
+
baseUri?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The Cacheable instance to use for caching requests.
|
|
36
|
+
*/
|
|
37
|
+
cache?: Cacheable;
|
|
38
|
+
};
|
|
39
|
+
type ExecutionContextRequest = {
|
|
40
|
+
id?: string;
|
|
41
|
+
causationId?: string;
|
|
42
|
+
correlationId?: string;
|
|
43
|
+
};
|
|
44
|
+
type ExecutionContextUser = {
|
|
45
|
+
id?: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
rules?: Record<string, unknown>[];
|
|
48
|
+
type?: string;
|
|
49
|
+
};
|
|
50
|
+
type ExecutionContextMember = {
|
|
51
|
+
id?: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
organization?: {
|
|
54
|
+
id?: string;
|
|
55
|
+
name?: string;
|
|
56
|
+
};
|
|
57
|
+
rules?: Record<string, unknown>[];
|
|
58
|
+
};
|
|
59
|
+
type ExecutionContextOrganization = {
|
|
60
|
+
id?: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
};
|
|
63
|
+
type ExecutionContextLocation = {
|
|
64
|
+
country?: string;
|
|
65
|
+
region?: string;
|
|
66
|
+
city?: string;
|
|
67
|
+
lat?: number;
|
|
68
|
+
lng?: number;
|
|
69
|
+
postalCode?: string;
|
|
70
|
+
timezone?: string;
|
|
71
|
+
};
|
|
72
|
+
type ExecutionContext = {
|
|
73
|
+
request?: ExecutionContextRequest;
|
|
74
|
+
user?: ExecutionContextUser;
|
|
75
|
+
member?: ExecutionContextMember;
|
|
76
|
+
organization?: ExecutionContextOrganization;
|
|
77
|
+
ipAddress?: string;
|
|
78
|
+
location?: ExecutionContextLocation;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Get the execution context for the provided API key.
|
|
82
|
+
* This validates the API key and returns information about the organization, user, and request context.
|
|
83
|
+
*
|
|
84
|
+
* @param apiKey - The API key for the Hyphen API.
|
|
85
|
+
* @param options - Additional options for the request.
|
|
86
|
+
* @returns The execution context.
|
|
87
|
+
* @throws Error if the API key is not provided or if the request fails.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import { getExecutionContext } from '@hyphen/sdk';
|
|
92
|
+
*
|
|
93
|
+
* const context = await getExecutionContext('your-api-key', {
|
|
94
|
+
* organizationId: 'optional-org-id',
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* console.log(context.organization?.name);
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function getExecutionContext(apiKey: string, options?: ExecutionContextOptions): Promise<ExecutionContext>;
|
|
101
|
+
|
|
24
102
|
interface HttpResponse<T = any> {
|
|
25
103
|
data: T;
|
|
26
104
|
status: number;
|
|
@@ -1049,4 +1127,4 @@ declare class Hyphen extends Hookified {
|
|
|
1049
1127
|
set apiKey(value: string | undefined);
|
|
1050
1128
|
}
|
|
1051
1129
|
|
|
1052
|
-
export { type EnvOptions, Hyphen, type HyphenOptions, type LoadEnvOptions, Toggle, type ToggleContext, type ToggleEvaluation, ToggleEvents, type ToggleOptions, type ToggleUser, env, loadEnv };
|
|
1130
|
+
export { type EnvOptions, type ExecutionContext, type ExecutionContextLocation, type ExecutionContextMember, type ExecutionContextOptions, type ExecutionContextOrganization, type ExecutionContextRequest, type ExecutionContextUser, Hyphen, type HyphenOptions, type LoadEnvOptions, Toggle, type ToggleContext, type ToggleEvaluation, ToggleEvents, type ToggleOptions, type ToggleUser, env, getExecutionContext, loadEnv };
|
package/dist/index.js
CHANGED
|
@@ -5,56 +5,72 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import process from "process";
|
|
8
|
-
import {
|
|
8
|
+
import { parseEnv } from "util";
|
|
9
|
+
function loadEnvFile(filePath, override) {
|
|
10
|
+
try {
|
|
11
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
12
|
+
const parsed = parseEnv(content);
|
|
13
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
14
|
+
if (override || process.env[key] === void 0) {
|
|
15
|
+
process.env[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
__name(loadEnvFile, "loadEnvFile");
|
|
9
22
|
function env(options) {
|
|
10
23
|
const local = options?.local ?? true;
|
|
11
24
|
const currentWorkingDirectory = options?.path ?? process.cwd();
|
|
12
25
|
const envPath = path.resolve(currentWorkingDirectory, ".env");
|
|
13
|
-
|
|
14
|
-
config({
|
|
15
|
-
path: envPath,
|
|
16
|
-
quiet: true,
|
|
17
|
-
debug: false
|
|
18
|
-
});
|
|
19
|
-
}
|
|
26
|
+
loadEnvFile(envPath, false);
|
|
20
27
|
if (local) {
|
|
21
28
|
const localEnvPath = path.resolve(currentWorkingDirectory, ".env.local");
|
|
22
|
-
|
|
23
|
-
config({
|
|
24
|
-
path: localEnvPath,
|
|
25
|
-
override: true,
|
|
26
|
-
quiet: true,
|
|
27
|
-
debug: false
|
|
28
|
-
});
|
|
29
|
-
}
|
|
29
|
+
loadEnvFile(localEnvPath, true);
|
|
30
30
|
}
|
|
31
31
|
const environment = options?.environment ?? process.env.NODE_ENV;
|
|
32
32
|
if (environment) {
|
|
33
33
|
const envSpecificPath = path.resolve(currentWorkingDirectory, `.env.${environment}`);
|
|
34
|
-
|
|
35
|
-
config({
|
|
36
|
-
path: envSpecificPath,
|
|
37
|
-
override: true,
|
|
38
|
-
quiet: true,
|
|
39
|
-
debug: false
|
|
40
|
-
});
|
|
41
|
-
}
|
|
34
|
+
loadEnvFile(envSpecificPath, true);
|
|
42
35
|
if (local) {
|
|
43
36
|
const envLocalPath = path.resolve(currentWorkingDirectory, `.env.${environment}.local`);
|
|
44
|
-
|
|
45
|
-
config({
|
|
46
|
-
path: envLocalPath,
|
|
47
|
-
override: true,
|
|
48
|
-
quiet: true,
|
|
49
|
-
debug: false
|
|
50
|
-
});
|
|
51
|
-
}
|
|
37
|
+
loadEnvFile(envLocalPath, true);
|
|
52
38
|
}
|
|
53
39
|
}
|
|
54
40
|
}
|
|
55
41
|
__name(env, "env");
|
|
56
42
|
var loadEnv = env;
|
|
57
43
|
|
|
44
|
+
// src/execution-context.ts
|
|
45
|
+
import { CacheableNet } from "@cacheable/net";
|
|
46
|
+
async function getExecutionContext(apiKey, options) {
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
throw new Error("API key is required");
|
|
49
|
+
}
|
|
50
|
+
const baseUri = options?.baseUri ?? "https://api.hyphen.ai";
|
|
51
|
+
let url = `${baseUri}/api/execution-context`;
|
|
52
|
+
if (options?.organizationId) {
|
|
53
|
+
url += `?organizationId=${encodeURIComponent(options.organizationId)}`;
|
|
54
|
+
}
|
|
55
|
+
const net = options?.cache ? new CacheableNet({
|
|
56
|
+
cache: options.cache
|
|
57
|
+
}) : new CacheableNet();
|
|
58
|
+
const caching = options?.cache !== void 0;
|
|
59
|
+
const response = await net.get(url, {
|
|
60
|
+
headers: {
|
|
61
|
+
"x-api-key": apiKey,
|
|
62
|
+
"content-type": "application/json",
|
|
63
|
+
accept: "application/json"
|
|
64
|
+
},
|
|
65
|
+
caching
|
|
66
|
+
});
|
|
67
|
+
if (response.response.status !== 200) {
|
|
68
|
+
throw new Error(`Failed to get execution context: ${response.response.statusText}`);
|
|
69
|
+
}
|
|
70
|
+
return response.data;
|
|
71
|
+
}
|
|
72
|
+
__name(getExecutionContext, "getExecutionContext");
|
|
73
|
+
|
|
58
74
|
// src/hyphen.ts
|
|
59
75
|
import { Hookified as Hookified3 } from "hookified";
|
|
60
76
|
|
|
@@ -63,7 +79,7 @@ import { Buffer as Buffer2 } from "buffer";
|
|
|
63
79
|
import process2 from "process";
|
|
64
80
|
|
|
65
81
|
// src/base-service.ts
|
|
66
|
-
import { CacheableNet } from "@cacheable/net";
|
|
82
|
+
import { CacheableNet as CacheableNet2 } from "@cacheable/net";
|
|
67
83
|
import { Cacheable } from "cacheable";
|
|
68
84
|
import { Hookified } from "hookified";
|
|
69
85
|
import pino from "pino";
|
|
@@ -85,7 +101,7 @@ var BaseService = class extends Hookified {
|
|
|
85
101
|
if (options && options.throwErrors !== void 0) {
|
|
86
102
|
this._throwErrors = options.throwErrors;
|
|
87
103
|
}
|
|
88
|
-
this._net = new
|
|
104
|
+
this._net = new CacheableNet2({
|
|
89
105
|
cache: this._cache
|
|
90
106
|
});
|
|
91
107
|
}
|
|
@@ -123,53 +139,53 @@ var BaseService = class extends Hookified {
|
|
|
123
139
|
this._log.info(message, ...args);
|
|
124
140
|
this.emit("info", message, ...args);
|
|
125
141
|
}
|
|
126
|
-
async get(url,
|
|
142
|
+
async get(url, config) {
|
|
127
143
|
let finalUrl = url;
|
|
128
|
-
if (
|
|
129
|
-
const params = new URLSearchParams(
|
|
144
|
+
if (config?.params) {
|
|
145
|
+
const params = new URLSearchParams(config.params);
|
|
130
146
|
finalUrl = `${url}?${params.toString()}`;
|
|
131
147
|
}
|
|
132
|
-
const { params: _, ...fetchConfig } =
|
|
148
|
+
const { params: _, ...fetchConfig } = config || {};
|
|
133
149
|
const response = await this._net.get(finalUrl, fetchConfig);
|
|
134
150
|
return {
|
|
135
151
|
data: response.data,
|
|
136
152
|
status: response.response.status,
|
|
137
153
|
statusText: response.response.statusText,
|
|
138
154
|
headers: response.response.headers,
|
|
139
|
-
config
|
|
155
|
+
config,
|
|
140
156
|
request: void 0
|
|
141
157
|
};
|
|
142
158
|
}
|
|
143
|
-
async post(url, data,
|
|
144
|
-
const response = await this._net.post(url, data,
|
|
159
|
+
async post(url, data, config) {
|
|
160
|
+
const response = await this._net.post(url, data, config);
|
|
145
161
|
return {
|
|
146
162
|
data: response.data,
|
|
147
163
|
status: response.response.status,
|
|
148
164
|
statusText: response.response.statusText,
|
|
149
165
|
headers: response.response.headers,
|
|
150
|
-
config
|
|
166
|
+
config,
|
|
151
167
|
request: void 0
|
|
152
168
|
};
|
|
153
169
|
}
|
|
154
|
-
async put(url, data,
|
|
155
|
-
const response = await this._net.put(url, data,
|
|
170
|
+
async put(url, data, config) {
|
|
171
|
+
const response = await this._net.put(url, data, config);
|
|
156
172
|
return {
|
|
157
173
|
data: response.data,
|
|
158
174
|
status: response.response.status,
|
|
159
175
|
statusText: response.response.statusText,
|
|
160
176
|
headers: response.response.headers,
|
|
161
|
-
config
|
|
177
|
+
config,
|
|
162
178
|
request: void 0
|
|
163
179
|
};
|
|
164
180
|
}
|
|
165
|
-
async delete(url,
|
|
181
|
+
async delete(url, config) {
|
|
166
182
|
const headers = {
|
|
167
|
-
...
|
|
183
|
+
...config?.headers
|
|
168
184
|
};
|
|
169
185
|
if (headers) {
|
|
170
186
|
delete headers["content-type"];
|
|
171
187
|
}
|
|
172
|
-
const { data: configData, ...restConfig } =
|
|
188
|
+
const { data: configData, ...restConfig } = config || {};
|
|
173
189
|
let body;
|
|
174
190
|
if (configData) {
|
|
175
191
|
body = typeof configData === "string" ? configData : JSON.stringify(configData);
|
|
@@ -197,18 +213,18 @@ var BaseService = class extends Hookified {
|
|
|
197
213
|
status: response.status,
|
|
198
214
|
statusText: response.statusText,
|
|
199
215
|
headers: response.headers,
|
|
200
|
-
config
|
|
216
|
+
config,
|
|
201
217
|
request: void 0
|
|
202
218
|
};
|
|
203
219
|
}
|
|
204
|
-
async patch(url, data,
|
|
205
|
-
const response = await this._net.patch(url, data,
|
|
220
|
+
async patch(url, data, config) {
|
|
221
|
+
const response = await this._net.patch(url, data, config);
|
|
206
222
|
return {
|
|
207
223
|
data: response.data,
|
|
208
224
|
status: response.response.status,
|
|
209
225
|
statusText: response.response.statusText,
|
|
210
226
|
headers: response.response.headers,
|
|
211
|
-
config
|
|
227
|
+
config,
|
|
212
228
|
request: void 0
|
|
213
229
|
};
|
|
214
230
|
}
|
|
@@ -685,6 +701,7 @@ var NetInfo = class extends BaseService {
|
|
|
685
701
|
const errorResult = {
|
|
686
702
|
ip,
|
|
687
703
|
type: "error",
|
|
704
|
+
/* v8 ignore next -- @preserve */
|
|
688
705
|
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
689
706
|
};
|
|
690
707
|
return errorResult;
|
|
@@ -728,7 +745,7 @@ var NetInfo = class extends BaseService {
|
|
|
728
745
|
};
|
|
729
746
|
|
|
730
747
|
// src/toggle.ts
|
|
731
|
-
import { CacheableNet as
|
|
748
|
+
import { CacheableNet as CacheableNet3 } from "@cacheable/net";
|
|
732
749
|
import { Hookified as Hookified2 } from "hookified";
|
|
733
750
|
var Toggle = class extends Hookified2 {
|
|
734
751
|
static {
|
|
@@ -739,7 +756,7 @@ var Toggle = class extends Hookified2 {
|
|
|
739
756
|
_applicationId;
|
|
740
757
|
_environment;
|
|
741
758
|
_horizonUrls = [];
|
|
742
|
-
_net = new
|
|
759
|
+
_net = new CacheableNet3();
|
|
743
760
|
_defaultContext;
|
|
744
761
|
_defaultTargetingKey = `${Math.random().toString(36).substring(7)}`;
|
|
745
762
|
/**
|
|
@@ -971,6 +988,7 @@ var Toggle = class extends Hookified2 {
|
|
|
971
988
|
try {
|
|
972
989
|
const context = {
|
|
973
990
|
application: this._applicationId ?? "",
|
|
991
|
+
/* v8 ignore next -- @preserve */
|
|
974
992
|
environment: this._environment ?? "development"
|
|
975
993
|
};
|
|
976
994
|
if (options?.context) {
|
|
@@ -1161,7 +1179,10 @@ var Toggle = class extends Hookified2 {
|
|
|
1161
1179
|
const data = await response.json();
|
|
1162
1180
|
return data;
|
|
1163
1181
|
} catch (error) {
|
|
1164
|
-
const fetchError =
|
|
1182
|
+
const fetchError = (
|
|
1183
|
+
/* v8 ignore next -- @preserve */
|
|
1184
|
+
error instanceof Error ? error : new Error("Unknown fetch error")
|
|
1185
|
+
);
|
|
1165
1186
|
const statusMatch = fetchError.message.match(/status (\d{3})/);
|
|
1166
1187
|
if (statusMatch) {
|
|
1167
1188
|
const status = statusMatch[1];
|
|
@@ -1429,6 +1450,7 @@ export {
|
|
|
1429
1450
|
Hyphen,
|
|
1430
1451
|
Toggle,
|
|
1431
1452
|
env,
|
|
1453
|
+
getExecutionContext,
|
|
1432
1454
|
loadEnv
|
|
1433
1455
|
};
|
|
1434
1456
|
/* v8 ignore next -- @preserve */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyphen/sdk",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Hyphen SDK for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -29,27 +29,29 @@
|
|
|
29
29
|
],
|
|
30
30
|
"author": "Team Hyphen <hello@hyphen.ai>",
|
|
31
31
|
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.12.0"
|
|
34
|
+
},
|
|
32
35
|
"devDependencies": {
|
|
33
|
-
"@biomejs/biome": "^2.
|
|
34
|
-
"@swc/core": "^1.
|
|
35
|
-
"@types/node": "^
|
|
36
|
-
"@vitest/coverage-v8": "^4.0.
|
|
37
|
-
"rimraf": "^6.
|
|
36
|
+
"@biomejs/biome": "^2.3.8",
|
|
37
|
+
"@swc/core": "^1.15.3",
|
|
38
|
+
"@types/node": "^25.0.0",
|
|
39
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
40
|
+
"rimraf": "^6.1.2",
|
|
38
41
|
"tsd": "^0.33.0",
|
|
39
|
-
"tsup": "^8.5.
|
|
42
|
+
"tsup": "^8.5.1",
|
|
40
43
|
"typescript": "^5.9.3",
|
|
41
|
-
"vitest": "^4.0.
|
|
44
|
+
"vitest": "^4.0.15"
|
|
42
45
|
},
|
|
43
46
|
"files": [
|
|
44
47
|
"dist",
|
|
45
48
|
"LICENSE"
|
|
46
49
|
],
|
|
47
50
|
"dependencies": {
|
|
48
|
-
"@cacheable/net": "^2.0.
|
|
49
|
-
"@faker-js/faker": "^10.
|
|
50
|
-
"cacheable": "^2.
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"pino": "^10.1.0"
|
|
51
|
+
"@cacheable/net": "^2.0.4",
|
|
52
|
+
"@faker-js/faker": "^10.2.0",
|
|
53
|
+
"cacheable": "^2.3.0",
|
|
54
|
+
"hookified": "^1.14.0",
|
|
55
|
+
"pino": "^10.1.1"
|
|
54
56
|
}
|
|
55
57
|
}
|