@honeyhive/control-plane-sdk 1.1.0 → 1.3.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/src/apiKeys.ts ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * What the SDK knows about the shape of HoneyHive API keys: the fine-grained
3
+ * control-plane key's format, checked at client construction, and the mask the
4
+ * verbose log renders a key with. Nothing here resolves or sends a key.
5
+ */
6
+
7
+ import { MalformedApiKeyError } from './errors.js';
8
+
9
+ /**
10
+ * The prefix of a fine-grained control-plane API key, whose values have the
11
+ * shape `hh_fgcp_<key id>_<key secret>`. It is the only credential the control
12
+ * plane's API accepts, so a configured key is checked against this shape at
13
+ * construction (see `checkControlPlaneApiKey`), and the mask below identifies
14
+ * the values whose key id can be shown.
15
+ */
16
+ const FGCP_KEY_PREFIX = 'hh_fgcp_';
17
+
18
+ /**
19
+ * A complete fine-grained control-plane key: the prefix, a 24-character id, and
20
+ * a 64-character secret over the URL-safe alphabet. Both lengths are fixed
21
+ * properties of the key format, so a value of any other shape is a truncated or
22
+ * corrupted key, never a newer variant.
23
+ */
24
+ const FGCP_KEY_PATTERN = /^hh_fgcp_[A-Za-z0-9]{24}_[A-Za-z0-9_-]{64}$/;
25
+
26
+ /**
27
+ * Returns `value` if it is a well-formed fine-grained control-plane key, and
28
+ * throws a {@link MalformedApiKeyError} otherwise, naming `source` (the option
29
+ * or environment variable the value came from) and what the value looks like
30
+ * instead. The message never echoes the value itself.
31
+ *
32
+ * This runs at client construction whether or not middleware is supplied: a
33
+ * present key goes into the `Authorization` header regardless, so a malformed
34
+ * one is a fault either way, and failing here names it where a 404 from the
35
+ * first request would not.
36
+ */
37
+ export function checkControlPlaneApiKey(value: string, source: string): string {
38
+ const candidate = value.trim();
39
+ if (FGCP_KEY_PATTERN.test(candidate)) {
40
+ return candidate;
41
+ }
42
+ let problem = 'the value provided is not a HoneyHive API key.';
43
+ if (candidate.startsWith(FGCP_KEY_PREFIX)) {
44
+ problem = 'the value provided begins with it but is not a complete key.';
45
+ } else if (candidate.startsWith('hh_')) {
46
+ problem = 'the value provided looks like a different kind of HoneyHive API key.';
47
+ }
48
+ throw new MalformedApiKeyError(
49
+ source,
50
+ `must be a fine-grained control plane API key, a value beginning with '${FGCP_KEY_PREFIX}'; ${problem}`,
51
+ );
52
+ }
53
+
54
+ /**
55
+ * The shape of the key id segment of a fine-grained key value: exactly 24
56
+ * alphanumeric characters, with `_` and `-` excluded so that an id can never
57
+ * read as two segments.
58
+ *
59
+ * The length and the alphabet are fixed properties of the key format, so a value
60
+ * whose id segment doesn't match this either wasn't issued by HoneyHive or has
61
+ * been altered in transit — either way it is redacted rather than rendered.
62
+ */
63
+ const FGCP_KEY_ID_PATTERN = /^[A-Za-z0-9]{24}$/;
64
+
65
+ /**
66
+ * Returns a display-safe rendering of an API key for verbose logging.
67
+ *
68
+ * Renders `hh_fgcp_<key id>_******` — the key's id and none of its secret. This
69
+ * is character-for-character the masked form HoneyHive displays for that key, so
70
+ * a verbose log line can be matched directly against a key in your account.
71
+ * Masking from the id rather than from the secret is what makes it unique per
72
+ * key and free of secret material.
73
+ *
74
+ * The id is `_`-free by construction, so it is everything up to the first `_`
75
+ * after the prefix.
76
+ *
77
+ * Anything else collapses to 8 fixed-width asterisks, revealing neither its
78
+ * length nor its content. That covers both a value that isn't a fine-grained key
79
+ * at all — the SDK forwards whatever it is given, so this function must assume
80
+ * it may be handed a coarse-grained HoneyHive key or a credential belonging to
81
+ * some other system entirely — and a fine-grained value whose id segment is
82
+ * truncated or mangled, where the characters after the prefix could be secret
83
+ * material rather than an id.
84
+ *
85
+ * Both guards are load-bearing, and the prefix guard especially: without it the
86
+ * unconditional slice below would chop 8 characters off an arbitrary token and
87
+ * any value whose next 24 characters happened to be alphanumerics followed by
88
+ * `_` would render as `hh_fgcp_<24 chars of that token>_******`, echoing part of
89
+ * a foreign credential under our own prefix. So the output is always either
90
+ * exactly the server's mask or fully redacted, never a partial echo.
91
+ */
92
+ export function maskApiKey(apiKey: string): string {
93
+ if (!apiKey.startsWith(FGCP_KEY_PREFIX)) {
94
+ return '********';
95
+ }
96
+ const rest = apiKey.slice(FGCP_KEY_PREFIX.length);
97
+ const separator = rest.indexOf('_');
98
+ // An empty id never matches the pattern, so a value with no separator at all
99
+ // takes the redacted path without a second branch.
100
+ const keyId = separator === -1 ? '' : rest.slice(0, separator);
101
+ if (!FGCP_KEY_ID_PATTERN.test(keyId)) {
102
+ return '********';
103
+ }
104
+ return `${FGCP_KEY_PREFIX}${keyId}_******`;
105
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The base error class and the errors the SDK throws about its own
3
+ * configuration. They sit apart from the request chassis so the key check can
4
+ * throw them without importing it.
5
+ */
6
+
7
+ /**
8
+ * HoneyHiveError is a base class for all errors thrown by the HoneyHive control
9
+ * plane client.
10
+ *
11
+ * This error is never thrown directly, but is useful for determining if an
12
+ * error is from the client with `err instanceof HoneyHiveError`
13
+ */
14
+ export class HoneyHiveError extends Error {}
15
+
16
+ /**
17
+ * Thrown at construction when no control plane API key is configured and no
18
+ * middleware is supplied to authenticate instead. Every control plane
19
+ * operation takes that one key, so a client without it could send nothing.
20
+ */
21
+ export class MissingApiKeyError extends HoneyHiveError {
22
+ constructor() {
23
+ super(
24
+ 'Missing API key: provide apiKey in options or set the HH_CONTROL_PLANE_API_KEY environment variable',
25
+ );
26
+ this.name = 'MissingApiKeyError';
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Thrown at construction when the control plane API key is set to a value that
32
+ * is not a fine-grained control plane key. The message names where the value
33
+ * came from and what it looks like instead, and never echoes the value.
34
+ *
35
+ * @property source - The option or environment variable the value came from.
36
+ */
37
+ export class MalformedApiKeyError extends HoneyHiveError {
38
+ public readonly source: string;
39
+ readonly #requirement: string;
40
+
41
+ constructor(source: string, requirement: string) {
42
+ super(`${source} ${requirement}`);
43
+ this.name = 'MalformedApiKeyError';
44
+ this.source = source;
45
+ this.#requirement = requirement;
46
+ }
47
+
48
+ /**
49
+ * The same message with `source` named as where the value came from, for a
50
+ * caller that supplied the value under a different name.
51
+ */
52
+ public messageFor(source: string): string {
53
+ return `${source} ${this.#requirement}`;
54
+ }
55
+ }
@@ -56,6 +56,8 @@ export interface CreateWorkspaceRequest {
56
56
  virtual_dataplane_id: string;
57
57
  /** @description Workspace display name. Allowed characters are letters, digits, space, underscore, hyphen, apostrophe and ampersand. The name must contain at least one letter or digit, and must not start with a space. */
58
58
  name: string;
59
+ /** @description Globally unique identifier for the workspace, letters, digits and underscores only. Omit it and the server derives one from the name with a random suffix appended. Supply it when the identifier has to match a value maintained outside HoneyHive, such as an identity provider group that grants access to this workspace. A slug already in use returns 409. */
60
+ slug?: string;
59
61
  /** @description Workspace description */
60
62
  description?: string;
61
63
  /**
@@ -127,6 +129,8 @@ export interface CreateVirtualDataplaneRequest {
127
129
  org_id: string;
128
130
  /** @description Virtual dataplane display name. Allowed characters are letters, digits, space, underscore, hyphen, apostrophe and ampersand. The name must contain at least one letter or digit, and must not start with a space. */
129
131
  name: string;
132
+ /** @description Globally unique identifier for the virtual dataplane, letters, digits and underscores only. Omit it and the server derives one from the name with a random suffix appended. A slug already in use returns 409. */
133
+ slug?: string;
130
134
  /** @description Physical cluster to host this virtual dataplane. Omit to place it alongside the existing virtual dataplanes in this org; required when the org has none yet, or when its virtual dataplanes span more than one cluster. */
131
135
  cluster_id?: string;
132
136
  /**
@@ -1,5 +1,7 @@
1
1
  // AUTO-GENERATED — do not edit manually. Run `pnpm turbo run generate` to regenerate.
2
2
 
3
+ import { type Client as OpenapiFetchClient } from 'openapi-fetch';
4
+
3
5
  import { type paths } from './types.js';
4
6
  import {
5
7
  type ListAlertsRequest,
@@ -35,12 +37,23 @@ import {
35
37
  } from './apiTypes.js';
36
38
  import { type ClientConfig, type FetchOptions, createApiClient, unwrap } from '../util.js';
37
39
 
40
+ /** Every security scheme the API's OpenAPI spec defines. */
41
+ export type SecurityScheme = 'ControlPlaneApiKey';
42
+
43
+ /**
44
+ * What `createApiClient` returns: for every security scheme the spec defines,
45
+ * the openapi-fetch client that carries the credential for it. The hand-written
46
+ * chassis builds this table, so a scheme added to or removed from the spec
47
+ * fails to compile there until the table says which client serves it.
48
+ */
49
+ export type ApiClients = Record<SecurityScheme, OpenapiFetchClient<paths>>;
50
+
38
51
  /** @inline */
39
52
  class AlertsNamespace {
40
- #client: ReturnType<typeof createApiClient<paths>>;
53
+ #clients: ApiClients;
41
54
 
42
- constructor(client: ReturnType<typeof createApiClient<paths>>) {
43
- this.#client = client;
55
+ constructor(clients: ApiClients) {
56
+ this.#clients = clients;
44
57
  }
45
58
 
46
59
  /**
@@ -52,8 +65,9 @@ class AlertsNamespace {
52
65
  */
53
66
  public list(request: ListAlertsRequest, options?: FetchOptions): Promise<ListAlertsResponse> {
54
67
  const { project_id, page, limit, status, sort_by, sort_order } = request;
68
+ const httpClient = this.#clients.ControlPlaneApiKey;
55
69
  return unwrap(
56
- this.#client.GET('/v1/projects/{project_id}/alerts', {
70
+ httpClient.GET('/v1/projects/{project_id}/alerts', {
57
71
  params: { path: { project_id }, query: { page, limit, status, sort_by, sort_order } },
58
72
  ...options,
59
73
  }),
@@ -71,8 +85,9 @@ class AlertsNamespace {
71
85
  */
72
86
  public create(request: CreateAlertRequest, options?: FetchOptions): Promise<CreateAlertResponse> {
73
87
  const { project_id, ...body } = request;
88
+ const httpClient = this.#clients.ControlPlaneApiKey;
74
89
  return unwrap(
75
- this.#client.POST('/v1/projects/{project_id}/alerts', {
90
+ httpClient.POST('/v1/projects/{project_id}/alerts', {
76
91
  params: { path: { project_id } },
77
92
  body,
78
93
  ...options,
@@ -89,8 +104,9 @@ class AlertsNamespace {
89
104
  */
90
105
  public get(request: GetAlertRequest, options?: FetchOptions): Promise<GetAlertResponse> {
91
106
  const { project_id, alert_id } = request;
107
+ const httpClient = this.#clients.ControlPlaneApiKey;
92
108
  return unwrap(
93
- this.#client.GET('/v1/projects/{project_id}/alerts/{alert_id}', {
109
+ httpClient.GET('/v1/projects/{project_id}/alerts/{alert_id}', {
94
110
  params: { path: { project_id, alert_id } },
95
111
  ...options,
96
112
  }),
@@ -100,10 +116,10 @@ class AlertsNamespace {
100
116
 
101
117
  /** @inline */
102
118
  class WorkspacesNamespace {
103
- #client: ReturnType<typeof createApiClient<paths>>;
119
+ #clients: ApiClients;
104
120
 
105
- constructor(client: ReturnType<typeof createApiClient<paths>>) {
106
- this.#client = client;
121
+ constructor(clients: ApiClients) {
122
+ this.#clients = clients;
107
123
  }
108
124
 
109
125
  /**
@@ -140,8 +156,9 @@ class WorkspacesNamespace {
140
156
  options?: FetchOptions,
141
157
  ): Promise<CreateWorkspaceResponse> {
142
158
  const { virtual_dataplane_id, ...body } = request;
159
+ const httpClient = this.#clients.ControlPlaneApiKey;
143
160
  return unwrap(
144
- this.#client.POST('/v1/virtual_dataplanes/{virtual_dataplane_id}/workspaces', {
161
+ httpClient.POST('/v1/virtual_dataplanes/{virtual_dataplane_id}/workspaces', {
145
162
  params: { path: { virtual_dataplane_id } },
146
163
  body,
147
164
  ...options,
@@ -160,8 +177,9 @@ class WorkspacesNamespace {
160
177
  */
161
178
  public get(request: GetWorkspaceRequest, options?: FetchOptions): Promise<GetWorkspaceResponse> {
162
179
  const { workspace_id } = request;
180
+ const httpClient = this.#clients.ControlPlaneApiKey;
163
181
  return unwrap(
164
- this.#client.GET('/v1/workspaces/{workspace_id}', {
182
+ httpClient.GET('/v1/workspaces/{workspace_id}', {
165
183
  params: { path: { workspace_id } },
166
184
  ...options,
167
185
  }),
@@ -182,8 +200,9 @@ class WorkspacesNamespace {
182
200
  options?: FetchOptions,
183
201
  ): Promise<UpdateWorkspaceResponse> {
184
202
  const { workspace_id, ...body } = request;
203
+ const httpClient = this.#clients.ControlPlaneApiKey;
185
204
  return unwrap(
186
- this.#client.PUT('/v1/workspaces/{workspace_id}', {
205
+ httpClient.PUT('/v1/workspaces/{workspace_id}', {
187
206
  params: { path: { workspace_id } },
188
207
  body,
189
208
  ...options,
@@ -213,8 +232,9 @@ class WorkspacesNamespace {
213
232
  options?: FetchOptions,
214
233
  ): Promise<DeleteWorkspaceResponse> {
215
234
  const { workspace_id, dangerously_delete_child_scopes } = request;
235
+ const httpClient = this.#clients.ControlPlaneApiKey;
216
236
  return unwrap(
217
- this.#client.DELETE('/v1/workspaces/{workspace_id}', {
237
+ httpClient.DELETE('/v1/workspaces/{workspace_id}', {
218
238
  params: { path: { workspace_id }, query: { dangerously_delete_child_scopes } },
219
239
  ...options,
220
240
  }),
@@ -224,10 +244,10 @@ class WorkspacesNamespace {
224
244
 
225
245
  /** @inline */
226
246
  class ProjectsNamespace {
227
- #client: ReturnType<typeof createApiClient<paths>>;
247
+ #clients: ApiClients;
228
248
 
229
- constructor(client: ReturnType<typeof createApiClient<paths>>) {
230
- this.#client = client;
249
+ constructor(clients: ApiClients) {
250
+ this.#clients = clients;
231
251
  }
232
252
 
233
253
  /**
@@ -259,8 +279,9 @@ class ProjectsNamespace {
259
279
  options?: FetchOptions,
260
280
  ): Promise<CreateProjectResponse> {
261
281
  const { workspace_id, ...body } = request;
282
+ const httpClient = this.#clients.ControlPlaneApiKey;
262
283
  return unwrap(
263
- this.#client.POST('/v1/workspaces/{workspace_id}/projects', {
284
+ httpClient.POST('/v1/workspaces/{workspace_id}/projects', {
264
285
  params: { path: { workspace_id } },
265
286
  body,
266
287
  ...options,
@@ -276,11 +297,9 @@ class ProjectsNamespace {
276
297
  */
277
298
  public get(request: GetProjectRequest, options?: FetchOptions): Promise<GetProjectResponse> {
278
299
  const { project_id } = request;
300
+ const httpClient = this.#clients.ControlPlaneApiKey;
279
301
  return unwrap(
280
- this.#client.GET('/v1/projects/{project_id}', {
281
- params: { path: { project_id } },
282
- ...options,
283
- }),
302
+ httpClient.GET('/v1/projects/{project_id}', { params: { path: { project_id } }, ...options }),
284
303
  );
285
304
  }
286
305
 
@@ -296,8 +315,9 @@ class ProjectsNamespace {
296
315
  options?: FetchOptions,
297
316
  ): Promise<UpdateProjectResponse> {
298
317
  const { project_id, ...body } = request;
318
+ const httpClient = this.#clients.ControlPlaneApiKey;
299
319
  return unwrap(
300
- this.#client.PUT('/v1/projects/{project_id}', {
320
+ httpClient.PUT('/v1/projects/{project_id}', {
301
321
  params: { path: { project_id } },
302
322
  body,
303
323
  ...options,
@@ -317,8 +337,9 @@ class ProjectsNamespace {
317
337
  options?: FetchOptions,
318
338
  ): Promise<DeleteProjectResponse> {
319
339
  const { project_id } = request;
340
+ const httpClient = this.#clients.ControlPlaneApiKey;
320
341
  return unwrap(
321
- this.#client.DELETE('/v1/projects/{project_id}', {
342
+ httpClient.DELETE('/v1/projects/{project_id}', {
322
343
  params: { path: { project_id } },
323
344
  ...options,
324
345
  }),
@@ -328,10 +349,10 @@ class ProjectsNamespace {
328
349
 
329
350
  /** @inline */
330
351
  class VirtualDataplanesNamespace {
331
- #client: ReturnType<typeof createApiClient<paths>>;
352
+ #clients: ApiClients;
332
353
 
333
- constructor(client: ReturnType<typeof createApiClient<paths>>) {
334
- this.#client = client;
354
+ constructor(clients: ApiClients) {
355
+ this.#clients = clients;
335
356
  }
336
357
 
337
358
  /**
@@ -355,12 +376,11 @@ class VirtualDataplanesNamespace {
355
376
  * A cluster admits an organization only if your control plane administrator configured it to,
356
377
  * so a `cluster_id` you can read from a sibling virtual data plane is not necessarily one you
357
378
  * may place a new virtual data plane on. A cluster that does not admit this organization
358
- * returns a 403. When you named the cluster, the fix is a configuration change your
359
- * administrator makes. When this endpoint inferred the cluster, send an explicit `cluster_id`
360
- * to place the virtual data plane on another cluster.
379
+ * returns a 404. When this endpoint inferred the cluster, send an explicit `cluster_id` to
380
+ * place the virtual data plane on another cluster.
361
381
  *
362
382
  * Deployments differ in who places virtual data planes. Where HoneyHive assigns them to
363
- * organizations automatically, this endpoint returns a 403 for every request and the
383
+ * organizations automatically, this endpoint returns a 404 for every request and the
364
384
  * placement is not yours to make. Deployments whose administrators own the scope tree are
365
385
  * the ones this endpoint serves.
366
386
  *
@@ -385,8 +405,9 @@ class VirtualDataplanesNamespace {
385
405
  options?: FetchOptions,
386
406
  ): Promise<CreateVirtualDataplaneResponse> {
387
407
  const { org_id, ...body } = request;
408
+ const httpClient = this.#clients.ControlPlaneApiKey;
388
409
  return unwrap(
389
- this.#client.POST('/v1/orgs/{org_id}/virtual_dataplanes', {
410
+ httpClient.POST('/v1/orgs/{org_id}/virtual_dataplanes', {
390
411
  params: { path: { org_id } },
391
412
  body,
392
413
  ...options,
@@ -413,8 +434,9 @@ class VirtualDataplanesNamespace {
413
434
  options?: FetchOptions,
414
435
  ): Promise<GetVirtualDataplaneResponse> {
415
436
  const { virtual_dataplane_id } = request;
437
+ const httpClient = this.#clients.ControlPlaneApiKey;
416
438
  return unwrap(
417
- this.#client.GET('/v1/virtual_dataplanes/{virtual_dataplane_id}', {
439
+ httpClient.GET('/v1/virtual_dataplanes/{virtual_dataplane_id}', {
418
440
  params: { path: { virtual_dataplane_id } },
419
441
  ...options,
420
442
  }),
@@ -433,15 +455,16 @@ class VirtualDataplanesNamespace {
433
455
  * `name`, or sends the current one, changes nothing.
434
456
  *
435
457
  * This endpoint is unavailable where HoneyHive assigns virtual data planes automatically,
436
- * and returns a 403 there.
458
+ * and returns a 404 there.
437
459
  */
438
460
  public update(
439
461
  request: UpdateVirtualDataplaneRequest,
440
462
  options?: FetchOptions,
441
463
  ): Promise<UpdateVirtualDataplaneResponse> {
442
464
  const { virtual_dataplane_id, ...body } = request;
465
+ const httpClient = this.#clients.ControlPlaneApiKey;
443
466
  return unwrap(
444
- this.#client.PUT('/v1/virtual_dataplanes/{virtual_dataplane_id}', {
467
+ httpClient.PUT('/v1/virtual_dataplanes/{virtual_dataplane_id}', {
445
468
  params: { path: { virtual_dataplane_id } },
446
469
  body,
447
470
  ...options,
@@ -467,15 +490,16 @@ class VirtualDataplanesNamespace {
467
490
  * archived, so read that first if you need the list.
468
491
  *
469
492
  * This endpoint is unavailable where HoneyHive assigns virtual data planes automatically,
470
- * and returns a 403 there.
493
+ * and returns a 404 there.
471
494
  */
472
495
  public delete(
473
496
  request: DeleteVirtualDataplaneRequest,
474
497
  options?: FetchOptions,
475
498
  ): Promise<DeleteVirtualDataplaneResponse> {
476
499
  const { virtual_dataplane_id, dangerously_delete_child_scopes } = request;
500
+ const httpClient = this.#clients.ControlPlaneApiKey;
477
501
  return unwrap(
478
- this.#client.DELETE('/v1/virtual_dataplanes/{virtual_dataplane_id}', {
502
+ httpClient.DELETE('/v1/virtual_dataplanes/{virtual_dataplane_id}', {
479
503
  params: { path: { virtual_dataplane_id }, query: { dangerously_delete_child_scopes } },
480
504
  ...options,
481
505
  }),
@@ -484,17 +508,17 @@ class VirtualDataplanesNamespace {
484
508
  }
485
509
 
486
510
  export class Client {
487
- #client: ReturnType<typeof createApiClient<paths>>;
511
+ #clients: ApiClients;
488
512
  readonly alerts: AlertsNamespace;
489
513
  readonly workspaces: WorkspacesNamespace;
490
514
  readonly projects: ProjectsNamespace;
491
515
  readonly virtualDataplanes: VirtualDataplanesNamespace;
492
516
 
493
517
  constructor(options: ClientConfig = {}) {
494
- this.#client = createApiClient<paths>(options);
495
- this.alerts = new AlertsNamespace(this.#client);
496
- this.workspaces = new WorkspacesNamespace(this.#client);
497
- this.projects = new ProjectsNamespace(this.#client);
498
- this.virtualDataplanes = new VirtualDataplanesNamespace(this.#client);
518
+ this.#clients = createApiClient(options);
519
+ this.alerts = new AlertsNamespace(this.#clients);
520
+ this.workspaces = new WorkspacesNamespace(this.#clients);
521
+ this.projects = new ProjectsNamespace(this.#clients);
522
+ this.virtualDataplanes = new VirtualDataplanesNamespace(this.#clients);
499
523
  }
500
524
  }
@@ -244,12 +244,11 @@ export interface paths {
244
244
  * A cluster admits an organization only if your control plane administrator configured it to,
245
245
  * so a `cluster_id` you can read from a sibling virtual data plane is not necessarily one you
246
246
  * may place a new virtual data plane on. A cluster that does not admit this organization
247
- * returns a 403. When you named the cluster, the fix is a configuration change your
248
- * administrator makes. When this endpoint inferred the cluster, send an explicit `cluster_id`
249
- * to place the virtual data plane on another cluster.
247
+ * returns a 404. When this endpoint inferred the cluster, send an explicit `cluster_id` to
248
+ * place the virtual data plane on another cluster.
250
249
  *
251
250
  * Deployments differ in who places virtual data planes. Where HoneyHive assigns them to
252
- * organizations automatically, this endpoint returns a 403 for every request and the
251
+ * organizations automatically, this endpoint returns a 404 for every request and the
253
252
  * placement is not yours to make. Deployments whose administrators own the scope tree are
254
253
  * the ones this endpoint serves.
255
254
  *
@@ -308,7 +307,7 @@ export interface paths {
308
307
  * `name`, or sends the current one, changes nothing.
309
308
  *
310
309
  * This endpoint is unavailable where HoneyHive assigns virtual data planes automatically,
311
- * and returns a 403 there.
310
+ * and returns a 404 there.
312
311
  */
313
312
  put: operations['updateVirtualDataplane'];
314
313
  post?: never;
@@ -329,7 +328,7 @@ export interface paths {
329
328
  * archived, so read that first if you need the list.
330
329
  *
331
330
  * This endpoint is unavailable where HoneyHive assigns virtual data planes automatically,
332
- * and returns a 403 there.
331
+ * and returns a 404 there.
333
332
  */
334
333
  delete: operations['deleteVirtualDataplane'];
335
334
  options?: never;
@@ -560,6 +559,8 @@ export interface components {
560
559
  PostVirtualDataplaneRequest: {
561
560
  /** @description Virtual dataplane display name. Allowed characters are letters, digits, space, underscore, hyphen, apostrophe and ampersand. The name must contain at least one letter or digit, and must not start with a space. */
562
561
  name: string;
562
+ /** @description Globally unique identifier for the virtual dataplane, letters, digits and underscores only. Omit it and the server derives one from the name with a random suffix appended. A slug already in use returns 409. */
563
+ slug?: string;
563
564
  /** @description Physical cluster to host this virtual dataplane. Omit to place it alongside the existing virtual dataplanes in this org; required when the org has none yet, or when its virtual dataplanes span more than one cluster. */
564
565
  cluster_id?: string;
565
566
  /**
@@ -607,6 +608,8 @@ export interface components {
607
608
  PostWorkspaceRequest: {
608
609
  /** @description Workspace display name. Allowed characters are letters, digits, space, underscore, hyphen, apostrophe and ampersand. The name must contain at least one letter or digit, and must not start with a space. */
609
610
  name: string;
611
+ /** @description Globally unique identifier for the workspace, letters, digits and underscores only. Omit it and the server derives one from the name with a random suffix appended. Supply it when the identifier has to match a value maintained outside HoneyHive, such as an identity provider group that grants access to this workspace. A slug already in use returns 409. */
612
+ slug?: string;
610
613
  /** @description Workspace description */
611
614
  description?: string;
612
615
  /**
@@ -899,6 +902,13 @@ export interface operations {
899
902
  };
900
903
  content?: never;
901
904
  };
905
+ /** @description The `slug` is already taken by another scope */
906
+ 409: {
907
+ headers: {
908
+ [name: string]: unknown;
909
+ };
910
+ content?: never;
911
+ };
902
912
  };
903
913
  };
904
914
  createProject: {
@@ -1196,15 +1206,15 @@ export interface operations {
1196
1206
  };
1197
1207
  content?: never;
1198
1208
  };
1199
- /** @description The caller may not create a virtual data plane here: the permission is missing at this organization, the API key's root scope does not contain it, this deployment assigns virtual data planes automatically, or the cluster (named or inferred) does not admit this organization */
1200
- 403: {
1209
+ /** @description Organization or cluster not found, or the caller may not create a virtual data plane here. The response does not say which. */
1210
+ 404: {
1201
1211
  headers: {
1202
1212
  [name: string]: unknown;
1203
1213
  };
1204
1214
  content?: never;
1205
1215
  };
1206
- /** @description Organization or cluster not found */
1207
- 404: {
1216
+ /** @description The `slug` is already taken by another scope */
1217
+ 409: {
1208
1218
  headers: {
1209
1219
  [name: string]: unknown;
1210
1220
  };
@@ -1274,14 +1284,7 @@ export interface operations {
1274
1284
  };
1275
1285
  content?: never;
1276
1286
  };
1277
- /** @description The caller may not update this virtual data plane, or this deployment assigns virtual data planes automatically */
1278
- 403: {
1279
- headers: {
1280
- [name: string]: unknown;
1281
- };
1282
- content?: never;
1283
- };
1284
- /** @description Virtual data plane not found */
1287
+ /** @description Virtual data plane not found, or the caller may not update it, or this deployment assigns virtual data planes automatically. The response does not say which. */
1285
1288
  404: {
1286
1289
  headers: {
1287
1290
  [name: string]: unknown;
@@ -1325,14 +1328,7 @@ export interface operations {
1325
1328
  };
1326
1329
  content?: never;
1327
1330
  };
1328
- /** @description The caller may not delete this virtual data plane, or this deployment assigns virtual data planes automatically */
1329
- 403: {
1330
- headers: {
1331
- [name: string]: unknown;
1332
- };
1333
- content?: never;
1334
- };
1335
- /** @description Virtual data plane not found */
1331
+ /** @description Virtual data plane not found, or the caller may not delete it, or this deployment assigns virtual data planes automatically. The response does not say which. */
1336
1332
  404: {
1337
1333
  headers: {
1338
1334
  [name: string]: unknown;
@@ -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.1.0';
3
+ export const SDK_VERSION = '1.3.0';
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Client } from './generated/client.js';
2
+ export { HoneyHiveError, MissingApiKeyError, MalformedApiKeyError } from './errors.js';
2
3
  export {
3
- HoneyHiveError,
4
4
  ApiError,
5
5
  NetworkError,
6
6
  type ClientConfig,