@springbrand/http 0.1.0-alpha.5 → 0.1.0-alpha.6

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.
@@ -5,6 +5,8 @@ description: Use SpringBrand shared HTTP contracts, Axios request clients, Query
5
5
 
6
6
  # Dune React HTTP
7
7
 
8
+ Shared error codes, canonical statuses and their HTTP statuses are defined in `@springbrand/errors`; this package reads them and does not restate them.
9
+
8
10
  Read the installed declarations and README before configuring a client. Public entry points own different runtime needs; import only the capability needed.
9
11
 
10
12
  | Entry point | Responsibility |
@@ -13,7 +15,7 @@ Read the installed declarations and README before configuring a client. Public e
13
15
  | @springbrand/http/errors | Error definitions, cancellation and transient failure classification |
14
16
  | @springbrand/http/client | Axios client and standard HTTP error parsing |
15
17
  | @springbrand/http/query | Optional TanStack React Query integration |
16
- | @springbrand/http/catalog | Project error catalog: typed HttpError, Zod response schemas, client decoder (zod peer) |
18
+ | @springbrand/http/catalog | Project error catalog: typed HttpError, Zod response schemas, client decoder (zod peer). Codes registered in `@springbrand/errors` must use the registered HTTP status |
17
19
  | @springbrand/http/hono | Hono responders with TypedResponse types for zod-openapi (hono peer) |
18
20
  | @springbrand/http/types | Type-only entry for every public type, so consumers emitting declarations stay portable |
19
21
  | @springbrand/http/server | Standard Response helpers, bounded body reading, If-Match, Content-Disposition, log diagnostics and service authentication |
package/dist/catalog.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isRegisteredErrorCode, httpStatusOf } from "@springbrand/errors";
1
2
  import { z } from "zod";
2
3
  import { ApiError } from "./errors.js";
3
4
  import { parseHttpError } from "./response.js";
@@ -24,6 +25,9 @@ function defineErrorCatalog(definitions, options = {}) {
24
25
  if (definition.status !== 0 && !(Number.isInteger(definition.status) && definition.status >= 400 && definition.status <= 599)) {
25
26
  throw new RangeError(`Error ${code} must use status 0 or a 4xx/5xx status`);
26
27
  }
28
+ if (isRegisteredErrorCode(code) && definition.status !== (httpStatusOf(code) ?? 0)) {
29
+ throw new RangeError(`Error ${code} must use status ${httpStatusOf(code) ?? 0} registered in @springbrand/errors`);
30
+ }
27
31
  }
28
32
  const codes = Object.keys(definitions).filter((code) => definitions[code].status !== 0);
29
33
  for (const code of Object.keys(detailSchemas)) {
@@ -1,6 +1,7 @@
1
+ import { httpStatusOf } from "@springbrand/errors";
1
2
  import { ApiError } from "../errors.js";
2
- const invalidJson = (cause) => new ApiError({ status: 400, code: "REQUEST_INVALID_JSON", message: "Expected a JSON object", kind: "http", cause });
3
- const tooLarge = () => new ApiError({ status: 413, code: "REQUEST_PAYLOAD_TOO_LARGE", message: "Request body is too large", kind: "http" });
3
+ const invalidJson = (cause) => new ApiError({ status: httpStatusOf("REQUEST_INVALID_JSON"), code: "REQUEST_INVALID_JSON", message: "Expected a JSON object", kind: "http", cause });
4
+ const tooLarge = () => new ApiError({ status: httpStatusOf("REQUEST_PAYLOAD_TOO_LARGE"), code: "REQUEST_PAYLOAD_TOO_LARGE", message: "Request body is too large", kind: "http" });
4
5
  async function readJson(request) {
5
6
  try {
6
7
  return await request.json();
@@ -53,7 +54,7 @@ function requireIfMatchRevision(request, subject) {
53
54
  const match = /^"([1-9]\d*)"$/.exec(request.headers.get("if-match") ?? "");
54
55
  const revision = match ? Number(match[1]) : Number.NaN;
55
56
  if (!Number.isSafeInteger(revision)) {
56
- throw new ApiError({ status: 400, code: "REQUEST_INVALID_INPUT", message: `If-Match must contain the current ${subject}, for example "3"`, kind: "http" });
57
+ throw new ApiError({ status: httpStatusOf("REQUEST_INVALID_INPUT"), code: "REQUEST_INVALID_INPUT", message: `If-Match must contain the current ${subject}, for example "3"`, kind: "http" });
57
58
  }
58
59
  return revision;
59
60
  }
@@ -1,3 +1,4 @@
1
+ import { httpStatusOf } from "@springbrand/errors";
1
2
  import { ApiError, errorDefinitions } from "../errors.js";
2
3
  function responseHeaders(options) {
3
4
  const requestId = options.requestId ?? crypto.randomUUID();
@@ -22,7 +23,7 @@ function toErrorResponse(error, options = {}) {
22
23
  if (error instanceof ApiError && error.kind === "http" && Number.isInteger(error.status) && error.status >= 400 && error.status <= 599 && /^[A-Z][A-Z0-9_]{0,127}$/.test(error.code) && !["NETWORK_ERROR", "REQUEST_TIMEOUT", "RESPONSE_TOO_LARGE"].includes(error.code)) {
23
24
  return fail({ status: error.status, code: error.code, message: error.message, errors: error.errors, details: error.details, requestId: options.requestId ?? error.requestId, headers: options.headers });
24
25
  }
25
- return fail({ status: 500, code: "INTERNAL_ERROR", message: errorDefinitions.INTERNAL_ERROR, ...options });
26
+ return fail({ status: httpStatusOf("INTERNAL_ERROR"), code: "INTERNAL_ERROR", message: errorDefinitions.INTERNAL_ERROR, ...options });
26
27
  }
27
28
  export {
28
29
  fail,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/http",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -60,7 +60,8 @@
60
60
  },
61
61
  "dependencies": {
62
62
  "axios": "1.20.0",
63
- "content-disposition": "2.0.1"
63
+ "content-disposition": "2.0.1",
64
+ "@springbrand/errors": "^0.1.0-alpha.0"
64
65
  },
65
66
  "peerDependencies": {
66
67
  "@tanstack/react-query": "^5.104.1",