@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.
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit manually. Run `pnpm turbo run generate` to regenerate.
2
2
 
3
- export const SDK_VERSION = '1.0.0';
3
+ export const SDK_VERSION = '1.2.0';
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 in practice. Sent as a bearer token as-is; the
110
- * control plane is what rejects a key it doesn't accept. Defaults to the
111
- * `HH_CONTROL_PLANE_API_KEY` environment variable.
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
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- needs to match openapi-fetch's own createClient<Paths extends {}> signature
158
- export function createApiClient<Paths extends {}>(
159
- options: ClientConfig,
160
- ): ReturnType<typeof createClient<Paths>> {
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
- const resolvedApiKey = apiKey ?? getEnv('HH_CONTROL_PLANE_API_KEY');
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 the missing-key check so verbose users can see what *did*
181
- // resolve when construction is about to fail.
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: ${resolvedApiKey ? maskApiKey(resolvedApiKey) : '(none)'}`);
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<Paths>({
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
- return client;
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
  /**