From 925c671485e67a75c3da6219ac72388d138c0086 Mon Sep 17 00:00:00 2001 From: Pasha Zayko Date: Wed, 20 Aug 2025 16:46:50 -0400 Subject: [PATCH 01/14] Adding new spec for URL Shortener Adding spec file and complete src handling for the URL Shortener product --- OpenAPI.code-workspace | 4 + specs/Url-Shortener.json | 1503 +++++++++++++++++ src/urlShortener/TypeScript/.npmignore | 9 + src/urlShortener/TypeScript/LICENSE | 21 + src/urlShortener/TypeScript/README.md | 109 ++ src/urlShortener/TypeScript/index.ts | 40 + src/urlShortener/TypeScript/package-lock.json | 1210 +++++++++++++ src/urlShortener/TypeScript/package.json | 43 + src/urlShortener/TypeScript/tsconfig.json | 16 + 9 files changed, 2955 insertions(+) create mode 100644 specs/Url-Shortener.json create mode 100644 src/urlShortener/TypeScript/.npmignore create mode 100644 src/urlShortener/TypeScript/LICENSE create mode 100644 src/urlShortener/TypeScript/README.md create mode 100644 src/urlShortener/TypeScript/index.ts create mode 100644 src/urlShortener/TypeScript/package-lock.json create mode 100644 src/urlShortener/TypeScript/package.json create mode 100644 src/urlShortener/TypeScript/tsconfig.json diff --git a/OpenAPI.code-workspace b/OpenAPI.code-workspace index 872042a..b525ac8 100644 --- a/OpenAPI.code-workspace +++ b/OpenAPI.code-workspace @@ -12,6 +12,10 @@ "name": "SHIELD", "path": "src/shield" }, + { + "name": "URL Shortener", + "path": "src/urlShortener" + }, { "name": "GitHub Actions", "path": ".github/workflows" diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json new file mode 100644 index 0000000..6441d15 --- /dev/null +++ b/specs/Url-Shortener.json @@ -0,0 +1,1503 @@ +{ + "components": { + "parameters": { + "id": { + "description": "Object ID that uniquely identifies the record.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "examples": [ + "b2fd105a-2594-437e-b934-1a62a51c28b4" + ], + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + } + }, + "responses": { + "204": { + "description": "Request is successful!" + }, + "400": { + "description": "Invalid input!" + }, + "404": { + "description": "The requested object was not found." + } + }, + "schemas": { + "Core.HealthReport": { + "title": "Core System - Health Report", + "description": "Health report that indicates if a service is down or not that the URL Shortener relies on.", + "examples": [ + { + "authClient": true, + "authServer": true, + "database": false + } + ], + "properties": { + "authClient": { + "description": "Flag that indicates if the client side authentication validation is working or not.", + "type": "boolean" + }, + "authServer": { + "description": "Flag that indicates if the server side authentication is working or not.", + "type": "boolean" + }, + "database": { + "description": "Flag that indicates if the ORM (Database) system is down (`false`) or not (`true`). False indicate the service is not working, true indicates the service is working.", + "type": "boolean" + } + }, + "type": "object", + "required": [ + "authClient", + "authServer", + "database" + ] + }, + "Redirect.ConfigurationRecord": { + "title": "Redirect - Configuration Record", + "description": "Object representing table entity with all available metadata.", + "examples": [ + { + "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", + "sourceUrl": "https://www.example.com/very-long-and-complex-url-with plenty-of-special-characters-and-non-repeated-words", + "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "customUserAgentMatcher": null, + "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "targetUrl": "https://www.example.com/target", + "targetUrlCustom": null, + "targetUrlMobile": null, + "targetUrlNodeJs": null, + "targetUrlPowershell": null, + "targetUrlPython": null, + "type": "temporary" + } + ], + "properties": { + "id": { + "description": "Object ID that uniquely identifies this record and can be used as a reference in any other table.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "sourceUrl": { + "description": "The codified version of the full URL that a user would be navigating to. It has to be unique in the table as it will be used during lookup.", + "type": "string", + "format": "uri" + }, + "createdBy": { + "description": "Object ID of the user who initially creates this record.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "customUserAgentMatcher": { + "description": "Custom user agent matcher to be used with the custom target url. Custom target url is non-operable if this is not set. String max char count is 512.", + "type": [ + "string", + "null" + ], + "maxLength": 512 + }, + "domainConfigId": { + "description": "Object ID of the domain configuration that this redirect record is associated with. Used for relationship mapping.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "updatedBy": { + "description": "Object ID of the last user who updated this record.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "targetUrl": { + "description": "Destination that the client will be redirected to if the source is matched. This is the default URL that will be used when no user agent is matched.", + "type": "string", + "format": "uri" + }, + "targetUrlCustom": { + "description": "Target URL for custom defined and matching user agent clients.", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "targetUrlMobile": { + "description": "Target URL for various mobile operating systems.", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "targetUrlNodeJs": { + "description": "Target URL for Node.JS clients.", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "targetUrlPowershell": { + "description": "Target URL for PowerShell clients.", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "targetUrlPython": { + "description": "Target URL for Python clients.", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "type": { + "description": "Type of the redirect. Used to calculate the status code to be sent to the caller.", + "type": "string", + "enum": [ + "temporary", + "permanent" + ] + } + }, + "type": "object", + "required": [ + "id", + "sourceUrl", + "createdBy", + "customUserAgentMatcher", + "domainConfigId", + "updatedBy", + "targetUrl", + "targetUrlCustom", + "targetUrlMobile", + "targetUrlNodeJs", + "targetUrlPowershell", + "targetUrlPython", + "type" + ] + }, + "Redirect.BannedTermRecord": { + "title": "Redirect - Banned Term Record", + "description": "Object representing a banned term that cannot be used in the vanity URLs.", + "examples": [ + { + "id": "d4f5e6a7-b8c9-4d0e-9f1a-2b3c4d5e6f70", + "name": "admin", + "type": "rootSegment" + } + ], + "properties": { + "id": { + "description": "Unique value that identifies this record and will be used as a reference in any other table.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "name": { + "description": "Term or component that is banned in any of the vanity URLs that users can create.", + "type": "string" + }, + "type": { + "description": "Specifies how the name ban operates: `rootSegment` operates matches like `https://example.com//*`, `global` operates matches like `https://example.com/**`.", + "type": "string", + "enum": [ + "rootSegment", + "global" + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "type" + ] + }, + "Redirect.DomainNameRecord": { + "title": "Redirect - Domain Name Record", + "description": "Object representing a domain name entry that will serve as a base for vanity URLs.", + "examples": [ + { + "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", + "allowHttp": false, + "count": 1, + "hidden": false, + "hostName": "example.com", + "type": "vanity" + } + ], + "properties": { + "id": { + "description": "Unique value that identifies this record and will be used as a reference in any other table.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "allowHttp": { + "description": "Flag that indicates if the domain accepts unencrypted traffic (`true`) or not (`false`).", + "type": "boolean" + }, + "count": { + "description": "Current increment for the id number if the domain is in ID number mode. Always incremented even if not in ID mode so as to be able to swap to ID mode if requested with no risk for collision.", + "type": "number", + "format": "int32" + }, + "hidden": { + "description": "Flag that indicates if the domain name is hidden from the user interface. The domain will always be visible on the domain name admin config page. This flag is useful for hiding names that are behind load balancers from end users to reduce confusion. Can also be used to `enable` or `disable` end user interaction in the UI with the specified names.", + "type": "boolean" + }, + "hostName": { + "description": "String to be used in matching the host name. Subdomains need to have their own record to be usable.", + "type": "string" + }, + "type": { + "description": "Flag that indicates the type of shortened URL that can be generated on the domain: `vanity` allows any user structure as long as it is unique. Example: `https://example.com/myUrl`, `idNumber` locks out user control of the vanity URL and hard codes it to an ID number. Example: `https://example.com/managedLink?id=123`.", + "type": "string", + "enum": [ + "vanity", + "idNumber" + ] + } + }, + "type": "object", + "required": [ + "id", + "allowHttp", + "count", + "hidden", + "hostName", + "type" + ] + }, + "Redirect.RbacAssignmentRecord": { + "title": "Redirect - RBAC Assignment Record", + "description": "Object representing an RBAC assignment entry to control user's AuthZ abilities when dealing with redirects/domains/banned terms records.", + "examples": [ + { + "id": "2e1c4b7a-8d3f-4e2b-9a1c-7f5d2b3e6a9c", + "assignmentTargetType": "RedirectConfig", + "createdAt": "2025-08-20T12:00:00Z", + "targetId": "5a7e2c1b-3d4f-4a8b-9e6a-2c1b7f3d8e4a", + "principalId": "9b2e7a1c-4d3f-5a8b-2c6e-1a7f3d9e8b5c", + "principalType": "user", + "role": "b8e2c7a1-4d3f-4a9b-8e6a-2c1b7f3d8e4a" + } + ], + "properties": { + "id": { + "description": "Unique value that identifies this record and will be used as a reference in any other table.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "assignmentTargetType": { + "description": "Type of object that this RBAC assignment is for.", + "type": "string", + "enum": [ + "RedirectConfig", + "DomainName", + "BannedName" + ] + }, + "createdAt": { + "description": "Time stamp of when the assignment was created.", + "type": "string", + "format": "date-time" + }, + "targetId": { + "description": "Unique ID of the system that this assignment describes.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "principalId": { + "description": "Object ID of the principal that this assignment targets.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "principalType": { + "description": "Flag used to optimize principal processing.", + "type": "string", + "enum": [ + "user", + "group" + ] + }, + "role": { + "description": "Role ID of the role that is assigned by this assignment.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + "type": "object", + "required": [ + "id", + "assignmentTargetType", + "createdAt", + "targetId", + "principalId", + "principalType", + "role" + ] + } + }, + "securitySchemes": { + "EntraID": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "The Access Token from Entra ID for the `URL Shortener` Enterprise App (may need to be created from the App Registration)." + } + } + }, + "externalDocs": { + "description": "Official Documentation", + "url": "https://docs.shilab.com" + }, + "info": { + "contact": { + "email": "elliot_huffman@shi.com", + "name": "SHI - Lab" + }, + "description": "Create vanity or numeric short version of your desired URL. Share it digitally, as QR code or print it.", + "title": "SHI URL Shortener", + "version": "1.0.0" + }, + "openapi": "3.1.0", + "paths": { + "/Api/Core/Health": { + "get": { + "summary": "Health of the Service for Probing", + "description": "Check the health of the various components of the URL Shortener and report back. Useful for automated health probing.", + "operationId": "/Api/Core/Health/Get", + "responses": { + "201": { + "description": "Service is operational!" + }, + "500": { + "description": "Service has a failure described with following report.", + "content": { + "application/json": { + "examples": { + "All services operational": { + "description": "Health check report shows all services as available and working.", + "value": { + "authClient": true, + "authServer": true, + "database": true + } + }, + "Database service down": { + "description": "Health check report shows that the database service is down.", + "value": { + "authClient": true, + "authServer": true, + "database": false + } + } + }, + "schema": { + "$ref": "#/components/schemas/Core.HealthReport" + } + } + } + } + }, + "tags": [ + "Core" + ], + "security": [] + } + }, + "/Api/Redirect": { + "get": { + "summary": "Retrieves All Redirect Configuration Records", + "description": "Retrieves all redirect records matching the requested filters, if any.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Redirect/Get", + "parameters": [ + { + "name": "sourceUrl", + "in": "query", + "description": "Filter by the `sourceUrl` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "createdBy", + "in": "query", + "description": "Filter by the `createdBy` field.", + "schema": { + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + { + "name": "customUserAgentMatcher", + "in": "query", + "description": "Filter by the `customUserAgentMatcher` field.", + "schema": { + "type": "string", + "maxLength": 512 + } + }, + { + "name": "domainConfigId", + "in": "query", + "description": "Filter by the `domainConfigId` field.", + "schema": { + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + { + "name": "updatedBy", + "in": "query", + "description": "Filter by the `updatedBy` field.", + "schema": { + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + { + "name": "targetUrl", + "in": "query", + "description": "Filter by the `targetUrl` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "targetUrlCustom", + "in": "query", + "description": "Filter by the `targetUrlCustom` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "targetUrlMobile", + "in": "query", + "description": "Filter by the `targetUrlMobile` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "targetUrlNodeJs", + "in": "query", + "description": "Filter by the `targetUrlNodeJs` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "targetUrlPowershell", + "in": "query", + "description": "Filter by the `targetUrlPowershell` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "targetUrlPython", + "in": "query", + "description": "Filter by the `targetUrlPython` field.", + "schema": { + "type": "string", + "format": "uri" + } + }, + { + "name": "type", + "in": "query", + "description": "Filter by the `type` field.", + "schema": { + "type": "string", + "enum": [ + "temporary", + "permanent" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "post": { + "summary": "Creates New Redirect Configuration Record", + "description": "Creates new URL redirect record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have scope (permission) restrictions.", + "operationId": "/Api/Redirect/Post", + "requestBody": { + "content": { + "application/json": { + "examples": { + "Missing a required field": { + "description": "Payload is missing one or more of the required fields.", + "value": { + "sourceUrl": "https://www.example.com/long-and-complex-url", + "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "customUserAgentMatcher": null, + "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45" + } + }, + "Complete and proper payload": { + "description": "Request where all required fields are present and valid.", + "value": { + "sourceUrl": "https://www.example.com/long-and-complex-url", + "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "customUserAgentMatcher": null, + "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "targetUrl": "https://www.example.com/target", + "targetUrlCustom": null, + "targetUrlMobile": null, + "targetUrlNodeJs": null, + "targetUrlPowershell": null, + "targetUrlPython": null, + "type": "temporary" + } + } + }, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + }, + { + "required": [ + "sourceUrl", + "customUserAgentMatcher", + "targetUrl", + "targetUrlCustom", + "targetUrlMobile", + "targetUrlNodeJs", + "targetUrlPowershell", + "targetUrlPython", + "type" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/Redirect/Check": { + "post": { + "summary": "Checks if Target Name is Available", + "description": "Validates if provided target name is available to be requested. Returns boolean `true` if the name is available, `false` if the name is already recorded.\n\nThis endpoint does not have scope (permission) restrictions.", + "operationId": "/Api/Redirect/Check/Post", + "requestBody": { + "content": { + "application/json": { + "examples": { + "Required field is missing": { + "description": "Payload is missing the required 'sourceName' field.", + "value": {} + }, + "Complete and accurate request": { + "description": "Payload includes all required and valid entries.", + "value": { + "sourceName": "my-vanity-url" + } + } + }, + "schema": { + "type": "object", + "properties": { + "sourceName": { + "description": "URL to check if is taken or not.", + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/Redirect/{id}": { + "get": { + "summary": "Retrieves Specific Redirect Configuration Record", + "description": "Retrieves all information from the database of the redirect record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Redirect/:id/Get", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "patch": { + "summary": "Updates Specific Redirect Configuration Record", + "description": "Updates one or more properties of the redirect record based on which fields are provided.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Redirect/:id/Patch", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "requestBody": { + "content": { + "application/json": { + "examples": { + "Sample request to update record": { + "description": "Payload contains one or more fields that need update in the database.", + "value": { + "targetUrl": "https://www.example.com/target-updated", + "targetUrlNodeJs": "https://www.example.com/target-nodejs", + "type": "permanent" + } + } + }, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + }, + { + "required": [] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "delete": { + "summary": "Removes Specific Redirect Configuration Record", + "description": "Deletes the redirect record from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Redirect/:id/Delete", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/204" + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/BannedName": { + "get": { + "summary": "Retrieves All Banned Name Records", + "description": "Retrieves all records of the banned terms that are not allowed to be used as targets.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/BannedName/Get", + "parameters": [ + { + "name": "name", + "in": "query", + "description": "Filter by the `name` field.", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "Filter by the `type` field.", + "schema": { + "type": "string", + "enum": [ + "rootSegment", + "global" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Redirect.BannedTermRecord" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "post": { + "summary": "Creates New Banned Name Record", + "description": "Create new record of the banned term.\n\nThis endpoint requires the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "operationId": "/Api/BannedName/Post", + "requestBody": { + "content": { + "application/json": { + "examples": { + "Missing a required field": { + "description": "Payload is missing one or more of the required fields.", + "value": { + "name": "admin" + } + }, + "Complete and proper payload": { + "description": "Request where all required fields are present and valid.", + "value": { + "name": "admin", + "type": "rootSegment" + } + } + }, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Redirect.BannedTermRecord" + }, + { + "required": [ + "name", + "type" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.BannedTermRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/BannedName/{id}": { + "get": { + "summary": "Retrieves Specific Banned Name Record", + "description": "Retrieves all information from the database of the banned name record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/BannedName/:id/Get", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.BannedTermRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "delete": { + "summary": "Removes Specific Banned Name Record", + "description": "Deletes the banned name record from the database.\n\nThis endpoint requires the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "operationId": "/Api/BannedName/:id/Delete", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/204" + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/Domain": { + "get": { + "summary": "Retrieves All Domain Name Records", + "description": "Retrieves all domain name records from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Domain/Get", + "parameters": [ + { + "name": "allowHttp", + "in": "query", + "description": "Filter by the `allowHttp` field.", + "schema": { + "type": "boolean" + } + }, + { + "name": "count", + "in": "query", + "description": "Filter by the `count` field.", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "hidden", + "in": "query", + "description": "Filter by the `hidden` field.", + "schema": { + "type": "boolean" + } + }, + { + "name": "hostName", + "in": "query", + "description": "Filter by the `hostName` field.", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "Filter by the `type` field.", + "schema": { + "type": "string", + "enum": [ + "vanity", + "idNumber" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Redirect.DomainNameRecord" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "post": { + "summary": "Creates New Domain Name Record", + "description": "Creates new domain name record in the database and returns information enriched with created metadata.\n\nThis endpoint requires the `Domain.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "operationId": "/Api/Domain/Post", + "requestBody": { + "content": { + "application/json": { + "examples": { + "Missing a required field": { + "description": "Payload is missing one or more of the required fields.", + "value": { + "allowHttp": true, + "hostName": "example.com" + } + }, + "Complete and proper payload": { + "description": "Request where all required fields are present and valid.", + "value": { + "allowHttp": false, + "count": 1, + "hidden": false, + "hostName": "example.com", + "type": "vanity" + } + } + }, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Redirect.DomainNameRecord" + }, + { + "required": [ + "allowHttp", + "count", + "hidden", + "hostName", + "type" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.DomainNameRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/Domain/{id}": { + "get": { + "summary": "Retrieves Specific Domain Name Record", + "description": "Retrieves all information from the database of the domain name record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Domain/:id/Get", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.DomainNameRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "patch": { + "summary": "Updates Specific Domain Name Record", + "description": "Updates one or more properties of the domain name record based on which fields are provided.\n\nThis endpoint requires the `Domain.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "operationId": "/Api/Domain/:id/Patch", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "requestBody": { + "content": { + "application/json": { + "examples": { + "Sample request to update record": { + "description": "Payload contains one or more fields that need update in the database.", + "value": { + "allowHttp": false, + "count": 18, + "type": "idNumber" + } + } + }, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Redirect.DomainNameRecord" + }, + { + "required": [] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.DomainNameRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "delete": { + "summary": "Removes Specific Domain Name Record", + "description": "Deletes the domain name record from the database.\n\nThis endpoint requires the `Domain.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "operationId": "/Api/Domain/:id/Delete", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/204" + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/Rbac": { + "get": { + "summary": "Retrieves All RBAC Assignment Records", + "description": "Retrieves all RBAC assignment records from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Rbac/Get", + "parameters": [ + { + "name": "assignmentTargetType", + "in": "query", + "description": "Filter by the `assignmentTargetType` field.", + "schema": { + "type": "string", + "enum": [ + "RedirectConfig", + "DomainName", + "BannedName" + ] + } + }, + { + "name": "createdAt", + "in": "query", + "description": "Filter by the `createdAt` field.", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "targetId", + "in": "query", + "description": "Filter by the `targetId` field.", + "schema": { + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + { + "name": "principalId", + "in": "query", + "description": "Filter by the `principalId` field.", + "schema": { + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + { + "name": "principalType", + "in": "query", + "description": "Filter by the `principalType` field.", + "schema": { + "type": "string", + "enum": [ + "user", + "group" + ] + } + }, + { + "name": "role", + "in": "query", + "description": "Filter by the `role` field.", + "schema": { + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "post": { + "summary": "Creates New RBAC Assignment Record", + "description": "Create new RBAC assignment record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Rbac/Post", + "requestBody": { + "content": { + "application/json": { + "examples": { + "Missing a required field": { + "description": "Payload is missing one or more of the required fields.", + "value": { + "targetId": "6f2a1b7e-3d4c-4a8b-9e6a-2c1b7f3d8e4a", + "principalId": "1a7e2b3c-5d4f-4c8b-9a6e-7f2b3d1c9e45", + "principalType": "user" + } + }, + "Complete and proper payload": { + "description": "Request where all required fields are present and valid.", + "value": { + "assignmentTargetType": "RedirectConfig", + "targetId": "8b2e7a1c-4d3f-5a8b-0c6e-1a7f3d9e8b5c", + "principalId": "c7e1a2b3-4d5f-4a8b-9e6a-2c1b7f3d8e4a", + "principalType": "user", + "role": "9e1c7b2a-5d3f-4a8b-9c2e-6f1a3d7e8b54" + } + } + }, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + }, + { + "required": [ + "assignmentTargetType", + "targetId", + "principalId", + "principalType", + "role" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + }, + "/Api/Rbac/{id}": { + "get": { + "summary": "Retrieves Specific RBAC Assignment Record", + "description": "Retrieves all information from the database of the RBAC assignment record.\n\nThis endpoint does not have scope (permission) restrictions.", + "operationId": "/Api/Rbac/:id/Get", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + } + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "404": { + "$ref": "#/components/responses/404" + } + }, + "tags": [ + "Redirect Management" + ] + }, + "delete": { + "summary": "Removes Specific RBAC Assignment Record", + "description": "Deletes the RBAC assignment record from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "operationId": "/Api/Rbac/:id/Delete", + "parameters": [ + { + "$ref": "#/components/parameters/id" + } + ], + "responses": { + "204": { + "$ref": "#/components/responses/204" + }, + "400": { + "$ref": "#/components/responses/400" + } + }, + "tags": [ + "Redirect Management" + ] + } + } + }, + "security": [ + { + "EntraID": [] + } + ], + "servers": [ + { + "description": "The service", + "url": "/" + } + ], + "tags": [ + { + "description": "Configures the specified web server to support and process the authentication API routes.", + "name": "Core" + }, + { + "description": "Collection of CRUD operations to manage all aspects of the redirect system: redirect themselves, banned terms, domains, and rbac controls.", + "name": "Redirect Management" + } + ] +} diff --git a/src/urlShortener/TypeScript/.npmignore b/src/urlShortener/TypeScript/.npmignore new file mode 100644 index 0000000..51eb220 --- /dev/null +++ b/src/urlShortener/TypeScript/.npmignore @@ -0,0 +1,9 @@ +# Ignore TypeScript source files while allowing library type descriptions +*.ts +!*.d.ts + +# Ignore the TypeScript config +tsconfig.json + +# Ignore the generated SDK source code +/sdk/ diff --git a/src/urlShortener/TypeScript/LICENSE b/src/urlShortener/TypeScript/LICENSE new file mode 100644 index 0000000..918c40e --- /dev/null +++ b/src/urlShortener/TypeScript/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 SHI International Corp. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/urlShortener/TypeScript/README.md b/src/urlShortener/TypeScript/README.md new file mode 100644 index 0000000..9eea993 --- /dev/null +++ b/src/urlShortener/TypeScript/README.md @@ -0,0 +1,109 @@ +# URL Shortener - TypeScript SDK + +This SDK provides a convenient TypeScript client for interacting with the SHI URL Shortener service. It is automatically generated from the OpenAPI specification located at [`Url-Shortener.json`](https://github.com/Software-Hardware-Integration-Lab/OpenAPI/blob/main/specs/Url-Shortener.json) using [Kiota](https://github.com/microsoft/kiota). + +All typing data is included in the package. + +## Installation + +Install the SDK using npm: + +```bash +npm install @shi-corp/sdk-url-shortener +``` + +## Usage + +Here's a basic example of how to use the SDK: + +```TypeScript +import { DefaultAzureCredential } from '@azure/identity' +import { urlShortenerClientFactory } from '@shi-corp/sdk-url-shortener'; + +/** Authentication session used to authenticate to URL Shortener. */ +const credential = new DefaultAzureCredential(); + +/** Base URL for your URL Shortener instance. Protocol specifier (`http`/`https`) is required, even for localhost. */ +const baseUrl = new URL('https://url-shortner.example.com'); + +/** + * Configured client for URL Shortener that can make authenticated web requests against backend. + * + * The third param, the scope is the `Application ID` of the `End User Login` app registration. + */ +const urlShortenerClient = urlShortenerClientFactory(credential, baseUrl, ['b9689d4e-0036-4f2f-8430-07adedb9ae7c/.default']); + +/** List of available redirect entries. */ +const results = await urlShortenerClient.api.redirect.get(); + +// Check if list is not empty +if (results?.length > 0) { + // Do something +} +``` + +### Advanced Usage + +You can optionally configure the SDK client with a custom base URL, including support for it being nested deep in a L7 load balancer: + +```TypeScript +/** Custom host and endpoint base to as an example for something behind a layer 7 load balancer, E.g. Azure App Gateway or Azure API Gateway. If in debug mode, run against localhost. */ +const customBaseUrl = debugMode ? new URL('http://localhost:3000') : new URL('https://custom-host.example.com/Ballance/Instance1/'); + +/** Configured instance of the URL Shortener client. */ +const customConfiguredClient = urlShortenerClientFactory(credential, customBaseUrl); +``` + +and/or scope (permission) list: + +```TypeScript +/** + * `.default` and explicit permissions can't exist in the same custom scope list at the same time, Entra ID doesn't support this. + * + * If not providing the `.default` scope, you can have any number of scopes (permissions) listed in different array indexes. + */ +const customScopes = ['your-custom-scope/something.read.all', 'your-custom-scope/everything.readwrite.all']; + +// Initialize the SDK client with custom configuration. +const customConfiguredClient = urlShortenerClientFactory(credential, void 0, customScopes); +``` + +## Project Structure + +- `bin/`: Compiled JavaScript files and type definitions. +- `sdk/`: Source TypeScript files generated by Kiota. + - `api/`: API endpoint definitions. + - `models/`: Data models used by the SDK. + +## Development + +### Prerequisites + +- [Node.js](https://nodejs.org/) - Latest LTS version +- [Kiota](https://github.com/microsoft/kiota) + +### Generating the SDK + +To regenerate the SDK from the OpenAPI specification, run: + +```bash +npm run-script generate:Sdk +``` + +### Building the SDK + +To build the SDK for production, run: + +```bash +npm run-script build:Prod +``` + +## License + +This SDK is licensed under the [MIT License](./LICENSE). + +## Support + +For issues or feature requests, please visit the [GitHub Issues page](https://github.com/Software-Hardware-Integration-Lab/OpenAPI/issues). + +For more information, visit the [official documentation](https://docs.shilab.com). diff --git a/src/urlShortener/TypeScript/index.ts b/src/urlShortener/TypeScript/index.ts new file mode 100644 index 0000000..c331326 --- /dev/null +++ b/src/urlShortener/TypeScript/index.ts @@ -0,0 +1,40 @@ +import { assert, assertGuardEquals } from 'typia'; +import { AzureIdentityAuthenticationProvider } from "@microsoft/kiota-authentication-azure"; +import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary"; +import type { TokenCredential } from "@azure/core-auth"; +import { createUrlShortenerClient } from "./sdk/urlShortenerClient.js"; + +// Export all of the SDK's types +export type * from './sdk/models/index.js'; + +/** + * Function that initializes the URL Shortener SDK. + * @param credential Configured authentication session from Entra ID. + * @param baseUrl Root of the URL that should have endpoints appended to it by the query building system. + * @param scopeList Where each array item is a different Entra ID standard scope to request on token retrieval. E.g. `['313f3894-325a-4aae-ba2b-bbdfdc1f063b/.default']` + * @returns Configured API client that is able to make requests against the specified URL Shortener instance. + */ +export function urlShortenerClientFactory(credential: TokenCredential, baseUrl: URL, scopeList: string[]) { + // #region Input Validation + assert(credential); + + assertGuardEquals(baseUrl); + + assertGuardEquals(scopeList); + // #endregion Input Validation + + /** List of hosts that are allowed when making API calls, this is used to prevent token leaks to threat actors. */ + const allowedHostList = new Set([baseUrl.host]); + + /** Authentication system that will be used to configure the SDK client. */ + const authProvider = new AzureIdentityAuthenticationProvider(credential, scopeList, void 0, allowedHostList); + + /** Instance of the URL Shortener SDK client initialization configuration. */ + const urlShortenerAdapter = new FetchRequestAdapter(authProvider); + + // Set the base URL to be what is provided, since the host name is unique every deployment + urlShortenerAdapter.baseUrl = baseUrl.href.endsWith('/') ? baseUrl.href.substring(0, baseUrl.href.length - 1) : baseUrl.href; + + /** Instance of the API client that can be used for URL Shortener access. */ + return createUrlShortenerClient(urlShortenerAdapter); +} diff --git a/src/urlShortener/TypeScript/package-lock.json b/src/urlShortener/TypeScript/package-lock.json new file mode 100644 index 0000000..83b5373 --- /dev/null +++ b/src/urlShortener/TypeScript/package-lock.json @@ -0,0 +1,1210 @@ +{ + "name": "@shi-corp/sdk-url-shortener", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@shi-corp/sdk-url-shortener", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-authentication-azure": "~1.0.0-preview.97", + "@microsoft/kiota-bundle": "~1.0.0-preview.97", + "typia": "~9.7.1" + }, + "devDependencies": { + "@azure/core-auth": "~1.10.0", + "@types/node": "~24.2.1", + "ts-patch": "~3.3.0", + "typescript": "~5.9.2" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.0.tgz", + "integrity": "sha512-88Djs5vBvGbHQHf5ZZcaoNHo6Y8BKZkt3cw2iuJIQzLEgH4Ox6Tm4hjFhbqOxyYsgIG/eJbFEHpxRIfEEWv5Ow==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.0.tgz", + "integrity": "sha512-o0psW8QWQ58fq3i24Q1K2XfS/jYTxr7O1HRcyUE9bV9NttLU+kYOH82Ixj8DGlMTOWgxm1Sss2QAfKK5UkSPxw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@microsoft/kiota-abstractions": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-abstractions/-/kiota-abstractions-1.0.0-preview.97.tgz", + "integrity": "sha512-ENazW/UmJWG5Si8jea2TfhYEsEmKEY+xDMzQW2tx4D7n7a6h6M6sVWLouz//jZ9oxMDIykXuP/fUcO7pHTdwgA==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.7.0", + "@std-uritemplate/std-uritemplate": "^2.0.0", + "tinyduration": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@microsoft/kiota-authentication-azure": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-authentication-azure/-/kiota-authentication-azure-1.0.0-preview.97.tgz", + "integrity": "sha512-s567kjpHMXRZx+XGEXGg3fl5xeA3HXIStxAn/xyYv1+Ww8dRgRwudTvc9++4yuKfU+9tOCKopEdMejOb8pXmVQ==", + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.5.0", + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "@opentelemetry/api": "^1.7.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@microsoft/kiota-bundle": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-bundle/-/kiota-bundle-1.0.0-preview.97.tgz", + "integrity": "sha512-3onjFdZpcFHM88gGYePNYj7iSDwlAhxutFXee9Thu+Ytkgegv75OyvbS02SgX71jolQbse/fn8hdyTpyBON5hQ==", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "@microsoft/kiota-http-fetchlibrary": "^1.0.0-preview.97", + "@microsoft/kiota-serialization-form": "^1.0.0-preview.97", + "@microsoft/kiota-serialization-json": "^1.0.0-preview.97", + "@microsoft/kiota-serialization-multipart": "^1.0.0-preview.97", + "@microsoft/kiota-serialization-text": "^1.0.0-preview.97" + } + }, + "node_modules/@microsoft/kiota-http-fetchlibrary": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-http-fetchlibrary/-/kiota-http-fetchlibrary-1.0.0-preview.97.tgz", + "integrity": "sha512-83+sLY6QEJ8D64+VLbrTzmmAtPq7LdD5+UwuX+E4q1ch4+Ty1EFgx9n61xM5fjG6AOVdqbWaTxhqI3C+5gIuzw==", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "@opentelemetry/api": "^1.7.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@microsoft/kiota-serialization-form": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-form/-/kiota-serialization-form-1.0.0-preview.97.tgz", + "integrity": "sha512-yOjiDjhF26WIerLs7kAOE0ij7GwmEg3KED7oAYV6/T5NQwQBIcJ+4O2LaemvqnQRhZmwyuFhWlh1nLt0yHP+7g==", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "tslib": "^2.6.2" + } + }, + "node_modules/@microsoft/kiota-serialization-json": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-json/-/kiota-serialization-json-1.0.0-preview.97.tgz", + "integrity": "sha512-U5gGuLPXjdL81qoFzd0Ffi/r1MWyprFS8BBHmHZ032KMF+GWtU4IFe5fy1KCdXheTliKtWu5YtnghXSiUQW8Lw==", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "tslib": "^2.6.2" + } + }, + "node_modules/@microsoft/kiota-serialization-multipart": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-multipart/-/kiota-serialization-multipart-1.0.0-preview.97.tgz", + "integrity": "sha512-Srabd69lwtaZT3RP6fC5PELs8B+ct7hJDmRW7t1ZBE7MKRHy7dGi0XtLd19rqIUsRFc7BU1NgcT//LkCSb6ccg==", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "tslib": "^2.6.2" + } + }, + "node_modules/@microsoft/kiota-serialization-text": { + "version": "1.0.0-preview.97", + "resolved": "https://registry.npmjs.org/@microsoft/kiota-serialization-text/-/kiota-serialization-text-1.0.0-preview.97.tgz", + "integrity": "sha512-FJLh8Gq9ZfVSqTr/QoltqLEY9oaHvefmI/WZvyGrNHBVQOR+2TBg32ptiGhRzjJQIZGgJdPj9GYApkDvo8T5MQ==", + "license": "MIT", + "dependencies": { + "@microsoft/kiota-abstractions": "^1.0.0-preview.97", + "tslib": "^2.6.2" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@samchon/openapi": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@samchon/openapi/-/openapi-4.7.1.tgz", + "integrity": "sha512-+rkMlSKMt7l3KGWJVWUle1CXEm0vA8FIF2rufHl+T1gN/gGrTEhL1gDK3FHYf8Nl5XReK0r1vL6Q2QTMwQN7xQ==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/@std-uritemplate/std-uritemplate": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@std-uritemplate/std-uritemplate/-/std-uritemplate-2.0.5.tgz", + "integrity": "sha512-AE3/wSo1J3UwRTN/iOopPKh7aaIjgXZYJ6UkndRL9E9nlEwknpGqGsIoIinWZXsUB6kBVsq0mxfYm4KwEr3VSQ==", + "license": "Apache-2.0" + }, + "node_modules/@types/node": { + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", + "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz", + "integrity": "sha512-sOx1PKSuFwnIl7z4RN0Ls7N9AQawmR9r66eI5rFCzLDIs8HTIYrIpH9QjYWoX0lkgGrkLxXhi4QnK7MizPRrIg==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/comment-json": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/global-prefix": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", + "license": "MIT", + "dependencies": { + "@inquirer/external-editor": "^1.0.0", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "license": "MIT", + "dependencies": { + "drange": "^1.0.2", + "ret": "^0.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/tinyduration": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tinyduration/-/tinyduration-3.4.1.tgz", + "integrity": "sha512-NemFoamVYn7TmtwZKZ3OiliM9fZkr6EWiTM+wKknco6POSy2gS689xx/pXip0JYp40HXpUw6k65CUYHWYUXdaA==", + "license": "MIT" + }, + "node_modules/ts-patch": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ts-patch/-/ts-patch-3.3.0.tgz", + "integrity": "sha512-zAOzDnd5qsfEnjd9IGy1IRuvA7ygyyxxdxesbhMdutt8AHFjD8Vw8hU2rMF89HX1BKRWFYqKHrO8Q6lw0NeUZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "global-prefix": "^4.0.0", + "minimist": "^1.2.8", + "resolve": "^1.22.2", + "semver": "^7.6.3", + "strip-ansi": "^6.0.1" + }, + "bin": { + "ts-patch": "bin/ts-patch.js", + "tspc": "bin/tspc.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typia": { + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/typia/-/typia-9.7.1.tgz", + "integrity": "sha512-HY9hhvAPhFvFHfjA3Ny1DmucUyrbsFalt+wr58oBJyNBFUxE6nTF8aJSpLOv1Bav6dZslNh39MEez0s7qLB1jw==", + "license": "MIT", + "dependencies": { + "@samchon/openapi": "^4.7.1", + "@standard-schema/spec": "^1.0.0", + "commander": "^10.0.0", + "comment-json": "^4.2.3", + "inquirer": "^8.2.5", + "package-manager-detector": "^0.2.0", + "randexp": "^0.5.3" + }, + "bin": { + "typia": "lib/executable/typia.js" + }, + "peerDependencies": { + "typescript": ">=4.8.0 <5.10.0" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/src/urlShortener/TypeScript/package.json b/src/urlShortener/TypeScript/package.json new file mode 100644 index 0000000..f3a1319 --- /dev/null +++ b/src/urlShortener/TypeScript/package.json @@ -0,0 +1,43 @@ +{ + "name": "@shi-corp/sdk-url-shortener", + "version": "1.0.0", + "type": "module", + "main": "bin/index.js", + "description": "SDK client used to interface with the URL Shortener application.", + "keywords": [ + "OpenAPI", + "URL Shortener", + "SDK", + "Automation", + "API", + "SHI" + ], + "homepage": "https://github.com/Software-Hardware-Integration-Lab/OpenAPI#readme", + "bugs": { + "url": "https://github.com/Software-Hardware-Integration-Lab/OpenAPI/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Software-Hardware-Integration-Lab/OpenAPI.git" + }, + "license": "MIT", + "author": "Elliot Huffman (elliot_huffman@shi.com)", + "scripts": { + "build:Dev": "tsc", + "prebuild:Prod": "tsc --sourceMap false --emitDeclarationOnly false", + "build:Prod": "tsc --sourceMap false --removeComments false", + "generate:Sdk": "kiota generate -l typescript -d ../../../specs/Url-Shortener.json -c UrlShortenerClient -o ./sdk/ --exclude-backward-compatible", + "prepare": "ts-patch install" + }, + "devDependencies": { + "@azure/core-auth": "~1.10.0", + "@types/node": "~24.2.1", + "ts-patch": "~3.3.0", + "typescript": "~5.9.2" + }, + "dependencies": { + "@microsoft/kiota-authentication-azure": "~1.0.0-preview.97", + "@microsoft/kiota-bundle": "~1.0.0-preview.97", + "typia": "~9.7.1" + } +} diff --git a/src/urlShortener/TypeScript/tsconfig.json b/src/urlShortener/TypeScript/tsconfig.json new file mode 100644 index 0000000..2380b4d --- /dev/null +++ b/src/urlShortener/TypeScript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "emitDeclarationOnly": true, + "noImplicitAny": false, + "outDir": "./bin", + "plugins": [ + { + "transform": "typia/lib/transform" + } + ] + }, + "extends": "../../../tsconfig.base.json", + "include": [ + "./**/*" + ] +} From 4a3d115f9e2eac4c5920141ed6f428d6c192596a Mon Sep 17 00:00:00 2001 From: Pasha Zayko Date: Wed, 20 Aug 2025 17:00:17 -0400 Subject: [PATCH 02/14] Smallest of typos Fixing completely irrelevant typo in a pseudo-example --- src/urlShortener/TypeScript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/urlShortener/TypeScript/README.md b/src/urlShortener/TypeScript/README.md index 9eea993..e74e625 100644 --- a/src/urlShortener/TypeScript/README.md +++ b/src/urlShortener/TypeScript/README.md @@ -24,7 +24,7 @@ import { urlShortenerClientFactory } from '@shi-corp/sdk-url-shortener'; const credential = new DefaultAzureCredential(); /** Base URL for your URL Shortener instance. Protocol specifier (`http`/`https`) is required, even for localhost. */ -const baseUrl = new URL('https://url-shortner.example.com'); +const baseUrl = new URL('https://url-shortener.example.com'); /** * Configured client for URL Shortener that can make authenticated web requests against backend. From fdf27266af15a77e94dd851e0136e8590a11d4af Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 20:15:09 -0400 Subject: [PATCH 03/14] Update Packages Regenerate lock Update version to reflect URL shortener version. --- src/urlShortener/TypeScript/package-lock.json | 12 ++++++------ src/urlShortener/TypeScript/package.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/urlShortener/TypeScript/package-lock.json b/src/urlShortener/TypeScript/package-lock.json index 83b5373..1d70d4a 100644 --- a/src/urlShortener/TypeScript/package-lock.json +++ b/src/urlShortener/TypeScript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@shi-corp/sdk-url-shortener", - "version": "1.0.0", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@shi-corp/sdk-url-shortener", - "version": "1.0.0", + "version": "0.0.1", "license": "MIT", "dependencies": { "@microsoft/kiota-authentication-azure": "~1.0.0-preview.97", @@ -15,7 +15,7 @@ }, "devDependencies": { "@azure/core-auth": "~1.10.0", - "@types/node": "~24.2.1", + "@types/node": "~24.3.0", "ts-patch": "~3.3.0", "typescript": "~5.9.2" } @@ -198,9 +198,9 @@ "license": "Apache-2.0" }, "node_modules/@types/node": { - "version": "24.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", - "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/src/urlShortener/TypeScript/package.json b/src/urlShortener/TypeScript/package.json index f3a1319..f57cb90 100644 --- a/src/urlShortener/TypeScript/package.json +++ b/src/urlShortener/TypeScript/package.json @@ -1,6 +1,6 @@ { "name": "@shi-corp/sdk-url-shortener", - "version": "1.0.0", + "version": "0.0.1", "type": "module", "main": "bin/index.js", "description": "SDK client used to interface with the URL Shortener application.", @@ -31,7 +31,7 @@ }, "devDependencies": { "@azure/core-auth": "~1.10.0", - "@types/node": "~24.2.1", + "@types/node": "~24.3.0", "ts-patch": "~3.3.0", "typescript": "~5.9.2" }, From 598d374f5e92eb47727fdf7397f9b34fce1990a2 Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 20:15:34 -0400 Subject: [PATCH 04/14] Add URL Shortener Spec Ensure that it is listed in the render system. --- .github/workflows/DeploySpecDocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/DeploySpecDocs.yml b/.github/workflows/DeploySpecDocs.yml index 20db2c4..7a4d752 100644 --- a/.github/workflows/DeploySpecDocs.yml +++ b/.github/workflows/DeploySpecDocs.yml @@ -49,7 +49,7 @@ jobs: run: | DIST_FILE="swagger-ui/dist/swagger-initializer.js" # Remove the 'url:' property and insert the 'urls:' array - sed -i '/url: /c\ urls: [\n {\n "url": "https://raw.githubusercontent.com/Software-Hardware-Integration-Lab/OpenAPI/refs/heads/main/specs/Data-Gateway.json",\n "name": "Data Gateway"\n },\n {\n "url": "https://raw.githubusercontent.com/Software-Hardware-Integration-Lab/OpenAPI/refs/heads/main/specs/SHIELD.json",\n "name": "SHIELD"\n }\n ],' "$DIST_FILE" + sed -i '/url: /c\ urls: [\n{\n"url": "https://raw.githubusercontent.com/Software-Hardware-Integration-Lab/OpenAPI/refs/heads/main/specs/Data-Gateway.json",\n"name": "Data Gateway"\n},\n{\n"url": "https://raw.githubusercontent.com/Software-Hardware-Integration-Lab/OpenAPI/refs/heads/main/specs/SHIELD.json",\n"name": "SHIELD"\n}\n{\n"url": "https://raw.githubusercontent.com/Software-Hardware-Integration-Lab/OpenAPI/refs/heads/main/specs/Url-Shortener.json",\n"name": "Data Gateway"\n}\n],' "$DIST_FILE" # Uploads the built artifact to github pages - name: Upload Artifact From bea2206e9a9914fe013f13ee3318752b28041acc Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 20:16:12 -0400 Subject: [PATCH 05/14] Correct Port Number Make it match the SUS app. --- src/urlShortener/TypeScript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/urlShortener/TypeScript/README.md b/src/urlShortener/TypeScript/README.md index e74e625..0d7e1b2 100644 --- a/src/urlShortener/TypeScript/README.md +++ b/src/urlShortener/TypeScript/README.md @@ -48,7 +48,7 @@ You can optionally configure the SDK client with a custom base URL, including su ```TypeScript /** Custom host and endpoint base to as an example for something behind a layer 7 load balancer, E.g. Azure App Gateway or Azure API Gateway. If in debug mode, run against localhost. */ -const customBaseUrl = debugMode ? new URL('http://localhost:3000') : new URL('https://custom-host.example.com/Ballance/Instance1/'); +const customBaseUrl = debugMode ? new URL('http://localhost:3004') : new URL('https://custom-host.example.com/Ballance/Instance1/'); /** Configured instance of the URL Shortener client. */ const customConfiguredClient = urlShortenerClientFactory(credential, customBaseUrl); From 3f508b5745c3583f7a889721bb928674442bed65 Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 20:24:01 -0400 Subject: [PATCH 06/14] Tweaks + Fixes Update terminology for schemas so that they no longer contain a trailing "record" as that is tacky. Update example for URL configuration as the vanity and destinations were mixed up. --- specs/Url-Shortener.json | 54 ++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 6441d15..4e08046 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -61,18 +61,18 @@ "database" ] }, - "Redirect.ConfigurationRecord": { + "Redirect.UrlConfiguration": { "title": "Redirect - Configuration Record", "description": "Object representing table entity with all available metadata.", "examples": [ { "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", - "sourceUrl": "https://www.example.com/very-long-and-complex-url-with plenty-of-special-characters-and-non-repeated-words", + "sourceUrl": "https://www.example.com/vanityUrl", "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", "customUserAgentMatcher": null, "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", - "targetUrl": "https://www.example.com/target", + "targetUrl": "https://www.example.com/very-long-and-complex-url-with plenty-of-special-characters-and-non-repeated-words", "targetUrlCustom": null, "targetUrlMobile": null, "targetUrlNodeJs": null, @@ -198,7 +198,7 @@ "type" ] }, - "Redirect.BannedTermRecord": { + "Redirect.BannedTerm": { "title": "Redirect - Banned Term Record", "description": "Object representing a banned term that cannot be used in the vanity URLs.", "examples": [ @@ -237,7 +237,7 @@ "type" ] }, - "Redirect.DomainNameRecord": { + "Redirect.DomainName": { "title": "Redirect - Domain Name Record", "description": "Object representing a domain name entry that will serve as a base for vanity URLs.", "examples": [ @@ -295,7 +295,7 @@ "type" ] }, - "Redirect.RbacAssignmentRecord": { + "Redirect.RbacAssignment": { "title": "Redirect - RBAC Assignment Record", "description": "Object representing an RBAC assignment entry to control user's AuthZ abilities when dealing with redirects/domains/banned terms records.", "examples": [ @@ -395,7 +395,7 @@ "email": "elliot_huffman@shi.com", "name": "SHI - Lab" }, - "description": "Create vanity or numeric short version of your desired URL. Share it digitally, as QR code or print it.", + "description": "Create vanity or numeric short version of your desired URL. Share it digitally, as QR code, or print it.", "title": "SHI URL Shortener", "version": "1.0.0" }, @@ -580,7 +580,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + "$ref": "#/components/schemas/Redirect.UrlConfiguration" } } } @@ -636,7 +636,7 @@ "schema": { "allOf": [ { - "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + "$ref": "#/components/schemas/Redirect.UrlConfiguration" }, { "required": [ @@ -662,7 +662,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + "$ref": "#/components/schemas/Redirect.UrlConfiguration" } } } @@ -744,7 +744,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + "$ref": "#/components/schemas/Redirect.UrlConfiguration" } } } @@ -785,7 +785,7 @@ "schema": { "allOf": [ { - "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + "$ref": "#/components/schemas/Redirect.UrlConfiguration" }, { "required": [] @@ -801,7 +801,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.ConfigurationRecord" + "$ref": "#/components/schemas/Redirect.UrlConfiguration" } } } @@ -874,7 +874,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Redirect.BannedTermRecord" + "$ref": "#/components/schemas/Redirect.BannedTerm" } } } @@ -916,7 +916,7 @@ "schema": { "allOf": [ { - "$ref": "#/components/schemas/Redirect.BannedTermRecord" + "$ref": "#/components/schemas/Redirect.BannedTerm" }, { "required": [ @@ -935,7 +935,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.BannedTermRecord" + "$ref": "#/components/schemas/Redirect.BannedTerm" } } } @@ -965,7 +965,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.BannedTermRecord" + "$ref": "#/components/schemas/Redirect.BannedTerm" } } } @@ -1063,7 +1063,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Redirect.DomainNameRecord" + "$ref": "#/components/schemas/Redirect.DomainName" } } } @@ -1109,7 +1109,7 @@ "schema": { "allOf": [ { - "$ref": "#/components/schemas/Redirect.DomainNameRecord" + "$ref": "#/components/schemas/Redirect.DomainName" }, { "required": [ @@ -1131,7 +1131,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.DomainNameRecord" + "$ref": "#/components/schemas/Redirect.DomainName" } } } @@ -1161,7 +1161,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.DomainNameRecord" + "$ref": "#/components/schemas/Redirect.DomainName" } } } @@ -1202,7 +1202,7 @@ "schema": { "allOf": [ { - "$ref": "#/components/schemas/Redirect.DomainNameRecord" + "$ref": "#/components/schemas/Redirect.DomainName" }, { "required": [] @@ -1218,7 +1218,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.DomainNameRecord" + "$ref": "#/components/schemas/Redirect.DomainName" } } } @@ -1341,7 +1341,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + "$ref": "#/components/schemas/Redirect.RbacAssignment" } } } @@ -1388,7 +1388,7 @@ "schema": { "allOf": [ { - "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + "$ref": "#/components/schemas/Redirect.RbacAssignment" }, { "required": [ @@ -1410,7 +1410,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + "$ref": "#/components/schemas/Redirect.RbacAssignment" } } } @@ -1440,7 +1440,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Redirect.RbacAssignmentRecord" + "$ref": "#/components/schemas/Redirect.RbacAssignment" } } } From 8f243d3df1eed94b730902c5d5f5bc39728268fa Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 20:32:04 -0400 Subject: [PATCH 07/14] Mark Protected Properties Ensure that IDs and immutable properties are not required during post operations and are properly marked in the retrieved data sets of the SDK. --- specs/Url-Shortener.json | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 4e08046..bc190bf 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -88,7 +88,8 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true }, "sourceUrl": { "description": "The codified version of the full URL that a user would be navigating to. It has to be unique in the table as it will be used during lookup.", @@ -101,7 +102,8 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true }, "customUserAgentMatcher": { "description": "Custom user agent matcher to be used with the custom target url. Custom target url is non-operable if this is not set. String max char count is 512.", @@ -112,12 +114,13 @@ "maxLength": 512 }, "domainConfigId": { - "description": "Object ID of the domain configuration that this redirect record is associated with. Used for relationship mapping.", + "description": "Object ID of the domain configuration that this redirect record is associated with.", "type": "string", "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true }, "updatedBy": { "description": "Object ID of the last user who updated this record.", @@ -215,7 +218,8 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true }, "name": { "description": "Term or component that is banned in any of the vanity URLs that users can create.", @@ -257,7 +261,8 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true }, "allowHttp": { "description": "Flag that indicates if the domain accepts unencrypted traffic (`true`) or not (`false`).", @@ -266,7 +271,8 @@ "count": { "description": "Current increment for the id number if the domain is in ID number mode. Always incremented even if not in ID mode so as to be able to swap to ID mode if requested with no risk for collision.", "type": "number", - "format": "int32" + "format": "int32", + "readOnly": true }, "hidden": { "description": "Flag that indicates if the domain name is hidden from the user interface. The domain will always be visible on the domain name admin config page. This flag is useful for hiding names that are behind load balancers from end users to reduce confusion. Can also be used to `enable` or `disable` end user interaction in the UI with the specified names.", @@ -316,7 +322,8 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true }, "assignmentTargetType": { "description": "Type of object that this RBAC assignment is for.", From b15fc2dce461456e056f7e2d04f56eac13d1d6de Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 21:07:09 -0400 Subject: [PATCH 08/14] Fix URL URL had a space in it which made it invalid. --- specs/Url-Shortener.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index bc190bf..06378fb 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -72,7 +72,7 @@ "customUserAgentMatcher": null, "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", - "targetUrl": "https://www.example.com/very-long-and-complex-url-with plenty-of-special-characters-and-non-repeated-words", + "targetUrl": "https://www.example.com/very-long-and-complex-url-with_plenty-of-special-characters-and-non-repeated-words", "targetUrlCustom": null, "targetUrlMobile": null, "targetUrlNodeJs": null, From 82528d565d34c307d09f85d8763a8eb0c65eb30f Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 21:07:30 -0400 Subject: [PATCH 09/14] Update Version Update the spec version to 3.1.1 to have some docs wording fixes. --- specs/Url-Shortener.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 06378fb..414f216 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -406,7 +406,7 @@ "title": "SHI URL Shortener", "version": "1.0.0" }, - "openapi": "3.1.0", + "openapi": "3.1.1", "paths": { "/Api/Core/Health": { "get": { From 35b91142e01be8b3cfa427205f59398c41d08665 Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 21:07:55 -0400 Subject: [PATCH 10/14] Match SUS Version The SUS version is currently at 0.0.1. This spec needs to match the server's version at all times. --- specs/Url-Shortener.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 414f216..5d2921d 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -404,7 +404,7 @@ }, "description": "Create vanity or numeric short version of your desired URL. Share it digitally, as QR code, or print it.", "title": "SHI URL Shortener", - "version": "1.0.0" + "version": "0.0.1" }, "openapi": "3.1.1", "paths": { From b10619fe60c846bfe79125126cd3553abffaca70 Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 21:09:16 -0400 Subject: [PATCH 11/14] Update Tags Break down the SUS service's components. It isn't SHIELD and its sole purpose is to provide URL shortening. Not anything else, so it can have more tags in the redirect side of things. --- specs/Url-Shortener.json | 54 ++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 5d2921d..e51745c 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -601,7 +601,7 @@ } }, "tags": [ - "Redirect Management" + "URL Redirect" ] }, "post": { @@ -679,7 +679,7 @@ } }, "tags": [ - "Redirect Management" + "URL Redirect" ] } }, @@ -731,7 +731,7 @@ } }, "tags": [ - "Redirect Management" + "URL Redirect" ] } }, @@ -764,7 +764,7 @@ } }, "tags": [ - "Redirect Management" + "URL Redirect" ] }, "patch": { @@ -821,7 +821,7 @@ } }, "tags": [ - "Redirect Management" + "URL Redirect" ] }, "delete": { @@ -842,7 +842,7 @@ } }, "tags": [ - "Redirect Management" + "URL Redirect" ] } }, @@ -895,7 +895,7 @@ } }, "tags": [ - "Redirect Management" + "Banned Name" ] }, "post": { @@ -952,7 +952,7 @@ } }, "tags": [ - "Redirect Management" + "Banned Name" ] } }, @@ -985,7 +985,7 @@ } }, "tags": [ - "Redirect Management" + "Banned Name" ] }, "delete": { @@ -1006,7 +1006,7 @@ } }, "tags": [ - "Redirect Management" + "Banned Name" ] } }, @@ -1084,7 +1084,7 @@ } }, "tags": [ - "Redirect Management" + "Domain Configuration" ] }, "post": { @@ -1148,7 +1148,7 @@ } }, "tags": [ - "Redirect Management" + "Domain Configuration" ] } }, @@ -1181,7 +1181,7 @@ } }, "tags": [ - "Redirect Management" + "Domain Configuration" ] }, "patch": { @@ -1238,7 +1238,7 @@ } }, "tags": [ - "Redirect Management" + "Domain Configuration" ] }, "delete": { @@ -1259,7 +1259,7 @@ } }, "tags": [ - "Redirect Management" + "Domain Configuration" ] } }, @@ -1362,7 +1362,7 @@ } }, "tags": [ - "Redirect Management" + "RBAC Assignment" ] }, "post": { @@ -1427,7 +1427,7 @@ } }, "tags": [ - "Redirect Management" + "RBAC Assignment" ] } }, @@ -1460,7 +1460,7 @@ } }, "tags": [ - "Redirect Management" + "RBAC Assignment" ] }, "delete": { @@ -1481,7 +1481,7 @@ } }, "tags": [ - "Redirect Management" + "RBAC Assignment" ] } } @@ -1503,8 +1503,20 @@ "name": "Core" }, { - "description": "Collection of CRUD operations to manage all aspects of the redirect system: redirect themselves, banned terms, domains, and rbac controls.", - "name": "Redirect Management" + "description": "Handles the redirection of short URLs to their target destinations, including support for different types of redirects and tracking.", + "name": "URL Redirect" + }, + { + "description": "Manages the list of terms that are prohibited from being used as targets in short URLs to prevent misuse or inappropriate content.", + "name": "Banned Name" + }, + { + "description": "Manages domain names used for generating short URLs, including support for vanity domains and domain-specific settings.", + "name": "Domain Configuration" + }, + { + "description": "Manages role-based access control (RBAC) assignments, allowing for fine-grained permissions and access management for users and groups within the URL shortening service.", + "name": "RBAC Assignment" } ] } From 994f20a311f69725016dc795aa469658d216bcd3 Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 21:25:03 -0400 Subject: [PATCH 12/14] Add Auth ID Route Since SUS will run out of many different tenants, the UI needs to know how to authenticate. Expose the configurations via REST API call. --- specs/Url-Shortener.json | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index e51745c..fd9d218 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -27,6 +27,9 @@ }, "404": { "description": "The requested object was not found." + }, + "500": { + "description": "Internal Server Error" } }, "schemas": { @@ -408,6 +411,56 @@ }, "openapi": "3.1.1", "paths": { + "/Api/Auth/Id": { + "get": { + "description": "Provides the Tenant ID and the Application ID of the service principal that access tokens need to be issued against. This is also useful for configuring public clients to be able to authenticate to for auth code flows.", + "operationId": "/Api/Auth/Id/Get", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "appId": { + "description": "Application ID that should be used in Access Tokens as the audience and the endpoint necessary for auth code flows.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true + }, + "tenantId": { + "description": "Tenant ID necessary for authority host URL configuration and UI customization.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true + } + }, + "type": "object", + "required": [ + "appId", + "tenantId" + ] + } + } + }, + "description": "OK" + }, + "500": { + "$ref": "#/components/responses/500" + } + }, + "tags": [ + "Core" + ], + "security": [], + "summary": "Retrieves the IDs required to authenticate." + } + }, "/Api/Core/Health": { "get": { "summary": "Health of the Service for Probing", From ea5f146e8d64f35ca1e96ce663783ff57376d4f7 Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 22:18:23 -0400 Subject: [PATCH 13/14] Type Narrowing Copilot identified these items to have stricter typing info. --- specs/Url-Shortener.json | 106 +++++++++------------------------------ 1 file changed, 25 insertions(+), 81 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index fd9d218..669b879 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -273,8 +273,9 @@ }, "count": { "description": "Current increment for the id number if the domain is in ID number mode. Always incremented even if not in ID mode so as to be able to swap to ID mode if requested with no risk for collision.", - "type": "number", + "type": "integer", "format": "int32", + "minimum": 0, "readOnly": true }, "hidden": { @@ -283,7 +284,8 @@ }, "hostName": { "description": "String to be used in matching the host name. Subdomains need to have their own record to be usable.", - "type": "string" + "type": "string", + "format": "hostname" }, "type": { "description": "Flag that indicates the type of shortened URL that can be generated on the domain: `vanity` allows any user structure as long as it is unique. Example: `https://example.com/myUrl`, `idNumber` locks out user control of the vanity URL and hard codes it to an ID number. Example: `https://example.com/managedLink?id=123`.", @@ -340,7 +342,8 @@ "createdAt": { "description": "Time stamp of when the assignment was created.", "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true }, "targetId": { "description": "Unique ID of the system that this assignment describes.", @@ -467,7 +470,7 @@ "description": "Check the health of the various components of the URL Shortener and report back. Useful for automated health probing.", "operationId": "/Api/Core/Health/Get", "responses": { - "201": { + "204": { "description": "Service is operational!" }, "500": { @@ -662,6 +665,7 @@ "description": "Creates new URL redirect record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have scope (permission) restrictions.", "operationId": "/Api/Redirect/Post", "requestBody": { + "required": true, "content": { "application/json": { "examples": { @@ -694,24 +698,7 @@ } }, "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/Redirect.UrlConfiguration" - }, - { - "required": [ - "sourceUrl", - "customUserAgentMatcher", - "targetUrl", - "targetUrlCustom", - "targetUrlMobile", - "targetUrlNodeJs", - "targetUrlPowershell", - "targetUrlPython", - "type" - ] - } - ] + "$ref": "#/components/schemas/Redirect.UrlConfiguration" } } } @@ -742,6 +729,7 @@ "description": "Validates if provided target name is available to be requested. Returns boolean `true` if the name is available, `false` if the name is already recorded.\n\nThis endpoint does not have scope (permission) restrictions.", "operationId": "/Api/Redirect/Check/Post", "requestBody": { + "required": true, "content": { "application/json": { "examples": { @@ -763,7 +751,10 @@ "description": "URL to check if is taken or not.", "type": "string" } - } + }, + "required": [ + "sourceName" + ] } } } @@ -843,14 +834,7 @@ } }, "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/Redirect.UrlConfiguration" - }, - { - "required": [] - } - ] + "$ref": "#/components/schemas/Redirect.UrlConfiguration" } } } @@ -956,6 +940,7 @@ "description": "Create new record of the banned term.\n\nThis endpoint requires the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", "operationId": "/Api/BannedName/Post", "requestBody": { + "required": true, "content": { "application/json": { "examples": { @@ -974,17 +959,7 @@ } }, "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/Redirect.BannedTerm" - }, - { - "required": [ - "name", - "type" - ] - } - ] + "$ref": "#/components/schemas/Redirect.BannedTerm" } } } @@ -1043,7 +1018,7 @@ }, "delete": { "summary": "Removes Specific Banned Name Record", - "description": "Deletes the banned name record from the database.\n\nThis endpoint requires the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "description": "Deletes the banned name record from the database.\n\nThis endpoint requires an RBAC assignment or the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", "operationId": "/Api/BannedName/:id/Delete", "parameters": [ { @@ -1083,7 +1058,8 @@ "description": "Filter by the `count` field.", "schema": { "type": "integer", - "format": "int32" + "format": "int32", + "minimum": 0 } }, { @@ -1145,6 +1121,7 @@ "description": "Creates new domain name record in the database and returns information enriched with created metadata.\n\nThis endpoint requires the `Domain.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", "operationId": "/Api/Domain/Post", "requestBody": { + "required": true, "content": { "application/json": { "examples": { @@ -1167,20 +1144,7 @@ } }, "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/Redirect.DomainName" - }, - { - "required": [ - "allowHttp", - "count", - "hidden", - "hostName", - "type" - ] - } - ] + "$ref": "#/components/schemas/Redirect.DomainName" } } } @@ -1254,20 +1218,12 @@ "description": "Payload contains one or more fields that need update in the database.", "value": { "allowHttp": false, - "count": 18, "type": "idNumber" } } }, "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/Redirect.DomainName" - }, - { - "required": [] - } - ] + "$ref": "#/components/schemas/Redirect.DomainName" } } } @@ -1423,6 +1379,7 @@ "description": "Create new RBAC assignment record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", "operationId": "/Api/Rbac/Post", "requestBody": { + "required": true, "content": { "application/json": { "examples": { @@ -1446,20 +1403,7 @@ } }, "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/Redirect.RbacAssignment" - }, - { - "required": [ - "assignmentTargetType", - "targetId", - "principalId", - "principalType", - "role" - ] - } - ] + "$ref": "#/components/schemas/Redirect.RbacAssignment" } } } From fcfd0be8d48944c09e67ea6e1e76324896c525fc Mon Sep 17 00:00:00 2001 From: Elliot Huffman Date: Fri, 22 Aug 2025 22:42:53 -0400 Subject: [PATCH 14/14] Update Permission Wording Makes it more clear. --- specs/Url-Shortener.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 669b879..3b046f5 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -511,7 +511,7 @@ "/Api/Redirect": { "get": { "summary": "Retrieves All Redirect Configuration Records", - "description": "Retrieves all redirect records matching the requested filters, if any.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all redirect records matching the requested filters, if any.\n\nThis endpoint requires that the principal has an RBAC assignment to view the associated records or have the role `Everything.ReadWrite.All`, or `Redirect.ReadWrite.All` assigned in Entra to view all regardless of RBAC.", "operationId": "/Api/Redirect/Get", "parameters": [ { @@ -662,7 +662,7 @@ }, "post": { "summary": "Creates New Redirect Configuration Record", - "description": "Creates new URL redirect record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have scope (permission) restrictions.", + "description": "Creates new URL redirect record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have scope (permission) restrictions. Creating principal is set as RBAC owner of the newly created URL.", "operationId": "/Api/Redirect/Post", "requestBody": { "required": true, @@ -782,7 +782,7 @@ "/Api/Redirect/{id}": { "get": { "summary": "Retrieves Specific Redirect Configuration Record", - "description": "Retrieves all information from the database of the redirect record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all information from the database of the redirect record.\n\nTo view the specific record, the principal has to be a part of an RBAC assignment or one of the following Entra roles: `Everything.ReadWrite.All`, or `Redirect.ReadWrite.All`.", "operationId": "/Api/Redirect/:id/Get", "parameters": [ { @@ -813,7 +813,7 @@ }, "patch": { "summary": "Updates Specific Redirect Configuration Record", - "description": "Updates one or more properties of the redirect record based on which fields are provided.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Updates one or more properties of the redirect record based on which fields are provided.\n\nThis endpoint requires either of the following RBAC assignments: `Owner` or `Contributor` on the specific record, or have the role `Everything.ReadWrite.All`, or `Redirect.ReadWrite.All` assigned in Entra to update regardless of RBAC.", "operationId": "/Api/Redirect/:id/Patch", "parameters": [ { @@ -863,7 +863,7 @@ }, "delete": { "summary": "Removes Specific Redirect Configuration Record", - "description": "Deletes the redirect record from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Deletes the redirect record from the database.\n\nThis endpoint requires an RBAC assignment of `Owner` or `Contributor` on the specific record, or the `Everything.ReadWrite.All`, or `Redirect.ReadWrite.All` scope (permission) regardless of permissions.", "operationId": "/Api/Redirect/:id/Delete", "parameters": [ { @@ -886,7 +886,7 @@ "/Api/BannedName": { "get": { "summary": "Retrieves All Banned Name Records", - "description": "Retrieves all records of the banned terms that are not allowed to be used as targets.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all records of the banned terms that are not allowed to be used as targets.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments to be able to view bans.", "operationId": "/Api/BannedName/Get", "parameters": [ { @@ -987,7 +987,7 @@ "/Api/BannedName/{id}": { "get": { "summary": "Retrieves Specific Banned Name Record", - "description": "Retrieves all information from the database of the banned name record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all information from the database of the banned name record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments to view the specific ban.", "operationId": "/Api/BannedName/:id/Get", "parameters": [ { @@ -1018,7 +1018,7 @@ }, "delete": { "summary": "Removes Specific Banned Name Record", - "description": "Deletes the banned name record from the database.\n\nThis endpoint requires an RBAC assignment or the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", + "description": "Deletes the banned name record from the database.\n\nThis endpoint requires an RBAC assignment and the `BanList.ReadWrite.All` or `Everything.ReadWrite.All` scope (permission).", "operationId": "/Api/BannedName/:id/Delete", "parameters": [ { @@ -1041,7 +1041,7 @@ "/Api/Domain": { "get": { "summary": "Retrieves All Domain Name Records", - "description": "Retrieves all domain name records from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all domain name records from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments.", "operationId": "/Api/Domain/Get", "parameters": [ { @@ -1172,7 +1172,7 @@ "/Api/Domain/{id}": { "get": { "summary": "Retrieves Specific Domain Name Record", - "description": "Retrieves all information from the database of the domain name record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all information from the database of the domain name record.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments.", "operationId": "/Api/Domain/:id/Get", "parameters": [ { @@ -1275,7 +1275,7 @@ "/Api/Rbac": { "get": { "summary": "Retrieves All RBAC Assignment Records", - "description": "Retrieves all RBAC assignment records from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Retrieves all RBAC assignment records from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments.", "operationId": "/Api/Rbac/Get", "parameters": [ { @@ -1376,7 +1376,7 @@ }, "post": { "summary": "Creates New RBAC Assignment Record", - "description": "Create new RBAC assignment record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Create new RBAC assignment record in the database and returns information enriched with created metadata.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments.", "operationId": "/Api/Rbac/Post", "requestBody": { "required": true, @@ -1462,7 +1462,7 @@ }, "delete": { "summary": "Removes Specific RBAC Assignment Record", - "description": "Deletes the RBAC assignment record from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's access level.", + "description": "Deletes the RBAC assignment record from the database.\n\nThis endpoint does not have implicit scope (permission) restrictions but includes an internal check on user's RBAC assignments.", "operationId": "/Api/Rbac/:id/Delete", "parameters": [ {