@twin.org/api-models 0.9.1-next.2 → 0.9.1-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,126 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { GeneralError, Guards, Is, StringHelper } from "@twin.org/core";
4
+ import { HeaderHelper, HeaderTypes, MimeTypes } from "@twin.org/web";
5
+ import { HttpUrlHelper } from "./httpUrlHelper.js";
6
+ /**
7
+ * Class to help with handling http headers.
8
+ */
9
+ export class HttpHeaderHelper {
10
+ /**
11
+ * Runtime name for the class.
12
+ */
13
+ static CLASS_NAME = "HttpHeaderHelper";
14
+ /**
15
+ * Set the Link header with a next cursor relation when a cursor is present.
16
+ * @param headers The response headers to mutate.
17
+ * @param url The request URL used as the base for the link.
18
+ * @param publicOrigin The public origin to substitute into the URL, if any.
19
+ * @param cursor The cursor value; when undefined or empty the header is not set.
20
+ */
21
+ static buildCursor(headers, url, publicOrigin, cursor) {
22
+ if (Is.stringValue(cursor)) {
23
+ headers[HeaderTypes.Link] = HeaderHelper.createLinkHeader(HttpUrlHelper.replaceOrigin(url, publicOrigin), { cursor }, "next");
24
+ }
25
+ }
26
+ /**
27
+ * Set the Location header to the encoded ID, optionally placed within a base URL or template.
28
+ * When baseUrl contains a colon-prefixed placeholder (e.g. "/path1/:id/path2") the
29
+ * encoded ID is substituted at that position; otherwise it is appended.
30
+ * The base URL may be an absolute URL or a relative path. When omitted the bare
31
+ * encoded ID is used.
32
+ * @param headers The response headers to mutate.
33
+ * @param id The resource ID to encode and place.
34
+ * @param baseUrl The optional base URL, relative path, or URL template.
35
+ */
36
+ static buildId(headers, id, baseUrl) {
37
+ Guards.stringValue(HttpHeaderHelper.CLASS_NAME, "id", id);
38
+ const encodedId = encodeURIComponent(id);
39
+ if (Is.stringValue(baseUrl)) {
40
+ if (baseUrl.includes("/:")) {
41
+ headers[HeaderTypes.Location] = baseUrl.replace(/\/:[^/?#]+/, `/${encodedId}`);
42
+ }
43
+ else {
44
+ headers[HeaderTypes.Location] = `${StringHelper.trimTrailingSlashes(baseUrl)}/${encodedId}`;
45
+ }
46
+ }
47
+ else {
48
+ headers[HeaderTypes.Location] = encodedId;
49
+ }
50
+ }
51
+ /**
52
+ * Set the Content-Type header to JSON-LD or JSON depending on the request Accept header.
53
+ * Uses HeaderHelper.extractAccept which parses the Accept header per RFC 7231 and returns
54
+ * entries ordered by quality descending, preserving original order for equal q-values.
55
+ * @param headers The response headers to mutate.
56
+ * @param requestHeaders The request headers to inspect for the Accept value.
57
+ */
58
+ static buildJsonContentType(headers, requestHeaders) {
59
+ const accepted = HeaderHelper.extractAccept(requestHeaders);
60
+ let contentType = MimeTypes.Json;
61
+ if (Is.arrayValue(accepted)) {
62
+ for (const { mimeType } of accepted) {
63
+ if (mimeType === MimeTypes.JsonLd) {
64
+ contentType = MimeTypes.JsonLd;
65
+ break;
66
+ }
67
+ if (mimeType === MimeTypes.Json || mimeType === "application/*" || mimeType === "*/*") {
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ headers[HeaderTypes.ContentType] = contentType;
73
+ }
74
+ /**
75
+ * Extract the cursor from the Link header's next relation.
76
+ * @param headers The response headers to extract the cursor from.
77
+ * @returns The cursor value or undefined if not present.
78
+ */
79
+ static extractCursor(headers) {
80
+ return HeaderHelper.extractLinkHeaderRelation(headers?.[HeaderTypes.Link], "next")
81
+ ?.urlQueryParams?.cursor;
82
+ }
83
+ /**
84
+ * Extract the resource ID from the Location response header.
85
+ * Handles absolute URLs (http://host/path/:id, http://host/path/:id?foo=bar),
86
+ * relative paths (/segment/:id, ./segment/:id), and bare ID values.
87
+ * When a URL template such as "/path1/:id/path2" is supplied the ID is extracted
88
+ * from the segment position marked by the first colon-prefixed placeholder.
89
+ * Without a template the last path segment is returned.
90
+ * @param headers The response headers containing the Location header.
91
+ * @param template Optional URL template with a colon-prefixed placeholder marking the ID position, e.g. "/path1/:id/path2".
92
+ * @returns The extracted ID string.
93
+ * @throws GeneralError If the Location header is missing, the template has no placeholder, or the ID cannot be extracted.
94
+ */
95
+ static extractId(headers, template) {
96
+ const location = headers?.[HeaderTypes.Location];
97
+ if (!Is.stringValue(location)) {
98
+ throw new GeneralError(HttpHeaderHelper.CLASS_NAME, "locationMissing");
99
+ }
100
+ const withoutQuery = location.split("?")[0];
101
+ if (Is.stringValue(template)) {
102
+ const templateSegments = template.split("/").filter(s => s.length > 0);
103
+ const placeholderIndex = templateSegments.findIndex(s => s.startsWith(":"));
104
+ if (placeholderIndex === -1) {
105
+ throw new GeneralError(HttpHeaderHelper.CLASS_NAME, "templateNoPlaceholder");
106
+ }
107
+ let path = withoutQuery;
108
+ try {
109
+ path = new URL(withoutQuery).pathname;
110
+ }
111
+ catch { }
112
+ const locationSegments = path.split("/").filter(s => s.length > 0 && s !== ".");
113
+ const id = locationSegments[placeholderIndex];
114
+ if (!Is.stringValue(id)) {
115
+ throw new GeneralError(HttpHeaderHelper.CLASS_NAME, "idNotFound");
116
+ }
117
+ return decodeURIComponent(id);
118
+ }
119
+ const id = withoutQuery.includes("/") ? withoutQuery.split("/").pop() : withoutQuery;
120
+ if (!Is.stringValue(id)) {
121
+ throw new GeneralError(HttpHeaderHelper.CLASS_NAME, "idNotFound");
122
+ }
123
+ return decodeURIComponent(id);
124
+ }
125
+ }
126
+ //# sourceMappingURL=httpHeaderHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpHeaderHelper.js","sourceRoot":"","sources":["../../../src/helpers/httpHeaderHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAqB,MAAM,eAAe,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAC5B;;OAEG;IACI,MAAM,CAAU,UAAU,sBAAsC;IAEvE;;;;;;OAMG;IACI,MAAM,CAAC,WAAW,CACxB,OAAqB,EACrB,GAAW,EACX,YAAgC,EAChC,MAA0B;QAE1B,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,gBAAgB,CACxD,aAAa,CAAC,aAAa,CAAC,GAAG,EAAE,YAAY,CAAC,EAC9C,EAAE,MAAM,EAAE,EACV,MAAM,CACN,CAAC;QACH,CAAC;IACF,CAAC;IAED;;;;;;;;;OASG;IACI,MAAM,CAAC,OAAO,CACpB,OAAqB,EACrB,EAAU,EACV,OAAgB;QAEhB,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAChE,MAAM,SAAS,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,SAAS,EAAE,CAAC,CAAC;YAChF,CAAC;iBAAM,CAAC;gBACP,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,GAAG,YAAY,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,SAAS,EAAE,CAAC;YAC7F,CAAC;QACF,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;QAC3C,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,oBAAoB,CACjC,OAAqB,EACrB,cAA6B;QAI7B,MAAM,QAAQ,GAAG,YAAY,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QAC5D,IAAI,WAAW,GAAW,SAAS,CAAC,IAAI,CAAC;QACzC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACrC,IAAI,QAAQ,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;oBACnC,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC;oBAC/B,MAAM;gBACP,CAAC;gBACD,IAAI,QAAQ,KAAK,SAAS,CAAC,IAAI,IAAI,QAAQ,KAAK,eAAe,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;oBACvF,MAAM;gBACP,CAAC;YACF,CAAC;QACF,CAAC;QACD,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,aAAa,CAAC,OAAsB;QACjD,OAAO,YAAY,CAAC,yBAAyB,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YACjF,EAAE,cAAc,EAAE,MAAM,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;OAWG;IACI,MAAM,CAAC,SAAS,CAAC,OAAsB,EAAE,QAAiB;QAChE,MAAM,QAAQ,GAAG,OAAO,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,YAAY,CAAC,gBAAgB,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5C,IAAI,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,MAAM,gBAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvE,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5E,IAAI,gBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,YAAY,CAAC,gBAAgB,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAC;YAC9E,CAAC;YAED,IAAI,IAAI,GAAG,YAAY,CAAC;YACxB,IAAI,CAAC;gBACJ,IAAI,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YAEV,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;YAChF,MAAM,EAAE,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;YAE9C,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,YAAY,CAAC,gBAAgB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;YACnE,CAAC;YACD,OAAO,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;QAED,MAAM,EAAE,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;QAErF,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,YAAY,CAAC,gBAAgB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards, Is, StringHelper } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HeaderHelper, HeaderTypes, MimeTypes, type IHttpHeaders } from \"@twin.org/web\";\nimport { HttpUrlHelper } from \"./httpUrlHelper.js\";\n\n/**\n * Class to help with handling http headers.\n */\nexport class HttpHeaderHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<HttpHeaderHelper>();\n\n\t/**\n\t * Set the Link header with a next cursor relation when a cursor is present.\n\t * @param headers The response headers to mutate.\n\t * @param url The request URL used as the base for the link.\n\t * @param publicOrigin The public origin to substitute into the URL, if any.\n\t * @param cursor The cursor value; when undefined or empty the header is not set.\n\t */\n\tpublic static buildCursor(\n\t\theaders: IHttpHeaders,\n\t\turl: string,\n\t\tpublicOrigin: string | undefined,\n\t\tcursor: string | undefined\n\t): asserts headers is IHttpHeaders & { [HeaderTypes.Link]: string | undefined } {\n\t\tif (Is.stringValue(cursor)) {\n\t\t\theaders[HeaderTypes.Link] = HeaderHelper.createLinkHeader(\n\t\t\t\tHttpUrlHelper.replaceOrigin(url, publicOrigin),\n\t\t\t\t{ cursor },\n\t\t\t\t\"next\"\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Set the Location header to the encoded ID, optionally placed within a base URL or template.\n\t * When baseUrl contains a colon-prefixed placeholder (e.g. \"/path1/:id/path2\") the\n\t * encoded ID is substituted at that position; otherwise it is appended.\n\t * The base URL may be an absolute URL or a relative path. When omitted the bare\n\t * encoded ID is used.\n\t * @param headers The response headers to mutate.\n\t * @param id The resource ID to encode and place.\n\t * @param baseUrl The optional base URL, relative path, or URL template.\n\t */\n\tpublic static buildId(\n\t\theaders: IHttpHeaders,\n\t\tid: string,\n\t\tbaseUrl?: string\n\t): asserts headers is IHttpHeaders & { [HeaderTypes.Location]: string } {\n\t\tGuards.stringValue(HttpHeaderHelper.CLASS_NAME, nameof(id), id);\n\t\tconst encodedId = encodeURIComponent(id);\n\t\tif (Is.stringValue(baseUrl)) {\n\t\t\tif (baseUrl.includes(\"/:\")) {\n\t\t\t\theaders[HeaderTypes.Location] = baseUrl.replace(/\\/:[^/?#]+/, `/${encodedId}`);\n\t\t\t} else {\n\t\t\t\theaders[HeaderTypes.Location] = `${StringHelper.trimTrailingSlashes(baseUrl)}/${encodedId}`;\n\t\t\t}\n\t\t} else {\n\t\t\theaders[HeaderTypes.Location] = encodedId;\n\t\t}\n\t}\n\n\t/**\n\t * Set the Content-Type header to JSON-LD or JSON depending on the request Accept header.\n\t * Uses HeaderHelper.extractAccept which parses the Accept header per RFC 7231 and returns\n\t * entries ordered by quality descending, preserving original order for equal q-values.\n\t * @param headers The response headers to mutate.\n\t * @param requestHeaders The request headers to inspect for the Accept value.\n\t */\n\tpublic static buildJsonContentType(\n\t\theaders: IHttpHeaders,\n\t\trequestHeaders?: IHttpHeaders\n\t): asserts headers is IHttpHeaders & {\n\t\t[HeaderTypes.ContentType]: typeof MimeTypes.JsonLd | typeof MimeTypes.Json;\n\t} {\n\t\tconst accepted = HeaderHelper.extractAccept(requestHeaders);\n\t\tlet contentType: string = MimeTypes.Json;\n\t\tif (Is.arrayValue(accepted)) {\n\t\t\tfor (const { mimeType } of accepted) {\n\t\t\t\tif (mimeType === MimeTypes.JsonLd) {\n\t\t\t\t\tcontentType = MimeTypes.JsonLd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (mimeType === MimeTypes.Json || mimeType === \"application/*\" || mimeType === \"*/*\") {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\theaders[HeaderTypes.ContentType] = contentType;\n\t}\n\n\t/**\n\t * Extract the cursor from the Link header's next relation.\n\t * @param headers The response headers to extract the cursor from.\n\t * @returns The cursor value or undefined if not present.\n\t */\n\tpublic static extractCursor(headers?: IHttpHeaders): string | undefined {\n\t\treturn HeaderHelper.extractLinkHeaderRelation(headers?.[HeaderTypes.Link], \"next\")\n\t\t\t?.urlQueryParams?.cursor;\n\t}\n\n\t/**\n\t * Extract the resource ID from the Location response header.\n\t * Handles absolute URLs (http://host/path/:id, http://host/path/:id?foo=bar),\n\t * relative paths (/segment/:id, ./segment/:id), and bare ID values.\n\t * When a URL template such as \"/path1/:id/path2\" is supplied the ID is extracted\n\t * from the segment position marked by the first colon-prefixed placeholder.\n\t * Without a template the last path segment is returned.\n\t * @param headers The response headers containing the Location header.\n\t * @param template Optional URL template with a colon-prefixed placeholder marking the ID position, e.g. \"/path1/:id/path2\".\n\t * @returns The extracted ID string.\n\t * @throws GeneralError If the Location header is missing, the template has no placeholder, or the ID cannot be extracted.\n\t */\n\tpublic static extractId(headers?: IHttpHeaders, template?: string): string {\n\t\tconst location = headers?.[HeaderTypes.Location];\n\t\tif (!Is.stringValue(location)) {\n\t\t\tthrow new GeneralError(HttpHeaderHelper.CLASS_NAME, \"locationMissing\");\n\t\t}\n\n\t\tconst withoutQuery = location.split(\"?\")[0];\n\n\t\tif (Is.stringValue(template)) {\n\t\t\tconst templateSegments = template.split(\"/\").filter(s => s.length > 0);\n\t\t\tconst placeholderIndex = templateSegments.findIndex(s => s.startsWith(\":\"));\n\t\t\tif (placeholderIndex === -1) {\n\t\t\t\tthrow new GeneralError(HttpHeaderHelper.CLASS_NAME, \"templateNoPlaceholder\");\n\t\t\t}\n\n\t\t\tlet path = withoutQuery;\n\t\t\ttry {\n\t\t\t\tpath = new URL(withoutQuery).pathname;\n\t\t\t} catch {}\n\n\t\t\tconst locationSegments = path.split(\"/\").filter(s => s.length > 0 && s !== \".\");\n\t\t\tconst id = locationSegments[placeholderIndex];\n\n\t\t\tif (!Is.stringValue(id)) {\n\t\t\t\tthrow new GeneralError(HttpHeaderHelper.CLASS_NAME, \"idNotFound\");\n\t\t\t}\n\t\t\treturn decodeURIComponent(id);\n\t\t}\n\n\t\tconst id = withoutQuery.includes(\"/\") ? withoutQuery.split(\"/\").pop() : withoutQuery;\n\n\t\tif (!Is.stringValue(id)) {\n\t\t\tthrow new GeneralError(HttpHeaderHelper.CLASS_NAME, \"idNotFound\");\n\t\t}\n\t\treturn decodeURIComponent(id);\n\t}\n}\n"]}
package/dist/es/index.js CHANGED
@@ -6,6 +6,7 @@ export * from "./factories/mimeTypeProcessorFactory.js";
6
6
  export * from "./factories/restRouteProcessorFactory.js";
7
7
  export * from "./factories/socketRouteProcessorFactory.js";
8
8
  export * from "./helpers/httpErrorHelper.js";
9
+ export * from "./helpers/httpHeaderHelper.js";
9
10
  export * from "./helpers/httpParameterHelper.js";
10
11
  export * from "./helpers/httpUrlHelper.js";
11
12
  export * from "./models/api/IServerFavIconResponse.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,yCAAyC,CAAC;AACxD,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wCAAwC,CAAC;AACvD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,kDAAkD,CAAC;AACjE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,iDAAiD,CAAC;AAChE,cAAc,2DAA2D,CAAC;AAC1E,cAAc,gDAAgD,CAAC;AAC/D,cAAc,sDAAsD,CAAC;AACrE,cAAc,uDAAuD,CAAC;AACtE,cAAc,oDAAoD,CAAC;AACnE,cAAc,2DAA2D,CAAC;AAC1E,cAAc,iDAAiD,CAAC;AAChE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,sCAAsC,CAAC;AACrD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,wDAAwD,CAAC;AACvE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,iCAAiC,CAAC;AAChD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yBAAyB,CAAC;AACxC,cAAc,wCAAwC,CAAC;AACvD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,yCAAyC,CAAC;AACxD,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4CAA4C,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./errors/forbiddenError.js\";\nexport * from \"./errors/tooManyRequestsError.js\";\nexport * from \"./factories/mimeTypeProcessorFactory.js\";\nexport * from \"./factories/restRouteProcessorFactory.js\";\nexport * from \"./factories/socketRouteProcessorFactory.js\";\nexport * from \"./helpers/httpErrorHelper.js\";\nexport * from \"./helpers/httpParameterHelper.js\";\nexport * from \"./helpers/httpUrlHelper.js\";\nexport * from \"./models/api/IServerFavIconResponse.js\";\nexport * from \"./models/api/IServerHealthResponse.js\";\nexport * from \"./models/api/IServerInfoResponse.js\";\nexport * from \"./models/api/IServerLivezResponse.js\";\nexport * from \"./models/api/IServerReadyzResponse.js\";\nexport * from \"./models/api/IServerRootResponse.js\";\nexport * from \"./models/api/IServerSpecResponse.js\";\nexport * from \"./models/config/IBaseRestClientConfig.js\";\nexport * from \"./models/config/IBaseSocketClientConfig.js\";\nexport * from \"./models/httpContextIdKeys.js\";\nexport * from \"./models/protocol/IHttpRequest.js\";\nexport * from \"./models/protocol/IHttpRequestContext.js\";\nexport * from \"./models/protocol/IHttpRequestPathParams.js\";\nexport * from \"./models/protocol/IHttpRequestQuery.js\";\nexport * from \"./models/protocol/IHttpResponse.js\";\nexport * from \"./models/protocol/IHttpServerRequest.js\";\nexport * from \"./models/protocol/ISocketRequestContext.js\";\nexport * from \"./models/protocol/ISocketServerRequest.js\";\nexport * from \"./models/requests/INoContentRequest.js\";\nexport * from \"./models/responses/errors/IBadRequestResponse.js\";\nexport * from \"./models/responses/errors/IConflictResponse.js\";\nexport * from \"./models/responses/errors/IForbiddenResponse.js\";\nexport * from \"./models/responses/errors/IInternalServerErrorResponse.js\";\nexport * from \"./models/responses/errors/INotFoundResponse.js\";\nexport * from \"./models/responses/errors/INotImplementedResponse.js\";\nexport * from \"./models/responses/errors/ITooManyRequestsResponse.js\";\nexport * from \"./models/responses/errors/IUnauthorizedResponse.js\";\nexport * from \"./models/responses/errors/IUnprocessableEntityResponse.js\";\nexport * from \"./models/responses/success/IAcceptedResponse.js\";\nexport * from \"./models/responses/success/ICreatedResponse.js\";\nexport * from \"./models/responses/success/INoContentResponse.js\";\nexport * from \"./models/responses/success/IOkResponse.js\";\nexport * from \"./models/routes/IBaseRoute.js\";\nexport * from \"./models/routes/IBaseRouteEntryPoint.js\";\nexport * from \"./models/routes/IRestRoute.js\";\nexport * from \"./models/routes/IRestRouteEntryPoint.js\";\nexport * from \"./models/routes/IRestRouteExample.js\";\nexport * from \"./models/routes/IRestRouteRequestExample.js\";\nexport * from \"./models/routes/IRestRouteResponseAttachmentOptions.js\";\nexport * from \"./models/routes/IRestRouteResponseExample.js\";\nexport * from \"./models/routes/IRestRouteResponseOptions.js\";\nexport * from \"./models/routes/ISocketRoute.js\";\nexport * from \"./models/routes/ISocketRouteEntryPoint.js\";\nexport * from \"./models/routes/ITag.js\";\nexport * from \"./models/server/IBaseRouteProcessor.js\";\nexport * from \"./models/server/IMimeTypeProcessor.js\";\nexport * from \"./models/server/IRestRouteProcessor.js\";\nexport * from \"./models/server/ISocketRouteProcessor.js\";\nexport * from \"./models/server/IWebServer.js\";\nexport * from \"./models/server/IWebServerOptions.js\";\nexport * from \"./models/services/IHealthComponent.js\";\nexport * from \"./models/services/IInformationComponent.js\";\nexport * from \"./models/services/IPlatformComponent.js\";\nexport * from \"./models/services/IServerInfo.js\";\nexport * from \"./models/services/ITenant.js\";\nexport * from \"./models/services/ITenantAdminComponent.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,yCAAyC,CAAC;AACxD,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wCAAwC,CAAC;AACvD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,yCAAyC,CAAC;AACxD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,kDAAkD,CAAC;AACjE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,iDAAiD,CAAC;AAChE,cAAc,2DAA2D,CAAC;AAC1E,cAAc,gDAAgD,CAAC;AAC/D,cAAc,sDAAsD,CAAC;AACrE,cAAc,uDAAuD,CAAC;AACtE,cAAc,oDAAoD,CAAC;AACnE,cAAc,2DAA2D,CAAC;AAC1E,cAAc,iDAAiD,CAAC;AAChE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yCAAyC,CAAC;AACxD,cAAc,sCAAsC,CAAC;AACrD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,wDAAwD,CAAC;AACvE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,iCAAiC,CAAC;AAChD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yBAAyB,CAAC;AACxC,cAAc,wCAAwC,CAAC;AACvD,cAAc,uCAAuC,CAAC;AACtD,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,yCAAyC,CAAC;AACxD,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4CAA4C,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./errors/forbiddenError.js\";\nexport * from \"./errors/tooManyRequestsError.js\";\nexport * from \"./factories/mimeTypeProcessorFactory.js\";\nexport * from \"./factories/restRouteProcessorFactory.js\";\nexport * from \"./factories/socketRouteProcessorFactory.js\";\nexport * from \"./helpers/httpErrorHelper.js\";\nexport * from \"./helpers/httpHeaderHelper.js\";\nexport * from \"./helpers/httpParameterHelper.js\";\nexport * from \"./helpers/httpUrlHelper.js\";\nexport * from \"./models/api/IServerFavIconResponse.js\";\nexport * from \"./models/api/IServerHealthResponse.js\";\nexport * from \"./models/api/IServerInfoResponse.js\";\nexport * from \"./models/api/IServerLivezResponse.js\";\nexport * from \"./models/api/IServerReadyzResponse.js\";\nexport * from \"./models/api/IServerRootResponse.js\";\nexport * from \"./models/api/IServerSpecResponse.js\";\nexport * from \"./models/config/IBaseRestClientConfig.js\";\nexport * from \"./models/config/IBaseSocketClientConfig.js\";\nexport * from \"./models/httpContextIdKeys.js\";\nexport * from \"./models/protocol/IHttpRequest.js\";\nexport * from \"./models/protocol/IHttpRequestContext.js\";\nexport * from \"./models/protocol/IHttpRequestPathParams.js\";\nexport * from \"./models/protocol/IHttpRequestQuery.js\";\nexport * from \"./models/protocol/IHttpResponse.js\";\nexport * from \"./models/protocol/IHttpServerRequest.js\";\nexport * from \"./models/protocol/ISocketRequestContext.js\";\nexport * from \"./models/protocol/ISocketServerRequest.js\";\nexport * from \"./models/requests/INoContentRequest.js\";\nexport * from \"./models/responses/errors/IBadRequestResponse.js\";\nexport * from \"./models/responses/errors/IConflictResponse.js\";\nexport * from \"./models/responses/errors/IForbiddenResponse.js\";\nexport * from \"./models/responses/errors/IInternalServerErrorResponse.js\";\nexport * from \"./models/responses/errors/INotFoundResponse.js\";\nexport * from \"./models/responses/errors/INotImplementedResponse.js\";\nexport * from \"./models/responses/errors/ITooManyRequestsResponse.js\";\nexport * from \"./models/responses/errors/IUnauthorizedResponse.js\";\nexport * from \"./models/responses/errors/IUnprocessableEntityResponse.js\";\nexport * from \"./models/responses/success/IAcceptedResponse.js\";\nexport * from \"./models/responses/success/ICreatedResponse.js\";\nexport * from \"./models/responses/success/INoContentResponse.js\";\nexport * from \"./models/responses/success/IOkResponse.js\";\nexport * from \"./models/routes/IBaseRoute.js\";\nexport * from \"./models/routes/IBaseRouteEntryPoint.js\";\nexport * from \"./models/routes/IRestRoute.js\";\nexport * from \"./models/routes/IRestRouteEntryPoint.js\";\nexport * from \"./models/routes/IRestRouteExample.js\";\nexport * from \"./models/routes/IRestRouteRequestExample.js\";\nexport * from \"./models/routes/IRestRouteResponseAttachmentOptions.js\";\nexport * from \"./models/routes/IRestRouteResponseExample.js\";\nexport * from \"./models/routes/IRestRouteResponseOptions.js\";\nexport * from \"./models/routes/ISocketRoute.js\";\nexport * from \"./models/routes/ISocketRouteEntryPoint.js\";\nexport * from \"./models/routes/ITag.js\";\nexport * from \"./models/server/IBaseRouteProcessor.js\";\nexport * from \"./models/server/IMimeTypeProcessor.js\";\nexport * from \"./models/server/IRestRouteProcessor.js\";\nexport * from \"./models/server/ISocketRouteProcessor.js\";\nexport * from \"./models/server/IWebServer.js\";\nexport * from \"./models/server/IWebServerOptions.js\";\nexport * from \"./models/services/IHealthComponent.js\";\nexport * from \"./models/services/IInformationComponent.js\";\nexport * from \"./models/services/IPlatformComponent.js\";\nexport * from \"./models/services/IServerInfo.js\";\nexport * from \"./models/services/ITenant.js\";\nexport * from \"./models/services/ITenantAdminComponent.js\";\n"]}
@@ -0,0 +1,62 @@
1
+ import { HeaderTypes, MimeTypes, type IHttpHeaders } from "@twin.org/web";
2
+ /**
3
+ * Class to help with handling http headers.
4
+ */
5
+ export declare class HttpHeaderHelper {
6
+ /**
7
+ * Runtime name for the class.
8
+ */
9
+ static readonly CLASS_NAME: string;
10
+ /**
11
+ * Set the Link header with a next cursor relation when a cursor is present.
12
+ * @param headers The response headers to mutate.
13
+ * @param url The request URL used as the base for the link.
14
+ * @param publicOrigin The public origin to substitute into the URL, if any.
15
+ * @param cursor The cursor value; when undefined or empty the header is not set.
16
+ */
17
+ static buildCursor(headers: IHttpHeaders, url: string, publicOrigin: string | undefined, cursor: string | undefined): asserts headers is IHttpHeaders & {
18
+ [HeaderTypes.Link]: string | undefined;
19
+ };
20
+ /**
21
+ * Set the Location header to the encoded ID, optionally placed within a base URL or template.
22
+ * When baseUrl contains a colon-prefixed placeholder (e.g. "/path1/:id/path2") the
23
+ * encoded ID is substituted at that position; otherwise it is appended.
24
+ * The base URL may be an absolute URL or a relative path. When omitted the bare
25
+ * encoded ID is used.
26
+ * @param headers The response headers to mutate.
27
+ * @param id The resource ID to encode and place.
28
+ * @param baseUrl The optional base URL, relative path, or URL template.
29
+ */
30
+ static buildId(headers: IHttpHeaders, id: string, baseUrl?: string): asserts headers is IHttpHeaders & {
31
+ [HeaderTypes.Location]: string;
32
+ };
33
+ /**
34
+ * Set the Content-Type header to JSON-LD or JSON depending on the request Accept header.
35
+ * Uses HeaderHelper.extractAccept which parses the Accept header per RFC 7231 and returns
36
+ * entries ordered by quality descending, preserving original order for equal q-values.
37
+ * @param headers The response headers to mutate.
38
+ * @param requestHeaders The request headers to inspect for the Accept value.
39
+ */
40
+ static buildJsonContentType(headers: IHttpHeaders, requestHeaders?: IHttpHeaders): asserts headers is IHttpHeaders & {
41
+ [HeaderTypes.ContentType]: typeof MimeTypes.JsonLd | typeof MimeTypes.Json;
42
+ };
43
+ /**
44
+ * Extract the cursor from the Link header's next relation.
45
+ * @param headers The response headers to extract the cursor from.
46
+ * @returns The cursor value or undefined if not present.
47
+ */
48
+ static extractCursor(headers?: IHttpHeaders): string | undefined;
49
+ /**
50
+ * Extract the resource ID from the Location response header.
51
+ * Handles absolute URLs (http://host/path/:id, http://host/path/:id?foo=bar),
52
+ * relative paths (/segment/:id, ./segment/:id), and bare ID values.
53
+ * When a URL template such as "/path1/:id/path2" is supplied the ID is extracted
54
+ * from the segment position marked by the first colon-prefixed placeholder.
55
+ * Without a template the last path segment is returned.
56
+ * @param headers The response headers containing the Location header.
57
+ * @param template Optional URL template with a colon-prefixed placeholder marking the ID position, e.g. "/path1/:id/path2".
58
+ * @returns The extracted ID string.
59
+ * @throws GeneralError If the Location header is missing, the template has no placeholder, or the ID cannot be extracted.
60
+ */
61
+ static extractId(headers?: IHttpHeaders, template?: string): string;
62
+ }
@@ -4,6 +4,7 @@ export * from "./factories/mimeTypeProcessorFactory.js";
4
4
  export * from "./factories/restRouteProcessorFactory.js";
5
5
  export * from "./factories/socketRouteProcessorFactory.js";
6
6
  export * from "./helpers/httpErrorHelper.js";
7
+ export * from "./helpers/httpHeaderHelper.js";
7
8
  export * from "./helpers/httpParameterHelper.js";
8
9
  export * from "./helpers/httpUrlHelper.js";
9
10
  export * from "./models/api/IServerFavIconResponse.js";
package/docs/changelog.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.1-next.3](https://github.com/iotaledger/twin-api/compare/api-models-v0.9.1-next.2...api-models-v0.9.1-next.3) (2026-06-29)
4
+
5
+
6
+ ### Features
7
+
8
+ * enhanced rest handling ([#208](https://github.com/iotaledger/twin-api/issues/208)) ([99d5f3f](https://github.com/iotaledger/twin-api/commit/99d5f3f96d262e57828d98d3f3b1e3da8a863378))
9
+
3
10
  ## [0.9.1-next.2](https://github.com/iotaledger/twin-api/compare/api-models-v0.9.1-next.1...api-models-v0.9.1-next.2) (2026-06-26)
4
11
 
5
12
 
@@ -0,0 +1,182 @@
1
+ # Class: HttpHeaderHelper
2
+
3
+ Class to help with handling http headers.
4
+
5
+ ## Constructors
6
+
7
+ ### Constructor
8
+
9
+ > **new HttpHeaderHelper**(): `HttpHeaderHelper`
10
+
11
+ #### Returns
12
+
13
+ `HttpHeaderHelper`
14
+
15
+ ## Properties
16
+
17
+ ### CLASS\_NAME {#class_name}
18
+
19
+ > `readonly` `static` **CLASS\_NAME**: `string`
20
+
21
+ Runtime name for the class.
22
+
23
+ ## Methods
24
+
25
+ ### buildCursor() {#buildcursor}
26
+
27
+ > `static` **buildCursor**(`headers`, `url`, `publicOrigin`, `cursor`): asserts headers is IHttpHeaders & \{ link: string \| undefined \}
28
+
29
+ Set the Link header with a next cursor relation when a cursor is present.
30
+
31
+ #### Parameters
32
+
33
+ ##### headers
34
+
35
+ `IHttpHeaders`
36
+
37
+ The response headers to mutate.
38
+
39
+ ##### url
40
+
41
+ `string`
42
+
43
+ The request URL used as the base for the link.
44
+
45
+ ##### publicOrigin
46
+
47
+ `string` \| `undefined`
48
+
49
+ The public origin to substitute into the URL, if any.
50
+
51
+ ##### cursor
52
+
53
+ `string` \| `undefined`
54
+
55
+ The cursor value; when undefined or empty the header is not set.
56
+
57
+ #### Returns
58
+
59
+ asserts headers is IHttpHeaders & \{ link: string \| undefined \}
60
+
61
+ ***
62
+
63
+ ### buildId() {#buildid}
64
+
65
+ > `static` **buildId**(`headers`, `id`, `baseUrl?`): `asserts headers is IHttpHeaders & { location: string }`
66
+
67
+ Set the Location header to the encoded ID, optionally placed within a base URL or template.
68
+ When baseUrl contains a colon-prefixed placeholder (e.g. "/path1/:id/path2") the
69
+ encoded ID is substituted at that position; otherwise it is appended.
70
+ The base URL may be an absolute URL or a relative path. When omitted the bare
71
+ encoded ID is used.
72
+
73
+ #### Parameters
74
+
75
+ ##### headers
76
+
77
+ `IHttpHeaders`
78
+
79
+ The response headers to mutate.
80
+
81
+ ##### id
82
+
83
+ `string`
84
+
85
+ The resource ID to encode and place.
86
+
87
+ ##### baseUrl?
88
+
89
+ `string`
90
+
91
+ The optional base URL, relative path, or URL template.
92
+
93
+ #### Returns
94
+
95
+ `asserts headers is IHttpHeaders & { location: string }`
96
+
97
+ ***
98
+
99
+ ### buildJsonContentType() {#buildjsoncontenttype}
100
+
101
+ > `static` **buildJsonContentType**(`headers`, `requestHeaders?`): asserts headers is IHttpHeaders & \{ content-type: "application/json" \| "application/ld+json" \}
102
+
103
+ Set the Content-Type header to JSON-LD or JSON depending on the request Accept header.
104
+ Uses HeaderHelper.extractAccept which parses the Accept header per RFC 7231 and returns
105
+ entries ordered by quality descending, preserving original order for equal q-values.
106
+
107
+ #### Parameters
108
+
109
+ ##### headers
110
+
111
+ `IHttpHeaders`
112
+
113
+ The response headers to mutate.
114
+
115
+ ##### requestHeaders?
116
+
117
+ `IHttpHeaders`
118
+
119
+ The request headers to inspect for the Accept value.
120
+
121
+ #### Returns
122
+
123
+ asserts headers is IHttpHeaders & \{ content-type: "application/json" \| "application/ld+json" \}
124
+
125
+ ***
126
+
127
+ ### extractCursor() {#extractcursor}
128
+
129
+ > `static` **extractCursor**(`headers?`): `string` \| `undefined`
130
+
131
+ Extract the cursor from the Link header's next relation.
132
+
133
+ #### Parameters
134
+
135
+ ##### headers?
136
+
137
+ `IHttpHeaders`
138
+
139
+ The response headers to extract the cursor from.
140
+
141
+ #### Returns
142
+
143
+ `string` \| `undefined`
144
+
145
+ The cursor value or undefined if not present.
146
+
147
+ ***
148
+
149
+ ### extractId() {#extractid}
150
+
151
+ > `static` **extractId**(`headers?`, `template?`): `string`
152
+
153
+ Extract the resource ID from the Location response header.
154
+ Handles absolute URLs (http://host/path/:id, http://host/path/:id?foo=bar),
155
+ relative paths (/segment/:id, ./segment/:id), and bare ID values.
156
+ When a URL template such as "/path1/:id/path2" is supplied the ID is extracted
157
+ from the segment position marked by the first colon-prefixed placeholder.
158
+ Without a template the last path segment is returned.
159
+
160
+ #### Parameters
161
+
162
+ ##### headers?
163
+
164
+ `IHttpHeaders`
165
+
166
+ The response headers containing the Location header.
167
+
168
+ ##### template?
169
+
170
+ `string`
171
+
172
+ Optional URL template with a colon-prefixed placeholder marking the ID position, e.g. "/path1/:id/path2".
173
+
174
+ #### Returns
175
+
176
+ `string`
177
+
178
+ The extracted ID string.
179
+
180
+ #### Throws
181
+
182
+ GeneralError If the Location header is missing, the template has no placeholder, or the ID cannot be extracted.
@@ -5,6 +5,7 @@
5
5
  - [ForbiddenError](classes/ForbiddenError.md)
6
6
  - [TooManyRequestsError](classes/TooManyRequestsError.md)
7
7
  - [HttpErrorHelper](classes/HttpErrorHelper.md)
8
+ - [HttpHeaderHelper](classes/HttpHeaderHelper.md)
8
9
  - [HttpParameterHelper](classes/HttpParameterHelper.md)
9
10
  - [HttpUrlHelper](classes/HttpUrlHelper.md)
10
11
 
package/locales/en.json CHANGED
@@ -1 +1,9 @@
1
- {}
1
+ {
2
+ "error": {
3
+ "httpHeaderHelper": {
4
+ "locationMissing": "The Location response header is missing or empty",
5
+ "idNotFound": "The resource ID could not be extracted from the Location header",
6
+ "templateNoPlaceholder": "The URL template contains no colon-prefixed placeholder segment"
7
+ }
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-models",
3
- "version": "0.9.1-next.2",
3
+ "version": "0.9.1-next.3",
4
4
  "description": "Shared API contracts, route types, and response models used across services and clients.",
5
5
  "repository": {
6
6
  "type": "git",