hono-ban 1.0.0 → 1.2.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,58 @@
1
1
  # hono-ban
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`4a17ce0`](https://github.com/ali-issa/hono-ban/commit/4a17ce0676177c9ceff6f877731ab8f7ce513e2b) Thanks [@ali-issa](https://github.com/ali-issa)! - Add `hono-ban/postgresql`: `postgresMapper(options?)` turns a Postgres driver error into a catalog
8
+ entry by SQLSTATE with constant client-facing text, for `createBan({ map })` or
9
+ `ban.onError({ map })`.
10
+
11
+ - Recognizes the errors of node-postgres, PGlite, postgres.js, Neon, and Bun, directly or through
12
+ the `cause` of an ORM wrapper such as Drizzle's `DrizzleQueryError` (also Sequelize 6 `original`
13
+ and Objection `nativeError`). Nothing from the driver's `message`, `detail`, or `hint` reaches
14
+ `detail`, `meta`, or `headers`; the driver error stays on the report.
15
+ - Built-in table: unique, exclusion, foreign key, and restrict violations are 409; not-null, check,
16
+ and data exceptions are 422; serialization failures, deadlocks, `NOWAIT` locks, read-only
17
+ replicas, and the connection, resource, and operator classes are 503, with `Retry-After` when
18
+ `retryAfter` is set. Codes with no row (syntax, privilege, `RAISE`, statement timeout, commit
19
+ outcome unknown) fall through to the handler's constant 500 with `handled: false`.
20
+ - `constraints`, `columns`, and `codes` override the entry, detail, meta, and headers per constraint
21
+ name, `table.column`, SQLSTATE, or class; a constraint or column can instead map to a validation
22
+ error with one issue at a path.
23
+ - `findPostgresError(value)` and `readPostgresFields(error)` are exported for `onReport` sinks.
24
+
25
+ ## 1.1.0
26
+
27
+ ### Minor Changes
28
+
29
+ - [`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.
30
+
31
+ - `problemDetails()` throws `TypeError` when `traceIdMember` names a reserved member (`status`,
32
+ `id`, `errors`, ...) or is not an RFC 9457 extension member name; `jsonApi()` rejects a
33
+ `traceIdMetaKey` the format writes itself (`stack`, `location`, `code`, ...) or that is not a
34
+ JSON:API member name; `googleApi()` rejects a `traceIdMetadataKey` outside the
35
+ `ErrorInfo.metadata` key grammar. Previously `traceIdMember: 'status'` replaced the numeric status
36
+ with the trace id.
37
+ - `ban.onError()` throws `TypeError` when `errorIdHeader`, `requestIdHeader`, or `headers` holds a
38
+ name or value `Headers` rejects. Previously the failure surfaced inside the handler's last-resort
39
+ fallback and the handler rejected.
40
+ - `googleApi()` drops `meta` keys that do not match `[a-z][a-zA-Z0-9-_]+` or exceed 64 characters,
41
+ as `error_details.proto` requires for `ErrorInfo.metadata`, and the 2020-12 schema states the rule
42
+ with `propertyNames`. Keys such as `order.id` or `UserId` were previously sent as-is.
43
+
44
+ ### Patch Changes
45
+
46
+ - [`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.
47
+
48
+ - `jsonApi()` renders one summary error object for `ban.validation([])` instead of an empty `errors`
49
+ array its schema rejects; `googleApi()` omits `BadRequest` when there are no issues instead of
50
+ sending an empty `fieldViolations`.
51
+ - `assertFormatConformance` from `hono-ban/testing` now also renders a validation error with no
52
+ issues per location.
53
+ - `ban.onError()` gains a third fallback tier: when the header merge itself throws, the response
54
+ still carries `Content-Type`, `Cache-Control: no-store`, and the error id header.
55
+
3
56
  ## 1.0.0
4
57
 
5
58
  ### 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
@@ -78,6 +90,55 @@ throw ban.ORDER_CONFLICT({
78
90
  });
79
91
  ```
80
92
 
93
+ ### Database errors
94
+
95
+ `hono-ban/postgresql` maps a Postgres driver error to a catalog entry by SQLSTATE with constant
96
+ client-facing text. It recognizes the errors of node-postgres, postgres.js, PGlite, Neon, and Bun,
97
+ thrown directly or wrapped by Drizzle, Kysely, TypeORM, MikroORM, Slonik, or Sequelize:
98
+
99
+ ```ts
100
+ import { postgresMapper } from 'hono-ban/postgresql';
101
+
102
+ const ban = createBan({
103
+ map: postgresMapper({
104
+ constraints: {
105
+ users_email_key: { issue: { path: ['email'], message: 'That email is already registered' } },
106
+ orders_customer_id_fkey: 'The customer does not exist',
107
+ },
108
+ columns: { 'users.email': { issue: { path: ['email'], message: 'Email is required' } } },
109
+ codes: { '42501': { key: 'FORBIDDEN' } },
110
+ retryAfter: 1,
111
+ }),
112
+ });
113
+ ```
114
+
115
+ A unique violation is a 409, a not-null or check violation a 422, a serialization failure or a
116
+ deadlock a 503 with `Retry-After`. `constraints` and `columns` refine the wording per constraint
117
+ name or `table.column`, or turn the violation into a validation error with one issue at a path, the
118
+ shape a form already handles; `codes` overrides the entry per SQLSTATE or class. Nothing from the
119
+ driver's message, detail, or hint reaches a body: the driver error stays on the report as
120
+ `report.error.cause`, and `findPostgresError(report.cause)` reads it out of any wrapper. Codes with
121
+ no row, such as a syntax error, a missing table, or a `RAISE EXCEPTION`, fall through to the
122
+ constant 500 with `handled: false`. The full table and the precedence rules are in SPEC section
123
+ 6.10.
124
+
125
+ To put an application-authored `RAISE` message on the wire, compose your own `map`:
126
+
127
+ ```ts
128
+ import { findPostgresError, postgresMapper } from 'hono-ban/postgresql';
129
+
130
+ const postgres = postgresMapper();
131
+ const ban = createBan({
132
+ map: (thrown, ban) => {
133
+ const error = findPostgresError(thrown);
134
+ if (error?.code === 'P0001' && error instanceof Error) {
135
+ return ban.badRequest(error.message, { cause: thrown });
136
+ }
137
+ return postgres(thrown, ban);
138
+ },
139
+ });
140
+ ```
141
+
81
142
  ### Headers some statuses require
82
143
 
83
144
  HTTP makes the application, not the library, responsible for a few headers: a 401 MUST carry
@@ -135,7 +196,9 @@ Content-Type: application/vnd.api+json
135
196
 
136
197
  `googleApi()` renders the AIP-193 shape Google's APIs use. It needs the `ErrorInfo.domain` AIP-193
137
198
  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.
199
+ status, and `rpcCodes` overrides it per code. `meta` becomes `ErrorInfo.metadata`, whose keys must
200
+ match `[a-z][a-zA-Z0-9-_]+` and stay under 64 characters (`error_details.proto`); a key outside that
201
+ grammar (`order.id`, `UserId`) is dropped rather than sent.
139
202
 
140
203
  ```ts
141
204
  import { googleApi } from 'hono-ban/formats/google-api';
@@ -305,7 +368,8 @@ app.onError(
305
368
  );
306
369
  ```
307
370
 
308
- Every response carries `X-Error-Id`; the same id is in the body and in the report.
371
+ Every response carries `X-Error-Id`; the same id is in the report and, for every built-in format
372
+ except Stripe (whose shape has no member for it), in the body.
309
373
 
310
374
  ### Localize or redact
311
375
 
@@ -333,12 +397,12 @@ app.onError(
333
397
  | `hono-ban/zod`, `hono-ban/valibot`, `hono-ban/standard-schema` | validator hooks and issue converters |
334
398
  | `hono-ban/openapi` | `errorResponses`, `errorResponse`, `errorSchema`, `validationResponse`, `validationSchema` |
335
399
  | `hono-ban/otel` | `traceIdFromOtel` |
400
+ | `hono-ban/postgresql` | `postgresMapper`, `findPostgresError`, `readPostgresFields` |
336
401
  | `hono-ban/testing` | `expectBanError`, `renderError`, `assertFormatConformance` |
337
402
 
338
- Database and driver error translation is not part of the core. Map driver errors to catalog entries
339
- in `createBan({ map })` with constant client-facing messages, never the driver's own text (a
340
- PostgreSQL `DETAIL` line can contain the failing row). A companion package for Postgres SQLSTATE
341
- mapping is planned and will be linked here when it ships.
403
+ Other databases go through `createBan({ map })` the same way `hono-ban/postgresql` does: catalog
404
+ entries with constant client-facing messages, never the driver's own text (a driver's detail line
405
+ can contain the failing row).
342
406
 
343
407
  ## Documentation
344
408
 
@@ -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,5 +1,6 @@
1
1
  import { n as safeStringify } from "../json-Bjfm-jxl.js";
2
2
  import "../constants-rUzQOaX_.js";
3
+ import { t as assertMemberName } from "../member-name-Bn3zp8gx.js";
3
4
  import { t as constant } from "../schema-helpers-B4HL9TmU.js";
4
5
  //#region src/formats/google-api/details.ts
5
6
  /** `google.protobuf.Any` type URLs: the `type.googleapis.com/` prefix plus the full message name. */
@@ -10,14 +11,25 @@ const REQUEST_INFO_TYPE = "type.googleapis.com/google.rpc.RequestInfo";
10
11
  const DEBUG_INFO_TYPE = "type.googleapis.com/google.rpc.DebugInfo";
11
12
  const HELP_TYPE = "type.googleapis.com/google.rpc.Help";
12
13
  /**
14
+ * `ErrorInfo.metadata` keys "must match a regular expression of
15
+ * `[a-z][a-zA-Z0-9-_]+`" and "must be limited to 64 characters in length"
16
+ * (the comment on the field in `error_details.proto`).
17
+ * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
18
+ */
19
+ const METADATA_KEY_PATTERN = /^[a-z][a-zA-Z0-9_-]{1,63}$/u;
20
+ /**
13
21
  * `ErrorInfo.metadata` is `map<string, string>`, so every value becomes a
14
22
  * string: strings as they are, everything else as JSON. Values JSON cannot
15
- * represent (`undefined`, functions, symbols) are dropped.
23
+ * represent (`undefined`, functions, symbols) are dropped, and so is every
24
+ * key outside `METADATA_KEY_PATTERN` (`order.id`, `UserId`): a key the
25
+ * grammar rejects would make the body fail the schema, and throwing here
26
+ * would turn the error being rendered into a 500 (SPEC 7.6.2).
16
27
  * @ref https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
17
28
  */
18
29
  function toMetadata(meta) {
19
30
  const metadata = {};
20
31
  for (const key of Object.keys(meta)) {
32
+ if (!METADATA_KEY_PATTERN.test(key)) continue;
21
33
  const value = meta[key];
22
34
  if (typeof value === "string") metadata[key] = value;
23
35
  else if (typeof value === "bigint") metadata[key] = value.toString();
@@ -174,14 +186,26 @@ function detail(typeUrl, dialect, properties, required) {
174
186
  additionalProperties: false
175
187
  };
176
188
  }
189
+ /**
190
+ * `metadata` keys follow the `error_details.proto` grammar the renderer
191
+ * enforces. `propertyNames` states it in JSON Schema 2020-12; the OpenAPI
192
+ * 3.0 schema object has neither it nor `patternProperties`, so that dialect
193
+ * types the values only.
194
+ * @ref https://json-schema.org/draft/2020-12/json-schema-core#name-propertynames
195
+ * @ref https://spec.openapis.org/oas/v3.0.3#properties
196
+ */
197
+ function metadata(dialect) {
198
+ return {
199
+ type: "object",
200
+ ...dialect === "openapi-3.0" ? {} : { propertyNames: { pattern: METADATA_KEY_PATTERN.source } },
201
+ additionalProperties: STRING
202
+ };
203
+ }
177
204
  function errorInfo$1(definition, ctx, options) {
178
205
  return detail(ERROR_INFO_TYPE, ctx.dialect, {
179
206
  reason: constant(definition.code, ctx.dialect),
180
207
  domain: constant(options.domain, ctx.dialect),
181
- metadata: {
182
- type: "object",
183
- additionalProperties: STRING
184
- }
208
+ metadata: metadata(ctx.dialect)
185
209
  }, ["reason", "domain"]);
186
210
  }
187
211
  function badRequest(dialect) {
@@ -271,6 +295,12 @@ function bodySchema(definition, ctx, options, validation) {
271
295
  * @ref https://google.aip.dev/193#http11json-representation
272
296
  */
273
297
  const GOOGLE_API_CONTENT_TYPE = "application/json";
298
+ /**
299
+ * `metadata` keys written from `ctx.meta` that a `traceIdMetadataKey` of the
300
+ * same name would silently replace: `location` carries the validation target
301
+ * (SPEC 8.2).
302
+ */
303
+ const RESERVED_METADATA_KEYS = /* @__PURE__ */ new Set(["location"]);
274
304
  function errorInfo(error, ctx, options) {
275
305
  const metadata = toMetadata(ctx.meta);
276
306
  if (options.traceIdMetadataKey !== false && ctx.traceId !== void 0) metadata[options.traceIdMetadataKey] = ctx.traceId;
@@ -293,7 +323,7 @@ function helpUrl(error, ctx, options) {
293
323
  */
294
324
  function renderBody(error, ctx, options, issues) {
295
325
  const details = [errorInfo(error, ctx, options)];
296
- if (issues !== void 0) details.push({
326
+ if (issues !== void 0 && issues.length > 0) details.push({
297
327
  "@type": BAD_REQUEST_TYPE,
298
328
  fieldViolations: issues.map((issue) => fieldViolation(issue))
299
329
  });
@@ -317,7 +347,9 @@ function renderBody(error, ctx, options, issues) {
317
347
  } };
318
348
  }
319
349
  /**
320
- * AIP-193 Google API errors (SPEC 7.6).
350
+ * AIP-193 Google API errors (SPEC 7.6). Throws `TypeError` when
351
+ * `traceIdMetadataKey` falls outside the `ErrorInfo.metadata` key grammar
352
+ * or names a key the format writes from `ctx.meta`.
321
353
  * @ref https://google.aip.dev/193
322
354
  */
323
355
  function googleApi(options) {
@@ -328,6 +360,7 @@ function googleApi(options) {
328
360
  traceIdMetadataKey: options.traceIdMetadataKey ?? "traceId",
329
361
  includeRequestInfo: options.includeRequestInfo ?? true
330
362
  };
363
+ if (resolved.traceIdMetadataKey !== false) assertMemberName("traceIdMetadataKey", resolved.traceIdMetadataKey, RESERVED_METADATA_KEYS, METADATA_KEY_PATTERN);
331
364
  const schemaOptions = {
332
365
  domain: resolved.domain,
333
366
  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,89 @@
1
+ import "../constants-rUzQOaX_.js";
2
+ import { t as assertMemberName } from "../member-name-Bn3zp8gx.js";
1
3
  import { t as constant } from "../schema-helpers-B4HL9TmU.js";
2
4
  import { a as pointerFromPath, r as nameFromPath, t as isPointerLocation } from "../issue-B-7iyCBE.js";
3
5
  import { n as readLocation } from "../validation-entries-9R5RK-AZ.js";
6
+ //#region src/formats/json-api/schema.ts
7
+ function errorObjectSchema(definition, ctx, options) {
8
+ return {
9
+ type: "object",
10
+ required: [
11
+ "status",
12
+ "code",
13
+ "title"
14
+ ],
15
+ properties: {
16
+ ...options.includeId ? { id: { type: "string" } } : {},
17
+ links: {
18
+ type: "object",
19
+ properties: {
20
+ type: {
21
+ type: "string",
22
+ format: "uri-reference"
23
+ },
24
+ about: {
25
+ type: "string",
26
+ format: "uri-reference"
27
+ }
28
+ },
29
+ additionalProperties: false
30
+ },
31
+ status: constant(String(definition.status), ctx.dialect),
32
+ code: constant(definition.code, ctx.dialect),
33
+ title: constant(definition.title, ctx.dialect),
34
+ detail: { type: "string" },
35
+ source: {
36
+ type: "object",
37
+ properties: {
38
+ pointer: { type: "string" },
39
+ parameter: { type: "string" },
40
+ header: { type: "string" }
41
+ },
42
+ additionalProperties: false
43
+ },
44
+ meta: { type: "object" }
45
+ },
46
+ additionalProperties: false
47
+ };
48
+ }
49
+ function bodySchema(definition, ctx, options) {
50
+ return {
51
+ type: "object",
52
+ required: ["errors"],
53
+ properties: { errors: {
54
+ type: "array",
55
+ minItems: 1,
56
+ items: errorObjectSchema(definition, ctx, options)
57
+ } },
58
+ additionalProperties: false
59
+ };
60
+ }
61
+ //#endregion
4
62
  //#region src/formats/json-api/index.ts
5
63
  /**
6
64
  * No parameters: the specification allows only `ext` and `profile`.
7
65
  * @ref https://jsonapi.org/format/#content-negotiation-servers
8
66
  */
9
67
  const JSON_API_CONTENT_TYPE = "application/vnd.api+json";
68
+ /**
69
+ * Member names: at least one character, starting and ending with a letter,
70
+ * digit, or a code point at U+0080 or above, with `-`, `_`, and space allowed
71
+ * in between.
72
+ * @ref https://jsonapi.org/format/#document-member-names
73
+ */
74
+ 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;
75
+ /**
76
+ * `meta` keys this format writes itself (7.2.1, 7.2.2); a `traceIdMetaKey`
77
+ * with one of these names would be overwritten or overwrite it.
78
+ */
79
+ const RESERVED_META_KEYS = /* @__PURE__ */ new Set([
80
+ "stack",
81
+ "location",
82
+ "name",
83
+ "code",
84
+ "expected",
85
+ "received"
86
+ ]);
10
87
  function links(error, ctx, options) {
11
88
  const base = options.typeLinkBaseUrl ?? ctx.docsBaseUrl;
12
89
  const type = error.type ?? (base === void 0 ? void 0 : `${base}/${error.code}`);
@@ -58,62 +135,10 @@ function issueMeta(issue, location, shared) {
58
135
  if (issue.received !== void 0) meta["received"] = issue.received;
59
136
  return meta;
60
137
  }
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
138
  /**
116
- * JSON:API 1.1 error documents (SPEC 7.2).
139
+ * JSON:API 1.1 error documents (SPEC 7.2). Throws `TypeError` when
140
+ * `traceIdMetaKey` names a `meta` key the format writes itself or is not a
141
+ * valid member name.
117
142
  * @ref https://jsonapi.org/format/#errors
118
143
  */
119
144
  function jsonApi(options = {}) {
@@ -123,6 +148,7 @@ function jsonApi(options = {}) {
123
148
  includeId: options.includeId ?? true,
124
149
  traceIdMetaKey: options.traceIdMetaKey ?? "traceId"
125
150
  };
151
+ if (resolved.traceIdMetaKey !== false) assertMemberName("traceIdMetaKey", resolved.traceIdMetaKey, RESERVED_META_KEYS, MEMBER_NAME_PATTERN);
126
152
  return {
127
153
  name: "json-api",
128
154
  contentType: JSON_API_CONTENT_TYPE,
@@ -132,6 +158,10 @@ function jsonApi(options = {}) {
132
158
  renderValidation(error, issues, ctx) {
133
159
  const location = readLocation(ctx.meta);
134
160
  const shared = sharedMeta(ctx, resolved);
161
+ if (issues.length === 0) return { errors: [errorObject(error, ctx, resolved, error.detail ?? "Request validation failed", void 0, {
162
+ ...shared,
163
+ location
164
+ })] };
135
165
  return { errors: issues.map((issue) => errorObject(error, ctx, resolved, issue.message, issueSource(issue, location), issueMeta(issue, location, shared))) };
136
166
  },
137
167
  schema(definition, ctx) {