@hyphen/sdk 2.0.1 → 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 +80 -59
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +79 -59
- 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,59 +177,56 @@ 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
|
-
body = typeof configData === "string" ? (
|
|
213
|
-
/* c8 ignore next */
|
|
214
|
-
configData
|
|
215
|
-
) : JSON.stringify(configData);
|
|
229
|
+
body = typeof configData === "string" ? configData : JSON.stringify(configData);
|
|
216
230
|
if (!headers["content-type"] && !headers["Content-Type"]) {
|
|
217
231
|
headers["content-type"] = "application/json";
|
|
218
232
|
}
|
|
@@ -237,18 +251,18 @@ var BaseService = class extends import_hookified.Hookified {
|
|
|
237
251
|
status: response.status,
|
|
238
252
|
statusText: response.statusText,
|
|
239
253
|
headers: response.headers,
|
|
240
|
-
config
|
|
254
|
+
config,
|
|
241
255
|
request: void 0
|
|
242
256
|
};
|
|
243
257
|
}
|
|
244
|
-
async patch(url, data,
|
|
245
|
-
const response = await this._net.patch(url, data,
|
|
258
|
+
async patch(url, data, config) {
|
|
259
|
+
const response = await this._net.patch(url, data, config);
|
|
246
260
|
return {
|
|
247
261
|
data: response.data,
|
|
248
262
|
status: response.response.status,
|
|
249
263
|
statusText: response.response.statusText,
|
|
250
264
|
headers: response.response.headers,
|
|
251
|
-
config
|
|
265
|
+
config,
|
|
252
266
|
request: void 0
|
|
253
267
|
};
|
|
254
268
|
}
|
|
@@ -725,6 +739,7 @@ var NetInfo = class extends BaseService {
|
|
|
725
739
|
const errorResult = {
|
|
726
740
|
ip,
|
|
727
741
|
type: "error",
|
|
742
|
+
/* v8 ignore next -- @preserve */
|
|
728
743
|
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
729
744
|
};
|
|
730
745
|
return errorResult;
|
|
@@ -768,7 +783,7 @@ var NetInfo = class extends BaseService {
|
|
|
768
783
|
};
|
|
769
784
|
|
|
770
785
|
// src/toggle.ts
|
|
771
|
-
var
|
|
786
|
+
var import_net3 = require("@cacheable/net");
|
|
772
787
|
var import_hookified2 = require("hookified");
|
|
773
788
|
var Toggle = class extends import_hookified2.Hookified {
|
|
774
789
|
static {
|
|
@@ -779,7 +794,7 @@ var Toggle = class extends import_hookified2.Hookified {
|
|
|
779
794
|
_applicationId;
|
|
780
795
|
_environment;
|
|
781
796
|
_horizonUrls = [];
|
|
782
|
-
_net = new
|
|
797
|
+
_net = new import_net3.CacheableNet();
|
|
783
798
|
_defaultContext;
|
|
784
799
|
_defaultTargetingKey = `${Math.random().toString(36).substring(7)}`;
|
|
785
800
|
/**
|
|
@@ -1011,6 +1026,7 @@ var Toggle = class extends import_hookified2.Hookified {
|
|
|
1011
1026
|
try {
|
|
1012
1027
|
const context = {
|
|
1013
1028
|
application: this._applicationId ?? "",
|
|
1029
|
+
/* v8 ignore next -- @preserve */
|
|
1014
1030
|
environment: this._environment ?? "development"
|
|
1015
1031
|
};
|
|
1016
1032
|
if (options?.context) {
|
|
@@ -1201,7 +1217,10 @@ var Toggle = class extends import_hookified2.Hookified {
|
|
|
1201
1217
|
const data = await response.json();
|
|
1202
1218
|
return data;
|
|
1203
1219
|
} catch (error) {
|
|
1204
|
-
const fetchError =
|
|
1220
|
+
const fetchError = (
|
|
1221
|
+
/* v8 ignore next -- @preserve */
|
|
1222
|
+
error instanceof Error ? error : new Error("Unknown fetch error")
|
|
1223
|
+
);
|
|
1205
1224
|
const statusMatch = fetchError.message.match(/status (\d{3})/);
|
|
1206
1225
|
if (statusMatch) {
|
|
1207
1226
|
const status = statusMatch[1];
|
|
@@ -1470,5 +1489,7 @@ var Hyphen = class extends import_hookified3.Hookified {
|
|
|
1470
1489
|
Hyphen,
|
|
1471
1490
|
Toggle,
|
|
1472
1491
|
env,
|
|
1492
|
+
getExecutionContext,
|
|
1473
1493
|
loadEnv
|
|
1474
1494
|
});
|
|
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,59 +139,56 @@ 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
|
-
body = typeof configData === "string" ? (
|
|
176
|
-
/* c8 ignore next */
|
|
177
|
-
configData
|
|
178
|
-
) : JSON.stringify(configData);
|
|
191
|
+
body = typeof configData === "string" ? configData : JSON.stringify(configData);
|
|
179
192
|
if (!headers["content-type"] && !headers["Content-Type"]) {
|
|
180
193
|
headers["content-type"] = "application/json";
|
|
181
194
|
}
|
|
@@ -200,18 +213,18 @@ var BaseService = class extends Hookified {
|
|
|
200
213
|
status: response.status,
|
|
201
214
|
statusText: response.statusText,
|
|
202
215
|
headers: response.headers,
|
|
203
|
-
config
|
|
216
|
+
config,
|
|
204
217
|
request: void 0
|
|
205
218
|
};
|
|
206
219
|
}
|
|
207
|
-
async patch(url, data,
|
|
208
|
-
const response = await this._net.patch(url, data,
|
|
220
|
+
async patch(url, data, config) {
|
|
221
|
+
const response = await this._net.patch(url, data, config);
|
|
209
222
|
return {
|
|
210
223
|
data: response.data,
|
|
211
224
|
status: response.response.status,
|
|
212
225
|
statusText: response.response.statusText,
|
|
213
226
|
headers: response.response.headers,
|
|
214
|
-
config
|
|
227
|
+
config,
|
|
215
228
|
request: void 0
|
|
216
229
|
};
|
|
217
230
|
}
|
|
@@ -688,6 +701,7 @@ var NetInfo = class extends BaseService {
|
|
|
688
701
|
const errorResult = {
|
|
689
702
|
ip,
|
|
690
703
|
type: "error",
|
|
704
|
+
/* v8 ignore next -- @preserve */
|
|
691
705
|
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
692
706
|
};
|
|
693
707
|
return errorResult;
|
|
@@ -731,7 +745,7 @@ var NetInfo = class extends BaseService {
|
|
|
731
745
|
};
|
|
732
746
|
|
|
733
747
|
// src/toggle.ts
|
|
734
|
-
import { CacheableNet as
|
|
748
|
+
import { CacheableNet as CacheableNet3 } from "@cacheable/net";
|
|
735
749
|
import { Hookified as Hookified2 } from "hookified";
|
|
736
750
|
var Toggle = class extends Hookified2 {
|
|
737
751
|
static {
|
|
@@ -742,7 +756,7 @@ var Toggle = class extends Hookified2 {
|
|
|
742
756
|
_applicationId;
|
|
743
757
|
_environment;
|
|
744
758
|
_horizonUrls = [];
|
|
745
|
-
_net = new
|
|
759
|
+
_net = new CacheableNet3();
|
|
746
760
|
_defaultContext;
|
|
747
761
|
_defaultTargetingKey = `${Math.random().toString(36).substring(7)}`;
|
|
748
762
|
/**
|
|
@@ -974,6 +988,7 @@ var Toggle = class extends Hookified2 {
|
|
|
974
988
|
try {
|
|
975
989
|
const context = {
|
|
976
990
|
application: this._applicationId ?? "",
|
|
991
|
+
/* v8 ignore next -- @preserve */
|
|
977
992
|
environment: this._environment ?? "development"
|
|
978
993
|
};
|
|
979
994
|
if (options?.context) {
|
|
@@ -1164,7 +1179,10 @@ var Toggle = class extends Hookified2 {
|
|
|
1164
1179
|
const data = await response.json();
|
|
1165
1180
|
return data;
|
|
1166
1181
|
} catch (error) {
|
|
1167
|
-
const fetchError =
|
|
1182
|
+
const fetchError = (
|
|
1183
|
+
/* v8 ignore next -- @preserve */
|
|
1184
|
+
error instanceof Error ? error : new Error("Unknown fetch error")
|
|
1185
|
+
);
|
|
1168
1186
|
const statusMatch = fetchError.message.match(/status (\d{3})/);
|
|
1169
1187
|
if (statusMatch) {
|
|
1170
1188
|
const status = statusMatch[1];
|
|
@@ -1432,5 +1450,7 @@ export {
|
|
|
1432
1450
|
Hyphen,
|
|
1433
1451
|
Toggle,
|
|
1434
1452
|
env,
|
|
1453
|
+
getExecutionContext,
|
|
1435
1454
|
loadEnv
|
|
1436
1455
|
};
|
|
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": "^
|
|
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": "^
|
|
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.0.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
|
}
|