@honeyhive/control-plane-sdk 1.0.0 → 1.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/CHANGELOG.md +27 -0
- package/README.md +3 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/apiKeys.d.ts +46 -0
- package/dist/apiKeys.d.ts.map +1 -0
- package/dist/apiKeys.js +97 -0
- package/dist/apiKeys.js.map +1 -0
- package/dist/generated/apiTypes.d.ts +154 -1
- package/dist/generated/apiTypes.d.ts.map +1 -1
- package/dist/generated/client.d.ts +206 -7
- package/dist/generated/client.d.ts.map +1 -1
- package/dist/generated/client.js +282 -23
- package/dist/generated/client.js.map +1 -1
- package/dist/generated/types.d.ts +668 -4
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/util.d.ts +12 -5
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +22 -65
- package/dist/util.js.map +1 -1
- package/package.json +2 -2
- package/release_notes.md +11 -2
- package/src/apiKeys.ts +103 -0
- package/src/generated/apiTypes.ts +172 -1
- package/src/generated/client.ts +360 -23
- package/src/generated/types.ts +668 -4
- package/src/generated/version.ts +1 -1
- package/src/index.ts +16 -0
- package/src/util.ts +32 -75
package/src/generated/version.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -12,15 +12,31 @@ export type {
|
|
|
12
12
|
ListAlertsRequest,
|
|
13
13
|
CreateAlertRequest,
|
|
14
14
|
GetAlertRequest,
|
|
15
|
+
CreateWorkspaceRequest,
|
|
16
|
+
GetWorkspaceRequest,
|
|
17
|
+
UpdateWorkspaceRequest,
|
|
18
|
+
DeleteWorkspaceRequest,
|
|
15
19
|
CreateProjectRequest,
|
|
16
20
|
GetProjectRequest,
|
|
17
21
|
UpdateProjectRequest,
|
|
18
22
|
DeleteProjectRequest,
|
|
23
|
+
CreateVirtualDataplaneRequest,
|
|
24
|
+
GetVirtualDataplaneRequest,
|
|
25
|
+
UpdateVirtualDataplaneRequest,
|
|
26
|
+
DeleteVirtualDataplaneRequest,
|
|
19
27
|
ListAlertsResponse,
|
|
20
28
|
CreateAlertResponse,
|
|
21
29
|
GetAlertResponse,
|
|
30
|
+
CreateWorkspaceResponse,
|
|
31
|
+
GetWorkspaceResponse,
|
|
32
|
+
UpdateWorkspaceResponse,
|
|
33
|
+
DeleteWorkspaceResponse,
|
|
22
34
|
CreateProjectResponse,
|
|
23
35
|
GetProjectResponse,
|
|
24
36
|
UpdateProjectResponse,
|
|
25
37
|
DeleteProjectResponse,
|
|
38
|
+
CreateVirtualDataplaneResponse,
|
|
39
|
+
GetVirtualDataplaneResponse,
|
|
40
|
+
UpdateVirtualDataplaneResponse,
|
|
41
|
+
DeleteVirtualDataplaneResponse,
|
|
26
42
|
} from './generated/apiTypes.js';
|
package/src/util.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import createClient, { type ClientOptions, type Middleware } from 'openapi-fetch';
|
|
3
3
|
|
|
4
|
+
import { checkControlPlaneApiKey, maskApiKey } from './apiKeys.js';
|
|
5
|
+
import { type ApiClients } from './generated/client.js';
|
|
6
|
+
import { type paths } from './generated/types.js';
|
|
4
7
|
import { SDK_VERSION } from './generated/version.js';
|
|
5
8
|
|
|
6
9
|
// The control plane's public, API-key entrypoint. This is deliberately not the
|
|
@@ -36,68 +39,6 @@ function getEnv(key: string, defaultValue?: string): string | undefined {
|
|
|
36
39
|
return defaultValue;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
/**
|
|
40
|
-
* The prefix of a fine-grained control-plane API key, whose values have the
|
|
41
|
-
* shape `hh_fgcp_<key id>_<key secret>`. In practice this is the only credential
|
|
42
|
-
* the control plane's API accepts, but the SDK does not enforce that — the
|
|
43
|
-
* prefix's job here is masking: it identifies the values whose key id can be
|
|
44
|
-
* shown, and everything else is redacted wholesale.
|
|
45
|
-
*/
|
|
46
|
-
const FGCP_KEY_PREFIX = 'hh_fgcp_';
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* The shape of the key id segment of a fine-grained key value: exactly 24
|
|
50
|
-
* alphanumeric characters, with `_` and `-` excluded so that an id can never
|
|
51
|
-
* read as two segments.
|
|
52
|
-
*
|
|
53
|
-
* The length and the alphabet are fixed properties of the key format, so a value
|
|
54
|
-
* whose id segment doesn't match this either wasn't issued by HoneyHive or has
|
|
55
|
-
* been altered in transit — either way it is redacted rather than rendered.
|
|
56
|
-
*/
|
|
57
|
-
const FGCP_KEY_ID_PATTERN = /^[A-Za-z0-9]{24}$/;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Returns a display-safe rendering of an API key for verbose logging.
|
|
61
|
-
*
|
|
62
|
-
* Renders `hh_fgcp_<key id>_******` — the key's id and none of its secret. This
|
|
63
|
-
* is character-for-character the masked form HoneyHive displays for that key, so
|
|
64
|
-
* a verbose log line can be matched directly against a key in your account.
|
|
65
|
-
* Masking from the id rather than from the secret is what makes it unique per
|
|
66
|
-
* key and free of secret material.
|
|
67
|
-
*
|
|
68
|
-
* The id is `_`-free by construction, so it is everything up to the first `_`
|
|
69
|
-
* after the prefix.
|
|
70
|
-
*
|
|
71
|
-
* Anything else collapses to 8 fixed-width asterisks, revealing neither its
|
|
72
|
-
* length nor its content. That covers both a value that isn't a fine-grained key
|
|
73
|
-
* at all — the SDK forwards whatever it is given, so this function must assume
|
|
74
|
-
* it may be handed a coarse-grained HoneyHive key or a credential belonging to
|
|
75
|
-
* some other system entirely — and a fine-grained value whose id segment is
|
|
76
|
-
* truncated or mangled, where the characters after the prefix could be secret
|
|
77
|
-
* material rather than an id.
|
|
78
|
-
*
|
|
79
|
-
* Both guards are load-bearing, and the prefix guard especially: without it the
|
|
80
|
-
* unconditional slice below would chop 8 characters off an arbitrary token and
|
|
81
|
-
* any value whose next 24 characters happened to be alphanumerics followed by
|
|
82
|
-
* `_` would render as `hh_fgcp_<24 chars of that token>_******`, echoing part of
|
|
83
|
-
* a foreign credential under our own prefix. So the output is always either
|
|
84
|
-
* exactly the server's mask or fully redacted, never a partial echo.
|
|
85
|
-
*/
|
|
86
|
-
function maskApiKey(apiKey: string): string {
|
|
87
|
-
if (!apiKey.startsWith(FGCP_KEY_PREFIX)) {
|
|
88
|
-
return '********';
|
|
89
|
-
}
|
|
90
|
-
const rest = apiKey.slice(FGCP_KEY_PREFIX.length);
|
|
91
|
-
const separator = rest.indexOf('_');
|
|
92
|
-
// An empty id never matches the pattern, so a value with no separator at all
|
|
93
|
-
// takes the redacted path without a second branch.
|
|
94
|
-
const keyId = separator === -1 ? '' : rest.slice(0, separator);
|
|
95
|
-
if (!FGCP_KEY_ID_PATTERN.test(keyId)) {
|
|
96
|
-
return '********';
|
|
97
|
-
}
|
|
98
|
-
return `${FGCP_KEY_PREFIX}${keyId}_******`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
42
|
/**
|
|
102
43
|
* Configuration options for the HoneyHive Control Plane client. They extend the
|
|
103
44
|
* options from openapi-fetch, but replace 'baseUrl' with 'controlPlaneUrl' so
|
|
@@ -106,9 +47,10 @@ function maskApiKey(apiKey: string): string {
|
|
|
106
47
|
export interface ClientConfig extends Omit<ClientOptions, 'baseUrl' | 'headers'> {
|
|
107
48
|
/**
|
|
108
49
|
* A fine-grained control-plane API key (`hh_fgcp_…`), the only credential the
|
|
109
|
-
* control plane's API accepts
|
|
110
|
-
*
|
|
111
|
-
*
|
|
50
|
+
* control plane's API accepts. Defaults to the `HH_CONTROL_PLANE_API_KEY`
|
|
51
|
+
* environment variable. The value is trimmed, and one that is not a
|
|
52
|
+
* well-formed fine-grained key throws at construction, naming the option or
|
|
53
|
+
* variable it came from. An empty value counts as no key.
|
|
112
54
|
*/
|
|
113
55
|
apiKey?: string;
|
|
114
56
|
controlPlaneUrl?: string;
|
|
@@ -154,13 +96,18 @@ function querySerializer(queryParams: Record<string, unknown>): string {
|
|
|
154
96
|
return uri.startsWith('?') ? uri.slice(1) : uri;
|
|
155
97
|
}
|
|
156
98
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Resolves the client's credential and returns the openapi-fetch client that
|
|
101
|
+
* carries it, keyed by the security scheme the spec defines. Every generated
|
|
102
|
+
* method indexes the result by its operation's scheme.
|
|
103
|
+
*/
|
|
104
|
+
export function createApiClient(options: ClientConfig): ApiClients {
|
|
161
105
|
const { apiKey, controlPlaneUrl, middleware, verbose, _internal_provenance, ...clientOptions } =
|
|
162
106
|
options;
|
|
163
|
-
|
|
107
|
+
// Option > env var. The source travels with the value so the shape check
|
|
108
|
+
// below can name where a bad value came from.
|
|
109
|
+
const [rawApiKey, apiKeySource] =
|
|
110
|
+
apiKey !== undefined ? [apiKey, 'apiKey'] : [getEnv('HH_CONTROL_PLANE_API_KEY'), 'HH_CONTROL_PLANE_API_KEY']; // prettier-ignore
|
|
164
111
|
|
|
165
112
|
// Resolution order: option > env var > default. For the option, any
|
|
166
113
|
// non-undefined value wins (so explicit undefined falls back). For the env
|
|
@@ -177,14 +124,19 @@ export function createApiClient<Paths extends {}>(
|
|
|
177
124
|
version: SDK_VERSION,
|
|
178
125
|
};
|
|
179
126
|
|
|
180
|
-
// Log before
|
|
181
|
-
// resolve when construction is about to
|
|
127
|
+
// Log before either check that can throw (a malformed key, a missing key) so
|
|
128
|
+
// verbose users can see what *did* resolve when construction is about to
|
|
129
|
+
// fail. The mask redacts a malformed key wholesale.
|
|
182
130
|
if (resolvedVerbose) {
|
|
183
131
|
console.error(`Control plane URL: ${resolvedControlPlaneUrl}`);
|
|
184
|
-
console.error(`API key: ${
|
|
132
|
+
console.error(`API key: ${rawApiKey ? maskApiKey(rawApiKey) : '(none)'}`);
|
|
185
133
|
console.error(`Package: ${provenance.package} v${provenance.version}`);
|
|
186
134
|
}
|
|
187
135
|
|
|
136
|
+
// A present key is checked for shape here, at construction, whether or not
|
|
137
|
+
// middleware is supplied. An empty key is no key (below), not a bad one.
|
|
138
|
+
const resolvedApiKey = rawApiKey ? checkControlPlaneApiKey(rawApiKey, apiKeySource) : rawApiKey;
|
|
139
|
+
|
|
188
140
|
// When middleware is supplied, it is assumed to handle authentication itself
|
|
189
141
|
// (for example by attaching a short-lived token per request), so no key is
|
|
190
142
|
// required. The URL always resolves (option > env > default), so only the key
|
|
@@ -204,7 +156,7 @@ export function createApiClient<Paths extends {}>(
|
|
|
204
156
|
headers.Authorization = `Bearer ${resolvedApiKey}`;
|
|
205
157
|
}
|
|
206
158
|
|
|
207
|
-
const client = createClient<
|
|
159
|
+
const client = createClient<paths>({
|
|
208
160
|
...clientOptions,
|
|
209
161
|
querySerializer,
|
|
210
162
|
// Always set (option > env > default). Middleware, when supplied, may still
|
|
@@ -221,7 +173,12 @@ export function createApiClient<Paths extends {}>(
|
|
|
221
173
|
client.use(...middleware);
|
|
222
174
|
}
|
|
223
175
|
|
|
224
|
-
|
|
176
|
+
// The control plane takes one credential kind, the fine-grained control
|
|
177
|
+
// plane key, so every scheme maps to the one client. Keyed by the schemes
|
|
178
|
+
// the spec defines, so a scheme added to or removed from the spec fails to
|
|
179
|
+
// compile here until this table says which client serves it.
|
|
180
|
+
const clients: ApiClients = { ControlPlaneApiKey: client };
|
|
181
|
+
return clients;
|
|
225
182
|
}
|
|
226
183
|
|
|
227
184
|
/**
|