@twin.org/api-models 0.0.3-next.28 → 0.0.3-next.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,6 +67,19 @@ export class HttpUrlHelper {
67
67
  return `${StringHelper.trimTrailingSlashes(origin)}/${StringHelper.trimLeadingSlashes(pathAndSearch)}`;
68
68
  }
69
69
  }
70
+ /**
71
+ * Encode a single URL path segment per RFC 3986 §3.3.
72
+ * Unlike encodeURIComponent, sub-delimiters ($ & + , ; =) and the colon and
73
+ * at-sign characters that are valid unencoded in path segments are preserved.
74
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
75
+ * @param segment The raw path segment value to encode.
76
+ * @returns The percent-encoded path segment.
77
+ */
78
+ static encodeUriPathSegment(segment) {
79
+ // RFC 3986 §3.3: only encode characters outside the allowed path segment set.
80
+ // Allowed: unreserved (A-Za-z0-9 - . _ ~), sub-delimiters (! $ & ' ( ) * + , ; =), and : @
81
+ return segment.replace(/[^\w!$&'()*+,.:;=@~-]/g, ch => encodeURIComponent(ch));
82
+ }
70
83
  /**
71
84
  * Replace the origin in the url.
72
85
  * @param url The url to replace the origin in.
@@ -1 +1 @@
1
- {"version":3,"file":"httpUrlHelper.js","sourceRoot":"","sources":["../../../src/helpers/httpUrlHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,MAAM,OAAO,aAAa;IACzB;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAAW;QACtC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,YAAY,CAAC,MAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,WAAW,CAAC,GAAW;QACpC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,YAAY,CAAC,QAAQ,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAAW;QACtC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,YAAY,CAAC,MAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,oBAAoB,CAAC,GAAW;QAC7C,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,GAAG,YAAY,CAAC,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,YAAY,CAAC,MAAc,EAAE,aAAqB;QAC/D,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;YACnD,OAAO,GAAG,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC;QACxG,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAAW,EAAE,SAAkB;QAC1D,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACzF,OAAO,GAAG,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAClF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;YACxC,SAAS,CAAC,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC;YAC3C,SAAS,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;YACnC,SAAS,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;YACnC,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,OAAO,GAAG,CAAC;IACZ,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is, StringHelper } from \"@twin.org/core\";\n\n/**\n * Class to help with handling http URLs.\n */\nexport class HttpUrlHelper {\n\t/**\n\t * Extract the origin from the url which includes protocol,host,port.\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/URL/origin\n\t * @param url The url to extract the origin from.\n\t * @returns The extracted origin.\n\t */\n\tpublic static extractOrigin(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn convertedUrl.origin;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Extract the path from the url.\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname\n\t * @param url The url to extract the path from.\n\t * @returns The extracted path.\n\t */\n\tpublic static extractPath(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn convertedUrl.pathname;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Extract the search from the url.\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/URL/search\n\t * @param url The url to extract the search from.\n\t * @returns The extracted search.\n\t */\n\tpublic static extractSearch(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn convertedUrl.search;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Extract the path and search from the url.\n\t * @param url The url to extract the path and search from.\n\t * @returns The extracted path and search.\n\t */\n\tpublic static extractPathAndSearch(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn `${convertedUrl.pathname}${convertedUrl.search}`;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Combine the urls parts.\n\t * @param origin The origin to combine.\n\t * @param pathAndSearch The path and search to combine.\n\t * @returns The combined parts.\n\t */\n\tpublic static combineParts(origin: string, pathAndSearch: string): string | undefined {\n\t\tif (Is.string(origin) && Is.string(pathAndSearch)) {\n\t\t\treturn `${StringHelper.trimTrailingSlashes(origin)}/${StringHelper.trimLeadingSlashes(pathAndSearch)}`;\n\t\t}\n\t}\n\n\t/**\n\t * Replace the origin in the url.\n\t * @param url The url to replace the origin in.\n\t * @param newOrigin The new origin to use.\n\t * @returns The url with the replaced origin.\n\t */\n\tpublic static replaceOrigin(url: string, newOrigin?: string): string {\n\t\tif (!Is.stringValue(url) || !Is.stringValue(newOrigin) || !newOrigin.startsWith(\"http\")) {\n\t\t\treturn url;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url.startsWith(\"/\") ? `http://placeholder${url}` : url);\n\t\t\tconst newParsedUrl = new URL(newOrigin);\n\t\t\tparsedUrl.protocol = newParsedUrl.protocol;\n\t\t\tparsedUrl.host = newParsedUrl.host;\n\t\t\tparsedUrl.port = newParsedUrl.port;\n\t\t\treturn parsedUrl.toString();\n\t\t} catch {}\n\n\t\treturn url;\n\t}\n}\n"]}
1
+ {"version":3,"file":"httpUrlHelper.js","sourceRoot":"","sources":["../../../src/helpers/httpUrlHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,MAAM,OAAO,aAAa;IACzB;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAAW;QACtC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,YAAY,CAAC,MAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,WAAW,CAAC,GAAW;QACpC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,YAAY,CAAC,QAAQ,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAAW;QACtC,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,YAAY,CAAC,MAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,oBAAoB,CAAC,GAAW;QAC7C,IAAI,CAAC;YACJ,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,GAAG,YAAY,CAAC,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,YAAY,CAAC,MAAc,EAAE,aAAqB;QAC/D,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;YACnD,OAAO,GAAG,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC;QACxG,CAAC;IACF,CAAC;IAED;;;;;;;OAOG;IACI,MAAM,CAAC,oBAAoB,CAAC,OAAe;QACjD,8EAA8E;QAC9E,2FAA2F;QAC3F,OAAO,OAAO,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAAW,EAAE,SAAkB;QAC1D,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACzF,OAAO,GAAG,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAClF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;YACxC,SAAS,CAAC,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC;YAC3C,SAAS,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;YACnC,SAAS,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;YACnC,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,OAAO,GAAG,CAAC;IACZ,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is, StringHelper } from \"@twin.org/core\";\n\n/**\n * Class to help with handling http URLs.\n */\nexport class HttpUrlHelper {\n\t/**\n\t * Extract the origin from the url which includes protocol,host,port.\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/URL/origin\n\t * @param url The url to extract the origin from.\n\t * @returns The extracted origin.\n\t */\n\tpublic static extractOrigin(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn convertedUrl.origin;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Extract the path from the url.\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname\n\t * @param url The url to extract the path from.\n\t * @returns The extracted path.\n\t */\n\tpublic static extractPath(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn convertedUrl.pathname;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Extract the search from the url.\n\t * @see https://developer.mozilla.org/en-US/docs/Web/API/URL/search\n\t * @param url The url to extract the search from.\n\t * @returns The extracted search.\n\t */\n\tpublic static extractSearch(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn convertedUrl.search;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Extract the path and search from the url.\n\t * @param url The url to extract the path and search from.\n\t * @returns The extracted path and search.\n\t */\n\tpublic static extractPathAndSearch(url: string): string | undefined {\n\t\ttry {\n\t\t\tconst convertedUrl = new URL(url);\n\t\t\treturn `${convertedUrl.pathname}${convertedUrl.search}`;\n\t\t} catch {}\n\t}\n\n\t/**\n\t * Combine the urls parts.\n\t * @param origin The origin to combine.\n\t * @param pathAndSearch The path and search to combine.\n\t * @returns The combined parts.\n\t */\n\tpublic static combineParts(origin: string, pathAndSearch: string): string | undefined {\n\t\tif (Is.string(origin) && Is.string(pathAndSearch)) {\n\t\t\treturn `${StringHelper.trimTrailingSlashes(origin)}/${StringHelper.trimLeadingSlashes(pathAndSearch)}`;\n\t\t}\n\t}\n\n\t/**\n\t * Encode a single URL path segment per RFC 3986 §3.3.\n\t * Unlike encodeURIComponent, sub-delimiters ($ & + , ; =) and the colon and\n\t * at-sign characters that are valid unencoded in path segments are preserved.\n\t * @see https://datatracker.ietf.org/doc/html/rfc3986#section-3.3\n\t * @param segment The raw path segment value to encode.\n\t * @returns The percent-encoded path segment.\n\t */\n\tpublic static encodeUriPathSegment(segment: string): string {\n\t\t// RFC 3986 §3.3: only encode characters outside the allowed path segment set.\n\t\t// Allowed: unreserved (A-Za-z0-9 - . _ ~), sub-delimiters (! $ & ' ( ) * + , ; =), and : @\n\t\treturn segment.replace(/[^\\w!$&'()*+,.:;=@~-]/g, ch => encodeURIComponent(ch));\n\t}\n\n\t/**\n\t * Replace the origin in the url.\n\t * @param url The url to replace the origin in.\n\t * @param newOrigin The new origin to use.\n\t * @returns The url with the replaced origin.\n\t */\n\tpublic static replaceOrigin(url: string, newOrigin?: string): string {\n\t\tif (!Is.stringValue(url) || !Is.stringValue(newOrigin) || !newOrigin.startsWith(\"http\")) {\n\t\t\treturn url;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parsedUrl = new URL(url.startsWith(\"/\") ? `http://placeholder${url}` : url);\n\t\t\tconst newParsedUrl = new URL(newOrigin);\n\t\t\tparsedUrl.protocol = newParsedUrl.protocol;\n\t\t\tparsedUrl.host = newParsedUrl.host;\n\t\t\tparsedUrl.port = newParsedUrl.port;\n\t\t\treturn parsedUrl.toString();\n\t\t} catch {}\n\n\t\treturn url;\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IBaseRestClientConfig.js","sourceRoot":"","sources":["../../../../src/models/config/IBaseRestClientConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHttpHeaders } from \"@twin.org/web\";\n\n/**\n * Definition for the configuration of a rest client.\n */\nexport interface IBaseRestClientConfig {\n\t/**\n\t * The endpoint where the api is hosted.\n\t */\n\tendpoint: string;\n\n\t/**\n\t * The prefix to the routes.\n\t */\n\tpathPrefix?: string;\n\n\t/**\n\t * The headers to include in requests.\n\t */\n\theaders?: IHttpHeaders;\n\n\t/**\n\t * Timeout for requests in ms.\n\t */\n\ttimeout?: number;\n\n\t/**\n\t * Include credentials in the request, defaults to true.\n\t */\n\tincludeCredentials?: boolean;\n}\n"]}
1
+ {"version":3,"file":"IBaseRestClientConfig.js","sourceRoot":"","sources":["../../../../src/models/config/IBaseRestClientConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IError } from \"@twin.org/core\";\nimport type { IHttpHeaders } from \"@twin.org/web\";\n\n/**\n * Definition for the configuration of a rest client.\n */\nexport interface IBaseRestClientConfig {\n\t/**\n\t * The endpoint where the api is hosted.\n\t */\n\tendpoint: string;\n\n\t/**\n\t * The prefix to the routes.\n\t */\n\tpathPrefix?: string;\n\n\t/**\n\t * The headers to include in requests.\n\t */\n\theaders?: IHttpHeaders;\n\n\t/**\n\t * Timeout for requests in ms.\n\t */\n\ttimeout?: number;\n\n\t/**\n\t * Include credentials in the request, defaults to true.\n\t */\n\tincludeCredentials?: boolean;\n\n\t/**\n\t * Hook to provide headers asynchronously.\n\t * @returns A promise that resolves to the headers.\n\t */\n\tcustomHeaders?: () => Promise<IHttpHeaders>;\n\n\t/**\n\t * Hook to provide an authorization header value asynchronously.\n\t * @returns A promise that resolves to the authorization header value.\n\t */\n\tcustomAuthHeader?: () => Promise<string>;\n\n\t/**\n\t * Hook to handle authorization failures asynchronously.\n\t * @returns A promise that resolves when the auth failure handling is complete.\n\t */\n\tonAuthFailure?: (err: IError) => Promise<void>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IHostingComponent.js","sourceRoot":"","sources":["../../../../src/models/services/IHostingComponent.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IComponent } from \"@twin.org/core\";\n\n/**\n * The information about the hosting of the API.\n */\nexport interface IHostingComponent extends IComponent {\n\t/**\n\t * Get the public origin for the hosting.\n\t * @param serverRequestUrl The url of the current server request if there is one.\n\t * @returns The public origin.\n\t */\n\tgetPublicOrigin(serverRequestUrl?: string): Promise<string>;\n\n\t/**\n\t * Get the public origin for the tenant if one exists.\n\t * @param tenantId The tenant identifier.\n\t * @returns The public origin for the tenant.\n\t */\n\tgetTenantOrigin(tenantId: string): Promise<string | undefined>;\n\n\t/**\n\t * Build a public url based on the public origin and the url provided.\n\t * @param url The url to build upon the public origin.\n\t * @returns The full url based on the public origin.\n\t */\n\tbuildPublicUrl(url: string): Promise<string>;\n}\n"]}
1
+ {"version":3,"file":"IHostingComponent.js","sourceRoot":"","sources":["../../../../src/models/services/IHostingComponent.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IComponent } from \"@twin.org/core\";\nimport type { IHttpRequestQuery } from \"../protocol/IHttpRequestQuery.js\";\n\n/**\n * The information about the hosting of the API.\n */\nexport interface IHostingComponent extends IComponent {\n\t/**\n\t * Get the public origin for the hosting.\n\t * @param serverRequestUrl The url of the current server request if there is one.\n\t * @returns The public origin.\n\t */\n\tgetPublicOrigin(serverRequestUrl?: string): Promise<string>;\n\n\t/**\n\t * Get the public origin for the tenant if one exists.\n\t * @param tenantId The tenant identifier.\n\t * @returns The public origin for the tenant.\n\t */\n\tgetTenantOrigin(tenantId: string): Promise<string | undefined>;\n\n\t/**\n\t * Build a public url based on the public origin and the url provided.\n\t * @param url The url to build upon the public origin.\n\t * @returns The full url based on the public origin.\n\t */\n\tbuildPublicUrl(url: string): Promise<string>;\n\n\t/**\n\t * Add encrypted key/value pairs to a URL's query string.\n\t * Existing query parameters on the URL are preserved; the provided params are\n\t * merged in and then encrypted before being written back to the URL.\n\t * @param url The base URL to add parameters to.\n\t * @param params The key/value pairs to encrypt and append.\n\t * @returns The URL with the encrypted parameters added.\n\t */\n\taddEncryptedParamsToUrl(url: string, params: IHttpRequestQuery): Promise<string>;\n\n\t/**\n\t * Decrypt specified keys from a query parameter object and return their plain-text values.\n\t * @param queryParams The HTTP request query containing the encrypted parameters.\n\t * @param keys The keys to decrypt.\n\t * @returns A map of the decrypted key/value pairs that were present.\n\t */\n\tgetDecryptedParamsFromQueryParams(\n\t\tqueryParams: IHttpRequestQuery | undefined,\n\t\tkeys: string[]\n\t): Promise<IHttpRequestQuery>;\n\n\t/**\n\t * Encrypt the tenant id and append it as a query parameter to the given URL.\n\t * @param url The URL to append the encrypted tenant token to.\n\t * @param tenantId The tenant identifier to encrypt and add.\n\t * @returns The URL with the encrypted tenant token added as a query parameter.\n\t */\n\taddTenantTokenToUrl(url: string, tenantId: string): Promise<string>;\n\t/**\n\t * Get the tenant token from the query parameters.\n\t * @param queryParams The HTTP request query containing the parameters.\n\t * @returns The tenant token if it exists.\n\t */\n\tgetTenantTokenFromQueryParams(\n\t\tqueryParams: IHttpRequestQuery | undefined\n\t): Promise<string | undefined>;\n\n\t/**\n\t * Encrypt query parameters using the hosting component's encryption mechanism.\n\t * @param httpRequestQuery The HTTP request query containing the parameters to encrypt.\n\t * @param keys The keys of the parameters to encrypt.\n\t * @returns A promise that resolves when the query parameters have been encrypted.\n\t */\n\tencryptQueryParams(\n\t\thttpRequestQuery: IHttpRequestQuery | undefined,\n\t\tkeys: string[]\n\t): Promise<void>;\n\n\t/**\n\t * Decrypt query parameters using the hosting component's encryption mechanism.\n\t * @param httpRequestQuery The HTTP request query containing the encrypted values.\n\t * @param keys The keys of the parameters to decrypt.\n\t * @returns A promise that resolves when the query parameters have been decrypted.\n\t */\n\tdecryptQueryParams(\n\t\thttpRequestQuery: IHttpRequestQuery | undefined,\n\t\tkeys: string[]\n\t): Promise<void>;\n\n\t/**\n\t * Encrypt a parameter value using the hosting component's encryption mechanism.\n\t * @param paramValue The value of the parameter to encrypt.\n\t * @returns A promise that resolves to the encrypted value of the parameter.\n\t */\n\tencryptParam(paramValue: string): Promise<string>;\n\n\t/**\n\t * Decrypt a parameter value using the hosting component's encryption mechanism.\n\t * @param encryptedValue The encrypted value of the parameter.\n\t * @returns A promise that resolves to the decrypted value of the parameter.\n\t */\n\tdecryptParam(encryptedValue: string): Promise<string>;\n}\n"]}
@@ -36,6 +36,15 @@ export declare class HttpUrlHelper {
36
36
  * @returns The combined parts.
37
37
  */
38
38
  static combineParts(origin: string, pathAndSearch: string): string | undefined;
39
+ /**
40
+ * Encode a single URL path segment per RFC 3986 §3.3.
41
+ * Unlike encodeURIComponent, sub-delimiters ($ & + , ; =) and the colon and
42
+ * at-sign characters that are valid unencoded in path segments are preserved.
43
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
44
+ * @param segment The raw path segment value to encode.
45
+ * @returns The percent-encoded path segment.
46
+ */
47
+ static encodeUriPathSegment(segment: string): string;
39
48
  /**
40
49
  * Replace the origin in the url.
41
50
  * @param url The url to replace the origin in.
@@ -1,3 +1,4 @@
1
+ import type { IError } from "@twin.org/core";
1
2
  import type { IHttpHeaders } from "@twin.org/web";
2
3
  /**
3
4
  * Definition for the configuration of a rest client.
@@ -23,4 +24,19 @@ export interface IBaseRestClientConfig {
23
24
  * Include credentials in the request, defaults to true.
24
25
  */
25
26
  includeCredentials?: boolean;
27
+ /**
28
+ * Hook to provide headers asynchronously.
29
+ * @returns A promise that resolves to the headers.
30
+ */
31
+ customHeaders?: () => Promise<IHttpHeaders>;
32
+ /**
33
+ * Hook to provide an authorization header value asynchronously.
34
+ * @returns A promise that resolves to the authorization header value.
35
+ */
36
+ customAuthHeader?: () => Promise<string>;
37
+ /**
38
+ * Hook to handle authorization failures asynchronously.
39
+ * @returns A promise that resolves when the auth failure handling is complete.
40
+ */
41
+ onAuthFailure?: (err: IError) => Promise<void>;
26
42
  }
@@ -1,4 +1,5 @@
1
1
  import type { IComponent } from "@twin.org/core";
2
+ import type { IHttpRequestQuery } from "../protocol/IHttpRequestQuery.js";
2
3
  /**
3
4
  * The information about the hosting of the API.
4
5
  */
@@ -21,4 +22,59 @@ export interface IHostingComponent extends IComponent {
21
22
  * @returns The full url based on the public origin.
22
23
  */
23
24
  buildPublicUrl(url: string): Promise<string>;
25
+ /**
26
+ * Add encrypted key/value pairs to a URL's query string.
27
+ * Existing query parameters on the URL are preserved; the provided params are
28
+ * merged in and then encrypted before being written back to the URL.
29
+ * @param url The base URL to add parameters to.
30
+ * @param params The key/value pairs to encrypt and append.
31
+ * @returns The URL with the encrypted parameters added.
32
+ */
33
+ addEncryptedParamsToUrl(url: string, params: IHttpRequestQuery): Promise<string>;
34
+ /**
35
+ * Decrypt specified keys from a query parameter object and return their plain-text values.
36
+ * @param queryParams The HTTP request query containing the encrypted parameters.
37
+ * @param keys The keys to decrypt.
38
+ * @returns A map of the decrypted key/value pairs that were present.
39
+ */
40
+ getDecryptedParamsFromQueryParams(queryParams: IHttpRequestQuery | undefined, keys: string[]): Promise<IHttpRequestQuery>;
41
+ /**
42
+ * Encrypt the tenant id and append it as a query parameter to the given URL.
43
+ * @param url The URL to append the encrypted tenant token to.
44
+ * @param tenantId The tenant identifier to encrypt and add.
45
+ * @returns The URL with the encrypted tenant token added as a query parameter.
46
+ */
47
+ addTenantTokenToUrl(url: string, tenantId: string): Promise<string>;
48
+ /**
49
+ * Get the tenant token from the query parameters.
50
+ * @param queryParams The HTTP request query containing the parameters.
51
+ * @returns The tenant token if it exists.
52
+ */
53
+ getTenantTokenFromQueryParams(queryParams: IHttpRequestQuery | undefined): Promise<string | undefined>;
54
+ /**
55
+ * Encrypt query parameters using the hosting component's encryption mechanism.
56
+ * @param httpRequestQuery The HTTP request query containing the parameters to encrypt.
57
+ * @param keys The keys of the parameters to encrypt.
58
+ * @returns A promise that resolves when the query parameters have been encrypted.
59
+ */
60
+ encryptQueryParams(httpRequestQuery: IHttpRequestQuery | undefined, keys: string[]): Promise<void>;
61
+ /**
62
+ * Decrypt query parameters using the hosting component's encryption mechanism.
63
+ * @param httpRequestQuery The HTTP request query containing the encrypted values.
64
+ * @param keys The keys of the parameters to decrypt.
65
+ * @returns A promise that resolves when the query parameters have been decrypted.
66
+ */
67
+ decryptQueryParams(httpRequestQuery: IHttpRequestQuery | undefined, keys: string[]): Promise<void>;
68
+ /**
69
+ * Encrypt a parameter value using the hosting component's encryption mechanism.
70
+ * @param paramValue The value of the parameter to encrypt.
71
+ * @returns A promise that resolves to the encrypted value of the parameter.
72
+ */
73
+ encryptParam(paramValue: string): Promise<string>;
74
+ /**
75
+ * Decrypt a parameter value using the hosting component's encryption mechanism.
76
+ * @param encryptedValue The encrypted value of the parameter.
77
+ * @returns A promise that resolves to the decrypted value of the parameter.
78
+ */
79
+ decryptParam(encryptedValue: string): Promise<string>;
24
80
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.29](https://github.com/twinfoundation/twin-api/compare/api-models-v0.0.3-next.28...api-models-v0.0.3-next.29) (2026-05-01)
4
+
5
+
6
+ ### Features
7
+
8
+ * hosting service ([#109](https://github.com/twinfoundation/twin-api/issues/109)) ([985bf1f](https://github.com/twinfoundation/twin-api/commit/985bf1f5c07b09ecb800df7120bc2422ac7a6d25))
9
+
3
10
  ## [0.0.3-next.28](https://github.com/twinfoundation/twin-api/compare/api-models-v0.0.3-next.27...api-models-v0.0.3-next.28) (2026-04-30)
4
11
 
5
12
 
package/docs/examples.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Models Examples
2
2
 
3
- These snippets show practical helpers for URL manipulation, request parameter conversion, and consistent error responses.
3
+ These snippets show practical helpers for URL manipulation, request parameter conversion, consistent error responses, and secure parameter encryption.
4
4
 
5
5
  ## HttpUrlHelper
6
6
 
@@ -61,3 +61,140 @@ try {
61
61
  console.log(mapped.error.message); // Error: Invalid request
62
62
  }
63
63
  ```
64
+
65
+ ## IHostingComponent
66
+
67
+ When working with the hosting component, you can encrypt and decrypt query parameters for secure transmission. The encryption mechanism automatically handles salt generation to prevent rainbow table attacks.
68
+
69
+ ```typescript
70
+ import type { IComponent, IHttpRequestQuery } from '@twin.org/api-models';
71
+ import { ComponentFactory } from '@twin.org/core';
72
+
73
+ interface IHostingComponent extends IComponent {
74
+ addEncryptedParamsToUrl(url: string, params: IHttpRequestQuery): Promise<string>;
75
+ getDecryptedParamsFromQueryParams(
76
+ queryParams: IHttpRequestQuery | undefined,
77
+ keys: string[]
78
+ ): Promise<IHttpRequestQuery>;
79
+ encryptParam(paramValue: string): Promise<string>;
80
+ decryptParam(encryptedValue: string): Promise<string>;
81
+ }
82
+
83
+ // Get the hosting component from the component factory
84
+ const hosting = ComponentFactory.get<IHostingComponent>('hosting');
85
+
86
+ // Encrypt parameters and add them to a URL
87
+ const baseUrl = 'https://api.example.com/callback';
88
+ const encrypted = await hosting.addEncryptedParamsToUrl(baseUrl, {
89
+ token: 'secret-token-123',
90
+ userId: 'user-456'
91
+ });
92
+ console.log(encrypted); // https://api.example.com/callback?x-enc-token=...&x-enc-userId=...
93
+ ```
94
+
95
+ ```typescript
96
+ import type { IHttpRequestQuery } from '@twin.org/api-models';
97
+ import { ComponentFactory } from '@twin.org/core';
98
+
99
+ interface IHostingComponent {
100
+ addEncryptedParamsToUrl(url: string, params: IHttpRequestQuery): Promise<string>;
101
+ getDecryptedParamsFromQueryParams(
102
+ queryParams: IHttpRequestQuery | undefined,
103
+ keys: string[]
104
+ ): Promise<IHttpRequestQuery>;
105
+ encryptQueryParams(
106
+ httpRequestQuery: IHttpRequestQuery | undefined,
107
+ keys: string[]
108
+ ): Promise<void>;
109
+ decryptQueryParams(
110
+ httpRequestQuery: IHttpRequestQuery | undefined,
111
+ keys: string[]
112
+ ): Promise<void>;
113
+ }
114
+
115
+ const hosting = ComponentFactory.get<IHostingComponent>('hosting');
116
+
117
+ // Decrypt specific parameters from a query object
118
+ const queryParams: IHttpRequestQuery = {
119
+ 'x-enc-token': 'CngBAgMEBQYH...',
120
+ 'x-enc-userId': 'CxgICSoLDAwN...',
121
+ status: 'active'
122
+ };
123
+
124
+ const decrypted = await hosting.getDecryptedParamsFromQueryParams(queryParams, ['token', 'userId']);
125
+ console.log(decrypted); // { token: 'secret-token-123', userId: 'user-456' }
126
+ ```
127
+
128
+ ```typescript
129
+ import type { IHttpRequestQuery } from '@twin.org/api-models';
130
+ import { ComponentFactory } from '@twin.org/core';
131
+
132
+ interface IHostingComponent {
133
+ addTenantTokenToUrl(url: string, tenantId: string): Promise<string>;
134
+ getTenantTokenFromQueryParams(
135
+ queryParams: IHttpRequestQuery | undefined
136
+ ): Promise<string | undefined>;
137
+ }
138
+
139
+ const hosting = ComponentFactory.get<IHostingComponent>('hosting');
140
+
141
+ // Add encrypted tenant token to a URL
142
+ const redirectUrl = 'https://tenant.example.com/dashboard';
143
+ const urlWithTenant = await hosting.addTenantTokenToUrl(
144
+ redirectUrl,
145
+ 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6'
146
+ );
147
+ console.log(urlWithTenant); // https://tenant.example.com/dashboard?x-enc-tenant-token=...
148
+ ```
149
+
150
+ ```typescript
151
+ import type { IHttpRequestQuery } from '@twin.org/api-models';
152
+ import { ComponentFactory } from '@twin.org/core';
153
+
154
+ interface IHostingComponent {
155
+ encryptQueryParams(
156
+ httpRequestQuery: IHttpRequestQuery | undefined,
157
+ keys: string[]
158
+ ): Promise<void>;
159
+ decryptQueryParams(
160
+ httpRequestQuery: IHttpRequestQuery | undefined,
161
+ keys: string[]
162
+ ): Promise<void>;
163
+ }
164
+
165
+ const hosting = ComponentFactory.get<IHostingComponent>('hosting');
166
+
167
+ // Encrypt specific query parameters in place
168
+ const query: IHttpRequestQuery = {
169
+ token: 'secret-123',
170
+ apiKey: 'key-456',
171
+ status: 'active'
172
+ };
173
+
174
+ await hosting.encryptQueryParams(query, ['token', 'apiKey']);
175
+ console.log(query); // { 'x-enc-token': '...', 'x-enc-apiKey': '...', status: 'active' }
176
+
177
+ // Decrypt the parameters back to their original keys
178
+ await hosting.decryptQueryParams(query, ['token', 'apiKey']);
179
+ console.log(query); // { token: 'secret-123', apiKey: 'key-456', status: 'active' }
180
+ ```
181
+
182
+ ```typescript
183
+ import { ComponentFactory } from '@twin.org/core';
184
+
185
+ interface IHostingComponent {
186
+ encryptParam(paramValue: string): Promise<string>;
187
+ decryptParam(encryptedValue: string): Promise<string>;
188
+ }
189
+
190
+ const hosting = ComponentFactory.get<IHostingComponent>('hosting');
191
+
192
+ // Encrypt a single parameter value
193
+ const plainText = 'confidential-data';
194
+ const encrypted = await hosting.encryptParam(plainText);
195
+ console.log(encrypted); // CngBAgMEBQYHCAkKCwwNDg8QERAh...
196
+
197
+ // Decrypt the parameter value
198
+ const decrypted = await hosting.decryptParam(encrypted);
199
+ console.log(decrypted); // confidential-data
200
+ ```
@@ -142,6 +142,34 @@ The combined parts.
142
142
 
143
143
  ***
144
144
 
145
+ ### encodeUriPathSegment() {#encodeuripathsegment}
146
+
147
+ > `static` **encodeUriPathSegment**(`segment`): `string`
148
+
149
+ Encode a single URL path segment per RFC 3986 §3.3.
150
+ Unlike encodeURIComponent, sub-delimiters ($ & + , ; =) and the colon and
151
+ at-sign characters that are valid unencoded in path segments are preserved.
152
+
153
+ #### Parameters
154
+
155
+ ##### segment
156
+
157
+ `string`
158
+
159
+ The raw path segment value to encode.
160
+
161
+ #### Returns
162
+
163
+ `string`
164
+
165
+ The percent-encoded path segment.
166
+
167
+ #### See
168
+
169
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
170
+
171
+ ***
172
+
145
173
  ### replaceOrigin() {#replaceorigin}
146
174
 
147
175
  > `static` **replaceOrigin**(`url`, `newOrigin?`): `string`
@@ -41,3 +41,51 @@ Timeout for requests in ms.
41
41
  > `optional` **includeCredentials?**: `boolean`
42
42
 
43
43
  Include credentials in the request, defaults to true.
44
+
45
+ ***
46
+
47
+ ### customHeaders? {#customheaders}
48
+
49
+ > `optional` **customHeaders?**: () => `Promise`\<`IHttpHeaders`\>
50
+
51
+ Hook to provide headers asynchronously.
52
+
53
+ #### Returns
54
+
55
+ `Promise`\<`IHttpHeaders`\>
56
+
57
+ A promise that resolves to the headers.
58
+
59
+ ***
60
+
61
+ ### customAuthHeader? {#customauthheader}
62
+
63
+ > `optional` **customAuthHeader?**: () => `Promise`\<`string`\>
64
+
65
+ Hook to provide an authorization header value asynchronously.
66
+
67
+ #### Returns
68
+
69
+ `Promise`\<`string`\>
70
+
71
+ A promise that resolves to the authorization header value.
72
+
73
+ ***
74
+
75
+ ### onAuthFailure? {#onauthfailure}
76
+
77
+ > `optional` **onAuthFailure?**: (`err`) => `Promise`\<`void`\>
78
+
79
+ Hook to handle authorization failures asynchronously.
80
+
81
+ #### Parameters
82
+
83
+ ##### err
84
+
85
+ `IError`
86
+
87
+ #### Returns
88
+
89
+ `Promise`\<`void`\>
90
+
91
+ A promise that resolves when the auth failure handling is complete.
@@ -71,3 +71,211 @@ The url to build upon the public origin.
71
71
  `Promise`\<`string`\>
72
72
 
73
73
  The full url based on the public origin.
74
+
75
+ ***
76
+
77
+ ### addEncryptedParamsToUrl() {#addencryptedparamstourl}
78
+
79
+ > **addEncryptedParamsToUrl**(`url`, `params`): `Promise`\<`string`\>
80
+
81
+ Add encrypted key/value pairs to a URL's query string.
82
+ Existing query parameters on the URL are preserved; the provided params are
83
+ merged in and then encrypted before being written back to the URL.
84
+
85
+ #### Parameters
86
+
87
+ ##### url
88
+
89
+ `string`
90
+
91
+ The base URL to add parameters to.
92
+
93
+ ##### params
94
+
95
+ [`IHttpRequestQuery`](IHttpRequestQuery.md)
96
+
97
+ The key/value pairs to encrypt and append.
98
+
99
+ #### Returns
100
+
101
+ `Promise`\<`string`\>
102
+
103
+ The URL with the encrypted parameters added.
104
+
105
+ ***
106
+
107
+ ### getDecryptedParamsFromQueryParams() {#getdecryptedparamsfromqueryparams}
108
+
109
+ > **getDecryptedParamsFromQueryParams**(`queryParams`, `keys`): `Promise`\<[`IHttpRequestQuery`](IHttpRequestQuery.md)\>
110
+
111
+ Decrypt specified keys from a query parameter object and return their plain-text values.
112
+
113
+ #### Parameters
114
+
115
+ ##### queryParams
116
+
117
+ [`IHttpRequestQuery`](IHttpRequestQuery.md) \| `undefined`
118
+
119
+ The HTTP request query containing the encrypted parameters.
120
+
121
+ ##### keys
122
+
123
+ `string`[]
124
+
125
+ The keys to decrypt.
126
+
127
+ #### Returns
128
+
129
+ `Promise`\<[`IHttpRequestQuery`](IHttpRequestQuery.md)\>
130
+
131
+ A map of the decrypted key/value pairs that were present.
132
+
133
+ ***
134
+
135
+ ### addTenantTokenToUrl() {#addtenanttokentourl}
136
+
137
+ > **addTenantTokenToUrl**(`url`, `tenantId`): `Promise`\<`string`\>
138
+
139
+ Encrypt the tenant id and append it as a query parameter to the given URL.
140
+
141
+ #### Parameters
142
+
143
+ ##### url
144
+
145
+ `string`
146
+
147
+ The URL to append the encrypted tenant token to.
148
+
149
+ ##### tenantId
150
+
151
+ `string`
152
+
153
+ The tenant identifier to encrypt and add.
154
+
155
+ #### Returns
156
+
157
+ `Promise`\<`string`\>
158
+
159
+ The URL with the encrypted tenant token added as a query parameter.
160
+
161
+ ***
162
+
163
+ ### getTenantTokenFromQueryParams() {#gettenanttokenfromqueryparams}
164
+
165
+ > **getTenantTokenFromQueryParams**(`queryParams`): `Promise`\<`string` \| `undefined`\>
166
+
167
+ Get the tenant token from the query parameters.
168
+
169
+ #### Parameters
170
+
171
+ ##### queryParams
172
+
173
+ [`IHttpRequestQuery`](IHttpRequestQuery.md) \| `undefined`
174
+
175
+ The HTTP request query containing the parameters.
176
+
177
+ #### Returns
178
+
179
+ `Promise`\<`string` \| `undefined`\>
180
+
181
+ The tenant token if it exists.
182
+
183
+ ***
184
+
185
+ ### encryptQueryParams() {#encryptqueryparams}
186
+
187
+ > **encryptQueryParams**(`httpRequestQuery`, `keys`): `Promise`\<`void`\>
188
+
189
+ Encrypt query parameters using the hosting component's encryption mechanism.
190
+
191
+ #### Parameters
192
+
193
+ ##### httpRequestQuery
194
+
195
+ [`IHttpRequestQuery`](IHttpRequestQuery.md) \| `undefined`
196
+
197
+ The HTTP request query containing the parameters to encrypt.
198
+
199
+ ##### keys
200
+
201
+ `string`[]
202
+
203
+ The keys of the parameters to encrypt.
204
+
205
+ #### Returns
206
+
207
+ `Promise`\<`void`\>
208
+
209
+ A promise that resolves when the query parameters have been encrypted.
210
+
211
+ ***
212
+
213
+ ### decryptQueryParams() {#decryptqueryparams}
214
+
215
+ > **decryptQueryParams**(`httpRequestQuery`, `keys`): `Promise`\<`void`\>
216
+
217
+ Decrypt query parameters using the hosting component's encryption mechanism.
218
+
219
+ #### Parameters
220
+
221
+ ##### httpRequestQuery
222
+
223
+ [`IHttpRequestQuery`](IHttpRequestQuery.md) \| `undefined`
224
+
225
+ The HTTP request query containing the encrypted values.
226
+
227
+ ##### keys
228
+
229
+ `string`[]
230
+
231
+ The keys of the parameters to decrypt.
232
+
233
+ #### Returns
234
+
235
+ `Promise`\<`void`\>
236
+
237
+ A promise that resolves when the query parameters have been decrypted.
238
+
239
+ ***
240
+
241
+ ### encryptParam() {#encryptparam}
242
+
243
+ > **encryptParam**(`paramValue`): `Promise`\<`string`\>
244
+
245
+ Encrypt a parameter value using the hosting component's encryption mechanism.
246
+
247
+ #### Parameters
248
+
249
+ ##### paramValue
250
+
251
+ `string`
252
+
253
+ The value of the parameter to encrypt.
254
+
255
+ #### Returns
256
+
257
+ `Promise`\<`string`\>
258
+
259
+ A promise that resolves to the encrypted value of the parameter.
260
+
261
+ ***
262
+
263
+ ### decryptParam() {#decryptparam}
264
+
265
+ > **decryptParam**(`encryptedValue`): `Promise`\<`string`\>
266
+
267
+ Decrypt a parameter value using the hosting component's encryption mechanism.
268
+
269
+ #### Parameters
270
+
271
+ ##### encryptedValue
272
+
273
+ `string`
274
+
275
+ The encrypted value of the parameter.
276
+
277
+ #### Returns
278
+
279
+ `Promise`\<`string`\>
280
+
281
+ A promise that resolves to the decrypted value of the parameter.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-models",
3
- "version": "0.0.3-next.28",
3
+ "version": "0.0.3-next.29",
4
4
  "description": "Shared API contracts, route types, and response models used across services and clients.",
5
5
  "repository": {
6
6
  "type": "git",