hono-ban 1.0.0 → 1.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # hono-ban
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`95a62e1`](https://github.com/ali-issa/hono-ban/commit/95a62e17f663f5f30522eda7fb76d4ef63b3487c) Thanks [@ali-issa](https://github.com/ali-issa)! - Validate options at construction instead of producing broken responses.
8
+
9
+ - `problemDetails()` throws `TypeError` when `traceIdMember` names a reserved member (`status`,
10
+ `id`, `errors`, ...) or is not an RFC 9457 extension member name; `jsonApi()` rejects a
11
+ `traceIdMetaKey` the format writes itself (`stack`, `location`, `code`, ...) or that is not a
12
+ JSON:API member name; `googleApi()` rejects a `traceIdMetadataKey` outside the
13
+ `ErrorInfo.metadata` key grammar. Previously `traceIdMember: 'status'` replaced the numeric status
14
+ with the trace id.
15
+ - `ban.onError()` throws `TypeError` when `errorIdHeader`, `requestIdHeader`, or `headers` holds a
16
+ name or value `Headers` rejects. Previously the failure surfaced inside the handler's last-resort
17
+ fallback and the handler rejected.
18
+ - `googleApi()` drops `meta` keys that do not match `[a-z][a-zA-Z0-9-_]+` or exceed 64 characters,
19
+ as `error_details.proto` requires for `ErrorInfo.metadata`, and the 2020-12 schema states the rule
20
+ with `propertyNames`. Keys such as `order.id` or `UserId` were previously sent as-is.
21
+
22
+ ### Patch Changes
23
+
24
+ - [`95a62e1`](https://github.com/ali-issa/hono-ban/commit/95a62e17f663f5f30522eda7fb76d4ef63b3487c) Thanks [@ali-issa](https://github.com/ali-issa)! - Keep every rendered validation body inside its own schema and the handler inside its guarantee.
25
+
26
+ - `jsonApi()` renders one summary error object for `ban.validation([])` instead of an empty `errors`
27
+ array its schema rejects; `googleApi()` omits `BadRequest` when there are no issues instead of
28
+ sending an empty `fieldViolations`.
29
+ - `assertFormatConformance` from `hono-ban/testing` now also renders a validation error with no
30
+ issues per location.
31
+ - `ban.onError()` gains a third fallback tier: when the header merge itself throws, the response
32
+ still carries `Content-Type`, `Cache-Control: no-store`, and the error id header.
33
+
3
34
  ## 1.0.0
4
35
 
5
36
  ### Major Changes
package/README.md CHANGED
@@ -61,6 +61,18 @@ order.total; // narrowed: Order
61
61
  Factories return errors and never throw them, so a bare `ban.notFound()` statement is a no-op. The
62
62
  `no-unused-expressions` rule (ESLint core, typescript-eslint, oxlint) flags the missing `throw`.
63
63
 
64
+ ### Unmatched routes
65
+
66
+ Hono answers a request no route matches from its own `notFound` handler with a plain-text 404, not
67
+ through `onError`. Throwing from `app.notFound` routes it through the handler, so unmatched routes
68
+ carry the same body, `X-Error-Id`, and report as every other error:
69
+
70
+ ```ts
71
+ app.notFound(() => {
72
+ throw ban.notFound();
73
+ });
74
+ ```
75
+
64
76
  ### Your own errors
65
77
 
66
78
  ```ts
@@ -135,7 +147,9 @@ Content-Type: application/vnd.api+json
135
147
 
136
148
  `googleApi()` renders the AIP-193 shape Google's APIs use. It needs the `ErrorInfo.domain` AIP-193
137
149
  requires; `status` comes from the catalog code when it names a `google.rpc.Code`, else from the HTTP
138
- status, and `rpcCodes` overrides it per code.
150
+ status, and `rpcCodes` overrides it per code. `meta` becomes `ErrorInfo.metadata`, whose keys must
151
+ match `[a-z][a-zA-Z0-9-_]+` and stay under 64 characters (`error_details.proto`); a key outside that
152
+ grammar (`order.id`, `UserId`) is dropped rather than sent.
139
153
 
140
154
  ```ts
141
155
  import { googleApi } from 'hono-ban/formats/google-api';
@@ -305,7 +319,8 @@ app.onError(
305
319
  );
306
320
  ```
307
321
 
308
- Every response carries `X-Error-Id`; the same id is in the body and in the report.
322
+ Every response carries `X-Error-Id`; the same id is in the report and, for every built-in format
323
+ except Stripe (whose shape has no member for it), in the body.
309
324
 
310
325
  ### Localize or redact
311
326
 
@@ -106,7 +106,9 @@ export interface GoogleApiBody {
106
106
  */
107
107
  export declare const GOOGLE_API_CONTENT_TYPE = "application/json";
108
108
  /**
109
- * AIP-193 Google API errors (SPEC 7.6).
109
+ * AIP-193 Google API errors (SPEC 7.6). Throws `TypeError` when
110
+ * `traceIdMetadataKey` falls outside the `ErrorInfo.metadata` key grammar
111
+ * or names a key the format writes from `ctx.meta`.
110
112
  * @ref https://google.aip.dev/193
111
113
  */
112
114
  export declare function googleApi(options: GoogleApiOptions): ErrorFormat<GoogleApiBody>;
@@ -1 +1 @@
1
- {"version":3,"file":"google-api.d.ts","names":[],"sources":["../../src/formats/google-api/details.ts","../../src/formats/google-api/rpc-code.ts","../../src/formats/google-api/index.ts"],"mappings":";;;;;;;UAgCiB;WACN;WACA;WACA;WACA,WAAW,SAAS;;;UAId;;WAEN;WACA;;WAEA;;UAGM;WACN;WACA,iBAAiB,cAAc;;;UAIzB;WACN;;WAEA;;;UAIM;WACN;WACA;;;UAIM;WACN;WACA,cAAc;;UAGR;WACN;WACA;;;UAIM;WACN;WACA,OAAO,cAAc;;KAGpB,kBACR,qBACA,sBACA,qBACA,uBACA,qBACA;;;;;;;;;;;;;;KC7EQ;;;iBCmCK;;;;;;;;WAQN;;;;;;;WAOA,WAAW,SAAS,eAAe;;WAEnC;;WAEA;;WAEA;;;iBAIM;;WAEN;WACA;;WAEA,QAAQ;WACR,SAAS,cAAc;;iBAGjB;WACN,OAAO;;;;;;qBAOL;;;;;wBA0FG,UACd,SAAS,mBACR,YAAY"}
1
+ {"version":3,"file":"google-api.d.ts","names":[],"sources":["../../src/formats/google-api/details.ts","../../src/formats/google-api/rpc-code.ts","../../src/formats/google-api/index.ts"],"mappings":";;;;;;;UAgCiB;WACN;WACA;WACA;WACA,WAAW,SAAS;;;UAId;;WAEN;WACA;;WAEA;;UAGM;WACN;WACA,iBAAiB,cAAc;;;UAIzB;WACN;;WAEA;;;UAIM;WACN;WACA;;;UAIM;WACN;WACA,cAAc;;UAGR;WACN;WACA;;;UAIM;WACN;WACA,OAAO,cAAc;;KAGpB,kBACR,qBACA,sBACA,qBACA,uBACA,qBACA;;;;;;;;;;;;;;KC7EQ;;;iBCqCK;;;;;;;;WAQN;;;;;;;WAOA,WAAW,SAAS,eAAe;;WAEnC;;WAEA;;WAEA;;;iBAIM;;WAEN;WACA;;WAEA,QAAQ;WACR,SAAS,cAAc;;iBAGjB;WACN,OAAO;;;;;;qBAOL;;;;;;;wBAsGG,UACd,SAAS,mBACR,YAAY"}
@@ -1,6 +1,6 @@
1
1
  import { n as safeStringify } from "../json-Bjfm-jxl.js";
2
- import "../constants-rUzQOaX_.js";
3
- import { t as constant } from "../schema-helpers-B4HL9TmU.js";
2
+ import { t as constant } from "../schema-helpers-bwCFve8i.js";
3
+ import { t as assertMemberName } from "../member-name-k4VABr91.js";
4
4
  //#region src/formats/google-api/details.ts
5
5
  /** `google.protobuf.Any` type URLs: the `type.googleapis.com/` prefix plus the full message name. */
6
6
  const ERROR_INFO_TYPE = "type.googleapis.com/google.rpc.ErrorInfo";
@@ -10,14 +10,25 @@ const REQUEST_INFO_TYPE = "type.googleapis.com/google.rpc.RequestInfo";
10
10
  const DEBUG_INFO_TYPE = "type.googleapis.com/google.rpc.DebugInfo";
11
11
  const HELP_TYPE = "type.googleapis.com/google.rpc.Help";
12
12
  /**
13
+ * `ErrorInfo.metadata` keys "must match a regular expression of
14
+ * `[a-z][a-zA-Z0-9-_]+`" and "must be limited to 64 characters in length"
15
+ * (the comment on the field in `error_details.proto`).
16
+ * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
17
+ */
18
+ const METADATA_KEY_PATTERN = /^[a-z][a-zA-Z0-9_-]{1,63}$/u;
19
+ /**
13
20
  * `ErrorInfo.metadata` is `map<string, string>`, so every value becomes a
14
21
  * string: strings as they are, everything else as JSON. Values JSON cannot
15
- * represent (`undefined`, functions, symbols) are dropped.
22
+ * represent (`undefined`, functions, symbols) are dropped, and so is every
23
+ * key outside `METADATA_KEY_PATTERN` (`order.id`, `UserId`): a key the
24
+ * grammar rejects would make the body fail the schema, and throwing here
25
+ * would turn the error being rendered into a 500 (SPEC 7.6.2).
16
26
  * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
17
27
  */
18
28
  function toMetadata(meta) {
19
29
  const metadata = {};
20
30
  for (const key of Object.keys(meta)) {
31
+ if (!METADATA_KEY_PATTERN.test(key)) continue;
21
32
  const value = meta[key];
22
33
  if (typeof value === "string") metadata[key] = value;
23
34
  else if (typeof value === "bigint") metadata[key] = value.toString();
@@ -174,14 +185,26 @@ function detail(typeUrl, dialect, properties, required) {
174
185
  additionalProperties: false
175
186
  };
176
187
  }
188
+ /**
189
+ * `metadata` keys follow the `error_details.proto` grammar the renderer
190
+ * enforces. `propertyNames` states it in JSON Schema 2020-12; the OpenAPI
191
+ * 3.0 schema object has neither it nor `patternProperties`, so that dialect
192
+ * types the values only.
193
+ * @ref https://json-schema.org/draft/2020-12/json-schema-core#name-propertynames
194
+ * @ref https://spec.openapis.org/oas/v3.0.3#properties
195
+ */
196
+ function metadata(dialect) {
197
+ return {
198
+ type: "object",
199
+ ...dialect === "openapi-3.0" ? {} : { propertyNames: { pattern: METADATA_KEY_PATTERN.source } },
200
+ additionalProperties: STRING
201
+ };
202
+ }
177
203
  function errorInfo$1(definition, ctx, options) {
178
204
  return detail(ERROR_INFO_TYPE, ctx.dialect, {
179
205
  reason: constant(definition.code, ctx.dialect),
180
206
  domain: constant(options.domain, ctx.dialect),
181
- metadata: {
182
- type: "object",
183
- additionalProperties: STRING
184
- }
207
+ metadata: metadata(ctx.dialect)
185
208
  }, ["reason", "domain"]);
186
209
  }
187
210
  function badRequest(dialect) {
@@ -271,6 +294,12 @@ function bodySchema(definition, ctx, options, validation) {
271
294
  * @ref https://google.aip.dev/193#http11json-representation
272
295
  */
273
296
  const GOOGLE_API_CONTENT_TYPE = "application/json";
297
+ /**
298
+ * `metadata` keys written from `ctx.meta` that a `traceIdMetadataKey` of the
299
+ * same name would silently replace: `location` carries the validation target
300
+ * (SPEC 8.2).
301
+ */
302
+ const RESERVED_METADATA_KEYS = /* @__PURE__ */ new Set(["location"]);
274
303
  function errorInfo(error, ctx, options) {
275
304
  const metadata = toMetadata(ctx.meta);
276
305
  if (options.traceIdMetadataKey !== false && ctx.traceId !== void 0) metadata[options.traceIdMetadataKey] = ctx.traceId;
@@ -293,7 +322,7 @@ function helpUrl(error, ctx, options) {
293
322
  */
294
323
  function renderBody(error, ctx, options, issues) {
295
324
  const details = [errorInfo(error, ctx, options)];
296
- if (issues !== void 0) details.push({
325
+ if (issues !== void 0 && issues.length > 0) details.push({
297
326
  "@type": BAD_REQUEST_TYPE,
298
327
  fieldViolations: issues.map((issue) => fieldViolation(issue))
299
328
  });
@@ -317,7 +346,9 @@ function renderBody(error, ctx, options, issues) {
317
346
  } };
318
347
  }
319
348
  /**
320
- * AIP-193 Google API errors (SPEC 7.6).
349
+ * AIP-193 Google API errors (SPEC 7.6). Throws `TypeError` when
350
+ * `traceIdMetadataKey` falls outside the `ErrorInfo.metadata` key grammar
351
+ * or names a key the format writes from `ctx.meta`.
321
352
  * @ref https://google.aip.dev/193
322
353
  */
323
354
  function googleApi(options) {
@@ -328,6 +359,7 @@ function googleApi(options) {
328
359
  traceIdMetadataKey: options.traceIdMetadataKey ?? "traceId",
329
360
  includeRequestInfo: options.includeRequestInfo ?? true
330
361
  };
362
+ if (resolved.traceIdMetadataKey !== false) assertMemberName("traceIdMetadataKey", resolved.traceIdMetadataKey, RESERVED_METADATA_KEYS, METADATA_KEY_PATTERN);
331
363
  const schemaOptions = {
332
364
  domain: resolved.domain,
333
365
  rpcCodes: resolved.rpcCodes,
@@ -1 +1 @@
1
- {"version":3,"file":"google-api.js","names":["retryInfo","errorInfo","retryInfo"],"sources":["../../src/formats/google-api/details.ts","../../src/formats/google-api/rpc-code.ts","../../src/formats/google-api/schema.ts","../../src/formats/google-api/index.ts"],"sourcesContent":["/**\n * The standard `google.rpc` detail payloads this format renders (SPEC 7.6.2)\n * in their proto3 JSON form: each message packed as a `google.protobuf.Any`\n * carries an `@type` member holding its type URL, field names are\n * lowerCamelCase, and fields at their default value (empty string, empty\n * repeated, empty map) are omitted.\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n * @ref https://protobuf.dev/programming-guides/json/\n * @packageDocumentation\n */\nimport type { ValidationIssue } from '../../validation/issue';\n\nimport { safeStringify } from '../../internal/json';\n\n/** `google.protobuf.Any` type URLs: the `type.googleapis.com/` prefix plus the full message name. */\nexport const ERROR_INFO_TYPE =\n 'type.googleapis.com/google.rpc.ErrorInfo' as const;\nexport const BAD_REQUEST_TYPE =\n 'type.googleapis.com/google.rpc.BadRequest' as const;\nexport const RETRY_INFO_TYPE =\n 'type.googleapis.com/google.rpc.RetryInfo' as const;\nexport const REQUEST_INFO_TYPE =\n 'type.googleapis.com/google.rpc.RequestInfo' as const;\nexport const DEBUG_INFO_TYPE =\n 'type.googleapis.com/google.rpc.DebugInfo' as const;\nexport const HELP_TYPE = 'type.googleapis.com/google.rpc.Help' as const;\n\n/**\n * `google.rpc.ErrorInfo`: the machine-readable identifier AIP-193 requires\n * in every error. `reason` is the catalog code, `domain` the format option.\n * @ref https://google.aip.dev/193#errorinfo\n */\nexport interface GoogleApiErrorInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.ErrorInfo';\n readonly reason: string;\n readonly domain: string;\n readonly metadata?: Readonly<Record<string, string>>;\n}\n\n/** `google.rpc.BadRequest.FieldViolation`, one per validation issue. */\nexport interface GoogleApiFieldViolation {\n /** Dot path with `[index]` for array positions, for example `items[0].sku`. */\n readonly field?: string;\n readonly description: string;\n /** The issue code in UPPER_SNAKE_CASE, when the validator gave one. */\n readonly reason?: string;\n}\n\nexport interface GoogleApiBadRequest {\n readonly '@type': 'type.googleapis.com/google.rpc.BadRequest';\n readonly fieldViolations: ReadonlyArray<GoogleApiFieldViolation>;\n}\n\n/** `google.rpc.RetryInfo`, from a delay-seconds `Retry-After` header. */\nexport interface GoogleApiRetryInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.RetryInfo';\n /** A `google.protobuf.Duration` in JSON form, for example `30s`. */\n readonly retryDelay: string;\n}\n\n/** `google.rpc.RequestInfo` carrying the error id (SPEC 10.2). */\nexport interface GoogleApiRequestInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.RequestInfo';\n readonly requestId: string;\n}\n\n/** `google.rpc.DebugInfo`, present only when the handler includes the stack. */\nexport interface GoogleApiDebugInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.DebugInfo';\n readonly stackEntries: ReadonlyArray<string>;\n}\n\nexport interface GoogleApiHelpLink {\n readonly description: string;\n readonly url: string;\n}\n\n/** `google.rpc.Help` with one link to the error's documentation. */\nexport interface GoogleApiHelp {\n readonly '@type': 'type.googleapis.com/google.rpc.Help';\n readonly links: ReadonlyArray<GoogleApiHelpLink>;\n}\n\nexport type GoogleApiDetail =\n | GoogleApiErrorInfo\n | GoogleApiBadRequest\n | GoogleApiRetryInfo\n | GoogleApiRequestInfo\n | GoogleApiDebugInfo\n | GoogleApiHelp;\n\n/**\n * `ErrorInfo.metadata` is `map<string, string>`, so every value becomes a\n * string: strings as they are, everything else as JSON. Values JSON cannot\n * represent (`undefined`, functions, symbols) are dropped.\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n */\nexport function toMetadata(\n meta: Readonly<Record<string, unknown>>,\n): Record<string, string> {\n const metadata: Record<string, string> = {};\n for (const key of Object.keys(meta)) {\n const value = meta[key];\n if (typeof value === 'string') {\n metadata[key] = value;\n } else if (typeof value === 'bigint') {\n metadata[key] = value.toString();\n } else if (\n value !== undefined &&\n typeof value !== 'function' &&\n typeof value !== 'symbol'\n ) {\n metadata[key] = safeStringify(value);\n }\n }\n return metadata;\n}\n\n/**\n * `BadRequest.FieldViolation.field`: \"a sequence of dot-separated identifiers\"\n * with repeated-field positions in square brackets, so `['items', 0, 'sku']`\n * is `items[0].sku`. An empty path names the whole request and yields no\n * `field` (proto3 JSON omits empty strings).\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n */\nexport function fieldPath(path: ReadonlyArray<string | number>): string {\n let out = '';\n for (const segment of path) {\n if (typeof segment === 'number') {\n out += `[${String(segment)}]`;\n } else {\n out += out === '' ? segment : `.${segment}`;\n }\n }\n return out;\n}\n\n/**\n * `FieldViolation.reason` \"should be at most 63 characters and match\n * `[A-Z][A-Z0-9_]+[A-Z0-9]`\"; validator codes are lower snake case\n * (`invalid_type`), so they are upper-cased. Not validated further.\n */\nexport function fieldViolation(\n issue: ValidationIssue,\n): GoogleApiFieldViolation {\n const field = fieldPath(issue.path);\n return {\n ...(field === '' ? {} : { field }),\n description: issue.message,\n ...(issue.code === undefined ? {} : { reason: issue.code.toUpperCase() }),\n };\n}\n\nconst DELAY_SECONDS_PATTERN = /^[0-9]+$/u;\n\n/**\n * `RetryInfo.retry_delay` from a `Retry-After` header in its delay-seconds\n * form. The HTTP-date form needs the current time to become a duration, and\n * formats are pure, so it is left out.\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-10.2.3\n * @ref https://protobuf.dev/programming-guides/json/ (Duration)\n */\nexport function retryInfo(headers: Headers): GoogleApiRetryInfo | undefined {\n const value = headers.get('retry-after');\n if (value === null || !DELAY_SECONDS_PATTERN.test(value)) {\n return undefined;\n }\n return { '@type': RETRY_INFO_TYPE, retryDelay: `${value}s` };\n}\n\n/** RFC 3986 section 3.1 scheme followed by a colon: an absolute URI. */\nconst ABSOLUTE_URI_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/u;\n\n/**\n * `Help.Link.url` \"must be an absolute URL, including scheme\", so a relative\n * `type` yields no `Help` payload.\n * @ref https://google.aip.dev/193#help\n * @ref https://www.rfc-editor.org/rfc/rfc3986#section-3.1\n */\nexport function help(code: string, url?: string): GoogleApiHelp | undefined {\n if (url === undefined || !ABSOLUTE_URI_PATTERN.test(url)) {\n return undefined;\n }\n return {\n '@type': HELP_TYPE,\n links: [{ description: `Documentation for ${code} errors`, url }],\n };\n}\n","/**\n * `google.rpc.Code` names and the HTTP status each one is derived from\n * (SPEC 7.6.1). The forward mapping (code to HTTP status) is the \"HTTP\n * Mapping\" line on every enum value in `code.proto`; it is many-to-one, so the\n * reverse table below is the one Google's own client library uses when it\n * turns an HTTP error into a status code.\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\n * @ref https://github.com/googleapis/gax-nodejs/blob/main/gax/src/status.ts\n * @packageDocumentation\n */\n\n/** Every value of the `google.rpc.Code` enum, by name. */\nexport type GoogleRpcCode =\n | 'OK'\n | 'CANCELLED'\n | 'UNKNOWN'\n | 'INVALID_ARGUMENT'\n | 'DEADLINE_EXCEEDED'\n | 'NOT_FOUND'\n | 'ALREADY_EXISTS'\n | 'PERMISSION_DENIED'\n | 'RESOURCE_EXHAUSTED'\n | 'FAILED_PRECONDITION'\n | 'ABORTED'\n | 'OUT_OF_RANGE'\n | 'UNIMPLEMENTED'\n | 'INTERNAL'\n | 'UNAVAILABLE'\n | 'DATA_LOSS'\n | 'UNAUTHENTICATED';\n\nconst RPC_CODES: ReadonlySet<string> = new Set<GoogleRpcCode>([\n 'OK',\n 'CANCELLED',\n 'UNKNOWN',\n 'INVALID_ARGUMENT',\n 'DEADLINE_EXCEEDED',\n 'NOT_FOUND',\n 'ALREADY_EXISTS',\n 'PERMISSION_DENIED',\n 'RESOURCE_EXHAUSTED',\n 'FAILED_PRECONDITION',\n 'ABORTED',\n 'OUT_OF_RANGE',\n 'UNIMPLEMENTED',\n 'INTERNAL',\n 'UNAVAILABLE',\n 'DATA_LOSS',\n 'UNAUTHENTICATED',\n]);\n\n/**\n * `HttpCodeToRpcCodeMap` from gax-nodejs, verbatim. Where several codes share\n * an HTTP status the table keeps the one gax picked: `INVALID_ARGUMENT` for\n * 400 (not `FAILED_PRECONDITION` or `OUT_OF_RANGE`), `ABORTED` for 409 (not\n * `ALREADY_EXISTS`), `INTERNAL` for 500 (not `UNKNOWN` or `DATA_LOSS`).\n * @ref https://github.com/googleapis/gax-nodejs/blob/main/gax/src/status.ts\n */\nconst BY_HTTP_STATUS: ReadonlyMap<number, GoogleRpcCode> = new Map<\n number,\n GoogleRpcCode\n>([\n [400, 'INVALID_ARGUMENT'],\n [401, 'UNAUTHENTICATED'],\n [403, 'PERMISSION_DENIED'],\n [404, 'NOT_FOUND'],\n [409, 'ABORTED'],\n [416, 'OUT_OF_RANGE'],\n [429, 'RESOURCE_EXHAUSTED'],\n [499, 'CANCELLED'],\n [501, 'UNIMPLEMENTED'],\n [503, 'UNAVAILABLE'],\n [504, 'DEADLINE_EXCEEDED'],\n]);\n\nconst SUCCESS = 200;\nconst REDIRECT = 300;\nconst CLIENT_ERROR = 400;\nconst SERVER_ERROR = 500;\nconst END = 600;\n\nexport function isGoogleRpcCode(value: string): value is GoogleRpcCode {\n return RPC_CODES.has(value);\n}\n\n/**\n * The `google.rpc.Code` name for an HTTP status, following\n * `rpcCodeFromHttpStatusCode` in gax-nodejs: the table first, then `OK` for\n * any 2xx, `FAILED_PRECONDITION` for any other 4xx, `INTERNAL` for any other\n * 5xx, and `UNKNOWN` for everything else.\n * @ref https://github.com/googleapis/gax-nodejs/blob/main/gax/src/status.ts\n */\nexport function rpcCodeFromStatus(status: number): GoogleRpcCode {\n const mapped = BY_HTTP_STATUS.get(status);\n if (mapped !== undefined) {\n return mapped;\n }\n if (status >= SUCCESS && status < REDIRECT) {\n return 'OK';\n }\n if (status >= CLIENT_ERROR && status < SERVER_ERROR) {\n return 'FAILED_PRECONDITION';\n }\n if (status >= SERVER_ERROR && status < END) {\n return 'INTERNAL';\n }\n return 'UNKNOWN';\n}\n\n/**\n * The `status` member for an entry (SPEC 7.6.1): an explicit per-code option,\n * else the catalog code itself when it already names a `google.rpc.Code`\n * (`NOT_FOUND`, `ALREADY_EXISTS`), else the HTTP-derived default.\n */\nexport function resolveRpcCode(\n code: string,\n status: number,\n overrides?: Readonly<Record<string, GoogleRpcCode>>,\n): GoogleRpcCode {\n const explicit = overrides?.[code];\n if (explicit !== undefined) {\n return explicit;\n }\n if (isGoogleRpcCode(code)) {\n return code;\n }\n return rpcCodeFromStatus(status);\n}\n","/**\n * JSON Schema for the AIP-193 body (SPEC 7.6.3). Every detail payload is a\n * closed object pinned to its `@type`; `details` is an array of the payloads\n * this format can render for the entry, at least one long because\n * `ErrorInfo` is always present. `contains` and `prefixItems` would state that\n * more precisely, but OpenAPI 3.0 has neither, so both dialects use `items`.\n * @ref https://google.aip.dev/193#http11json-representation\n * @ref https://spec.openapis.org/oas/v3.0.3#schema-object\n * @packageDocumentation\n */\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { JsonSchema, SchemaContext, SchemaDialect } from '../context';\nimport type { GoogleRpcCode } from './rpc-code';\n\nimport { constant } from '../schema-helpers';\nimport {\n BAD_REQUEST_TYPE,\n DEBUG_INFO_TYPE,\n ERROR_INFO_TYPE,\n HELP_TYPE,\n REQUEST_INFO_TYPE,\n RETRY_INFO_TYPE,\n} from './details';\nimport { resolveRpcCode } from './rpc-code';\n\nexport interface GoogleApiSchemaOptions {\n readonly domain: string;\n readonly rpcCodes: Readonly<Record<string, GoogleRpcCode>> | undefined;\n readonly includeRequestInfo: boolean;\n}\n\nconst STRING: JsonSchema = { type: 'string' };\n\nfunction detail(\n typeUrl: string,\n dialect: SchemaDialect,\n properties: Record<string, JsonSchema>,\n required: ReadonlyArray<string>,\n): JsonSchema {\n return {\n type: 'object',\n required: ['@type', ...required],\n properties: { '@type': constant(typeUrl, dialect), ...properties },\n additionalProperties: false,\n };\n}\n\nfunction errorInfo(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: GoogleApiSchemaOptions,\n): JsonSchema {\n return detail(\n ERROR_INFO_TYPE,\n ctx.dialect,\n {\n reason: constant(definition.code, ctx.dialect),\n domain: constant(options.domain, ctx.dialect),\n metadata: { type: 'object', additionalProperties: STRING },\n },\n ['reason', 'domain'],\n );\n}\n\nfunction badRequest(dialect: SchemaDialect): JsonSchema {\n return detail(\n BAD_REQUEST_TYPE,\n dialect,\n {\n fieldViolations: {\n type: 'array',\n items: {\n type: 'object',\n required: ['description'],\n properties: { field: STRING, description: STRING, reason: STRING },\n additionalProperties: false,\n },\n },\n },\n ['fieldViolations'],\n );\n}\n\nfunction retryInfo(dialect: SchemaDialect): JsonSchema {\n return detail(\n RETRY_INFO_TYPE,\n dialect,\n { retryDelay: { type: 'string', pattern: '^[0-9]+s$' } },\n ['retryDelay'],\n );\n}\n\nfunction requestInfo(dialect: SchemaDialect): JsonSchema {\n return detail(REQUEST_INFO_TYPE, dialect, { requestId: STRING }, [\n 'requestId',\n ]);\n}\n\nfunction debugInfo(dialect: SchemaDialect): JsonSchema {\n return detail(\n DEBUG_INFO_TYPE,\n dialect,\n { stackEntries: { type: 'array', items: STRING } },\n ['stackEntries'],\n );\n}\n\nfunction helpDetail(dialect: SchemaDialect): JsonSchema {\n return detail(\n HELP_TYPE,\n dialect,\n {\n links: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n required: ['description', 'url'],\n properties: {\n description: STRING,\n url: { type: 'string', format: 'uri' },\n },\n additionalProperties: false,\n },\n },\n },\n ['links'],\n );\n}\n\nexport function bodySchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: GoogleApiSchemaOptions,\n validation: boolean,\n): JsonSchema {\n const payloads: Array<JsonSchema> = [errorInfo(definition, ctx, options)];\n if (validation) {\n payloads.push(badRequest(ctx.dialect));\n }\n payloads.push(retryInfo(ctx.dialect));\n if (options.includeRequestInfo) {\n payloads.push(requestInfo(ctx.dialect));\n }\n payloads.push(debugInfo(ctx.dialect), helpDetail(ctx.dialect));\n return {\n type: 'object',\n required: ['error'],\n properties: {\n error: {\n type: 'object',\n required: ['code', 'message', 'status', 'details'],\n properties: {\n code: constant(definition.status, ctx.dialect),\n message: STRING,\n status: constant(\n resolveRpcCode(\n definition.code,\n definition.status,\n options.rpcCodes,\n ),\n ctx.dialect,\n ),\n details: { type: 'array', minItems: 1, items: { anyOf: payloads } },\n },\n additionalProperties: false,\n },\n },\n additionalProperties: false,\n };\n}\n","/**\n * Google API error format (SPEC 7.6): the AIP-193 HTTP/1.1+JSON\n * representation of `google.rpc.Status`, `{ error: { code, message, status,\n * details } }`, with `code` the HTTP status, `status` the `google.rpc.Code`\n * name, and `details` the standard payloads from `error_details.proto`\n * packed as `google.protobuf.Any`. `ErrorInfo` is always present, as AIP-193\n * requires.\n * @ref https://google.aip.dev/193#http11json-representation\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n * @packageDocumentation\n */\nimport type { BanError } from '../../core/ban-error';\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { ValidationIssue } from '../../validation/issue';\nimport type { JsonSchema, RenderContext, SchemaContext } from '../context';\nimport type { ErrorFormat } from '../types';\nimport type { GoogleApiDetail, GoogleApiErrorInfo } from './details';\nimport type { GoogleRpcCode } from './rpc-code';\n\nimport { VALIDATION_DETAIL } from '../../internal/constants';\nimport {\n BAD_REQUEST_TYPE,\n DEBUG_INFO_TYPE,\n ERROR_INFO_TYPE,\n fieldViolation,\n help,\n REQUEST_INFO_TYPE,\n retryInfo,\n toMetadata,\n} from './details';\nimport { resolveRpcCode } from './rpc-code';\nimport { bodySchema } from './schema';\n\nexport type {\n GoogleApiBadRequest,\n GoogleApiDebugInfo,\n GoogleApiDetail,\n GoogleApiErrorInfo,\n GoogleApiFieldViolation,\n GoogleApiHelp,\n GoogleApiHelpLink,\n GoogleApiRequestInfo,\n GoogleApiRetryInfo,\n} from './details';\nexport type { GoogleRpcCode } from './rpc-code';\n\nexport interface GoogleApiOptions {\n /**\n * `ErrorInfo.domain`: \"the logical grouping to which the reason belongs\",\n * globally unique, typically the service name (`pubsub.googleapis.com`).\n * Required because AIP-193 requires `ErrorInfo` and a domain has no\n * derivable default.\n * @ref https://google.aip.dev/193#errorinfo\n */\n readonly domain: string;\n /**\n * `status` per catalog code, for entries whose HTTP status maps to the\n * wrong `google.rpc.Code` by default (a 409 that is `ALREADY_EXISTS`, not\n * `ABORTED`). A catalog code that is itself a `google.rpc.Code` name needs\n * no entry here.\n */\n readonly rpcCodes?: Readonly<Record<string, GoogleRpcCode>> | undefined;\n /** Base for the `Help` link; overrides the instance `docsBaseUrl`. */\n readonly helpLinkBaseUrl?: string | undefined;\n /** Key for the trace id inside `ErrorInfo.metadata`. Default `traceId`; `false` disables. */\n readonly traceIdMetadataKey?: string | false | undefined;\n /** Emit `RequestInfo.requestId` with the error id. Default true. */\n readonly includeRequestInfo?: boolean | undefined;\n}\n\n/** The `error` member: AIP-193's `Error.Status`, not `google.rpc.Status`. */\nexport interface GoogleApiStatus {\n /** The HTTP status code. */\n readonly code: number;\n readonly message: string;\n /** The `google.rpc.Code` name. */\n readonly status: GoogleRpcCode;\n readonly details: ReadonlyArray<GoogleApiDetail>;\n}\n\nexport interface GoogleApiBody {\n readonly error: GoogleApiStatus;\n}\n\n/**\n * Google APIs answer errors as plain JSON; there is no dedicated media type.\n * @ref https://google.aip.dev/193#http11json-representation\n */\nexport const GOOGLE_API_CONTENT_TYPE = 'application/json';\n\ninterface Resolved {\n readonly domain: string;\n readonly rpcCodes: Readonly<Record<string, GoogleRpcCode>> | undefined;\n readonly helpLinkBaseUrl: string | undefined;\n readonly traceIdMetadataKey: string | false;\n readonly includeRequestInfo: boolean;\n}\n\nfunction errorInfo(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n): GoogleApiErrorInfo {\n const metadata = toMetadata(ctx.meta);\n if (options.traceIdMetadataKey !== false && ctx.traceId !== undefined) {\n metadata[options.traceIdMetadataKey] = ctx.traceId;\n }\n return {\n '@type': ERROR_INFO_TYPE,\n reason: error.code,\n domain: options.domain,\n ...(Object.keys(metadata).length === 0 ? {} : { metadata }),\n };\n}\n\nfunction helpUrl(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n): string | undefined {\n if (error.type !== undefined) {\n return error.type;\n }\n const base = options.helpLinkBaseUrl ?? ctx.docsBaseUrl;\n return base === undefined ? undefined : `${base}/${error.code}`;\n}\n\n/**\n * Detail order (SPEC 7.6.2): `ErrorInfo`, `BadRequest` (validation only),\n * `RetryInfo`, `RequestInfo`, `DebugInfo`, `Help`. Each type appears at most\n * once, as AIP-193 requires.\n */\nfunction renderBody(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n issues?: ReadonlyArray<ValidationIssue>,\n): GoogleApiBody {\n const details: Array<GoogleApiDetail> = [errorInfo(error, ctx, options)];\n if (issues !== undefined) {\n details.push({\n '@type': BAD_REQUEST_TYPE,\n fieldViolations: issues.map((issue) => fieldViolation(issue)),\n });\n }\n const retry = retryInfo(error.headers);\n if (retry !== undefined) {\n details.push(retry);\n }\n if (options.includeRequestInfo) {\n details.push({ '@type': REQUEST_INFO_TYPE, requestId: error.id });\n }\n if (ctx.stack !== undefined) {\n details.push({\n '@type': DEBUG_INFO_TYPE,\n stackEntries: ctx.stack.split('\\n'),\n });\n }\n const link = help(error.code, helpUrl(error, ctx, options));\n if (link !== undefined) {\n details.push(link);\n }\n return {\n error: {\n code: error.status,\n message:\n error.detail ??\n (issues === undefined ? error.title : VALIDATION_DETAIL),\n status: resolveRpcCode(error.code, error.status, options.rpcCodes),\n details,\n },\n };\n}\n\n/**\n * AIP-193 Google API errors (SPEC 7.6).\n * @ref https://google.aip.dev/193\n */\nexport function googleApi(\n options: GoogleApiOptions,\n): ErrorFormat<GoogleApiBody> {\n const resolved: Resolved = {\n domain: options.domain,\n rpcCodes: options.rpcCodes,\n helpLinkBaseUrl: options.helpLinkBaseUrl,\n traceIdMetadataKey: options.traceIdMetadataKey ?? 'traceId',\n includeRequestInfo: options.includeRequestInfo ?? true,\n };\n const schemaOptions = {\n domain: resolved.domain,\n rpcCodes: resolved.rpcCodes,\n includeRequestInfo: resolved.includeRequestInfo,\n };\n return {\n name: 'google-api',\n contentType: GOOGLE_API_CONTENT_TYPE,\n render(error: BanError, ctx: RenderContext): GoogleApiBody {\n return renderBody(error, ctx, resolved);\n },\n renderValidation(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): GoogleApiBody {\n return renderBody(error, ctx, resolved, issues);\n },\n schema(definition: ResolvedDefinition, ctx: SchemaContext): JsonSchema {\n return bodySchema(definition, ctx, schemaOptions, false);\n },\n validationSchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema {\n return bodySchema(definition, ctx, schemaOptions, true);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"google-api.js","names":["retryInfo","errorInfo","retryInfo"],"sources":["../../src/formats/google-api/details.ts","../../src/formats/google-api/rpc-code.ts","../../src/formats/google-api/schema.ts","../../src/formats/google-api/index.ts"],"sourcesContent":["/**\n * The standard `google.rpc` detail payloads this format renders (SPEC 7.6.2)\n * in their proto3 JSON form: each message packed as a `google.protobuf.Any`\n * carries an `@type` member holding its type URL, field names are\n * lowerCamelCase, and fields at their default value (empty string, empty\n * repeated, empty map) are omitted.\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n * @ref https://protobuf.dev/programming-guides/json/\n * @packageDocumentation\n */\nimport type { ValidationIssue } from '../../validation/issue';\n\nimport { safeStringify } from '../../internal/json';\n\n/** `google.protobuf.Any` type URLs: the `type.googleapis.com/` prefix plus the full message name. */\nexport const ERROR_INFO_TYPE =\n 'type.googleapis.com/google.rpc.ErrorInfo' as const;\nexport const BAD_REQUEST_TYPE =\n 'type.googleapis.com/google.rpc.BadRequest' as const;\nexport const RETRY_INFO_TYPE =\n 'type.googleapis.com/google.rpc.RetryInfo' as const;\nexport const REQUEST_INFO_TYPE =\n 'type.googleapis.com/google.rpc.RequestInfo' as const;\nexport const DEBUG_INFO_TYPE =\n 'type.googleapis.com/google.rpc.DebugInfo' as const;\nexport const HELP_TYPE = 'type.googleapis.com/google.rpc.Help' as const;\n\n/**\n * `google.rpc.ErrorInfo`: the machine-readable identifier AIP-193 requires\n * in every error. `reason` is the catalog code, `domain` the format option.\n * @ref https://google.aip.dev/193#errorinfo\n */\nexport interface GoogleApiErrorInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.ErrorInfo';\n readonly reason: string;\n readonly domain: string;\n readonly metadata?: Readonly<Record<string, string>>;\n}\n\n/** `google.rpc.BadRequest.FieldViolation`, one per validation issue. */\nexport interface GoogleApiFieldViolation {\n /** Dot path with `[index]` for array positions, for example `items[0].sku`. */\n readonly field?: string;\n readonly description: string;\n /** The issue code in UPPER_SNAKE_CASE, when the validator gave one. */\n readonly reason?: string;\n}\n\nexport interface GoogleApiBadRequest {\n readonly '@type': 'type.googleapis.com/google.rpc.BadRequest';\n readonly fieldViolations: ReadonlyArray<GoogleApiFieldViolation>;\n}\n\n/** `google.rpc.RetryInfo`, from a delay-seconds `Retry-After` header. */\nexport interface GoogleApiRetryInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.RetryInfo';\n /** A `google.protobuf.Duration` in JSON form, for example `30s`. */\n readonly retryDelay: string;\n}\n\n/** `google.rpc.RequestInfo` carrying the error id (SPEC 10.2). */\nexport interface GoogleApiRequestInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.RequestInfo';\n readonly requestId: string;\n}\n\n/** `google.rpc.DebugInfo`, present only when the handler includes the stack. */\nexport interface GoogleApiDebugInfo {\n readonly '@type': 'type.googleapis.com/google.rpc.DebugInfo';\n readonly stackEntries: ReadonlyArray<string>;\n}\n\nexport interface GoogleApiHelpLink {\n readonly description: string;\n readonly url: string;\n}\n\n/** `google.rpc.Help` with one link to the error's documentation. */\nexport interface GoogleApiHelp {\n readonly '@type': 'type.googleapis.com/google.rpc.Help';\n readonly links: ReadonlyArray<GoogleApiHelpLink>;\n}\n\nexport type GoogleApiDetail =\n | GoogleApiErrorInfo\n | GoogleApiBadRequest\n | GoogleApiRetryInfo\n | GoogleApiRequestInfo\n | GoogleApiDebugInfo\n | GoogleApiHelp;\n\n/**\n * `ErrorInfo.metadata` keys \"must match a regular expression of\n * `[a-z][a-zA-Z0-9-_]+`\" and \"must be limited to 64 characters in length\"\n * (the comment on the field in `error_details.proto`).\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n */\nexport const METADATA_KEY_PATTERN: RegExp = /^[a-z][a-zA-Z0-9_-]{1,63}$/u;\n\n/**\n * `ErrorInfo.metadata` is `map<string, string>`, so every value becomes a\n * string: strings as they are, everything else as JSON. Values JSON cannot\n * represent (`undefined`, functions, symbols) are dropped, and so is every\n * key outside `METADATA_KEY_PATTERN` (`order.id`, `UserId`): a key the\n * grammar rejects would make the body fail the schema, and throwing here\n * would turn the error being rendered into a 500 (SPEC 7.6.2).\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n */\nexport function toMetadata(\n meta: Readonly<Record<string, unknown>>,\n): Record<string, string> {\n const metadata: Record<string, string> = {};\n for (const key of Object.keys(meta)) {\n if (!METADATA_KEY_PATTERN.test(key)) {\n continue;\n }\n const value = meta[key];\n if (typeof value === 'string') {\n metadata[key] = value;\n } else if (typeof value === 'bigint') {\n metadata[key] = value.toString();\n } else if (\n value !== undefined &&\n typeof value !== 'function' &&\n typeof value !== 'symbol'\n ) {\n metadata[key] = safeStringify(value);\n }\n }\n return metadata;\n}\n\n/**\n * `BadRequest.FieldViolation.field`: \"a sequence of dot-separated identifiers\"\n * with repeated-field positions in square brackets, so `['items', 0, 'sku']`\n * is `items[0].sku`. An empty path names the whole request and yields no\n * `field` (proto3 JSON omits empty strings).\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n */\nexport function fieldPath(path: ReadonlyArray<string | number>): string {\n let out = '';\n for (const segment of path) {\n if (typeof segment === 'number') {\n out += `[${String(segment)}]`;\n } else {\n out += out === '' ? segment : `.${segment}`;\n }\n }\n return out;\n}\n\n/**\n * `FieldViolation.reason` \"should be at most 63 characters and match\n * `[A-Z][A-Z0-9_]+[A-Z0-9]`\"; validator codes are lower snake case\n * (`invalid_type`), so they are upper-cased. Not validated further.\n */\nexport function fieldViolation(\n issue: ValidationIssue,\n): GoogleApiFieldViolation {\n const field = fieldPath(issue.path);\n return {\n ...(field === '' ? {} : { field }),\n description: issue.message,\n ...(issue.code === undefined ? {} : { reason: issue.code.toUpperCase() }),\n };\n}\n\nconst DELAY_SECONDS_PATTERN = /^[0-9]+$/u;\n\n/**\n * `RetryInfo.retry_delay` from a `Retry-After` header in its delay-seconds\n * form. The HTTP-date form needs the current time to become a duration, and\n * formats are pure, so it is left out.\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-10.2.3\n * @ref https://protobuf.dev/programming-guides/json/ (Duration)\n */\nexport function retryInfo(headers: Headers): GoogleApiRetryInfo | undefined {\n const value = headers.get('retry-after');\n if (value === null || !DELAY_SECONDS_PATTERN.test(value)) {\n return undefined;\n }\n return { '@type': RETRY_INFO_TYPE, retryDelay: `${value}s` };\n}\n\n/** RFC 3986 section 3.1 scheme followed by a colon: an absolute URI. */\nconst ABSOLUTE_URI_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/u;\n\n/**\n * `Help.Link.url` \"must be an absolute URL, including scheme\", so a relative\n * `type` yields no `Help` payload.\n * @ref https://google.aip.dev/193#help\n * @ref https://www.rfc-editor.org/rfc/rfc3986#section-3.1\n */\nexport function help(code: string, url?: string): GoogleApiHelp | undefined {\n if (url === undefined || !ABSOLUTE_URI_PATTERN.test(url)) {\n return undefined;\n }\n return {\n '@type': HELP_TYPE,\n links: [{ description: `Documentation for ${code} errors`, url }],\n };\n}\n","/**\n * `google.rpc.Code` names and the HTTP status each one is derived from\n * (SPEC 7.6.1). The forward mapping (code to HTTP status) is the \"HTTP\n * Mapping\" line on every enum value in `code.proto`; it is many-to-one, so the\n * reverse table below is the one Google's own client library uses when it\n * turns an HTTP error into a status code.\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\n * @ref https://github.com/googleapis/gax-nodejs/blob/main/gax/src/status.ts\n * @packageDocumentation\n */\n\n/** Every value of the `google.rpc.Code` enum, by name. */\nexport type GoogleRpcCode =\n | 'OK'\n | 'CANCELLED'\n | 'UNKNOWN'\n | 'INVALID_ARGUMENT'\n | 'DEADLINE_EXCEEDED'\n | 'NOT_FOUND'\n | 'ALREADY_EXISTS'\n | 'PERMISSION_DENIED'\n | 'RESOURCE_EXHAUSTED'\n | 'FAILED_PRECONDITION'\n | 'ABORTED'\n | 'OUT_OF_RANGE'\n | 'UNIMPLEMENTED'\n | 'INTERNAL'\n | 'UNAVAILABLE'\n | 'DATA_LOSS'\n | 'UNAUTHENTICATED';\n\nconst RPC_CODES: ReadonlySet<string> = new Set<GoogleRpcCode>([\n 'OK',\n 'CANCELLED',\n 'UNKNOWN',\n 'INVALID_ARGUMENT',\n 'DEADLINE_EXCEEDED',\n 'NOT_FOUND',\n 'ALREADY_EXISTS',\n 'PERMISSION_DENIED',\n 'RESOURCE_EXHAUSTED',\n 'FAILED_PRECONDITION',\n 'ABORTED',\n 'OUT_OF_RANGE',\n 'UNIMPLEMENTED',\n 'INTERNAL',\n 'UNAVAILABLE',\n 'DATA_LOSS',\n 'UNAUTHENTICATED',\n]);\n\n/**\n * `HttpCodeToRpcCodeMap` from gax-nodejs, verbatim. Where several codes share\n * an HTTP status the table keeps the one gax picked: `INVALID_ARGUMENT` for\n * 400 (not `FAILED_PRECONDITION` or `OUT_OF_RANGE`), `ABORTED` for 409 (not\n * `ALREADY_EXISTS`), `INTERNAL` for 500 (not `UNKNOWN` or `DATA_LOSS`).\n * @ref https://github.com/googleapis/gax-nodejs/blob/main/gax/src/status.ts\n */\nconst BY_HTTP_STATUS: ReadonlyMap<number, GoogleRpcCode> = new Map<\n number,\n GoogleRpcCode\n>([\n [400, 'INVALID_ARGUMENT'],\n [401, 'UNAUTHENTICATED'],\n [403, 'PERMISSION_DENIED'],\n [404, 'NOT_FOUND'],\n [409, 'ABORTED'],\n [416, 'OUT_OF_RANGE'],\n [429, 'RESOURCE_EXHAUSTED'],\n [499, 'CANCELLED'],\n [501, 'UNIMPLEMENTED'],\n [503, 'UNAVAILABLE'],\n [504, 'DEADLINE_EXCEEDED'],\n]);\n\nconst SUCCESS = 200;\nconst REDIRECT = 300;\nconst CLIENT_ERROR = 400;\nconst SERVER_ERROR = 500;\nconst END = 600;\n\nexport function isGoogleRpcCode(value: string): value is GoogleRpcCode {\n return RPC_CODES.has(value);\n}\n\n/**\n * The `google.rpc.Code` name for an HTTP status, following\n * `rpcCodeFromHttpStatusCode` in gax-nodejs: the table first, then `OK` for\n * any 2xx, `FAILED_PRECONDITION` for any other 4xx, `INTERNAL` for any other\n * 5xx, and `UNKNOWN` for everything else.\n * @ref https://github.com/googleapis/gax-nodejs/blob/main/gax/src/status.ts\n */\nexport function rpcCodeFromStatus(status: number): GoogleRpcCode {\n const mapped = BY_HTTP_STATUS.get(status);\n if (mapped !== undefined) {\n return mapped;\n }\n if (status >= SUCCESS && status < REDIRECT) {\n return 'OK';\n }\n if (status >= CLIENT_ERROR && status < SERVER_ERROR) {\n return 'FAILED_PRECONDITION';\n }\n if (status >= SERVER_ERROR && status < END) {\n return 'INTERNAL';\n }\n return 'UNKNOWN';\n}\n\n/**\n * The `status` member for an entry (SPEC 7.6.1): an explicit per-code option,\n * else the catalog code itself when it already names a `google.rpc.Code`\n * (`NOT_FOUND`, `ALREADY_EXISTS`), else the HTTP-derived default.\n */\nexport function resolveRpcCode(\n code: string,\n status: number,\n overrides?: Readonly<Record<string, GoogleRpcCode>>,\n): GoogleRpcCode {\n const explicit = overrides?.[code];\n if (explicit !== undefined) {\n return explicit;\n }\n if (isGoogleRpcCode(code)) {\n return code;\n }\n return rpcCodeFromStatus(status);\n}\n","/**\n * JSON Schema for the AIP-193 body (SPEC 7.6.3). Every detail payload is a\n * closed object pinned to its `@type`; `details` is an array of the payloads\n * this format can render for the entry, at least one long because\n * `ErrorInfo` is always present. `contains` and `prefixItems` would state that\n * more precisely, but OpenAPI 3.0 has neither, so both dialects use `items`.\n * @ref https://google.aip.dev/193#http11json-representation\n * @ref https://spec.openapis.org/oas/v3.0.3#schema-object\n * @packageDocumentation\n */\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { JsonSchema, SchemaContext, SchemaDialect } from '../context';\nimport type { GoogleRpcCode } from './rpc-code';\n\nimport { constant } from '../schema-helpers';\nimport {\n BAD_REQUEST_TYPE,\n DEBUG_INFO_TYPE,\n ERROR_INFO_TYPE,\n HELP_TYPE,\n METADATA_KEY_PATTERN,\n REQUEST_INFO_TYPE,\n RETRY_INFO_TYPE,\n} from './details';\nimport { resolveRpcCode } from './rpc-code';\n\nexport interface GoogleApiSchemaOptions {\n readonly domain: string;\n readonly rpcCodes: Readonly<Record<string, GoogleRpcCode>> | undefined;\n readonly includeRequestInfo: boolean;\n}\n\nconst STRING: JsonSchema = { type: 'string' };\n\nfunction detail(\n typeUrl: string,\n dialect: SchemaDialect,\n properties: Record<string, JsonSchema>,\n required: ReadonlyArray<string>,\n): JsonSchema {\n return {\n type: 'object',\n required: ['@type', ...required],\n properties: { '@type': constant(typeUrl, dialect), ...properties },\n additionalProperties: false,\n };\n}\n\n/**\n * `metadata` keys follow the `error_details.proto` grammar the renderer\n * enforces. `propertyNames` states it in JSON Schema 2020-12; the OpenAPI\n * 3.0 schema object has neither it nor `patternProperties`, so that dialect\n * types the values only.\n * @ref https://json-schema.org/draft/2020-12/json-schema-core#name-propertynames\n * @ref https://spec.openapis.org/oas/v3.0.3#properties\n */\nfunction metadata(dialect: SchemaDialect): JsonSchema {\n return {\n type: 'object',\n ...(dialect === 'openapi-3.0'\n ? {}\n : { propertyNames: { pattern: METADATA_KEY_PATTERN.source } }),\n additionalProperties: STRING,\n };\n}\n\nfunction errorInfo(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: GoogleApiSchemaOptions,\n): JsonSchema {\n return detail(\n ERROR_INFO_TYPE,\n ctx.dialect,\n {\n reason: constant(definition.code, ctx.dialect),\n domain: constant(options.domain, ctx.dialect),\n metadata: metadata(ctx.dialect),\n },\n ['reason', 'domain'],\n );\n}\n\nfunction badRequest(dialect: SchemaDialect): JsonSchema {\n return detail(\n BAD_REQUEST_TYPE,\n dialect,\n {\n fieldViolations: {\n type: 'array',\n items: {\n type: 'object',\n required: ['description'],\n properties: { field: STRING, description: STRING, reason: STRING },\n additionalProperties: false,\n },\n },\n },\n ['fieldViolations'],\n );\n}\n\nfunction retryInfo(dialect: SchemaDialect): JsonSchema {\n return detail(\n RETRY_INFO_TYPE,\n dialect,\n { retryDelay: { type: 'string', pattern: '^[0-9]+s$' } },\n ['retryDelay'],\n );\n}\n\nfunction requestInfo(dialect: SchemaDialect): JsonSchema {\n return detail(REQUEST_INFO_TYPE, dialect, { requestId: STRING }, [\n 'requestId',\n ]);\n}\n\nfunction debugInfo(dialect: SchemaDialect): JsonSchema {\n return detail(\n DEBUG_INFO_TYPE,\n dialect,\n { stackEntries: { type: 'array', items: STRING } },\n ['stackEntries'],\n );\n}\n\nfunction helpDetail(dialect: SchemaDialect): JsonSchema {\n return detail(\n HELP_TYPE,\n dialect,\n {\n links: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n required: ['description', 'url'],\n properties: {\n description: STRING,\n url: { type: 'string', format: 'uri' },\n },\n additionalProperties: false,\n },\n },\n },\n ['links'],\n );\n}\n\nexport function bodySchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: GoogleApiSchemaOptions,\n validation: boolean,\n): JsonSchema {\n const payloads: Array<JsonSchema> = [errorInfo(definition, ctx, options)];\n if (validation) {\n payloads.push(badRequest(ctx.dialect));\n }\n payloads.push(retryInfo(ctx.dialect));\n if (options.includeRequestInfo) {\n payloads.push(requestInfo(ctx.dialect));\n }\n payloads.push(debugInfo(ctx.dialect), helpDetail(ctx.dialect));\n return {\n type: 'object',\n required: ['error'],\n properties: {\n error: {\n type: 'object',\n required: ['code', 'message', 'status', 'details'],\n properties: {\n code: constant(definition.status, ctx.dialect),\n message: STRING,\n status: constant(\n resolveRpcCode(\n definition.code,\n definition.status,\n options.rpcCodes,\n ),\n ctx.dialect,\n ),\n details: { type: 'array', minItems: 1, items: { anyOf: payloads } },\n },\n additionalProperties: false,\n },\n },\n additionalProperties: false,\n };\n}\n","/**\n * Google API error format (SPEC 7.6): the AIP-193 HTTP/1.1+JSON\n * representation of `google.rpc.Status`, `{ error: { code, message, status,\n * details } }`, with `code` the HTTP status, `status` the `google.rpc.Code`\n * name, and `details` the standard payloads from `error_details.proto`\n * packed as `google.protobuf.Any`. `ErrorInfo` is always present, as AIP-193\n * requires.\n * @ref https://google.aip.dev/193#http11json-representation\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto\n * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto\n * @packageDocumentation\n */\nimport type { BanError } from '../../core/ban-error';\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { ValidationIssue } from '../../validation/issue';\nimport type { JsonSchema, RenderContext, SchemaContext } from '../context';\nimport type { ErrorFormat } from '../types';\nimport type { GoogleApiDetail, GoogleApiErrorInfo } from './details';\nimport type { GoogleRpcCode } from './rpc-code';\n\nimport { VALIDATION_DETAIL } from '../../internal/constants';\nimport { assertMemberName } from '../../internal/member-name';\nimport {\n BAD_REQUEST_TYPE,\n DEBUG_INFO_TYPE,\n ERROR_INFO_TYPE,\n fieldViolation,\n help,\n METADATA_KEY_PATTERN,\n REQUEST_INFO_TYPE,\n retryInfo,\n toMetadata,\n} from './details';\nimport { resolveRpcCode } from './rpc-code';\nimport { bodySchema } from './schema';\n\nexport type {\n GoogleApiBadRequest,\n GoogleApiDebugInfo,\n GoogleApiDetail,\n GoogleApiErrorInfo,\n GoogleApiFieldViolation,\n GoogleApiHelp,\n GoogleApiHelpLink,\n GoogleApiRequestInfo,\n GoogleApiRetryInfo,\n} from './details';\nexport type { GoogleRpcCode } from './rpc-code';\n\nexport interface GoogleApiOptions {\n /**\n * `ErrorInfo.domain`: \"the logical grouping to which the reason belongs\",\n * globally unique, typically the service name (`pubsub.googleapis.com`).\n * Required because AIP-193 requires `ErrorInfo` and a domain has no\n * derivable default.\n * @ref https://google.aip.dev/193#errorinfo\n */\n readonly domain: string;\n /**\n * `status` per catalog code, for entries whose HTTP status maps to the\n * wrong `google.rpc.Code` by default (a 409 that is `ALREADY_EXISTS`, not\n * `ABORTED`). A catalog code that is itself a `google.rpc.Code` name needs\n * no entry here.\n */\n readonly rpcCodes?: Readonly<Record<string, GoogleRpcCode>> | undefined;\n /** Base for the `Help` link; overrides the instance `docsBaseUrl`. */\n readonly helpLinkBaseUrl?: string | undefined;\n /** Key for the trace id inside `ErrorInfo.metadata`. Default `traceId`; `false` disables. */\n readonly traceIdMetadataKey?: string | false | undefined;\n /** Emit `RequestInfo.requestId` with the error id. Default true. */\n readonly includeRequestInfo?: boolean | undefined;\n}\n\n/** The `error` member: AIP-193's `Error.Status`, not `google.rpc.Status`. */\nexport interface GoogleApiStatus {\n /** The HTTP status code. */\n readonly code: number;\n readonly message: string;\n /** The `google.rpc.Code` name. */\n readonly status: GoogleRpcCode;\n readonly details: ReadonlyArray<GoogleApiDetail>;\n}\n\nexport interface GoogleApiBody {\n readonly error: GoogleApiStatus;\n}\n\n/**\n * Google APIs answer errors as plain JSON; there is no dedicated media type.\n * @ref https://google.aip.dev/193#http11json-representation\n */\nexport const GOOGLE_API_CONTENT_TYPE = 'application/json';\n\n/**\n * `metadata` keys written from `ctx.meta` that a `traceIdMetadataKey` of the\n * same name would silently replace: `location` carries the validation target\n * (SPEC 8.2).\n */\nconst RESERVED_METADATA_KEYS: ReadonlySet<string> = new Set(['location']);\n\ninterface Resolved {\n readonly domain: string;\n readonly rpcCodes: Readonly<Record<string, GoogleRpcCode>> | undefined;\n readonly helpLinkBaseUrl: string | undefined;\n readonly traceIdMetadataKey: string | false;\n readonly includeRequestInfo: boolean;\n}\n\nfunction errorInfo(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n): GoogleApiErrorInfo {\n const metadata = toMetadata(ctx.meta);\n if (options.traceIdMetadataKey !== false && ctx.traceId !== undefined) {\n metadata[options.traceIdMetadataKey] = ctx.traceId;\n }\n return {\n '@type': ERROR_INFO_TYPE,\n reason: error.code,\n domain: options.domain,\n ...(Object.keys(metadata).length === 0 ? {} : { metadata }),\n };\n}\n\nfunction helpUrl(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n): string | undefined {\n if (error.type !== undefined) {\n return error.type;\n }\n const base = options.helpLinkBaseUrl ?? ctx.docsBaseUrl;\n return base === undefined ? undefined : `${base}/${error.code}`;\n}\n\n/**\n * Detail order (SPEC 7.6.2): `ErrorInfo`, `BadRequest` (validation only),\n * `RetryInfo`, `RequestInfo`, `DebugInfo`, `Help`. Each type appears at most\n * once, as AIP-193 requires.\n */\nfunction renderBody(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n issues?: ReadonlyArray<ValidationIssue>,\n): GoogleApiBody {\n const details: Array<GoogleApiDetail> = [errorInfo(error, ctx, options)];\n // proto3 JSON omits an empty repeated field and the schema requires\n // `fieldViolations`, so a validation error without issues has no\n // `BadRequest`; `message` still says validation failed.\n if (issues !== undefined && issues.length > 0) {\n details.push({\n '@type': BAD_REQUEST_TYPE,\n fieldViolations: issues.map((issue) => fieldViolation(issue)),\n });\n }\n const retry = retryInfo(error.headers);\n if (retry !== undefined) {\n details.push(retry);\n }\n if (options.includeRequestInfo) {\n details.push({ '@type': REQUEST_INFO_TYPE, requestId: error.id });\n }\n if (ctx.stack !== undefined) {\n details.push({\n '@type': DEBUG_INFO_TYPE,\n stackEntries: ctx.stack.split('\\n'),\n });\n }\n const link = help(error.code, helpUrl(error, ctx, options));\n if (link !== undefined) {\n details.push(link);\n }\n return {\n error: {\n code: error.status,\n message:\n error.detail ??\n (issues === undefined ? error.title : VALIDATION_DETAIL),\n status: resolveRpcCode(error.code, error.status, options.rpcCodes),\n details,\n },\n };\n}\n\n/**\n * AIP-193 Google API errors (SPEC 7.6). Throws `TypeError` when\n * `traceIdMetadataKey` falls outside the `ErrorInfo.metadata` key grammar\n * or names a key the format writes from `ctx.meta`.\n * @ref https://google.aip.dev/193\n */\nexport function googleApi(\n options: GoogleApiOptions,\n): ErrorFormat<GoogleApiBody> {\n const resolved: Resolved = {\n domain: options.domain,\n rpcCodes: options.rpcCodes,\n helpLinkBaseUrl: options.helpLinkBaseUrl,\n traceIdMetadataKey: options.traceIdMetadataKey ?? 'traceId',\n includeRequestInfo: options.includeRequestInfo ?? true,\n };\n if (resolved.traceIdMetadataKey !== false) {\n assertMemberName(\n 'traceIdMetadataKey',\n resolved.traceIdMetadataKey,\n RESERVED_METADATA_KEYS,\n METADATA_KEY_PATTERN,\n );\n }\n const schemaOptions = {\n domain: resolved.domain,\n rpcCodes: resolved.rpcCodes,\n includeRequestInfo: resolved.includeRequestInfo,\n };\n return {\n name: 'google-api',\n contentType: GOOGLE_API_CONTENT_TYPE,\n render(error: BanError, ctx: RenderContext): GoogleApiBody {\n return renderBody(error, ctx, resolved);\n },\n renderValidation(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): GoogleApiBody {\n return renderBody(error, ctx, resolved, issues);\n },\n schema(definition: ResolvedDefinition, ctx: SchemaContext): JsonSchema {\n return bodySchema(definition, ctx, schemaOptions, false);\n },\n validationSchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema {\n return bodySchema(definition, ctx, schemaOptions, true);\n },\n };\n}\n"],"mappings":"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"}
@@ -43,7 +43,9 @@ export interface JsonApiBody {
43
43
  */
44
44
  export declare const JSON_API_CONTENT_TYPE = "application/vnd.api+json";
45
45
  /**
46
- * JSON:API 1.1 error documents (SPEC 7.2).
46
+ * JSON:API 1.1 error documents (SPEC 7.2). Throws `TypeError` when
47
+ * `traceIdMetaKey` names a `meta` key the format writes itself or is not a
48
+ * valid member name.
47
49
  * @ref https://jsonapi.org/format/#errors
48
50
  */
49
51
  export declare function jsonApi(options?: JsonApiOptions): ErrorFormat<JsonApiBody>;
@@ -1 +1 @@
1
- {"version":3,"file":"json-api.d.ts","names":[],"sources":["../../src/formats/json-api/index.ts"],"mappings":";;iBAoBiB;;WAEN;;WAEA,cACH,OAAO,UAAU,KAAK;;WAGnB;;WAEA;;iBAGM;WACN;WACA;;iBAGM;WACN;WACA;WACA;;;;;;iBAOM;WACN;WACA,QAAQ;;WAER;WACA;WACA;WACA;WACA,SAAS;WACT,OAAO,SAAS;;iBAGV;WACN,QAAQ,cAAc;;;;;;qBAOpB;;;;;wBA4KG,QACd,UAAS,iBACR,YAAY"}
1
+ {"version":3,"file":"json-api.d.ts","names":[],"sources":["../../src/formats/json-api/index.ts"],"mappings":";;iBAsBiB;;WAEN;;WAEA,cACH,OAAO,UAAU,KAAK;;WAGnB;;WAEA;;iBAGM;WACN;WACA;;iBAGM;WACN;WACA;WACA;;;;;;iBAOM;WACN;WACA,QAAQ;;WAER;WACA;WACA;WACA;WACA,SAAS;WACT,OAAO,SAAS;;iBAGV;WACN,QAAQ,cAAc;;;;;;qBAOpB;;;;;;;wBAsIG,QACd,UAAS,iBACR,YAAY"}
@@ -1,12 +1,88 @@
1
- import { t as constant } from "../schema-helpers-B4HL9TmU.js";
1
+ import { t as constant } from "../schema-helpers-bwCFve8i.js";
2
+ import { t as assertMemberName } from "../member-name-k4VABr91.js";
2
3
  import { a as pointerFromPath, r as nameFromPath, t as isPointerLocation } from "../issue-B-7iyCBE.js";
3
4
  import { n as readLocation } from "../validation-entries-9R5RK-AZ.js";
5
+ //#region src/formats/json-api/schema.ts
6
+ function errorObjectSchema(definition, ctx, options) {
7
+ return {
8
+ type: "object",
9
+ required: [
10
+ "status",
11
+ "code",
12
+ "title"
13
+ ],
14
+ properties: {
15
+ ...options.includeId ? { id: { type: "string" } } : {},
16
+ links: {
17
+ type: "object",
18
+ properties: {
19
+ type: {
20
+ type: "string",
21
+ format: "uri-reference"
22
+ },
23
+ about: {
24
+ type: "string",
25
+ format: "uri-reference"
26
+ }
27
+ },
28
+ additionalProperties: false
29
+ },
30
+ status: constant(String(definition.status), ctx.dialect),
31
+ code: constant(definition.code, ctx.dialect),
32
+ title: constant(definition.title, ctx.dialect),
33
+ detail: { type: "string" },
34
+ source: {
35
+ type: "object",
36
+ properties: {
37
+ pointer: { type: "string" },
38
+ parameter: { type: "string" },
39
+ header: { type: "string" }
40
+ },
41
+ additionalProperties: false
42
+ },
43
+ meta: { type: "object" }
44
+ },
45
+ additionalProperties: false
46
+ };
47
+ }
48
+ function bodySchema(definition, ctx, options) {
49
+ return {
50
+ type: "object",
51
+ required: ["errors"],
52
+ properties: { errors: {
53
+ type: "array",
54
+ minItems: 1,
55
+ items: errorObjectSchema(definition, ctx, options)
56
+ } },
57
+ additionalProperties: false
58
+ };
59
+ }
60
+ //#endregion
4
61
  //#region src/formats/json-api/index.ts
5
62
  /**
6
63
  * No parameters: the specification allows only `ext` and `profile`.
7
64
  * @ref https://jsonapi.org/format/#content-negotiation-servers
8
65
  */
9
66
  const JSON_API_CONTENT_TYPE = "application/vnd.api+json";
67
+ /**
68
+ * Member names: at least one character, starting and ending with a letter,
69
+ * digit, or a code point at U+0080 or above, with `-`, `_`, and space allowed
70
+ * in between.
71
+ * @ref https://jsonapi.org/format/#document-member-names
72
+ */
73
+ const MEMBER_NAME_PATTERN = /^[A-Za-z0-9\u0080-\u{10FFFF}](?:[A-Za-z0-9\u0080-\u{10FFFF} _-]*[A-Za-z0-9\u0080-\u{10FFFF}])?$/u;
74
+ /**
75
+ * `meta` keys this format writes itself (7.2.1, 7.2.2); a `traceIdMetaKey`
76
+ * with one of these names would be overwritten or overwrite it.
77
+ */
78
+ const RESERVED_META_KEYS = /* @__PURE__ */ new Set([
79
+ "stack",
80
+ "location",
81
+ "name",
82
+ "code",
83
+ "expected",
84
+ "received"
85
+ ]);
10
86
  function links(error, ctx, options) {
11
87
  const base = options.typeLinkBaseUrl ?? ctx.docsBaseUrl;
12
88
  const type = error.type ?? (base === void 0 ? void 0 : `${base}/${error.code}`);
@@ -58,62 +134,10 @@ function issueMeta(issue, location, shared) {
58
134
  if (issue.received !== void 0) meta["received"] = issue.received;
59
135
  return meta;
60
136
  }
61
- function errorObjectSchema(definition, ctx, options) {
62
- return {
63
- type: "object",
64
- required: [
65
- "status",
66
- "code",
67
- "title"
68
- ],
69
- properties: {
70
- ...options.includeId ? { id: { type: "string" } } : {},
71
- links: {
72
- type: "object",
73
- properties: {
74
- type: {
75
- type: "string",
76
- format: "uri-reference"
77
- },
78
- about: {
79
- type: "string",
80
- format: "uri-reference"
81
- }
82
- },
83
- additionalProperties: false
84
- },
85
- status: constant(String(definition.status), ctx.dialect),
86
- code: constant(definition.code, ctx.dialect),
87
- title: constant(definition.title, ctx.dialect),
88
- detail: { type: "string" },
89
- source: {
90
- type: "object",
91
- properties: {
92
- pointer: { type: "string" },
93
- parameter: { type: "string" },
94
- header: { type: "string" }
95
- },
96
- additionalProperties: false
97
- },
98
- meta: { type: "object" }
99
- },
100
- additionalProperties: false
101
- };
102
- }
103
- function bodySchema(definition, ctx, options) {
104
- return {
105
- type: "object",
106
- required: ["errors"],
107
- properties: { errors: {
108
- type: "array",
109
- minItems: 1,
110
- items: errorObjectSchema(definition, ctx, options)
111
- } },
112
- additionalProperties: false
113
- };
114
- }
115
137
  /**
116
- * JSON:API 1.1 error documents (SPEC 7.2).
138
+ * JSON:API 1.1 error documents (SPEC 7.2). Throws `TypeError` when
139
+ * `traceIdMetaKey` names a `meta` key the format writes itself or is not a
140
+ * valid member name.
117
141
  * @ref https://jsonapi.org/format/#errors
118
142
  */
119
143
  function jsonApi(options = {}) {
@@ -123,6 +147,7 @@ function jsonApi(options = {}) {
123
147
  includeId: options.includeId ?? true,
124
148
  traceIdMetaKey: options.traceIdMetaKey ?? "traceId"
125
149
  };
150
+ if (resolved.traceIdMetaKey !== false) assertMemberName("traceIdMetaKey", resolved.traceIdMetaKey, RESERVED_META_KEYS, MEMBER_NAME_PATTERN);
126
151
  return {
127
152
  name: "json-api",
128
153
  contentType: JSON_API_CONTENT_TYPE,
@@ -132,6 +157,10 @@ function jsonApi(options = {}) {
132
157
  renderValidation(error, issues, ctx) {
133
158
  const location = readLocation(ctx.meta);
134
159
  const shared = sharedMeta(ctx, resolved);
160
+ if (issues.length === 0) return { errors: [errorObject(error, ctx, resolved, error.detail ?? "Request validation failed", void 0, {
161
+ ...shared,
162
+ location
163
+ })] };
135
164
  return { errors: issues.map((issue) => errorObject(error, ctx, resolved, issue.message, issueSource(issue, location), issueMeta(issue, location, shared))) };
136
165
  },
137
166
  schema(definition, ctx) {
@@ -1 +1 @@
1
- {"version":3,"file":"json-api.js","names":[],"sources":["../../src/formats/json-api/index.ts"],"sourcesContent":["/**\n * JSON:API 1.1 error document format (SPEC 7.2): `{ errors: [ErrorObject] }`\n * with one error object per validation issue.\n * @ref https://jsonapi.org/format/#errors\n * @packageDocumentation\n */\nimport type { BanError } from '../../core/ban-error';\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { IssueLocation, ValidationIssue } from '../../validation/issue';\nimport type { JsonSchema, RenderContext, SchemaContext } from '../context';\nimport type { ErrorFormat } from '../types';\n\nimport {\n isPointerLocation,\n nameFromPath,\n pointerFromPath,\n} from '../../validation/issue';\nimport { constant } from '../schema-helpers';\nimport { readLocation } from '../validation-entries';\n\nexport interface JsonApiOptions {\n /** Base for `links.type`; overrides the instance `docsBaseUrl`. */\n readonly typeLinkBaseUrl?: string | undefined;\n /** Produces `links.about`, a URL for this occurrence. */\n readonly aboutLink?:\n | ((error: BanError, ctx: RenderContext) => string | undefined)\n | undefined;\n /** Emit `id`. Default true. */\n readonly includeId?: boolean | undefined;\n /** Key for the trace id inside `meta`. Default `traceId`; `false` disables. */\n readonly traceIdMetaKey?: string | false | undefined;\n}\n\nexport interface JsonApiLinks {\n readonly type?: string;\n readonly about?: string;\n}\n\nexport interface JsonApiSource {\n readonly pointer?: string;\n readonly parameter?: string;\n readonly header?: string;\n}\n\n/**\n * A JSON:API 1.1 error object.\n * @ref https://jsonapi.org/format/#error-objects\n */\nexport interface JsonApiErrorObject {\n readonly id?: string;\n readonly links?: JsonApiLinks;\n /** The HTTP status as a string, as the specification requires. */\n readonly status: string;\n readonly code: string;\n readonly title: string;\n readonly detail?: string;\n readonly source?: JsonApiSource;\n readonly meta?: Readonly<Record<string, unknown>>;\n}\n\nexport interface JsonApiBody {\n readonly errors: ReadonlyArray<JsonApiErrorObject>;\n}\n\n/**\n * No parameters: the specification allows only `ext` and `profile`.\n * @ref https://jsonapi.org/format/#content-negotiation-servers\n */\nexport const JSON_API_CONTENT_TYPE = 'application/vnd.api+json';\n\ninterface Resolved {\n readonly typeLinkBaseUrl: string | undefined;\n readonly aboutLink:\n | ((error: BanError, ctx: RenderContext) => string | undefined)\n | undefined;\n readonly includeId: boolean;\n readonly traceIdMetaKey: string | false;\n}\n\nfunction links(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n): JsonApiLinks | undefined {\n const base = options.typeLinkBaseUrl ?? ctx.docsBaseUrl;\n const type =\n error.type ?? (base === undefined ? undefined : `${base}/${error.code}`);\n const about = options.aboutLink?.(error, ctx);\n if (type === undefined && about === undefined) {\n return undefined;\n }\n return {\n ...(type === undefined ? {} : { type }),\n ...(about === undefined ? {} : { about }),\n };\n}\n\nfunction sharedMeta(\n ctx: RenderContext,\n options: Resolved,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { ...ctx.meta };\n if (options.traceIdMetaKey !== false && ctx.traceId !== undefined) {\n meta[options.traceIdMetaKey] = ctx.traceId;\n }\n if (ctx.stack !== undefined) {\n meta['stack'] = ctx.stack;\n }\n return meta;\n}\n\nfunction errorObject(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n detail: string | undefined,\n source: JsonApiSource | undefined,\n meta: Record<string, unknown>,\n): JsonApiErrorObject {\n const linkMembers = links(error, ctx, options);\n return {\n ...(options.includeId ? { id: error.id } : {}),\n ...(linkMembers === undefined ? {} : { links: linkMembers }),\n status: String(error.status),\n code: error.code,\n title: error.title,\n ...(detail === undefined ? {} : { detail }),\n ...(source === undefined ? {} : { source }),\n ...(Object.keys(meta).length === 0 ? {} : { meta }),\n };\n}\n\n/**\n * `source.pointer` for bodies, `source.parameter` for query parameters,\n * `source.header` for headers. JSON:API has no member for path or cookie\n * parameters, so those are described in `meta` instead.\n * @ref https://jsonapi.org/format/#error-objects\n */\nfunction issueSource(\n issue: ValidationIssue,\n location: IssueLocation,\n): JsonApiSource | undefined {\n if (isPointerLocation(location)) {\n return { pointer: pointerFromPath(issue.path) };\n }\n if (location === 'query') {\n return { parameter: nameFromPath(issue.path) };\n }\n if (location === 'header') {\n return { header: nameFromPath(issue.path) };\n }\n return undefined;\n}\n\nfunction issueMeta(\n issue: ValidationIssue,\n location: IssueLocation,\n shared: Record<string, unknown>,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { ...shared, location };\n if (location === 'param' || location === 'cookie') {\n meta['name'] = nameFromPath(issue.path);\n }\n if (issue.code !== undefined) {\n meta['code'] = issue.code;\n }\n if (issue.expected !== undefined) {\n meta['expected'] = issue.expected;\n }\n if (issue.received !== undefined) {\n meta['received'] = issue.received;\n }\n return meta;\n}\n\nfunction errorObjectSchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: Resolved,\n): JsonSchema {\n const properties: Record<string, JsonSchema> = {\n ...(options.includeId ? { id: { type: 'string' } } : {}),\n // Links are URI references (RFC 3986 section 4.1), the same class RFC\n // 9457 uses for `type`, so a relative `about` link conforms too.\n // @ref https://jsonapi.org/format/#document-links\n // @ref https://www.rfc-editor.org/rfc/rfc3986#section-4.1\n links: {\n type: 'object',\n properties: {\n type: { type: 'string', format: 'uri-reference' },\n about: { type: 'string', format: 'uri-reference' },\n },\n additionalProperties: false,\n },\n status: constant(String(definition.status), ctx.dialect),\n code: constant(definition.code, ctx.dialect),\n title: constant(definition.title, ctx.dialect),\n detail: { type: 'string' },\n source: {\n type: 'object',\n properties: {\n pointer: { type: 'string' },\n parameter: { type: 'string' },\n header: { type: 'string' },\n },\n additionalProperties: false,\n },\n meta: { type: 'object' },\n };\n return {\n type: 'object',\n required: ['status', 'code', 'title'],\n properties,\n additionalProperties: false,\n };\n}\n\nfunction bodySchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: Resolved,\n): JsonSchema {\n return {\n type: 'object',\n required: ['errors'],\n properties: {\n errors: {\n type: 'array',\n minItems: 1,\n items: errorObjectSchema(definition, ctx, options),\n },\n },\n additionalProperties: false,\n };\n}\n\n/**\n * JSON:API 1.1 error documents (SPEC 7.2).\n * @ref https://jsonapi.org/format/#errors\n */\nexport function jsonApi(\n options: JsonApiOptions = {},\n): ErrorFormat<JsonApiBody> {\n const resolved: Resolved = {\n typeLinkBaseUrl: options.typeLinkBaseUrl,\n aboutLink: options.aboutLink,\n includeId: options.includeId ?? true,\n traceIdMetaKey: options.traceIdMetaKey ?? 'traceId',\n };\n return {\n name: 'json-api',\n contentType: JSON_API_CONTENT_TYPE,\n render(error: BanError, ctx: RenderContext): JsonApiBody {\n return {\n errors: [\n errorObject(\n error,\n ctx,\n resolved,\n error.detail,\n undefined,\n sharedMeta(ctx, resolved),\n ),\n ],\n };\n },\n renderValidation(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): JsonApiBody {\n const location = readLocation(ctx.meta);\n const shared = sharedMeta(ctx, resolved);\n return {\n errors: issues.map((issue) =>\n errorObject(\n error,\n ctx,\n resolved,\n issue.message,\n issueSource(issue, location),\n issueMeta(issue, location, shared),\n ),\n ),\n };\n },\n schema(definition: ResolvedDefinition, ctx: SchemaContext): JsonSchema {\n return bodySchema(definition, ctx, resolved);\n },\n validationSchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema {\n return bodySchema(definition, ctx, resolved);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"json-api.js","names":[],"sources":["../../src/formats/json-api/schema.ts","../../src/formats/json-api/index.ts"],"sourcesContent":["/**\n * JSON Schema for the JSON:API error document (SPEC 7.2.3): a closed\n * `{ errors: [ErrorObject, ...] }` whose error objects pin `status`, `code`,\n * and `title` to the definition and are closed too.\n * @ref https://jsonapi.org/format/#error-objects\n * @packageDocumentation\n */\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { JsonSchema, SchemaContext } from '../context';\n\nimport { constant } from '../schema-helpers';\n\nexport interface JsonApiSchemaOptions {\n readonly includeId: boolean;\n}\n\nfunction errorObjectSchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: JsonApiSchemaOptions,\n): JsonSchema {\n const properties: Record<string, JsonSchema> = {\n ...(options.includeId ? { id: { type: 'string' } } : {}),\n // Links are URI references (RFC 3986 section 4.1), the same class RFC\n // 9457 uses for `type`, so a relative `about` link conforms too.\n // @ref https://jsonapi.org/format/#document-links\n // @ref https://www.rfc-editor.org/rfc/rfc3986#section-4.1\n links: {\n type: 'object',\n properties: {\n type: { type: 'string', format: 'uri-reference' },\n about: { type: 'string', format: 'uri-reference' },\n },\n additionalProperties: false,\n },\n status: constant(String(definition.status), ctx.dialect),\n code: constant(definition.code, ctx.dialect),\n title: constant(definition.title, ctx.dialect),\n detail: { type: 'string' },\n source: {\n type: 'object',\n properties: {\n pointer: { type: 'string' },\n parameter: { type: 'string' },\n header: { type: 'string' },\n },\n additionalProperties: false,\n },\n meta: { type: 'object' },\n };\n return {\n type: 'object',\n required: ['status', 'code', 'title'],\n properties,\n additionalProperties: false,\n };\n}\n\nexport function bodySchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n options: JsonApiSchemaOptions,\n): JsonSchema {\n return {\n type: 'object',\n required: ['errors'],\n properties: {\n errors: {\n type: 'array',\n minItems: 1,\n items: errorObjectSchema(definition, ctx, options),\n },\n },\n additionalProperties: false,\n };\n}\n","/**\n * JSON:API 1.1 error document format (SPEC 7.2): `{ errors: [ErrorObject] }`\n * with one error object per validation issue.\n * @ref https://jsonapi.org/format/#errors\n * @packageDocumentation\n */\nimport type { BanError } from '../../core/ban-error';\nimport type { ResolvedDefinition } from '../../core/definition';\nimport type { IssueLocation, ValidationIssue } from '../../validation/issue';\nimport type { JsonSchema, RenderContext, SchemaContext } from '../context';\nimport type { ErrorFormat } from '../types';\n\nimport { VALIDATION_DETAIL } from '../../internal/constants';\nimport { assertMemberName } from '../../internal/member-name';\nimport {\n isPointerLocation,\n nameFromPath,\n pointerFromPath,\n} from '../../validation/issue';\nimport { readLocation } from '../validation-entries';\nimport { bodySchema } from './schema';\n\nexport interface JsonApiOptions {\n /** Base for `links.type`; overrides the instance `docsBaseUrl`. */\n readonly typeLinkBaseUrl?: string | undefined;\n /** Produces `links.about`, a URL for this occurrence. */\n readonly aboutLink?:\n | ((error: BanError, ctx: RenderContext) => string | undefined)\n | undefined;\n /** Emit `id`. Default true. */\n readonly includeId?: boolean | undefined;\n /** Key for the trace id inside `meta`. Default `traceId`; `false` disables. */\n readonly traceIdMetaKey?: string | false | undefined;\n}\n\nexport interface JsonApiLinks {\n readonly type?: string;\n readonly about?: string;\n}\n\nexport interface JsonApiSource {\n readonly pointer?: string;\n readonly parameter?: string;\n readonly header?: string;\n}\n\n/**\n * A JSON:API 1.1 error object.\n * @ref https://jsonapi.org/format/#error-objects\n */\nexport interface JsonApiErrorObject {\n readonly id?: string;\n readonly links?: JsonApiLinks;\n /** The HTTP status as a string, as the specification requires. */\n readonly status: string;\n readonly code: string;\n readonly title: string;\n readonly detail?: string;\n readonly source?: JsonApiSource;\n readonly meta?: Readonly<Record<string, unknown>>;\n}\n\nexport interface JsonApiBody {\n readonly errors: ReadonlyArray<JsonApiErrorObject>;\n}\n\n/**\n * No parameters: the specification allows only `ext` and `profile`.\n * @ref https://jsonapi.org/format/#content-negotiation-servers\n */\nexport const JSON_API_CONTENT_TYPE = 'application/vnd.api+json';\n\n/**\n * Member names: at least one character, starting and ending with a letter,\n * digit, or a code point at U+0080 or above, with `-`, `_`, and space allowed\n * in between.\n * @ref https://jsonapi.org/format/#document-member-names\n */\nconst MEMBER_NAME_PATTERN =\n /^[A-Za-z0-9\\u0080-\\u{10FFFF}](?:[A-Za-z0-9\\u0080-\\u{10FFFF} _-]*[A-Za-z0-9\\u0080-\\u{10FFFF}])?$/u;\n/**\n * `meta` keys this format writes itself (7.2.1, 7.2.2); a `traceIdMetaKey`\n * with one of these names would be overwritten or overwrite it.\n */\nconst RESERVED_META_KEYS: ReadonlySet<string> = new Set([\n 'stack',\n 'location',\n 'name',\n 'code',\n 'expected',\n 'received',\n]);\n\ninterface Resolved {\n readonly typeLinkBaseUrl: string | undefined;\n readonly aboutLink:\n | ((error: BanError, ctx: RenderContext) => string | undefined)\n | undefined;\n readonly includeId: boolean;\n readonly traceIdMetaKey: string | false;\n}\n\nfunction links(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n): JsonApiLinks | undefined {\n const base = options.typeLinkBaseUrl ?? ctx.docsBaseUrl;\n const type =\n error.type ?? (base === undefined ? undefined : `${base}/${error.code}`);\n const about = options.aboutLink?.(error, ctx);\n if (type === undefined && about === undefined) {\n return undefined;\n }\n return {\n ...(type === undefined ? {} : { type }),\n ...(about === undefined ? {} : { about }),\n };\n}\n\nfunction sharedMeta(\n ctx: RenderContext,\n options: Resolved,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { ...ctx.meta };\n if (options.traceIdMetaKey !== false && ctx.traceId !== undefined) {\n meta[options.traceIdMetaKey] = ctx.traceId;\n }\n if (ctx.stack !== undefined) {\n meta['stack'] = ctx.stack;\n }\n return meta;\n}\n\nfunction errorObject(\n error: BanError,\n ctx: RenderContext,\n options: Resolved,\n detail: string | undefined,\n source: JsonApiSource | undefined,\n meta: Record<string, unknown>,\n): JsonApiErrorObject {\n const linkMembers = links(error, ctx, options);\n return {\n ...(options.includeId ? { id: error.id } : {}),\n ...(linkMembers === undefined ? {} : { links: linkMembers }),\n status: String(error.status),\n code: error.code,\n title: error.title,\n ...(detail === undefined ? {} : { detail }),\n ...(source === undefined ? {} : { source }),\n ...(Object.keys(meta).length === 0 ? {} : { meta }),\n };\n}\n\n/**\n * `source.pointer` for bodies, `source.parameter` for query parameters,\n * `source.header` for headers. JSON:API has no member for path or cookie\n * parameters, so those are described in `meta` instead.\n * @ref https://jsonapi.org/format/#error-objects\n */\nfunction issueSource(\n issue: ValidationIssue,\n location: IssueLocation,\n): JsonApiSource | undefined {\n if (isPointerLocation(location)) {\n return { pointer: pointerFromPath(issue.path) };\n }\n if (location === 'query') {\n return { parameter: nameFromPath(issue.path) };\n }\n if (location === 'header') {\n return { header: nameFromPath(issue.path) };\n }\n return undefined;\n}\n\nfunction issueMeta(\n issue: ValidationIssue,\n location: IssueLocation,\n shared: Record<string, unknown>,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { ...shared, location };\n if (location === 'param' || location === 'cookie') {\n meta['name'] = nameFromPath(issue.path);\n }\n if (issue.code !== undefined) {\n meta['code'] = issue.code;\n }\n if (issue.expected !== undefined) {\n meta['expected'] = issue.expected;\n }\n if (issue.received !== undefined) {\n meta['received'] = issue.received;\n }\n return meta;\n}\n\n/**\n * JSON:API 1.1 error documents (SPEC 7.2). Throws `TypeError` when\n * `traceIdMetaKey` names a `meta` key the format writes itself or is not a\n * valid member name.\n * @ref https://jsonapi.org/format/#errors\n */\nexport function jsonApi(\n options: JsonApiOptions = {},\n): ErrorFormat<JsonApiBody> {\n const resolved: Resolved = {\n typeLinkBaseUrl: options.typeLinkBaseUrl,\n aboutLink: options.aboutLink,\n includeId: options.includeId ?? true,\n traceIdMetaKey: options.traceIdMetaKey ?? 'traceId',\n };\n if (resolved.traceIdMetaKey !== false) {\n assertMemberName(\n 'traceIdMetaKey',\n resolved.traceIdMetaKey,\n RESERVED_META_KEYS,\n MEMBER_NAME_PATTERN,\n );\n }\n return {\n name: 'json-api',\n contentType: JSON_API_CONTENT_TYPE,\n render(error: BanError, ctx: RenderContext): JsonApiBody {\n return {\n errors: [\n errorObject(\n error,\n ctx,\n resolved,\n error.detail,\n undefined,\n sharedMeta(ctx, resolved),\n ),\n ],\n };\n },\n renderValidation(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): JsonApiBody {\n const location = readLocation(ctx.meta);\n const shared = sharedMeta(ctx, resolved);\n if (issues.length === 0) {\n // `errors` must hold at least one object (7.2.3), so a validation\n // error without issues renders the summary the detail would carry.\n return {\n errors: [\n errorObject(\n error,\n ctx,\n resolved,\n error.detail ?? VALIDATION_DETAIL,\n undefined,\n { ...shared, location },\n ),\n ],\n };\n }\n return {\n errors: issues.map((issue) =>\n errorObject(\n error,\n ctx,\n resolved,\n issue.message,\n issueSource(issue, location),\n issueMeta(issue, location, shared),\n ),\n ),\n };\n },\n schema(definition: ResolvedDefinition, ctx: SchemaContext): JsonSchema {\n return bodySchema(definition, ctx, resolved);\n },\n validationSchema(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema {\n return bodySchema(definition, ctx, resolved);\n },\n };\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
- import "../constants-rUzQOaX_.js";
2
- import { t as constant } from "../schema-helpers-B4HL9TmU.js";
1
+ import { t as constant } from "../schema-helpers-bwCFve8i.js";
3
2
  import { n as readLocation, r as toValidationEntries, t as VALIDATION_ENTRIES_SCHEMA } from "../validation-entries-9R5RK-AZ.js";
4
3
  //#region src/formats/plain/index.ts
5
4
  const PLAIN_CONTENT_TYPE = "application/json";