@xemahq/biome-host-api-client 0.3.27 → 0.3.29

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.
@@ -223,14 +223,66 @@ const customFetch = async (url, options) => {
223
223
  }
224
224
  };
225
225
  exports.customFetch = customFetch;
226
- async function parseBody(response) {
227
- const contentType = response.headers.get('content-type');
228
- if (contentType?.includes('application/json')) {
229
- return response.json();
226
+ /**
227
+ * Does this media type carry JSON?
228
+ *
229
+ * ── THE OUTAGE THIS FIXES ────────────────────────────────────────────────
230
+ *
231
+ * This used to be `contentType?.includes('application/json')`, and EVERY error
232
+ * this fleet returns failed that test. The platform's `GlobalExceptionFilter`
233
+ * emits RFC 9457 problem documents and sets the media type explicitly —
234
+ * `.type(PROBLEM_DETAILS_CONTENT_TYPE)` before `.json()`, so it cannot be
235
+ * overridden — and that constant is `application/problem+json`, which does NOT
236
+ * contain the substring `application/json`: after `application/` comes
237
+ * `problem+json`.
238
+ *
239
+ * So every non-2xx body fell through to `response.text()` and reached the
240
+ * caller as an UNPARSED STRING. `ClientError.body` is typed `unknown`, so
241
+ * nothing complained; every consumer that reads a machine-readable code off an
242
+ * error — `error.body.code`, `error.body.details.code` — silently read
243
+ * `undefined` instead, for every error, in every service.
244
+ *
245
+ * Measured in production on 2026-09-15: skill-registry-api's
246
+ * `resolveOrNull()` absorbs `RELEASE_CHANNEL_NOT_FOUND` into `null` by exactly
247
+ * that read. With the code unreadable the absorb never fired, a routine "no
248
+ * pointer on this channel" 404 became a 500 on `GET /skills` and
249
+ * `GET /describe-objects`, and agent-session-api's `apply_control_bundle` step
250
+ * aborted EVERY session launch in the organisation — 7,871 failed resolutions
251
+ * an hour. A fix written for that exact page months earlier was present in the
252
+ * running image and could not help, because the classifier it repaired was
253
+ * being handed a string.
254
+ *
255
+ * ── WHY THE STRUCTURED SUFFIX, NOT A SECOND SUBSTRING ────────────────────
256
+ *
257
+ * Adding `|| includes('application/problem+json')` would fix this one media
258
+ * type and leave the next one — `application/vnd.api+json`, and anything else
259
+ * a service legitimately emits. RFC 6839 defines `+json` as the structured
260
+ * syntax suffix meaning "this is JSON"; that is the actual rule, so it is the
261
+ * rule implemented. Parameters are stripped first, because
262
+ * `application/problem+json; charset=utf-8` is the same media type.
263
+ */
264
+ function isJsonMediaType(contentType) {
265
+ if (contentType === null) {
266
+ return false;
230
267
  }
268
+ const essence = contentType.split(';')[0]?.trim().toLowerCase() ?? '';
269
+ return essence === 'application/json' || essence.endsWith('+json');
270
+ }
271
+ async function parseBody(response) {
272
+ // 204 FIRST, and deliberately — this ORDER is carried from `develop`, which
273
+ // fixed the same defect independently and got this half right where `main`
274
+ // did not. A 204 carries no body, so `response.json()` on one throws
275
+ // SyntaxError; and a 204 whose headers STILL declare a JSON content type is
276
+ // ordinary, because the framework sets the header before the handler returns
277
+ // nothing. With the JSON branch first, a documented `undefined` becomes a
278
+ // thrown parse error. Pinned by a case in `transport-problem-json.test.cjs`.
231
279
  if (response.status === 204) {
232
280
  return undefined;
233
281
  }
282
+ const contentType = response.headers.get('content-type');
283
+ if (isJsonMediaType(contentType)) {
284
+ return response.json();
285
+ }
234
286
  return response.text();
235
287
  }
236
288
  function parseRetryAfter(value) {
@@ -37,6 +37,7 @@ export declare const ContributionKind: {
37
37
  readonly 'opencode-plugin': "opencode-plugin";
38
38
  readonly capability: "capability";
39
39
  readonly 'resource-ownership': "resource-ownership";
40
+ readonly 'resource-kind': "resource-kind";
40
41
  readonly 'resource-definition': "resource-definition";
41
42
  readonly 'stage-machine': "stage-machine";
42
43
  readonly 'search-type': "search-type";
@@ -44,5 +45,6 @@ export declare const ContributionKind: {
44
45
  readonly 'canonical-object-type': "canonical-object-type";
45
46
  readonly 'resource-role': "resource-role";
46
47
  readonly 'ingestion-source': "ingestion-source";
48
+ readonly 'notification-descriptor': "notification-descriptor";
47
49
  readonly 'contribution-kind': "contribution-kind";
48
50
  };
@@ -36,6 +36,7 @@ exports.ContributionKind = {
36
36
  'opencode-plugin': 'opencode-plugin',
37
37
  capability: 'capability',
38
38
  'resource-ownership': 'resource-ownership',
39
+ 'resource-kind': 'resource-kind',
39
40
  'resource-definition': 'resource-definition',
40
41
  'stage-machine': 'stage-machine',
41
42
  'search-type': 'search-type',
@@ -43,5 +44,6 @@ exports.ContributionKind = {
43
44
  'canonical-object-type': 'canonical-object-type',
44
45
  'resource-role': 'resource-role',
45
46
  'ingestion-source': 'ingestion-source',
47
+ 'notification-descriptor': 'notification-descriptor',
46
48
  'contribution-kind': 'contribution-kind',
47
49
  };
@@ -4,6 +4,9 @@
4
4
  * OpenAPI spec version: 0.1.1
5
5
  */
6
6
  import type { ProblemFieldIssueDto } from './problemFieldIssueDto.js';
7
+ /**
8
+ * RFC 9457 problem document. The declared members are the reserved core; extension members are TOP-LEVEL and OPEN, owned by the refusing plane.
9
+ */
7
10
  export interface ProblemDetailsDto {
8
11
  /** Absolute URI identifying the refusal TYPE. Derived one-to-one from `code`, so a caller may branch on either and can never be told two different things. Never `about:blank`. */
9
12
  type: string;
@@ -21,4 +24,5 @@ export interface ProblemDetailsDto {
21
24
  traceId?: string;
22
25
  /** Field-level issues, when the refusal is about the request body. */
23
26
  errors?: ProblemFieldIssueDto[];
27
+ [key: string]: unknown;
24
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/biome-host-api-client",
3
- "version": "0.3.27",
3
+ "version": "0.3.29",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -19,7 +19,7 @@
19
19
  "service": "biome-host-api",
20
20
  "biome": "biome-host",
21
21
  "target": "server",
22
- "generator": "@xemahq/api-client-generator@0.19.0",
22
+ "generator": "@xemahq/api-client-generator@0.21.0",
23
23
  "source": "openapi.public.json"
24
24
  },
25
25
  "license": "LicenseRef-Xema-BSL-1.1",