@vuevox/sdk 0.2.0 → 0.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/README.md CHANGED
@@ -37,17 +37,43 @@ const vuevox = createVueVoxClient({
37
37
  });
38
38
 
39
39
  const hello = await vuevox.hello();
40
- console.log(hello.message);
40
+ console.log(hello.data.message);
41
+ console.log(hello.requestId);
41
42
 
42
43
  const spaces = await vuevox.listSpaces({ limit: 50 });
43
- console.log(spaces.data);
44
+ console.log(spaces.data.data);
45
+ console.log(spaces.requestId);
44
46
  ```
45
47
 
46
48
  The SDK requests and caches a short-lived access token using client credentials, then sends it as a bearer token for API calls.
47
49
 
50
+ ## Request IDs
51
+
52
+ Every Developer API response includes an `X-Request-Id` header. SDK endpoint methods return response metadata with the response body so you can log that ID for support requests.
53
+
54
+ ```ts
55
+ const spaces = await vuevox.listSpaces({ limit: 50 });
56
+
57
+ console.log(spaces.requestId);
58
+ console.log(spaces.data.data);
59
+ ```
60
+
61
+ For centralized logging, pass `onResponse`. The hook runs for every SDK-managed HTTP response, including the token request.
62
+
63
+ ```ts
64
+ const vuevox = createVueVoxClient({
65
+ clientId: process.env.VUEVOX_CLIENT_ID!,
66
+ clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
67
+ scope: ["hello:read", "spaces:read"],
68
+ onResponse: ({ method, path, status, requestId }) => {
69
+ console.log({ method, path, status, requestId });
70
+ },
71
+ });
72
+ ```
73
+
48
74
  ## Error Handling
49
75
 
50
- API errors throw `VueVoxApiError`.
76
+ API errors throw `VueVoxApiError`. The SDK exposes `error.requestId` from either the response header or error body.
51
77
 
52
78
  ```ts
53
79
  import { VueVoxApiError, createVueVoxClient } from "@vuevox/sdk";
@@ -62,7 +88,7 @@ try {
62
88
  await vuevox.listSpaces({ limit: 50 });
63
89
  } catch (error) {
64
90
  if (error instanceof VueVoxApiError) {
65
- console.error(error.status, error.code, error.message);
91
+ console.error(error.status, error.code, error.message, error.requestId);
66
92
  }
67
93
 
68
94
  throw error;
@@ -127,10 +153,10 @@ List endpoints use cursor pagination.
127
153
  ```ts
128
154
  const firstPage = await vuevox.listSpaces({ limit: 50 });
129
155
 
130
- if (firstPage.pagination.nextCursor) {
156
+ if (firstPage.data.pagination.nextCursor) {
131
157
  const secondPage = await vuevox.listSpaces({
132
158
  limit: 50,
133
- cursor: firstPage.pagination.nextCursor,
159
+ cursor: firstPage.data.pagination.nextCursor,
134
160
  });
135
161
  }
136
162
  ```
package/dist/client.d.ts CHANGED
@@ -5,17 +5,29 @@ export interface ListSpacesOptions {
5
5
  limit?: number;
6
6
  cursor?: string;
7
7
  }
8
+ export interface VueVoxResponseMetadata {
9
+ requestId?: string;
10
+ status: number;
11
+ }
12
+ export interface VueVoxApiResponse<T> extends VueVoxResponseMetadata {
13
+ data: T;
14
+ }
15
+ export interface VueVoxResponseEvent extends VueVoxResponseMetadata {
16
+ method: string;
17
+ path: string;
18
+ }
8
19
  export interface VueVoxClientOptions {
9
20
  baseUrl?: string;
10
21
  clientId: string;
11
22
  clientSecret: string;
12
23
  scope?: string | string[];
13
24
  fetch?: typeof fetch;
25
+ onResponse?: (event: VueVoxResponseEvent) => void;
14
26
  }
15
27
  export declare function createVueVoxClient(options: VueVoxClientOptions): {
16
28
  getAccessToken: () => Promise<string>;
17
- hello: () => Promise<HelloResponse>;
18
- listSpaces: (options?: ListSpacesOptions) => Promise<SpacesListResponse>;
29
+ hello: () => Promise<VueVoxApiResponse<HelloResponse>>;
30
+ listSpaces: (listOptions?: ListSpacesOptions) => Promise<VueVoxApiResponse<SpacesListResponse>>;
19
31
  raw: import("openapi-fetch").Client<paths, `${string}/${string}`>;
20
32
  };
21
33
  export {};
package/dist/client.js CHANGED
@@ -22,12 +22,14 @@ export function createVueVoxClient(options) {
22
22
  }),
23
23
  });
24
24
  const body = await parseJson(response);
25
+ const requestId = getRequestId(response, body);
26
+ notifyResponse(options, "POST", "/oauth/token", response, requestId);
25
27
  if (!response.ok) {
26
28
  const error = isErrorResponse(body) ? body.error : null;
27
- throw new VueVoxApiError(response.status, error?.code ?? "token_request_failed", error?.message ?? "VueVox token request failed.", isErrorResponse(body) ? body : undefined);
29
+ throw new VueVoxApiError(response.status, error?.code ?? "token_request_failed", error?.message ?? "VueVox token request failed.", isErrorResponse(body) ? body : undefined, requestId);
28
30
  }
29
31
  if (!isTokenResponse(body)) {
30
- throw new VueVoxApiError(response.status, "invalid_token_response", "VueVox returned an invalid token response.");
32
+ throw new VueVoxApiError(response.status, "invalid_token_response", "VueVox returned an invalid token response.", undefined, requestId);
31
33
  }
32
34
  cachedToken = {
33
35
  accessToken: body.access_token,
@@ -42,25 +44,29 @@ export function createVueVoxClient(options) {
42
44
  Authorization: `Bearer ${accessToken}`,
43
45
  },
44
46
  });
47
+ const requestId = getRequestId(response, error);
48
+ notifyResponse(options, "GET", "/v1/hello", response, requestId);
45
49
  if (error) {
46
- throw new VueVoxApiError(response.status, error.error.code, error.error.message, error);
50
+ throw new VueVoxApiError(response.status, error.error.code, error.error.message, error, requestId);
47
51
  }
48
- return data;
52
+ return withMetadata(data, response, requestId);
49
53
  }
50
- async function listSpaces(options = {}) {
54
+ async function listSpaces(listOptions = {}) {
51
55
  const accessToken = await getAccessToken();
52
56
  const { data, error, response } = await raw.GET("/v1/spaces", {
53
57
  params: {
54
- query: options,
58
+ query: listOptions,
55
59
  },
56
60
  headers: {
57
61
  Authorization: `Bearer ${accessToken}`,
58
62
  },
59
63
  });
64
+ const requestId = getRequestId(response, error);
65
+ notifyResponse(options, "GET", "/v1/spaces", response, requestId);
60
66
  if (error) {
61
- throw new VueVoxApiError(response.status, error.error.code, error.error.message, error);
67
+ throw new VueVoxApiError(response.status, error.error.code, error.error.message, error, requestId);
62
68
  }
63
- return data;
69
+ return withMetadata(data, response, requestId);
64
70
  }
65
71
  return {
66
72
  getAccessToken,
@@ -92,3 +98,24 @@ function isTokenResponse(value) {
92
98
  function isErrorResponse(value) {
93
99
  return Boolean(value && "error" in value);
94
100
  }
101
+ function getRequestId(response, body) {
102
+ return response.headers.get("X-Request-Id") ?? (isErrorBody(body) ? body.error.requestId : undefined);
103
+ }
104
+ function isErrorBody(value) {
105
+ return Boolean(value && "error" in value);
106
+ }
107
+ function withMetadata(data, response, requestId) {
108
+ return {
109
+ data,
110
+ requestId,
111
+ status: response.status,
112
+ };
113
+ }
114
+ function notifyResponse(options, method, path, response, requestId) {
115
+ options.onResponse?.({
116
+ method,
117
+ path,
118
+ requestId,
119
+ status: response.status,
120
+ });
121
+ }
package/dist/errors.d.ts CHANGED
@@ -3,6 +3,7 @@ export type VueVoxErrorResponse = components["schemas"]["ErrorResponse"];
3
3
  export declare class VueVoxApiError extends Error {
4
4
  readonly status: number;
5
5
  readonly code: string;
6
+ readonly requestId?: string;
6
7
  readonly response?: VueVoxErrorResponse;
7
- constructor(status: number, code: string, message: string, response?: VueVoxErrorResponse);
8
+ constructor(status: number, code: string, message: string, response?: VueVoxErrorResponse, requestId?: string);
8
9
  }
package/dist/errors.js CHANGED
@@ -1,12 +1,14 @@
1
1
  export class VueVoxApiError extends Error {
2
2
  status;
3
3
  code;
4
+ requestId;
4
5
  response;
5
- constructor(status, code, message, response) {
6
+ constructor(status, code, message, response, requestId) {
6
7
  super(message);
7
8
  this.name = "VueVoxApiError";
8
9
  this.status = status;
9
10
  this.code = code;
11
+ this.requestId = requestId ?? response?.error.requestId;
10
12
  this.response = response;
11
13
  }
12
14
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createVueVoxClient } from "./client.js";
2
- export type { ListSpacesOptions, VueVoxClientOptions } from "./client.js";
2
+ export type { ListSpacesOptions, VueVoxApiResponse, VueVoxClientOptions, VueVoxResponseEvent, VueVoxResponseMetadata, } from "./client.js";
3
3
  export { VueVoxApiError } from "./errors.js";
4
4
  export type { VueVoxErrorResponse } from "./errors.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuevox/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript SDK for the VueVox Developer API.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",