@labelgrid/core 0.2.2 → 0.2.3

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 CHANGED
@@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  `@labelgrid/core` is primarily an internal shared client for the LabelGrid MCP
9
9
  server and CLI — there are no API-stability promises before 1.0.
10
10
 
11
+ ## [Unreleased]
12
+
13
+ ## [0.2.3] - 2026-09-30
14
+
15
+ ### Changed
16
+
17
+ - Public API `error_code` and `details` fields are preserved on normalized
18
+ errors, including typed 404 and 5xx responses.
19
+ - Normalized errors also preserve the API's `blocking_issues` field, which
20
+ lists the review issues that block a confirm-review request.
21
+
11
22
  ## [0.2.2] - 2026-08-20
12
23
 
13
24
  ### Added
@@ -15,6 +15,9 @@ export type ApiError = {
15
15
  suggestion?: string;
16
16
  retry_after_seconds?: number;
17
17
  errors?: unknown;
18
+ details?: unknown;
19
+ /** Actionable release-review issues returned when confirm-review is blocked. */
20
+ blocking_issues?: unknown;
18
21
  /** Structured validation detail passed through verbatim from the API (422). */
19
22
  errors_structured?: unknown;
20
23
  };
package/dist/api/http.js CHANGED
@@ -56,8 +56,8 @@ function buildQuery(query) {
56
56
  return parts.length > 0 ? `?${parts.join('&')}` : '';
57
57
  }
58
58
  /**
59
- * Extracts a code/message/errors triple from any of the four backend error body
60
- * shapes: `{message}`, `{error: string}`, `{errors}`, `{error: {code, message}}`.
59
+ * Extracts typed fields from the backend's top-level and nested error shapes,
60
+ * including public `error_code` and `details` fields.
61
61
  */
62
62
  function extractServerError(body) {
63
63
  if (typeof body === 'string') {
@@ -69,11 +69,22 @@ function extractServerError(body) {
69
69
  const record = body;
70
70
  const errors = record.errors;
71
71
  const errorsStructured = record.errors_structured;
72
+ const details = record.details;
73
+ const blockingIssues = record.blocking_issues;
74
+ const topLevelCode = typeof record.error_code === 'string'
75
+ ? record.error_code
76
+ : typeof record.code === 'string'
77
+ ? record.code
78
+ : undefined;
72
79
  // Shape: { error: { code, message } }
73
80
  if (record.error !== null && typeof record.error === 'object') {
74
81
  const nested = record.error;
75
82
  return {
76
- code: typeof nested.code === 'string' ? nested.code : undefined,
83
+ code: typeof nested.error_code === 'string'
84
+ ? nested.error_code
85
+ : typeof nested.code === 'string'
86
+ ? nested.code
87
+ : topLevelCode,
77
88
  message: typeof nested.message === 'string'
78
89
  ? nested.message
79
90
  : typeof nested.error === 'string'
@@ -81,24 +92,30 @@ function extractServerError(body) {
81
92
  : undefined,
82
93
  errors,
83
94
  errors_structured: errorsStructured,
95
+ details,
96
+ blocking_issues: blockingIssues,
84
97
  };
85
98
  }
86
99
  // Shape: { error: 'string' }
87
100
  if (typeof record.error === 'string') {
88
101
  return {
89
- code: typeof record.code === 'string' ? record.code : undefined,
102
+ code: topLevelCode,
90
103
  message: record.error,
91
104
  errors,
92
105
  errors_structured: errorsStructured,
106
+ details,
107
+ blocking_issues: blockingIssues,
93
108
  };
94
109
  }
95
110
  // Shapes: { message } and/or { errors } and/or top-level { code }
96
111
  const parts = {
97
- code: typeof record.code === 'string' ? record.code : undefined,
112
+ code: topLevelCode,
98
113
  message: typeof record.message === 'string' ? record.message : undefined,
99
114
  field: typeof record.field === 'string' ? record.field : undefined,
100
115
  errors,
101
116
  errors_structured: errorsStructured,
117
+ details,
118
+ blocking_issues: blockingIssues,
102
119
  };
103
120
  // Derive a message from the first validation error when none was given.
104
121
  if (parts.message === undefined && errors !== null && typeof errors === 'object') {
@@ -129,6 +146,8 @@ function normalizeError(res, body) {
129
146
  status,
130
147
  ...(server.field !== undefined ? { field: server.field } : {}),
131
148
  ...(server.errors !== undefined ? { errors: server.errors } : {}),
149
+ ...(server.details !== undefined ? { details: server.details } : {}),
150
+ ...(server.blocking_issues !== undefined ? { blocking_issues: server.blocking_issues } : {}),
132
151
  ...extra,
133
152
  });
134
153
  switch (status) {
@@ -139,7 +158,7 @@ function normalizeError(res, body) {
139
158
  case 403:
140
159
  return withCommon(server.code ?? 'FORBIDDEN', server.message ?? 'Forbidden.');
141
160
  case 404:
142
- return withCommon('NOT_FOUND', server.message ?? 'The requested resource was not found.');
161
+ return withCommon(server.code ?? 'NOT_FOUND', server.message ?? 'The requested resource was not found.');
143
162
  case 409:
144
163
  return withCommon(server.code ?? 'CONFLICT', server.message ?? 'The request conflicts with the current state.');
145
164
  case 422:
@@ -156,7 +175,7 @@ function normalizeError(res, body) {
156
175
  }
157
176
  default:
158
177
  if (status >= 500) {
159
- return withCommon('SERVER_ERROR', server.message ?? 'The server encountered an error.');
178
+ return withCommon(server.code ?? 'SERVER_ERROR', server.message ?? 'The server encountered an error.');
160
179
  }
161
180
  return withCommon(server.code ?? 'ERROR', server.message ?? `Request failed with status ${status}.`);
162
181
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@labelgrid/core",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Shared LabelGrid public-API client: HTTP transport, uploads, content types, the catalog-entity registry, and redacting logging",
5
5
  "type": "module",
6
6
  "keywords": ["labelgrid", "music-distribution", "api-client"],