diff --git a/common/dist/schemas/config_v3.schema.json b/common/dist/schemas/config_v3.schema.json index f9e967a..02711e4 100644 --- a/common/dist/schemas/config_v3.schema.json +++ b/common/dist/schemas/config_v3.schema.json @@ -5584,6 +5584,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -5780,6 +5830,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -8527,6 +8627,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" @@ -13538,6 +13687,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13734,6 +13933,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/dist/schemas/report_v3.schema.json b/common/dist/schemas/report_v3.schema.json index 6cc1350..5868b73 100644 --- a/common/dist/schemas/report_v3.schema.json +++ b/common/dist/schemas/report_v3.schema.json @@ -6017,6 +6017,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -6213,6 +6263,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13494,6 +13594,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13690,6 +13840,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/dist/schemas/resolvedTests_v3.schema.json b/common/dist/schemas/resolvedTests_v3.schema.json index db578a4..8126dfc 100644 --- a/common/dist/schemas/resolvedTests_v3.schema.json +++ b/common/dist/schemas/resolvedTests_v3.schema.json @@ -5597,6 +5597,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -5793,6 +5843,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -8540,6 +8640,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" @@ -13551,6 +13700,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13747,6 +13946,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -22243,6 +22492,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -22439,6 +22738,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -29720,6 +30069,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -29916,6 +30315,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/dist/schemas/screenshot_v3.schema.json b/common/dist/schemas/screenshot_v3.schema.json index b503691..b681351 100644 --- a/common/dist/schemas/screenshot_v3.schema.json +++ b/common/dist/schemas/screenshot_v3.schema.json @@ -187,6 +187,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -383,6 +433,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/dist/schemas/sourceIntegration_v3.schema.json b/common/dist/schemas/sourceIntegration_v3.schema.json new file mode 100644 index 0000000..52d9e0c --- /dev/null +++ b/common/dist/schemas/sourceIntegration_v3.schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "description": "Information about the source integration for a file, enabling upload of changed files back to the source CMS.", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] +} \ No newline at end of file diff --git a/common/dist/schemas/spec_v3.schema.json b/common/dist/schemas/spec_v3.schema.json index 6c1d56a..4fb10e2 100644 --- a/common/dist/schemas/spec_v3.schema.json +++ b/common/dist/schemas/spec_v3.schema.json @@ -5999,6 +5999,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -6195,6 +6245,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13476,6 +13576,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13672,6 +13822,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/dist/schemas/step_v3.schema.json b/common/dist/schemas/step_v3.schema.json index 8960667..fd6e96b 100644 --- a/common/dist/schemas/step_v3.schema.json +++ b/common/dist/schemas/step_v3.schema.json @@ -4799,6 +4799,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -4995,6 +5045,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/dist/schemas/test_v3.schema.json b/common/dist/schemas/test_v3.schema.json index 9d86ed1..5e3f97b 100644 --- a/common/dist/schemas/test_v3.schema.json +++ b/common/dist/schemas/test_v3.schema.json @@ -5398,6 +5398,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -5594,6 +5644,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -12875,6 +12975,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13071,6 +13221,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/resolvePaths.js b/common/src/resolvePaths.js index d6304de..7a217d3 100644 --- a/common/src/resolvePaths.js +++ b/common/src/resolvePaths.js @@ -79,8 +79,8 @@ async function resolvePaths({ * @remark HTTP and HTTPS URLs are returned unchanged without resolution. */ function resolve(baseType, relativePath, filePath) { - // If the path is an http:// or https:// URL, return it - if (relativePath.startsWith("https://") || relativePath.startsWith("http://")) { + // If the path is an http:// or https:// URL, or a heretto: URI, return it + if (relativePath.startsWith("https://") || relativePath.startsWith("http://") || relativePath.startsWith("heretto:")) { return relativePath; } @@ -190,10 +190,11 @@ async function resolvePaths({ objectType: objectType, }); } else if (typeof object[property] === "string") { - // If the property begins with "https://" or "http://", skip it + // If the property begins with "https://", "http://", or "heretto:", skip it if ( object[property].startsWith("https://") || - object[property].startsWith("http://") + object[property].startsWith("http://") || + object[property].startsWith("heretto:") ) { continue; } diff --git a/common/src/schemas/dereferenceSchemas.js b/common/src/schemas/dereferenceSchemas.js index cd1d7c5..3709834 100644 --- a/common/src/schemas/dereferenceSchemas.js +++ b/common/src/schemas/dereferenceSchemas.js @@ -44,6 +44,7 @@ async function dereferenceSchemas() { "runShell_v3.schema.json", "saveCookie_v3.schema.json", "screenshot_v3.schema.json", + "sourceIntegration_v3.schema.json", "spec_v3.schema.json", "step_v3.schema.json", "stopRecord_v3.schema.json", diff --git a/common/src/schemas/output_schemas/config_v3.schema.json b/common/src/schemas/output_schemas/config_v3.schema.json index f9e967a..02711e4 100644 --- a/common/src/schemas/output_schemas/config_v3.schema.json +++ b/common/src/schemas/output_schemas/config_v3.schema.json @@ -5584,6 +5584,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -5780,6 +5830,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -8527,6 +8627,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" @@ -13538,6 +13687,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13734,6 +13933,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/output_schemas/report_v3.schema.json b/common/src/schemas/output_schemas/report_v3.schema.json index 6cc1350..5868b73 100644 --- a/common/src/schemas/output_schemas/report_v3.schema.json +++ b/common/src/schemas/output_schemas/report_v3.schema.json @@ -6017,6 +6017,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -6213,6 +6263,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13494,6 +13594,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13690,6 +13840,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/output_schemas/resolvedTests_v3.schema.json b/common/src/schemas/output_schemas/resolvedTests_v3.schema.json index db578a4..8126dfc 100644 --- a/common/src/schemas/output_schemas/resolvedTests_v3.schema.json +++ b/common/src/schemas/output_schemas/resolvedTests_v3.schema.json @@ -5597,6 +5597,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -5793,6 +5843,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -8540,6 +8640,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" @@ -13551,6 +13700,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13747,6 +13946,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -22243,6 +22492,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -22439,6 +22738,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -29720,6 +30069,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -29916,6 +30315,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/output_schemas/screenshot_v3.schema.json b/common/src/schemas/output_schemas/screenshot_v3.schema.json index b503691..b681351 100644 --- a/common/src/schemas/output_schemas/screenshot_v3.schema.json +++ b/common/src/schemas/output_schemas/screenshot_v3.schema.json @@ -187,6 +187,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -383,6 +433,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/output_schemas/sourceIntegration_v3.schema.json b/common/src/schemas/output_schemas/sourceIntegration_v3.schema.json new file mode 100644 index 0000000..52d9e0c --- /dev/null +++ b/common/src/schemas/output_schemas/sourceIntegration_v3.schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "description": "Information about the source integration for a file, enabling upload of changed files back to the source CMS.", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] +} \ No newline at end of file diff --git a/common/src/schemas/output_schemas/spec_v3.schema.json b/common/src/schemas/output_schemas/spec_v3.schema.json index 6c1d56a..4fb10e2 100644 --- a/common/src/schemas/output_schemas/spec_v3.schema.json +++ b/common/src/schemas/output_schemas/spec_v3.schema.json @@ -5999,6 +5999,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -6195,6 +6245,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13476,6 +13576,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13672,6 +13822,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/output_schemas/step_v3.schema.json b/common/src/schemas/output_schemas/step_v3.schema.json index 8960667..fd6e96b 100644 --- a/common/src/schemas/output_schemas/step_v3.schema.json +++ b/common/src/schemas/output_schemas/step_v3.schema.json @@ -4799,6 +4799,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -4995,6 +5045,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/output_schemas/test_v3.schema.json b/common/src/schemas/output_schemas/test_v3.schema.json index 9d86ed1..5e3f97b 100644 --- a/common/src/schemas/output_schemas/test_v3.schema.json +++ b/common/src/schemas/output_schemas/test_v3.schema.json @@ -5398,6 +5398,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -5594,6 +5644,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -12875,6 +12975,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -13071,6 +13221,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/schemas.json b/common/src/schemas/schemas.json index 37ea4f1..50f923a 100644 --- a/common/src/schemas/schemas.json +++ b/common/src/schemas/schemas.json @@ -5982,6 +5982,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -6178,6 +6228,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -8925,6 +9025,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" @@ -13936,6 +14085,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -14132,6 +14331,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -26352,6 +26601,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -26548,6 +26847,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -29295,6 +29644,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" @@ -34306,6 +34704,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -34502,6 +34950,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -42998,6 +43496,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -43194,6 +43742,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -50475,6 +51073,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -50671,6 +51319,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -59662,6 +60360,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -59858,6 +60606,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -67139,6 +67937,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -67335,6 +68183,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -71165,6 +72063,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -71361,6 +72309,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -71557,6 +72555,56 @@ } ] }, + "sourceIntegration_v3": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "description": "Information about the source integration for a file, enabling upload of changed files back to the source CMS.", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] + }, "spec_v3": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "specification", @@ -77558,6 +78606,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -77754,6 +78852,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -85035,6 +86183,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -85231,6 +86429,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -92792,6 +94040,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -92988,6 +94286,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -100624,6 +101972,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -100820,6 +102218,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -108101,6 +109549,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" @@ -108297,6 +109795,56 @@ } } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "type": "object", + "additionalProperties": false, + "required": [ + "type", + "integrationName" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": [ + "heretto" + ] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/src_schemas/config_v3.schema.json b/common/src/schemas/src_schemas/config_v3.schema.json index 5b5be5c..84d8ee5 100644 --- a/common/src/schemas/src_schemas/config_v3.schema.json +++ b/common/src/schemas/src_schemas/config_v3.schema.json @@ -258,6 +258,55 @@ "type": "string", "description": "Local path where Heretto content was downloaded. Set automatically during processing.", "readOnly": true + }, + "fileMapping": { + "type": "object", + "description": "Mapping of local file paths to Heretto file metadata. Set automatically during content loading.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "fileId": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "filePath": { + "type": "string", + "description": "The path of the file in Heretto." + } + } + } + }, + "uploadOnChange": { + "type": "boolean", + "description": "If `true`, uploads changed screenshots and other media files back to Heretto CMS after test execution.", + "default": false + }, + "resourceDependencies": { + "type": "object", + "description": "Mapping of Heretto file paths to their UUIDs and metadata. Set automatically during content loading by fetching ditamap resource dependencies.", + "readOnly": true, + "additionalProperties": { + "type": "object", + "properties": { + "uuid": { + "type": "string", + "description": "The UUID of the file in Heretto." + }, + "fullPath": { + "type": "string", + "description": "The full xmldb path of the file in Heretto." + }, + "name": { + "type": "string", + "description": "The file name." + }, + "parentFolderId": { + "type": "string", + "description": "The UUID of the parent folder in Heretto." + } + } + } } }, "title": "Heretto CMS integration" diff --git a/common/src/schemas/src_schemas/screenshot_v3.schema.json b/common/src/schemas/src_schemas/screenshot_v3.schema.json index 48e6291..a231415 100644 --- a/common/src/schemas/src_schemas/screenshot_v3.schema.json +++ b/common/src/schemas/src_schemas/screenshot_v3.schema.json @@ -68,6 +68,10 @@ "$ref": "#/components/schemas/crop_element" } ] + }, + "sourceIntegration": { + "description": "Information about the source integration for this screenshot, enabling upload of changed files back to the source CMS. Set automatically during test resolution for files from integrations.", + "$ref": "sourceIntegration_v3.schema.json#" } }, "title": "Capture screenshot (detailed)" diff --git a/common/src/schemas/src_schemas/sourceIntegration_v3.schema.json b/common/src/schemas/src_schemas/sourceIntegration_v3.schema.json new file mode 100644 index 0000000..d71ebc3 --- /dev/null +++ b/common/src/schemas/src_schemas/sourceIntegration_v3.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "sourceIntegration", + "description": "Information about the source integration for a file, enabling upload of changed files back to the source CMS.", + "type": "object", + "additionalProperties": false, + "required": ["type", "integrationName"], + "properties": { + "type": { + "type": "string", + "description": "The type of integration. Currently supported: 'heretto'. Additional types may be added in the future.", + "enum": ["heretto"] + }, + "integrationName": { + "type": "string", + "description": "The name of the integration configuration in the config file. Used to look up authentication credentials." + }, + "fileId": { + "type": "string", + "description": "The unique identifier (UUID) of the file in the source CMS. If not provided, the file will be looked up by path." + }, + "filePath": { + "type": "string", + "description": "The path of the file in the source CMS. Used for lookup if fileId is not available." + }, + "contentPath": { + "type": "string", + "description": "The local path to the file that references this source. Used for resolving relative paths." + } + }, + "examples": [ + { + "type": "heretto", + "integrationName": "my-heretto", + "fileId": "8f3ed200-cbba-11e1-ac51-c82a1446d15c", + "filePath": "/db/organizations/example/repositories/docs/images/screenshot.png" + }, + { + "type": "heretto", + "integrationName": "my-heretto", + "filePath": "images/screenshot.png", + "contentPath": "/tmp/doc-detective/heretto_abc123/topic.dita" + } + ] +} diff --git a/core/dev/index.js b/core/dev/index.js index deb49b7..2fc5bd7 100644 --- a/core/dev/index.js +++ b/core/dev/index.js @@ -8,6 +8,7 @@ require("dotenv").config({ path: path.join(__dirname, ".env") }); async function main() { const json = { input: ["heretto:example"], + // input: ["c:\\Users\\hawkeyexl\\AppData\\Local\\Temp\\doc-detective\\heretto_c96e625d5c1ee50972362046445a5ca4\\ot-output\\dita\\_topics\\espresso.dita"], logLevel: "debug", runOn: [ { @@ -30,6 +31,7 @@ async function main() { organizationId: process.env.HERETTO_ORG_ID, // Organization ID is the subdomain of your Heretto instance (e.g., "silva" for "silva.heretto.com") username: process.env.HERETTO_USERNAME, // Your Heretto username/email apiToken: process.env.HERETTO_TOKEN, // Your Heretto API token (https://help.heretto.com/en/heretto-ccms/api/ccms-api-authentication/basic-authentication) + uploadOnChange: true, // Upload changed screenshots back to Heretto }, ], }, diff --git a/core/package-lock.json b/core/package-lock.json index 25b9037..8e6989c 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -19,8 +19,8 @@ "appium-geckodriver": "^2.1.4", "appium-safari-driver": "^4.1.4", "axios": "^1.13.2", - "doc-detective-common": "3.6.0-dev.1", - "doc-detective-resolver": "3.6.1-dev.1", + "doc-detective-common": "file:../common", + "doc-detective-resolver": "file:../resolver", "dotenv": "^17.2.3", "geckodriver": "^6.1.0", "jq-web": "^0.6.2", @@ -72,6 +72,51 @@ "@img/sharp-win32-x64": "^0.34.5" } }, + "../common": { + "name": "doc-detective-common", + "version": "3.6.0-dev.1", + "license": "AGPL-3.0-only", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^15.1.3", + "ajv": "^8.17.1", + "ajv-errors": "^3.0.0", + "ajv-formats": "^3.0.1", + "ajv-keywords": "^5.1.0", + "axios": "^1.13.2", + "yaml": "^2.8.2" + }, + "devDependencies": { + "chai": "^6.2.1", + "mocha": "^11.7.5", + "sinon": "^21.0.0" + } + }, + "../resolver": { + "name": "doc-detective-resolver", + "version": "3.6.1-dev.1", + "license": "AGPL-3.0-only", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^15.1.3", + "adm-zip": "^0.5.16", + "ajv": "^8.17.1", + "axios": "^1.13.2", + "doc-detective-common": "file:../common", + "dotenv": "^17.2.3", + "fast-xml-parser": "^5.3.3", + "json-schema-faker": "^0.5.9", + "posthog-node": "^5.17.2" + }, + "devDependencies": { + "body-parser": "^2.2.1", + "chai": "^6.2.1", + "express": "^5.2.1", + "mocha": "^11.7.5", + "proxyquire": "^2.1.3", + "semver": "^7.7.3", + "sinon": "^21.0.0", + "yaml": "^2.8.2" + } + }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "15.1.3", "license": "MIT", @@ -1579,15 +1624,6 @@ "node": ">= 0.6" } }, - "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "license": "MIT", - "engines": { - "node": ">=12.0" - } - }, "node_modules/agent-base": { "version": "7.1.4", "license": "MIT", @@ -1598,6 +1634,7 @@ "node_modules/ajv": { "version": "8.17.1", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -1609,13 +1646,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-errors": { - "version": "3.0.0", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, "node_modules/ajv-formats": { "version": "3.0.1", "license": "MIT", @@ -1631,16 +1661,6 @@ } } }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "license": "MIT", @@ -1670,6 +1690,7 @@ "integrity": "sha512-MIifVZdSLdFBRY084PaaQyU8ilG+fgGoVdzGkRLj9Q8hZ2x/ENwjiOveyWLxcjsWMYYwUtm3TTKRvWGrJvX3Iw==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/base-plugin": "^3.0.5", @@ -6091,7 +6112,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-driver/-/base-driver-10.1.2.tgz", "integrity": "sha512-AxCYznylRw4xpOx30dAIQ8m5GsVM9ob/MPwc/KyiKhtofLNxQQlFLvLs7SeX7s8iwvwr0puMasOqlWrWy3gVbA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "@appium/types": "^1.1.2", @@ -6126,7 +6146,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-plugin/-/base-plugin-3.0.5.tgz", "integrity": "sha512-w27EqXQ3zY7xKp61IizbzhZGlxQvFr8uKxeELP5TU/+YxnjU8MyVYmWCG7RXp0dh9n7mZen/WPjZtvqdiBOsMQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/support": "^7.0.4" @@ -6141,7 +6160,6 @@ "resolved": "https://registry.npmjs.org/@appium/docutils/-/docutils-2.2.0.tgz", "integrity": "sha512-bwZIShYLRnWCRdGEgjBX+POXyI0X8fbZ3FNHWDlNzYjCBodYckiMw8X48WDwMRd0AeP5mzjBbHSpH7DKfebuFg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "chalk": "4.1.2", @@ -6171,7 +6189,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -6345,7 +6362,6 @@ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", - "peer": true, "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", @@ -6547,7 +6563,6 @@ "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", "license": "Apache-2.0", - "peer": true, "dependencies": { "kleur": "^4.1.0" }, @@ -6563,7 +6578,6 @@ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", "license": "MIT", - "peer": true, "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" @@ -6585,8 +6599,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/@xmldom/xmldom": { "version": "0.8.11", @@ -6614,7 +6627,6 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", - "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -6628,7 +6640,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -6674,7 +6685,6 @@ "integrity": "sha512-MIifVZdSLdFBRY084PaaQyU8ilG+fgGoVdzGkRLj9Q8hZ2x/ENwjiOveyWLxcjsWMYYwUtm3TTKRvWGrJvX3Iw==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/base-plugin": "^3.0.5", @@ -6740,7 +6750,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -6858,8 +6867,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0", - "peer": true + "license": "Python-2.0" }, "node_modules/appium-geckodriver/node_modules/async": { "version": "3.2.6", @@ -6969,7 +6977,6 @@ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "5.1.2" }, @@ -6991,7 +6998,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", - "peer": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -7003,7 +7009,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7024,7 +7029,6 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -7084,7 +7088,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -7110,7 +7113,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -7133,7 +7135,6 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -7178,7 +7179,6 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "license": "MIT", - "peer": true, "dependencies": { "restore-cursor": "^3.1.0" }, @@ -7191,7 +7191,6 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -7204,7 +7203,6 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "license": "ISC", - "peer": true, "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", @@ -7219,7 +7217,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7231,15 +7228,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/cliui/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -7257,7 +7252,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7273,7 +7267,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -7291,7 +7284,6 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8" } @@ -7301,7 +7293,6 @@ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" @@ -7333,7 +7324,6 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -7346,7 +7336,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -7356,7 +7345,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -7369,7 +7357,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -7391,7 +7378,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } @@ -7417,7 +7403,6 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "license": "MIT", - "peer": true, "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -7433,7 +7418,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -7447,7 +7431,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -7457,7 +7440,6 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -7467,7 +7449,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.6.0" } @@ -7543,7 +7524,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -7561,7 +7541,6 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "license": "MIT", - "peer": true, "dependencies": { "clone": "^1.0.2" }, @@ -7583,7 +7562,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -7603,15 +7581,13 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/diff": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.3.1" } @@ -7646,8 +7622,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/emoji-regex": { "version": "8.0.0", @@ -7659,15 +7634,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -7731,7 +7704,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -7740,15 +7712,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -7785,7 +7755,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -7859,7 +7828,6 @@ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 4.9.1" } @@ -7868,15 +7836,13 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -7913,8 +7879,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/follow-redirects": { "version": "1.15.11", @@ -8006,7 +7971,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8016,7 +7980,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -8070,7 +8033,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -8080,7 +8042,6 @@ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -8192,8 +8153,7 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/has-flag": { "version": "4.0.0", @@ -8255,7 +8215,6 @@ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -8268,8 +8227,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", @@ -8277,7 +8235,6 @@ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8294,7 +8251,6 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -8304,15 +8260,13 @@ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", - "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -8332,15 +8286,13 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/iconv-lite": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "license": "MIT", - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -8392,7 +8344,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.10" } @@ -8432,7 +8383,6 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -8450,8 +8400,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/is-stream": { "version": "2.0.1", @@ -8566,7 +8515,6 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -8575,8 +8523,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/lazystream": { "version": "1.0.1", @@ -8625,7 +8572,6 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14" }, @@ -8696,7 +8642,6 @@ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -8732,7 +8677,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -8742,7 +8686,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -8755,7 +8698,6 @@ "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "3.1.0", "methods": "~1.1.2", @@ -8771,7 +8713,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -8780,15 +8721,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8798,7 +8737,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8808,7 +8746,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -8825,7 +8762,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -8835,8 +8771,7 @@ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/minipass": { "version": "7.1.2", @@ -8861,7 +8796,6 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", - "peer": true, "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", @@ -8878,7 +8812,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -8887,15 +8820,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -8923,7 +8854,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8963,7 +8893,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -8976,15 +8905,13 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -8997,7 +8924,6 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -9016,7 +8942,6 @@ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "license": "MIT", - "peer": true, "dependencies": { "fn.name": "1.x.x" } @@ -9026,7 +8951,6 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "license": "MIT", - "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -9042,7 +8966,6 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "license": "MIT", - "peer": true, "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -9096,7 +9019,6 @@ "resolved": "https://registry.npmjs.org/package-changed/-/package-changed-3.0.0.tgz", "integrity": "sha512-HSRbrO+Ab5AuqqYGSevtKJ1Yt96jW1VKV7wrp8K4SKj5tyDp/7D96uPCQyCPiNtWTEH/7nA3hZ4z2slbc9yFxg==", "license": "ISC", - "peer": true, "dependencies": { "commander": "^6.2.0" }, @@ -9141,7 +9063,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -9191,7 +9112,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -9287,7 +9207,6 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", - "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -9307,7 +9226,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -9323,7 +9241,6 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -9333,7 +9250,6 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -9482,7 +9398,6 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "license": "MIT", - "peer": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -9496,7 +9411,6 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -9519,7 +9433,6 @@ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -9528,8 +9441,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/sanitize-filename": { "version": "1.6.3", @@ -9545,8 +9457,7 @@ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/semver": { "version": "7.7.3", @@ -9565,7 +9476,6 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", @@ -9588,7 +9498,6 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", "license": "MIT", - "peer": true, "dependencies": { "etag": "~1.8.1", "fresh": "~0.5.2", @@ -9605,7 +9514,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -9628,15 +9536,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", - "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -9657,8 +9563,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-geckodriver/node_modules/sharp": { "version": "0.34.5", @@ -9743,7 +9648,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -9763,7 +9667,6 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -9780,7 +9683,6 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9799,7 +9701,6 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9877,7 +9778,6 @@ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -9895,7 +9795,6 @@ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -9911,7 +9810,6 @@ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -9926,7 +9824,6 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -9936,7 +9833,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10150,8 +10046,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/through": { "version": "2.3.8", @@ -10164,7 +10059,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.6" } @@ -10174,7 +10068,6 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14.0.0" } @@ -10215,7 +10108,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", - "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -10239,7 +10131,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10284,7 +10175,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10295,7 +10185,6 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -10305,7 +10194,6 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "license": "MIT", - "peer": true, "dependencies": { "defaults": "^1.0.3" } @@ -10330,7 +10218,6 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", @@ -10353,7 +10240,6 @@ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "license": "MIT", - "peer": true, "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", @@ -10368,7 +10254,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10383,7 +10268,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10398,7 +10282,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10440,7 +10323,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -10471,7 +10353,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -10481,7 +10362,6 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -10497,7 +10377,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "license": "MIT", - "peer": true, "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", @@ -10515,7 +10394,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "license": "ISC", - "peer": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } @@ -10524,15 +10402,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/yargs/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -10550,7 +10426,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -10636,7 +10511,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-driver/-/base-driver-10.1.2.tgz", "integrity": "sha512-AxCYznylRw4xpOx30dAIQ8m5GsVM9ob/MPwc/KyiKhtofLNxQQlFLvLs7SeX7s8iwvwr0puMasOqlWrWy3gVbA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "@appium/types": "^1.1.2", @@ -10671,7 +10545,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-plugin/-/base-plugin-3.0.5.tgz", "integrity": "sha512-w27EqXQ3zY7xKp61IizbzhZGlxQvFr8uKxeELP5TU/+YxnjU8MyVYmWCG7RXp0dh9n7mZen/WPjZtvqdiBOsMQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/support": "^7.0.4" @@ -10686,7 +10559,6 @@ "resolved": "https://registry.npmjs.org/@appium/docutils/-/docutils-2.2.0.tgz", "integrity": "sha512-bwZIShYLRnWCRdGEgjBX+POXyI0X8fbZ3FNHWDlNzYjCBodYckiMw8X48WDwMRd0AeP5mzjBbHSpH7DKfebuFg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "chalk": "4.1.2", @@ -10716,7 +10588,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -10749,7 +10620,6 @@ "resolved": "https://registry.npmjs.org/@appium/schema/-/schema-1.0.0.tgz", "integrity": "sha512-eowz+sV6YxuHdsuRizEhG+AGeeMsUiS9ryWLkdFSqQlsNB+uDudehpYQiRkrrtaGOBzq7a3GDUB7zjc6eR80QA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "json-schema": "0.4.0", "source-map-support": "0.5.21" @@ -10764,7 +10634,6 @@ "resolved": "https://registry.npmjs.org/@appium/support/-/support-7.0.4.tgz", "integrity": "sha512-xIK7cFzouZJLIG6RMmg50M+EsILSeO9e9sSkR4xNSaJajaop7LtpkbMfO5JpTgdNiIUYyIsr/3TusQhxnpPNvw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/logger": "^2.0.3", "@appium/tsconfig": "^1.1.1", @@ -10815,7 +10684,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -10832,7 +10700,6 @@ "resolved": "https://registry.npmjs.org/@appium/tsconfig/-/tsconfig-1.1.1.tgz", "integrity": "sha512-ikjo037sWgY2Oy0HRPGnrKHnOdUh9JyzstD7E6HlFqcZu8hvOP1hDQmKdoBTz8gkmSbZWcMRZmWaL3Yqaz2pLw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@tsconfig/node20": "20.1.8" }, @@ -10846,7 +10713,6 @@ "resolved": "https://registry.npmjs.org/@appium/types/-/types-1.1.2.tgz", "integrity": "sha512-BDyX99GCXWqsfeDxsqsvb6EIfQD7SLTXcCbmcI1PKDTK2wg9znKOtE0YLzXgI6TFQV3+40Xs6za6La/Mv8/rVQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/logger": "^2.0.3", "@appium/schema": "^1.0.0", @@ -10863,7 +10729,6 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -10878,7 +10743,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -10888,7 +10752,6 @@ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.1.90" } @@ -10898,7 +10761,6 @@ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", - "peer": true, "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", @@ -10921,7 +10783,6 @@ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=18" } @@ -10938,7 +10799,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -10961,7 +10821,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -10992,7 +10851,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "license": "ISC", - "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -11010,7 +10868,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11022,15 +10879,13 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", - "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -11048,7 +10903,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11064,7 +10918,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -11083,7 +10936,6 @@ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=14" } @@ -11093,7 +10945,6 @@ "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", "license": "Apache-2.0", - "peer": true, "dependencies": { "kleur": "^4.1.0" }, @@ -11109,7 +10960,6 @@ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", "license": "MIT", - "peer": true, "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" @@ -11119,29 +10969,25 @@ "version": "20.1.8", "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.8.tgz", "integrity": "sha512-Em+IdPfByIzWRRpqWL4Z7ArLHZGxmc36BxE3jCz9nBFSm+5aLaPMZyjwu4yetvyKXeogWcxik4L1jB5JTWfw7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@xmldom/xmldom": { "version": "0.8.11", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" } @@ -11151,7 +10997,6 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", - "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -11164,7 +11009,6 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", - "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -11178,7 +11022,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -11196,7 +11039,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11209,7 +11051,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -11226,7 +11067,6 @@ "integrity": "sha512-MIifVZdSLdFBRY084PaaQyU8ilG+fgGoVdzGkRLj9Q8hZ2x/ENwjiOveyWLxcjsWMYYwUtm3TTKRvWGrJvX3Iw==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/base-plugin": "^3.0.5", @@ -11271,7 +11111,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -11288,7 +11127,6 @@ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "license": "MIT", - "peer": true, "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", @@ -11307,7 +11145,6 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", "license": "MIT", - "peer": true, "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", @@ -11326,7 +11163,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -11336,7 +11172,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", - "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -11356,15 +11191,13 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/archiver-utils/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -11380,7 +11213,6 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -11396,22 +11228,19 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0", - "peer": true + "license": "Python-2.0" }, "node_modules/appium-safari-driver/node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/async-lock": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/asyncbox": { "version": "3.0.0", @@ -11431,15 +11260,13 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/axios": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", - "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -11451,7 +11278,6 @@ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", "license": "Apache-2.0", - "peer": true, "peerDependencies": { "react-native-b4a": "*" }, @@ -11465,15 +11291,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/bare-events": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "license": "Apache-2.0", - "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -11501,22 +11325,19 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/base64-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base64-stream/-/base64-stream-1.0.0.tgz", "integrity": "sha512-BQQZftaO48FcE1Kof9CmXMFaAdqkcNorgc8CxesZv9nMbbTF1EFyQe89UOuh//QMmdtfUDXyO8rgUalemL5ODA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "5.1.2" }, @@ -11529,7 +11350,6 @@ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", "license": "Unlicense", - "peer": true, "engines": { "node": ">=0.6" } @@ -11539,7 +11359,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", - "peer": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -11551,7 +11370,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11572,7 +11390,6 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -11597,7 +11414,6 @@ "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.1.tgz", "integrity": "sha512-Ese7052fdWrxp/vqSJkydgx/1MdBnNOCV2XVfbmdGWD2H6EYza+Q4pyYSuVSnCUD22hfI/BFI4jHaC3NLXLlJQ==", "license": "MIT", - "peer": true, "dependencies": { "stream-buffers": "2.2.x" } @@ -11607,7 +11423,6 @@ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", "license": "MIT", - "peer": true, "dependencies": { "big-integer": "1.6.x" }, @@ -11634,7 +11449,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -11645,7 +11459,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -11661,7 +11474,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -11671,7 +11483,6 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -11685,7 +11496,6 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -11702,7 +11512,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11719,7 +11528,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11732,7 +11540,6 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "license": "MIT", - "peer": true, "dependencies": { "restore-cursor": "^3.1.0" }, @@ -11745,7 +11552,6 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -11758,7 +11564,6 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "license": "ISC", - "peer": true, "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", @@ -11773,7 +11578,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11785,15 +11589,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/cliui/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -11811,7 +11613,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11827,7 +11628,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -11845,7 +11645,6 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8" } @@ -11855,7 +11654,6 @@ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" @@ -11869,7 +11667,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -11881,15 +11678,13 @@ "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", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/color-string": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -11902,7 +11697,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -11912,7 +11706,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -11925,7 +11718,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -11935,7 +11727,6 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", - "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -11948,7 +11739,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } @@ -11958,7 +11748,6 @@ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "license": "MIT", - "peer": true, "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", @@ -11975,7 +11764,6 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "license": "MIT", - "peer": true, "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -11991,7 +11779,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12005,7 +11792,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12015,7 +11801,6 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12025,7 +11810,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.6.0" } @@ -12034,15 +11818,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "license": "Apache-2.0", - "peer": true, "bin": { "crc32": "bin/crc32.njs" }, @@ -12055,7 +11837,6 @@ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "license": "MIT", - "peer": true, "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" @@ -12069,7 +11850,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -12083,15 +11863,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -12107,7 +11885,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -12125,7 +11902,6 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "license": "MIT", - "peer": true, "dependencies": { "clone": "^1.0.2" }, @@ -12138,7 +11914,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.4.0" } @@ -12148,7 +11923,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12159,7 +11933,6 @@ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -12169,15 +11942,13 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/diff": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.3.1" } @@ -12187,7 +11958,6 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -12201,43 +11971,37 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/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", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12247,7 +12011,6 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "license": "MIT", - "peer": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -12257,7 +12020,6 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -12267,7 +12029,6 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -12277,7 +12038,6 @@ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -12290,7 +12050,6 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -12306,7 +12065,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -12315,15 +12073,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12333,7 +12089,6 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -12343,7 +12098,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.x" } @@ -12353,7 +12107,6 @@ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bare-events": "^2.7.0" } @@ -12363,7 +12116,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -12413,8 +12165,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/fast-uri": { "version": "3.1.0", @@ -12438,7 +12189,6 @@ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 4.9.1" } @@ -12447,15 +12197,13 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -12477,7 +12225,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -12493,8 +12240,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/follow-redirects": { "version": "1.15.11", @@ -12507,7 +12253,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=4.0" }, @@ -12522,7 +12267,6 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", - "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -12539,7 +12283,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", - "peer": true, "engines": { "node": ">=14" }, @@ -12552,7 +12295,6 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -12569,7 +12311,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12579,7 +12320,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12592,7 +12332,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12602,7 +12341,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12611,7 +12349,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ftp-response-parser/-/ftp-response-parser-1.0.1.tgz", "integrity": "sha512-++Ahlo2hs/IC7UVQzjcSAfeUpCwTTzs4uvG5XfGnsinIFkWUYF4xWwPd5qZuK8MJrmUIxFMuHcfqaosCDjvIWw==", - "peer": true, "dependencies": { "readable-stream": "^1.0.31" }, @@ -12623,15 +12360,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/ftp-response-parser/node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "license": "MIT", - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -12643,15 +12378,13 @@ "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/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==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12661,7 +12394,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -12671,7 +12403,6 @@ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12684,7 +12415,6 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -12709,7 +12439,6 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", - "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -12723,7 +12452,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -12768,7 +12496,6 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -12780,23 +12507,20 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/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", - "peer": true, "engines": { "node": ">=8" } @@ -12806,7 +12530,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -12819,7 +12542,6 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", - "peer": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -12835,7 +12557,6 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", - "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -12847,8 +12568,7 @@ "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/hpack.js": { "version": "2.1.6", @@ -12856,7 +12576,6 @@ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -12869,8 +12588,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", @@ -12878,7 +12596,6 @@ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -12895,7 +12612,6 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -12905,15 +12621,13 @@ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", - "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -12933,15 +12647,13 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/iconv-lite": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "license": "MIT", - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -12971,22 +12683,19 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/appium-safari-driver/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", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.10" } @@ -12995,15 +12704,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/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==", "license": "MIT", - "peer": true, "dependencies": { "hasown": "^2.0.2" }, @@ -13019,7 +12726,6 @@ "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", - "peer": true, "engines": { "node": ">=8" } @@ -13029,7 +12735,6 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13047,15 +12752,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -13068,7 +12771,6 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -13090,7 +12792,6 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -13105,15 +12806,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/jsftp": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/jsftp/-/jsftp-2.1.3.tgz", "integrity": "sha512-r79EVB8jaNAZbq8hvanL8e8JGu2ZNr2bXdHC4ZdQhRImpSPpnWwm5DYVzQ5QxJmtGtKhNNuvqGgbNaFl604fEQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^3.1.0", "ftp-response-parser": "^1.0.1", @@ -13131,7 +12830,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -13140,15 +12838,13 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "license": "(AFL-2.1 OR BSD-3-Clause)", - "peer": true + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/appium-safari-driver/node_modules/json-schema-traverse": { "version": "1.0.0", @@ -13162,7 +12858,6 @@ "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.1.0.tgz", "integrity": "sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14.14.0" } @@ -13172,7 +12867,6 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -13181,15 +12875,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "license": "MIT", - "peer": true, "dependencies": { "readable-stream": "^2.0.5" }, @@ -13201,15 +12893,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/lazystream/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13225,7 +12915,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -13235,7 +12924,6 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14" }, @@ -13247,15 +12935,13 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -13271,7 +12957,6 @@ "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "license": "ISC", - "peer": true, "dependencies": { "signal-exit": "^3.0.2" } @@ -13293,7 +12978,6 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -13310,7 +12994,6 @@ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -13337,7 +13020,6 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -13347,7 +13029,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -13357,7 +13038,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -13370,7 +13050,6 @@ "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "3.1.0", "methods": "~1.1.2", @@ -13386,7 +13065,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -13395,15 +13073,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13413,7 +13089,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13423,7 +13098,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -13440,7 +13114,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -13450,8 +13123,7 @@ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/minipass": { "version": "7.1.2", @@ -13467,7 +13139,6 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -13477,7 +13148,6 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", - "peer": true, "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", @@ -13494,7 +13164,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -13503,15 +13172,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -13523,15 +13190,13 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "license": "MIT", - "peer": true, "bin": { "ncp": "bin/ncp" } @@ -13541,7 +13206,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13572,7 +13236,6 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -13585,7 +13248,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver" } @@ -13595,7 +13257,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -13605,7 +13266,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -13618,15 +13278,13 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -13639,7 +13297,6 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -13649,7 +13306,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", - "peer": true, "dependencies": { "wrappy": "1" } @@ -13659,7 +13315,6 @@ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "license": "MIT", - "peer": true, "dependencies": { "fn.name": "1.x.x" } @@ -13669,7 +13324,6 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "license": "MIT", - "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -13685,7 +13339,6 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "license": "MIT", - "peer": true, "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -13709,7 +13362,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "license": "MIT", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -13725,7 +13377,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -13741,7 +13392,6 @@ "resolved": "https://registry.npmjs.org/package-changed/-/package-changed-3.0.0.tgz", "integrity": "sha512-HSRbrO+Ab5AuqqYGSevtKJ1Yt96jW1VKV7wrp8K4SKj5tyDp/7D96uPCQyCPiNtWTEH/7nA3hZ4z2slbc9yFxg==", "license": "ISC", - "peer": true, "dependencies": { "commander": "^6.2.0" }, @@ -13760,7 +13410,6 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -13778,7 +13427,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/parse-listing/-/parse-listing-1.1.3.tgz", "integrity": "sha512-a1p1i+9Qyc8pJNwdrSvW1g5TPxRH0sywVi6OzVvYHRo6xwF9bDWBxtH0KkxeOOvhUE8vAMtiSfsYQFOuK901eA==", - "peer": true, "engines": { "node": ">=0.6.21" } @@ -13788,7 +13436,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -13798,7 +13445,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13808,7 +13454,6 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13817,8 +13462,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/path-scurry": { "version": "2.0.1", @@ -13841,7 +13485,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -13851,22 +13494,19 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/pkg-dir": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "license": "MIT", - "peer": true, "dependencies": { "find-up": "^5.0.0" }, @@ -13879,7 +13519,6 @@ "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "license": "MIT", - "peer": true, "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -13894,7 +13533,6 @@ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -13927,7 +13565,6 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6.0" } @@ -13936,15 +13573,13 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", - "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -13957,15 +13592,13 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -13981,7 +13614,6 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13991,7 +13623,6 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -14007,7 +13638,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "license": "MIT", - "peer": true, "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -14023,7 +13653,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=8" } @@ -14033,7 +13662,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", - "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -14064,7 +13692,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -14075,7 +13702,6 @@ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "minimatch": "^5.1.0" } @@ -14085,7 +13711,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -14095,7 +13720,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -14118,7 +13742,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "license": "MIT", - "peer": true, "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", @@ -14139,7 +13762,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14149,7 +13771,6 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "license": "MIT", - "peer": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -14182,7 +13803,6 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -14198,15 +13818,13 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -14215,15 +13833,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/sanitize-filename": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "license": "WTFPL OR ISC", - "peer": true, "dependencies": { "truncate-utf8-bytes": "^1.0.0" } @@ -14233,8 +13849,7 @@ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/semver": { "version": "7.7.3", @@ -14253,7 +13868,6 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", @@ -14276,7 +13890,6 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", "license": "MIT", - "peer": true, "dependencies": { "etag": "~1.8.1", "fresh": "~0.5.2", @@ -14293,7 +13906,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -14316,15 +13928,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", - "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -14345,8 +13955,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/sharp": { "version": "0.34.5", @@ -14355,7 +13964,6 @@ "hasInstallScript": true, "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -14399,7 +14007,6 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -14412,7 +14019,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14434,7 +14040,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -14454,7 +14059,6 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -14471,7 +14075,6 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -14490,7 +14093,6 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -14509,8 +14111,7 @@ "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", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/source-map": { "version": "0.6.1", @@ -14536,7 +14137,6 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -14546,15 +14146,13 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "license": "CC-BY-3.0", - "peer": true + "license": "CC-BY-3.0" }, "node_modules/appium-safari-driver/node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "license": "MIT", - "peer": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -14564,8 +14162,7 @@ "version": "3.0.22", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "license": "CC0-1.0", - "peer": true + "license": "CC0-1.0" }, "node_modules/appium-safari-driver/node_modules/spdy": { "version": "4.0.2", @@ -14573,7 +14170,6 @@ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -14591,7 +14187,6 @@ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -14607,7 +14202,6 @@ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -14622,7 +14216,6 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -14632,7 +14225,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -14642,7 +14234,6 @@ "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", "license": "Unlicense", - "peer": true, "engines": { "node": ">= 0.10.0" } @@ -14652,7 +14243,6 @@ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", "license": "MIT", - "peer": true, "dependencies": { "duplexer": "~0.1.1", "through": "~2.3.4" @@ -14663,7 +14253,6 @@ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", "license": "MIT", - "peer": true, "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", @@ -14675,7 +14264,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -14698,15 +14286,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/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", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14722,7 +14308,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14737,7 +14322,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14751,7 +14335,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14764,7 +14347,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14774,7 +14356,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14784,7 +14365,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -14800,7 +14380,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -14813,7 +14392,6 @@ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", "license": "MIT", - "peer": true, "engines": { "node": ">=20" }, @@ -14826,7 +14404,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "license": "MIT", - "peer": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -14853,7 +14430,6 @@ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "b4a": "^1.6.4" } @@ -14862,22 +14438,19 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/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", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.6" } @@ -14887,7 +14460,6 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14.0.0" } @@ -14897,7 +14469,6 @@ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "license": "WTFPL", - "peer": true, "dependencies": { "utf8-byte-length": "^1.0.1" } @@ -14914,7 +14485,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.3.0.tgz", "integrity": "sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==", "license": "(MIT OR CC0-1.0)", - "peer": true, "dependencies": { "tagged-tag": "^1.0.0" }, @@ -14930,7 +14500,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", - "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -14945,7 +14514,6 @@ "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", "license": "MIT or GPL-2.0", - "peer": true, "engines": { "node": ">= 0.4.0" } @@ -14955,7 +14523,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -14964,15 +14531,13 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "license": "(WTFPL OR MIT)", - "peer": true + "license": "(WTFPL OR MIT)" }, "node_modules/appium-safari-driver/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", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/uuid": { "version": "13.0.0", @@ -14992,7 +14557,6 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "license": "Apache-2.0", - "peer": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -15003,7 +14567,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -15014,7 +14577,6 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -15024,7 +14586,6 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "license": "MIT", - "peer": true, "dependencies": { "defaults": "^1.0.3" } @@ -15049,7 +14610,6 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", @@ -15072,7 +14632,6 @@ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "license": "MIT", - "peer": true, "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", @@ -15087,7 +14646,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15102,7 +14660,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15117,7 +14674,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15136,7 +14692,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15153,15 +14708,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -15183,7 +14736,6 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.0" } @@ -15193,7 +14745,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -15203,7 +14754,6 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -15219,7 +14769,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "license": "MIT", - "peer": true, "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", @@ -15237,7 +14786,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "license": "ISC", - "peer": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } @@ -15246,15 +14794,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/yargs/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -15272,7 +14818,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -15288,7 +14833,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", "license": "MIT", - "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" @@ -15302,7 +14846,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -15312,7 +14855,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -15325,7 +14867,6 @@ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "license": "MIT", - "peer": true, "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", @@ -16502,35 +16043,12 @@ } }, "node_modules/doc-detective-common": { - "version": "3.6.0-dev.1", - "resolved": "https://registry.npmjs.org/doc-detective-common/-/doc-detective-common-3.6.0-dev.1.tgz", - "integrity": "sha512-e+3FNyqjhPUZRq+4A1t7G+au07RZockzCHdQ6LDaQQySPtAiNSO42v48ylbHIu4ZOn06SO933rVJe/b+e1GVdw==", - "license": "AGPL-3.0-only", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^15.1.3", - "ajv": "^8.17.1", - "ajv-errors": "^3.0.0", - "ajv-formats": "^3.0.1", - "ajv-keywords": "^5.1.0", - "axios": "^1.13.2", - "yaml": "^2.8.2" - } + "resolved": "../common", + "link": true }, "node_modules/doc-detective-resolver": { - "version": "3.6.1-dev.1", - "resolved": "https://registry.npmjs.org/doc-detective-resolver/-/doc-detective-resolver-3.6.1-dev.1.tgz", - "integrity": "sha512-3jjr/hRuBAnMiMJsQmdA0APkXv0zlQzoPtpKCTFo5MPBxbN7C5MV76ReEPYUlHOpDFTaqo1CTbCXLNMEgiGQ+g==", - "license": "AGPL-3.0-only", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^15.1.3", - "adm-zip": "^0.5.16", - "ajv": "^8.17.1", - "axios": "^1.13.2", - "doc-detective-common": "3.6.0-dev.1", - "dotenv": "^17.2.3", - "json-schema-faker": "^0.5.9", - "posthog-node": "^5.17.2" - } + "resolved": "../resolver", + "link": true }, "node_modules/dom-serializer": { "version": "2.0.0", @@ -17811,6 +17329,7 @@ "node_modules/jsep": { "version": "1.4.0", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } diff --git a/core/package.json b/core/package.json index aefb6a2..e04c212 100644 --- a/core/package.json +++ b/core/package.json @@ -36,8 +36,8 @@ "appium-geckodriver": "^2.1.4", "appium-safari-driver": "^4.1.4", "axios": "^1.13.2", - "doc-detective-common": "3.6.0-dev.1", - "doc-detective-resolver": "3.6.1-dev.1", + "doc-detective-common": "file:../common", + "doc-detective-resolver": "file:../resolver", "dotenv": "^17.2.3", "geckodriver": "^6.1.0", "jq-web": "^0.6.2", diff --git a/core/src/integrations/heretto.js b/core/src/integrations/heretto.js new file mode 100644 index 0000000..382d7d2 --- /dev/null +++ b/core/src/integrations/heretto.js @@ -0,0 +1,942 @@ +/** + * Heretto CMS uploader - handles uploading files back to Heretto CMS. + */ + +const fs = require("fs"); +const path = require("path"); +const https = require("https"); +const http = require("http"); +const { URL } = require("url"); + +/** + * Heretto uploader class implementing the uploader interface. + */ +class HerettoUploader { + /** + * Checks if this uploader can handle the given source integration. + * @param {Object} sourceIntegration - Source integration metadata + * @returns {boolean} True if this uploader handles Heretto integrations + */ + canHandle(sourceIntegration) { + return sourceIntegration?.type === "heretto"; + } + + /** + * Uploads a file to Heretto CMS. + * @param {Object} options - Upload options + * @param {Object} options.config - Doc Detective config + * @param {Object} options.integrationConfig - Heretto integration config + * @param {string} options.localFilePath - Local file path to upload + * @param {Object} options.sourceIntegration - Source integration metadata + * @param {Function} options.log - Logging function + * @returns {Promise} Upload result with status and description + */ + async upload({ config, integrationConfig, localFilePath, sourceIntegration, log }) { + const result = { + status: "FAIL", + description: "", + }; + + // Validate we have the necessary configuration + if (!integrationConfig) { + result.description = "No Heretto integration configuration found"; + return result; + } + + if (!integrationConfig.organizationId || !integrationConfig.apiToken) { + result.description = "Heretto integration missing organizationId or apiToken"; + return result; + } + + // Construct the API base URL from organizationId + const apiBaseUrl = `https://${integrationConfig.organizationId}.heretto.com`; + + // Resolve the file ID using resource dependencies map + let fileId = sourceIntegration.fileId; + let parentFolderId = sourceIntegration.parentFolderId; + const filename = path.basename(sourceIntegration.filePath); + const relativeFilePath = sourceIntegration.filePath; + + // Try to resolve from resource dependencies map first (most accurate) + if (!fileId && integrationConfig.resourceDependencies) { + const resolvedFile = this.resolveFromDependencies({ + resourceDependencies: integrationConfig.resourceDependencies, + filePath: relativeFilePath, + filename, + log: (level, msg) => log(config, level, msg), + }); + + if (resolvedFile) { + fileId = resolvedFile.uuid; + if (!parentFolderId && resolvedFile.parentFolderId) { + parentFolderId = resolvedFile.parentFolderId; + } + log(config, "debug", `Resolved from dependencies: ${relativeFilePath} -> ${fileId}`); + } + } + + if (!fileId) { + log(config, "debug", `No fileId found, resolving correct folder for: ${sourceIntegration.filePath}`); + + try { + // STEP 1: Resolve the correct target folder first + // This ensures we upload to the right location, not just any file with the same name + if (!parentFolderId && integrationConfig.resourceDependencies) { + const folderResolution = this.findParentFolderFromDependencies({ + resourceDependencies: integrationConfig.resourceDependencies, + filePath: relativeFilePath, + log: (level, msg) => log(config, level, msg), + }); + + parentFolderId = folderResolution.folderId; + + // If not found in dependencies, try to find the target folder as a child + // of the ditamap's parent folder via API + if (!parentFolderId && folderResolution.ditamapParentFolderId && folderResolution.targetFolderName) { + log(config, "debug", `Searching for folder '${folderResolution.targetFolderName}' in ditamap's parent folder`); + parentFolderId = await this.getChildFolderByName({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + parentFolderId: folderResolution.ditamapParentFolderId, + folderName: folderResolution.targetFolderName, + log: (level, msg) => log(config, level, msg), + }); + } + } + + // Fall back to folder search if not found in dependencies + if (!parentFolderId && relativeFilePath) { + const parentDirPath = path.dirname(relativeFilePath); + if (parentDirPath && parentDirPath !== ".") { + const folderName = path.basename(parentDirPath); + log(config, "debug", `Searching for parent folder by name: ${folderName}`); + + parentFolderId = await this.searchFolderByName({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + folderName, + log: (level, msg) => log(config, level, msg), + }); + } + } + + // STEP 2: Look for the file in the correct folder + if (parentFolderId) { + log(config, "debug", `Looking for file '${filename}' in target folder ${parentFolderId}`); + fileId = await this.getFileInFolder({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + folderId: parentFolderId, + filename, + log: (level, msg) => log(config, level, msg), + }); + + if (fileId) { + log(config, "debug", `Found existing file in target folder with ID: ${fileId}`); + } else { + // STEP 3: File doesn't exist in target folder - create it + log(config, "debug", `File not in target folder, creating new document`); + + const mimeType = this.getContentType(localFilePath); + const createResult = await this.createDocument({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + parentFolderId, + filename, + mimeType, + log: (level, msg) => log(config, level, msg), + }); + + if (createResult.created) { + fileId = createResult.documentId; + log(config, "info", `Created new document in Heretto with ID: ${fileId}`); + } else if (createResult.existsInFolder) { + // This shouldn't happen since we just checked, but handle it anyway + log(config, "debug", `File exists in folder (race condition), searching for its ID`); + fileId = await this.getFileInFolder({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + folderId: parentFolderId, + filename, + log: (level, msg) => log(config, level, msg), + }); + + if (!fileId) { + result.description = `File exists in folder but could not get its ID: ${filename}`; + return result; + } + } else { + result.description = `Failed to create document in Heretto: ${filename}`; + return result; + } + } + } else { + // Last resort: search globally by filename (may find file in wrong folder) + log(config, "debug", `No target folder found, searching globally for file: ${filename}`); + fileId = await this.searchFileByName({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + filename, + log: (level, msg) => log(config, level, msg), + }); + + if (!fileId) { + result.description = `Could not find file or parent folder in Heretto: ${sourceIntegration.filePath}`; + return result; + } + } + } catch (error) { + result.description = `Error searching/creating file: ${error.message}`; + return result; + } + } + + // Read the local file + if (!fs.existsSync(localFilePath)) { + result.description = `Local file not found: ${localFilePath}`; + return result; + } + + const fileContent = fs.readFileSync(localFilePath); + const contentType = this.getContentType(localFilePath); + + // Upload to Heretto + try { + await this.uploadFile({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + documentId: fileId, + content: fileContent, + contentType, + log: (level, msg) => log(config, level, msg), + }); + + result.status = "PASS"; + result.description = `Successfully uploaded to Heretto (document ID: ${fileId})`; + } catch (error) { + result.description = `Upload failed: ${error.message}`; + } + + return result; + } + + /** + * Resolves a file path to its UUID using the resource dependencies map. + * @param {Object} options - Resolution options + * @returns {Object|null} File info with uuid and parentFolderId, or null if not found + */ + resolveFromDependencies({ resourceDependencies, filePath, filename, log }) { + if (!resourceDependencies) return null; + + // Normalize the file path for comparison + const normalizedPath = filePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\.\.\//, ""); + + // Try exact path match first + for (const [depPath, info] of Object.entries(resourceDependencies)) { + if (depPath.startsWith("_")) continue; // Skip internal keys + + const normalizedDepPath = depPath.replace(/\\/g, "/"); + + // Check if paths match (accounting for relative path variations) + if (normalizedDepPath === normalizedPath || + normalizedDepPath.endsWith("/" + normalizedPath) || + normalizedDepPath.endsWith(normalizedPath)) { + log("debug", `Found exact path match in dependencies: ${depPath}`); + return info; + } + } + + // Try filename match with parent folder context + const parentDir = path.dirname(normalizedPath); + const parentFolderName = path.basename(parentDir); + + for (const [depPath, info] of Object.entries(resourceDependencies)) { + if (depPath.startsWith("_")) continue; + + const depFilename = path.basename(depPath); + const depParentDir = path.dirname(depPath); + const depParentFolderName = path.basename(depParentDir); + + // Match by filename and parent folder name + if (depFilename === filename && depParentFolderName === parentFolderName) { + log("debug", `Found filename+folder match in dependencies: ${depPath}`); + return info; + } + } + + // Try filename-only match as last resort + for (const [depPath, info] of Object.entries(resourceDependencies)) { + if (depPath.startsWith("_")) continue; + + const depFilename = path.basename(depPath); + if (depFilename === filename) { + log("debug", `Found filename match in dependencies: ${depPath}`); + return info; + } + } + + log("debug", `No match found in dependencies for: ${filePath}`); + return null; + } + + /** + * Finds the parent folder ID for a file path using resource dependencies. + * Returns the target folder name for API lookup if not found in dependencies. + * @param {Object} options - Resolution options + * @returns {Object} { folderId, targetFolderName, ditamapParentFolderId } + */ + findParentFolderFromDependencies({ resourceDependencies, filePath, log }) { + const result = { + folderId: null, + targetFolderName: null, + ditamapParentFolderId: null, + }; + + if (!resourceDependencies) return result; + + // Normalize path and get parent directory + const normalizedPath = filePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\.\.\//, ""); + const parentDir = path.dirname(normalizedPath); + const targetFolderName = path.basename(parentDir); + + result.targetFolderName = targetFolderName; + result.ditamapParentFolderId = resourceDependencies._ditamapParentFolderId || null; + + log("debug", `Looking for parent folder '${targetFolderName}' in dependencies`); + + // Find a sibling file in the same folder to get the parent folder ID + for (const [depPath, info] of Object.entries(resourceDependencies)) { + if (depPath.startsWith("_")) continue; + + const depParentDir = path.dirname(depPath); + const depFolderName = path.basename(depParentDir); + + // If we find a file in the same folder, use its parent folder ID + if (depFolderName === targetFolderName && info.parentFolderId) { + log("debug", `Found sibling file ${depPath} with parent folder ID: ${info.parentFolderId}`); + result.folderId = info.parentFolderId; + return result; + } + } + + // Alternative: look for folder paths in the dependencies + for (const [depPath, info] of Object.entries(resourceDependencies)) { + if (depPath.startsWith("_")) continue; + + // Check if this is the folder itself (ends with folder name) + if (depPath.endsWith("/" + targetFolderName) || depPath === targetFolderName) { + log("debug", `Found folder ${depPath} with ID: ${info.uuid}`); + result.folderId = info.uuid; + return result; + } + } + + log("debug", `Could not find parent folder '${targetFolderName}' in dependencies, will search via API`); + return result; + } + + /** + * Gets a child folder within a parent folder by name. + * @param {Object} options - Search options + * @returns {Promise} Child folder ID if found, null otherwise + */ + async getChildFolderByName({ apiBaseUrl, apiToken, username, parentFolderId, folderName, log }) { + const folderUrl = new URL(`/rest/all-files/${parentFolderId}`, apiBaseUrl); + + return new Promise((resolve, reject) => { + const protocol = folderUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: folderUrl.hostname, + port: folderUrl.port || (folderUrl.protocol === "https:" ? 443 : 80), + path: folderUrl.pathname, + method: "GET", + headers: { + "Authorization": `Basic ${authString}`, + "Accept": "application/xml", + }, + }; + + log("debug", `Looking for child folder '${folderName}' in parent ${parentFolderId}`); + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode === 200) { + try { + // Parse XML to find the folder by name in children + // Looking for: + const escapedFolderName = folderName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const folderMatch = data.match(new RegExp(` { + log("debug", `Error getting folder contents: ${error.message}`); + resolve(null); + }); + + req.end(); + }); + } + + /** + * Creates a new document in Heretto. + * @param {Object} options - Creation options + * @returns {Promise} Result with created: boolean, documentId: string (if successful or already exists) + */ + async createDocument({ apiBaseUrl, apiToken, username, parentFolderId, filename, mimeType, log }) { + const createUrl = new URL(`/rest/all-files/${parentFolderId}`, apiBaseUrl); + + const createBody = `${this.escapeXml(filename)}${mimeType}`; + + return new Promise((resolve, reject) => { + const protocol = createUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: createUrl.hostname, + port: createUrl.port || (createUrl.protocol === "https:" ? 443 : 80), + path: createUrl.pathname, + method: "POST", + headers: { + "Content-Type": "application/xml", + "Authorization": `Basic ${authString}`, + "Content-Length": Buffer.byteLength(createBody), + }, + }; + + log("debug", `Creating document at ${createUrl.toString()}`); + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode === 200 || res.statusCode === 201) { + try { + // Parse the XML response to extract the document ID + // Response format: ... + const idMatch = data.match(/id="([^"]+)"/); + if (idMatch && idMatch[1]) { + log("debug", `Document created successfully with ID: ${idMatch[1]}`); + resolve({ created: true, documentId: idMatch[1] }); + } else { + log("warning", `Document created but could not parse ID from response: ${data}`); + reject(new Error("Could not parse document ID from create response")); + } + } catch (parseError) { + reject(new Error(`Failed to parse create response: ${parseError.message}`)); + } + } else if (res.statusCode === 400 && data.includes("already exists")) { + // File already exists in this folder - we need to find its ID + log("debug", `Document already exists in folder, will search for existing file`); + resolve({ created: false, existsInFolder: true, parentFolderId }); + } else { + reject(new Error(`Create document failed with status ${res.statusCode}: ${data}`)); + } + }); + }); + + req.on("error", (error) => { + reject(new Error(`Create document request error: ${error.message}`)); + }); + + req.write(createBody); + req.end(); + }); + } + + /** + * Gets file information from a specific folder. + * @param {Object} options - Options + * @returns {Promise} File ID if found, null otherwise + */ + async getFileInFolder({ apiBaseUrl, apiToken, username, folderId, filename, log }) { + const folderUrl = new URL(`/rest/all-files/${folderId}`, apiBaseUrl); + + return new Promise((resolve, reject) => { + const protocol = folderUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: folderUrl.hostname, + port: folderUrl.port || (folderUrl.protocol === "https:" ? 443 : 80), + path: folderUrl.pathname, + method: "GET", + headers: { + "Authorization": `Basic ${authString}`, + "Accept": "application/xml", + }, + }; + + log("debug", `Getting folder contents: ${folderUrl.toString()}`); + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode === 200) { + try { + // Parse XML to find the file by name + // Looking for child resources with matching name + // Example: ... + const escapedFilename = filename.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const nameIdMatch = data.match(new RegExp(`id="([^"]+)"[^>]*name="${escapedFilename}"`, 'i')); + const idNameMatch = data.match(new RegExp(`name="${escapedFilename}"[^>]*id="([^"]+)"`, 'i')); + + const match = nameIdMatch || idNameMatch; + if (match && match[1]) { + log("debug", `Found file ${filename} with ID: ${match[1]}`); + resolve(match[1]); + } else { + log("debug", `File ${filename} not found in folder ${folderId}`); + resolve(null); + } + } catch (parseError) { + log("debug", `Error parsing folder contents: ${parseError.message}`); + resolve(null); + } + } else { + log("debug", `Failed to get folder contents: ${res.statusCode}`); + resolve(null); + } + }); + }); + + req.on("error", (error) => { + log("debug", `Error getting folder contents: ${error.message}`); + resolve(null); + }); + + req.end(); + }); + } + + /** + * Escapes special characters for XML. + * @param {string} str - String to escape + * @returns {string} Escaped string + */ + escapeXml(str) { + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + /** + * Searches for a folder in Heretto by name. + * @param {Object} options - Search options + * @returns {Promise} Folder ID if found, null otherwise + */ + async searchFolderByName({ apiBaseUrl, apiToken, username, folderName, log }) { + const searchUrl = new URL("/ezdnxtgen/api/search", apiBaseUrl); + + const searchBody = JSON.stringify({ + queryString: folderName, + searchResultType: "FOLDERS_ONLY", + }); + + return new Promise((resolve, reject) => { + const protocol = searchUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: searchUrl.hostname, + port: searchUrl.port || (searchUrl.protocol === "https:" ? 443 : 80), + path: searchUrl.pathname, + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Basic ${authString}`, + "Content-Length": Buffer.byteLength(searchBody), + }, + }; + + log("debug", `Searching for folder: ${folderName}`); + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode >= 200 && res.statusCode < 300) { + try { + // Handle empty response body (no results) + if (!data || data.trim() === "") { + log("debug", "Folder search returned empty response - no results found"); + resolve(null); + return; + } + + const result = JSON.parse(data); + // Find the matching folder in results + if (result.searchResults && result.searchResults.length > 0) { + // Look for exact folder name match + const match = result.searchResults.find( + (r) => r.name === folderName || r.title === folderName + ); + if (match) { + log("debug", `Found folder: ${folderName} with ID: ${match.uuid || match.id}`); + resolve(match.uuid || match.id); + } else { + // Take first result as fallback + log("debug", `Exact folder match not found, using first result: ${result.searchResults[0].uuid || result.searchResults[0].id}`); + resolve(result.searchResults[0].uuid || result.searchResults[0].id); + } + } else { + log("debug", `No folders found matching: ${folderName}`); + resolve(null); + } + } catch (parseError) { + reject(new Error(`Failed to parse folder search response: ${parseError.message}`)); + } + } else { + reject(new Error(`Folder search request failed with status ${res.statusCode}: ${data}`)); + } + }); + }); + + req.on("error", (error) => { + reject(new Error(`Folder search request error: ${error.message}`)); + }); + + req.write(searchBody); + req.end(); + }); + } + + /** + * Searches for a file in Heretto by filename. + * @param {Object} options - Search options + * @returns {Promise} Document ID if found, null otherwise + */ + async searchFileByName({ apiBaseUrl, apiToken, username, filename, log }) { + const searchUrl = new URL("/ezdnxtgen/api/search", apiBaseUrl); + + const searchBody = JSON.stringify({ + queryString: filename, + searchResultType: "FILES_ONLY", + }); + + return new Promise((resolve, reject) => { + const protocol = searchUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: searchUrl.hostname, + port: searchUrl.port || (searchUrl.protocol === "https:" ? 443 : 80), + path: searchUrl.pathname, + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Basic ${authString}`, + "Content-Length": Buffer.byteLength(searchBody), + }, + }; + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode >= 200 && res.statusCode < 300) { + try { + // Handle empty response body (no results) + if (!data || data.trim() === "") { + log("debug", "Search returned empty response - no results found"); + resolve(null); + return; + } + + const result = JSON.parse(data); + // Find the matching file in results + if (result.searchResults && result.searchResults.length > 0) { + // Look for exact filename match + const match = result.searchResults.find( + (r) => r.name === filename || r.title === filename + ); + if (match) { + resolve(match.uuid || match.id); + } else { + // Take first result as fallback + resolve(result.searchResults[0].uuid || result.searchResults[0].id); + } + } else { + resolve(null); + } + } catch (parseError) { + reject(new Error(`Failed to parse search response: ${parseError.message}`)); + } + } else { + reject(new Error(`Search request failed with status ${res.statusCode}: ${data}`)); + } + }); + }); + + req.on("error", (error) => { + reject(new Error(`Search request error: ${error.message}`)); + }); + + req.write(searchBody); + req.end(); + }); + } + + /** + * Uploads file content to Heretto. + * @param {Object} options - Upload options + * @returns {Promise} + */ + async uploadFile({ apiBaseUrl, apiToken, username, documentId, content, contentType, log }) { + const uploadUrl = new URL(`/rest/all-files/${documentId}/content`, apiBaseUrl); + + return new Promise((resolve, reject) => { + const protocol = uploadUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: uploadUrl.hostname, + port: uploadUrl.port || (uploadUrl.protocol === "https:" ? 443 : 80), + path: uploadUrl.pathname, + method: "PUT", + headers: { + "Content-Type": contentType, + "Authorization": `Basic ${authString}`, + "Content-Length": Buffer.byteLength(content), + }, + }; + + log("debug", `Uploading to ${uploadUrl.toString()}`); + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode >= 200 && res.statusCode < 300) { + log("debug", `Upload successful: ${res.statusCode}`); + resolve(); + } else { + reject(new Error(`Upload failed with status ${res.statusCode}: ${data}`)); + } + }); + }); + + req.on("error", (error) => { + reject(new Error(`Upload request error: ${error.message}`)); + }); + + req.write(content); + req.end(); + }); + } + + /** + * Gets document information from Heretto. + * @param {Object} options - Options + * @returns {Promise} Document info including id, name, mimeType, folderUuid, uri + */ + async getDocumentInfo({ apiBaseUrl, apiToken, username, documentId, log }) { + const docUrl = new URL(`/rest/all-files/${documentId}`, apiBaseUrl); + + return new Promise((resolve, reject) => { + const protocol = docUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: docUrl.hostname, + port: docUrl.port || (docUrl.protocol === "https:" ? 443 : 80), + path: docUrl.pathname, + method: "GET", + headers: { + "Authorization": `Basic ${authString}`, + "Accept": "application/xml", + }, + }; + + log("debug", `Getting document info: ${docUrl.toString()}`); + + const req = protocol.request(options, (res) => { + let data = ""; + + res.on("data", (chunk) => { + data += chunk; + }); + + res.on("end", () => { + if (res.statusCode === 200) { + try { + // Parse XML response to extract document info + // The tag has id and folder-uuid as ATTRIBUTES + // But , , are CHILD ELEMENTS + + // Extract attributes from the opening tag + const resourceMatch = data.match(/]+)>/); + let id = null; + let folderUuid = null; + + if (resourceMatch) { + const attrs = resourceMatch[1]; + const idMatch = attrs.match(/\bid="([^"]+)"/); + const folderMatch = attrs.match(/\bfolder-uuid="([^"]+)"/); + id = idMatch ? idMatch[1] : null; + folderUuid = folderMatch ? folderMatch[1] : null; + } + + // Extract child elements + const nameMatch = data.match(/([^<]+)<\/name>/); + const mimeMatch = data.match(/([^<]+)<\/mime-type>/); + const uriMatch = data.match(/([^<]+)<\/xmldb-uri>/); + + resolve({ + id, + name: nameMatch ? nameMatch[1] : null, + mimeType: mimeMatch ? mimeMatch[1] : null, + folderUuid, + uri: uriMatch ? uriMatch[1] : null, + rawXml: data, + }); + } catch (parseError) { + reject(new Error(`Failed to parse document info: ${parseError.message}`)); + } + } else { + reject(new Error(`Get document info failed with status ${res.statusCode}: ${data}`)); + } + }); + }); + + req.on("error", (error) => { + reject(new Error(`Get document info request error: ${error.message}`)); + }); + + req.end(); + }); + } + + /** + * Gets document content from Heretto. + * @param {Object} options - Options + * @returns {Promise} Document content as buffer + */ + async getDocumentContent({ apiBaseUrl, apiToken, username, documentId, log }) { + const contentUrl = new URL(`/rest/all-files/${documentId}/content`, apiBaseUrl); + + return new Promise((resolve, reject) => { + const protocol = contentUrl.protocol === "https:" ? https : http; + const authString = Buffer.from(`${username}:${apiToken}`).toString("base64"); + + const options = { + hostname: contentUrl.hostname, + port: contentUrl.port || (contentUrl.protocol === "https:" ? 443 : 80), + path: contentUrl.pathname, + method: "GET", + headers: { + "Authorization": `Basic ${authString}`, + }, + }; + + log("debug", `Getting document content: ${contentUrl.toString()}`); + + const req = protocol.request(options, (res) => { + const chunks = []; + + res.on("data", (chunk) => { + chunks.push(chunk); + }); + + res.on("end", () => { + if (res.statusCode === 200) { + resolve(Buffer.concat(chunks)); + } else { + reject(new Error(`Get document content failed with status ${res.statusCode}`)); + } + }); + }); + + req.on("error", (error) => { + reject(new Error(`Get document content request error: ${error.message}`)); + }); + + req.end(); + }); + } + + /** + * Determines the content type based on file extension. + * @param {string} filePath - File path + * @returns {string} MIME content type + */ + getContentType(filePath) { + const ext = path.extname(filePath).toLowerCase(); + + const contentTypes = { + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".gif": "image/gif", + ".svg": "image/svg+xml", + ".webp": "image/webp", + ".bmp": "image/bmp", + ".ico": "image/x-icon", + ".pdf": "application/pdf", + ".xml": "application/xml", + ".dita": "application/xml", + ".ditamap": "application/xml", + }; + + return contentTypes[ext] || "application/octet-stream"; + } +} + +module.exports = { + HerettoUploader, +}; diff --git a/core/src/integrations/index.js b/core/src/integrations/index.js new file mode 100644 index 0000000..847cc66 --- /dev/null +++ b/core/src/integrations/index.js @@ -0,0 +1,231 @@ +/** + * Integration uploader module - provides extensible file upload capability + * for different CMS integrations. + * + * Each integration uploader implements a common interface: + * - canHandle(sourceIntegration) - returns true if this uploader handles the integration type + * - upload({ config, localFilePath, sourceIntegration, log }) - uploads the file + */ + +const { HerettoUploader } = require("./heretto"); + +// Registry of available uploaders +const uploaders = [ + new HerettoUploader(), +]; + +/** + * Finds the appropriate uploader for a given source integration. + * @param {Object} sourceIntegration - Source integration metadata from step result + * @returns {Object|null} Uploader instance or null if none found + */ +function getUploader(sourceIntegration) { + if (!sourceIntegration?.type) return null; + + for (const uploader of uploaders) { + if (uploader.canHandle(sourceIntegration)) { + return uploader; + } + } + + return null; +} + +/** + * Collects all changed files from a test report that have source integrations. + * @param {Object} report - Test execution report + * @returns {Array} Array of { localPath, sourceIntegration } objects + */ +function collectChangedFiles(report) { + const changedFiles = []; + + if (!report?.specs) return changedFiles; + + for (const spec of report.specs) { + for (const test of spec.tests || []) { + for (const context of test.contexts || []) { + for (const step of context.steps || []) { + // Check if this step has a changed screenshot with source integration + if ( + step.screenshot && + step.outputs?.changed === true && + step.outputs?.sourceIntegration + ) { + changedFiles.push({ + localPath: step.outputs.screenshotPath, + sourceIntegration: step.outputs.sourceIntegration, + stepId: step.stepId, + testId: test.testId, + specId: spec.specId, + }); + } + } + } + } + } + + return changedFiles; +} + +/** + * Uploads all changed files back to their source integrations. + * Uses best-effort approach - continues uploading even if individual uploads fail. + * @param {Object} options - Upload options + * @param {Object} options.config - Doc Detective config + * @param {Object} options.report - Test execution report + * @param {Function} options.log - Logging function + * @returns {Promise} Upload results summary + */ +async function uploadChangedFiles({ config, report, log }) { + const results = { + total: 0, + successful: 0, + failed: 0, + skipped: 0, + details: [], + }; + + const changedFiles = collectChangedFiles(report); + results.total = changedFiles.length; + + if (changedFiles.length === 0) { + log(config, "debug", "No changed files to upload."); + return results; + } + + log(config, "info", `Found ${changedFiles.length} changed file(s) to upload.`); + + for (const file of changedFiles) { + const uploader = getUploader(file.sourceIntegration); + + if (!uploader) { + log( + config, + "warning", + `No uploader found for integration type: ${file.sourceIntegration.type}` + ); + results.skipped++; + results.details.push({ + localPath: file.localPath, + status: "SKIPPED", + reason: `No uploader for type: ${file.sourceIntegration.type}`, + }); + continue; + } + + // Get the integration config for API credentials + const integrationConfig = getIntegrationConfig( + config, + file.sourceIntegration + ); + + if (!integrationConfig) { + log( + config, + "warning", + `No integration config found for: ${file.sourceIntegration.integrationName}` + ); + results.skipped++; + results.details.push({ + localPath: file.localPath, + status: "SKIPPED", + reason: `No integration config found for: ${file.sourceIntegration.integrationName}`, + }); + continue; + } + + try { + log( + config, + "info", + `Uploading ${file.localPath} to ${file.sourceIntegration.type}...` + ); + + const uploadResult = await uploader.upload({ + config, + integrationConfig, + localFilePath: file.localPath, + sourceIntegration: file.sourceIntegration, + log, + }); + + if (uploadResult.status === "PASS") { + results.successful++; + log(config, "info", `Successfully uploaded: ${file.localPath}`); + } else { + results.failed++; + log( + config, + "warning", + `Failed to upload ${file.localPath}: ${uploadResult.description}` + ); + } + + results.details.push({ + localPath: file.localPath, + status: uploadResult.status, + description: uploadResult.description, + }); + } catch (error) { + results.failed++; + log( + config, + "warning", + `Error uploading ${file.localPath}: ${error.message}` + ); + results.details.push({ + localPath: file.localPath, + status: "FAIL", + description: error.message, + }); + } + } + + log( + config, + "info", + `Upload complete: ${results.successful} successful, ${results.failed} failed, ${results.skipped} skipped` + ); + + return results; +} + +/** + * Gets the integration configuration for a source integration. + * @param {Object} config - Doc Detective config + * @param {Object} sourceIntegration - Source integration metadata + * @returns {Object|null} Integration configuration or null if not found + */ +function getIntegrationConfig(config, sourceIntegration) { + if (!sourceIntegration?.type || !sourceIntegration?.integrationName) { + return null; + } + + switch (sourceIntegration.type) { + case "heretto": + return config?.integrations?.heretto?.find( + (h) => h.name === sourceIntegration.integrationName + ); + default: + return null; + } +} + +/** + * Registers a new uploader. + * @param {Object} uploader - Uploader instance implementing canHandle and upload methods + */ +function registerUploader(uploader) { + if (typeof uploader.canHandle !== "function" || typeof uploader.upload !== "function") { + throw new Error("Uploader must implement canHandle and upload methods"); + } + uploaders.push(uploader); +} + +module.exports = { + getUploader, + collectChangedFiles, + uploadChangedFiles, + getIntegrationConfig, + registerUploader, +}; diff --git a/core/src/tests.js b/core/src/tests.js index 5787966..39eb9c8 100644 --- a/core/src/tests.js +++ b/core/src/tests.js @@ -26,6 +26,7 @@ const { randomUUID } = require("crypto"); const { setAppiumHome } = require("./appium"); const { resolveExpression } = require("./expressions"); const { getEnvironment, getAvailableApps } = require("./config"); +const { uploadChangedFiles } = require("./integrations"); exports.runSpecs = runSpecs; exports.runViaApi = runViaApi; @@ -797,6 +798,24 @@ async function runSpecs({ resolvedTests }) { kill(appium.pid); } + // Upload changed files back to source integrations (best-effort) + // This automatically syncs any changed screenshots back to their source CMS + if (config?.integrations?.heretto?.length > 0) { + try { + const uploadResults = await uploadChangedFiles({ config, report, log }); + report.uploadResults = uploadResults; + } catch (error) { + log(config, "warning", `Failed to upload changed files: ${error.message}`); + report.uploadResults = { + total: 0, + successful: 0, + failed: 0, + skipped: 0, + error: error.message, + }; + } + } + return report; } diff --git a/core/src/tests/saveScreenshot.js b/core/src/tests/saveScreenshot.js index b339df6..f8bc3d6 100644 --- a/core/src/tests/saveScreenshot.js +++ b/core/src/tests/saveScreenshot.js @@ -21,7 +21,9 @@ async function saveScreenshot({ config, step, driver }) { let result = { status: "PASS", description: "Saved screenshot.", - outputs: {}, + outputs: { + changed: false, // Indicates if screenshot was changed/replaced + }, }; let element; @@ -291,6 +293,12 @@ async function saveScreenshot({ config, step, driver }) { // Replace old file with new file result.description += ` Overwrote existing file.`; fs.renameSync(filePath, existFilePath); + result.outputs.screenshotPath = existFilePath; + result.outputs.changed = true; + // Preserve sourceIntegration metadata + if (step.screenshot.sourceIntegration) { + result.outputs.sourceIntegration = step.screenshot.sourceIntegration; + } return result; } let fractionalDiff; @@ -362,11 +370,22 @@ async function saveScreenshot({ config, step, driver }) { )}) is greater than the max accepted variation (${ step.screenshot.maxVariation }).`; + result.outputs.changed = true; + result.outputs.screenshotPath = existFilePath; + // Preserve sourceIntegration metadata for upload processing + if (step.screenshot.sourceIntegration) { + result.outputs.sourceIntegration = step.screenshot.sourceIntegration; + } return result; } else { result.description += ` Screenshots are within maximum accepted variation: ${fractionalDiff.toFixed( 2 )}.`; + result.outputs.screenshotPath = existFilePath; + // Preserve sourceIntegration metadata + if (step.screenshot.sourceIntegration) { + result.outputs.sourceIntegration = step.screenshot.sourceIntegration; + } if (step.screenshot.overwrite != "true") { fs.unlinkSync(filePath); } @@ -374,6 +393,17 @@ async function saveScreenshot({ config, step, driver }) { } } + // Set output path for new screenshots + if (!result.outputs.screenshotPath) { + result.outputs.screenshotPath = filePath; + // Mark new screenshots as changed so they can be uploaded + result.outputs.changed = true; + // Preserve sourceIntegration metadata + if (step.screenshot.sourceIntegration) { + result.outputs.sourceIntegration = step.screenshot.sourceIntegration; + } + } + // PASS return result; } diff --git a/core/test/heretto-upload.test.js b/core/test/heretto-upload.test.js new file mode 100644 index 0000000..99bbdc6 --- /dev/null +++ b/core/test/heretto-upload.test.js @@ -0,0 +1,438 @@ +const fs = require("fs"); +const path = require("path"); +const assert = require("assert").strict; +const { HerettoUploader } = require("../src/integrations/heretto"); + +/** + * Integration tests for Heretto upload functionality. + * These tests require valid Heretto credentials and perform actual API calls. + * + * Required environment variables: + * - HERETTO_ORG_ID: Heretto organization ID + * - HERETTO_USERNAME: Heretto username + * - HERETTO_TOKEN: Heretto API token + */ +describe("Heretto Upload Integration", function () { + this.timeout(60000); // 60 second timeout for API calls + + const herettoUploader = new HerettoUploader(); + + // Get credentials from environment + const orgId = process.env.HERETTO_ORG_ID; + const username = process.env.HERETTO_USERNAME; + const apiToken = process.env.HERETTO_TOKEN; + + // Skip tests if credentials are not available + const hasCredentials = orgId && username && apiToken; + + // Known UUIDs from the E2E tests + const KNOWN_DITAMAP_ID = "265fa972-253f-4e6c-8b17-cdd4838111ea"; + const KNOWN_COFFEE_GUIDE_FOLDER_ID = "6acdcac0-ef31-4873-a40c-2345c75c0a14"; + + // Test document tracking + let testDocumentId = null; + const testFilename = `test-upload-${Date.now()}.png`; + + const apiBaseUrl = hasCredentials ? `https://${orgId}.heretto.com` : null; + + const mockLog = (level, msg) => { + if (process.env.DEBUG_HERETTO) { + console.log(`[${level}] ${msg}`); + } + }; + + before(function () { + if (!hasCredentials) { + console.log(" Skipping Heretto integration tests - credentials not available"); + console.log(" Set HERETTO_ORG_ID, HERETTO_USERNAME, and HERETTO_TOKEN to run these tests"); + this.skip(); + } + }); + + describe("getDocumentInfo", function () { + it("retrieves document information for known ditamap", async function () { + if (!hasCredentials) this.skip(); + + const docInfo = await herettoUploader.getDocumentInfo({ + apiBaseUrl, + apiToken, + username, + documentId: KNOWN_DITAMAP_ID, + log: mockLog, + }); + + // Log raw XML for debugging + if (process.env.DEBUG_HERETTO) { + console.log("Raw XML (first 500 chars):", docInfo.rawXml?.substring(0, 500)); + } + + assert.ok(docInfo, "Should return document info"); + assert.equal(docInfo.id, KNOWN_DITAMAP_ID, "Document ID should match"); + assert.equal(docInfo.name, "heretto_coffee_brewing_guide.ditamap", "Document name should match"); + assert.ok(docInfo.folderUuid, "Should have folder UUID"); + assert.ok(docInfo.uri, "Should have URI"); + + // Verify we get the Coffee_Guide folder + assert.equal(docInfo.folderUuid, KNOWN_COFFEE_GUIDE_FOLDER_ID, "Should be in Coffee_Guide folder"); + }); + + it("returns document mime type", async function () { + if (!hasCredentials) this.skip(); + + const docInfo = await herettoUploader.getDocumentInfo({ + apiBaseUrl, + apiToken, + username, + documentId: KNOWN_DITAMAP_ID, + log: mockLog, + }); + + assert.ok(docInfo.mimeType, "Should have mime type"); + }); + }); + + describe("getFileInFolder", function () { + it("finds file in known folder", async function () { + if (!hasCredentials) this.skip(); + + // The ditamap should be in the Coffee_Guide folder + const fileId = await herettoUploader.getFileInFolder({ + apiBaseUrl, + apiToken, + username, + folderId: KNOWN_COFFEE_GUIDE_FOLDER_ID, + filename: "heretto_coffee_brewing_guide.ditamap", + log: mockLog, + }); + + assert.ok(fileId, "Should find the ditamap in the folder"); + assert.equal(fileId, KNOWN_DITAMAP_ID, "File ID should match known ditamap ID"); + }); + + it("returns null for non-existent file in folder", async function () { + if (!hasCredentials) this.skip(); + + const fileId = await herettoUploader.getFileInFolder({ + apiBaseUrl, + apiToken, + username, + folderId: KNOWN_COFFEE_GUIDE_FOLDER_ID, + filename: "definitely-does-not-exist-xyz123.png", + log: mockLog, + }); + + assert.equal(fileId, null, "Should return null for non-existent file"); + }); + }); + + describe("createDocument and upload flow", function () { + it("creates a new document in known folder", async function () { + if (!hasCredentials) this.skip(); + + const createResult = await herettoUploader.createDocument({ + apiBaseUrl, + apiToken, + username, + parentFolderId: KNOWN_COFFEE_GUIDE_FOLDER_ID, + filename: testFilename, + mimeType: "image/png", + log: mockLog, + }); + + assert.ok(createResult, "Should return create result"); + + if (createResult.created) { + assert.ok(createResult.documentId, "Should have document ID when created"); + testDocumentId = createResult.documentId; + } else if (createResult.existsInFolder) { + // File already exists, get its ID + const existingId = await herettoUploader.getFileInFolder({ + apiBaseUrl, + apiToken, + username, + folderId: KNOWN_COFFEE_GUIDE_FOLDER_ID, + filename: testFilename, + log: mockLog, + }); + assert.ok(existingId, "Should find existing file ID"); + testDocumentId = existingId; + } + + assert.ok(testDocumentId, "Should have a document ID"); + }); + + it("uploads content to the created document", async function () { + if (!hasCredentials) this.skip(); + if (!testDocumentId) this.skip(); + + // Create a simple test PNG (1x1 red pixel) + const pngContent = Buffer.from([ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, // IHDR chunk + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, + 0xDE, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, // IDAT chunk + 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0xFE, + 0xD4, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, // IEND chunk + 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + ]); + + await herettoUploader.uploadFile({ + apiBaseUrl, + apiToken, + username, + documentId: testDocumentId, + content: pngContent, + contentType: "image/png", + log: mockLog, + }); + + // If we get here without throwing, upload succeeded + assert.ok(true, "Upload should succeed"); + }); + + it("verifies uploaded document location and content", async function () { + if (!hasCredentials) this.skip(); + if (!testDocumentId) this.skip(); + + // Get document info to verify location + const docInfo = await herettoUploader.getDocumentInfo({ + apiBaseUrl, + apiToken, + username, + documentId: testDocumentId, + log: mockLog, + }); + + assert.ok(docInfo, "Should get document info"); + assert.equal(docInfo.id, testDocumentId, "Document ID should match"); + assert.equal(docInfo.name, testFilename, "Filename should match"); + assert.equal(docInfo.folderUuid, KNOWN_COFFEE_GUIDE_FOLDER_ID, "Should be in the correct folder"); + assert.equal(docInfo.mimeType, "image/png", "MIME type should be image/png"); + + // Get document content to verify it was uploaded + const content = await herettoUploader.getDocumentContent({ + apiBaseUrl, + apiToken, + username, + documentId: testDocumentId, + log: mockLog, + }); + + assert.ok(content, "Should get document content"); + assert.ok(Buffer.isBuffer(content), "Content should be a Buffer"); + assert.ok(content.length > 0, "Content should not be empty"); + + // Verify PNG signature + assert.equal(content[0], 0x89, "Should start with PNG signature"); + assert.equal(content[1], 0x50, "Second byte of PNG signature"); + assert.equal(content[2], 0x4E, "Third byte of PNG signature"); + assert.equal(content[3], 0x47, "Fourth byte of PNG signature"); + }); + + it("can update existing document content", async function () { + if (!hasCredentials) this.skip(); + if (!testDocumentId) this.skip(); + + // Create a different PNG (1x1 blue pixel - different content) + const newPngContent = Buffer.from([ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, + 0xDE, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, + 0x54, 0x08, 0xD7, 0x63, 0xF8, 0x0F, 0xC0, 0x00, // Different color + 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0xFE, + 0xD4, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + ]); + + // Upload the new content + await herettoUploader.uploadFile({ + apiBaseUrl, + apiToken, + username, + documentId: testDocumentId, + content: newPngContent, + contentType: "image/png", + log: mockLog, + }); + + // Verify the content was updated + const content = await herettoUploader.getDocumentContent({ + apiBaseUrl, + apiToken, + username, + documentId: testDocumentId, + log: mockLog, + }); + + assert.ok(content, "Should get updated content"); + assert.ok(Buffer.isBuffer(content), "Content should be a Buffer"); + + // Content should match what we uploaded + assert.deepEqual(content, newPngContent, "Content should match uploaded content"); + }); + }); + + describe("full upload method with ditamap fallback", function () { + it("uploads a file using ditamap parent folder fallback", async function () { + if (!hasCredentials) this.skip(); + + // Create a temp file to upload + const tempDir = path.resolve("./test/temp-heretto-upload"); + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + } + + const localFilename = `integration-test-${Date.now()}.png`; + const localFilePath = path.join(tempDir, localFilename); + + // Create a simple PNG file + const pngContent = Buffer.from([ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, + 0xDE, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, + 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x05, 0xFE, + 0xD4, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + ]); + + fs.writeFileSync(localFilePath, pngContent); + + // The _media folder ID inside Coffee_Guide + const KNOWN_MEDIA_FOLDER_ID = "afa9ac13-f700-4a6b-8c28-9ea32786ab20"; + + try { + // Use the full upload method with resourceDependencies (ditamap parent folder) + // The fix now correctly resolves the target folder (_media) from the filePath + // and finds it by name within the ditamap parent folder + const result = await herettoUploader.upload({ + config: {}, + integrationConfig: { + organizationId: orgId, + username, + apiToken, + resourceDependencies: { + "_ditamapParentFolderId": KNOWN_COFFEE_GUIDE_FOLDER_ID, + }, + }, + localFilePath, + sourceIntegration: { + type: "heretto", + integrationName: "test", + filePath: `_media/${localFilename}`, + contentPath: "/content/topic.dita", + }, + log: mockLog, + }); + + assert.equal(result.status, "PASS", `Upload should succeed: ${result.description}`); + assert.ok(result.description.includes("Successfully uploaded"), "Should have success message"); + + // Extract document ID from result + const docIdMatch = result.description.match(/document ID: ([a-f0-9-]+)/i); + assert.ok(docIdMatch, "Should have document ID in result"); + + const uploadedDocId = docIdMatch[1]; + + // Verify the upload by getting document info + const docInfo = await herettoUploader.getDocumentInfo({ + apiBaseUrl, + apiToken, + username, + documentId: uploadedDocId, + log: mockLog, + }); + + assert.ok(docInfo, "Should get document info"); + assert.equal(docInfo.name, localFilename, "Filename should match"); + // The fix now correctly places files in the _media folder, not the ditamap parent + assert.equal(docInfo.folderUuid, KNOWN_MEDIA_FOLDER_ID, "Should be in _media folder (correct behavior)"); + + // Verify content + const uploadedContent = await herettoUploader.getDocumentContent({ + apiBaseUrl, + apiToken, + username, + documentId: uploadedDocId, + log: mockLog, + }); + + assert.deepEqual(uploadedContent, pngContent, "Uploaded content should match"); + + } finally { + // Cleanup + if (fs.existsSync(localFilePath)) { + fs.unlinkSync(localFilePath); + } + if (fs.existsSync(tempDir)) { + fs.rmdirSync(tempDir); + } + } + }); + + it("uploads and verifies existing file update", async function () { + if (!hasCredentials) this.skip(); + + // Use the known screenshot file from the E2E test + const knownScreenshotId = "411d629b-cee0-4960-8f92-6b1cf54302d4"; + + // Create new content to upload + const newContent = Buffer.from([ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, // 2x2 instead of 1x1 + 0x08, 0x02, 0x00, 0x00, 0x00, 0xFD, 0xD4, 0x9A, + 0x73, 0x00, 0x00, 0x00, 0x12, 0x49, 0x44, 0x41, + 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0xF0, + 0x9F, 0x81, 0xE1, 0x3F, 0x03, 0x00, 0x06, 0xB0, + 0x02, 0x01, 0x89, 0xC7, 0xF4, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, + 0x60, 0x82 + ]); + + // Upload to existing document + await herettoUploader.uploadFile({ + apiBaseUrl, + apiToken, + username, + documentId: knownScreenshotId, + content: newContent, + contentType: "image/png", + log: mockLog, + }); + + // Verify the document info + const docInfo = await herettoUploader.getDocumentInfo({ + apiBaseUrl, + apiToken, + username, + documentId: knownScreenshotId, + log: mockLog, + }); + + assert.ok(docInfo, "Should get document info"); + assert.equal(docInfo.name, "la_pavoni_screenshot.png", "Filename should be la_pavoni_screenshot.png"); + + // Verify content was updated + const content = await herettoUploader.getDocumentContent({ + apiBaseUrl, + apiToken, + username, + documentId: knownScreenshotId, + log: mockLog, + }); + + assert.ok(content, "Should get content"); + assert.ok(Buffer.isBuffer(content), "Content should be buffer"); + // Content length will differ from what we uploaded due to re-encoding + // Just verify it's a valid PNG + assert.equal(content[0], 0x89, "Should be PNG"); + }); + }); +}); diff --git a/core/test/screenshot.test.js b/core/test/screenshot.test.js new file mode 100644 index 0000000..72069f9 --- /dev/null +++ b/core/test/screenshot.test.js @@ -0,0 +1,273 @@ +const fs = require("fs"); +const path = require("path"); +const assert = require("assert").strict; +const { runTests } = require("../src"); +const { createServer } = require("./server"); + +// Create a server for screenshot tests +const server = createServer({ + port: 8093, + staticDir: "./test/server/public", +}); + +// Start the server before tests +before(async () => { + try { + await server.start(); + } catch (error) { + console.error(`Failed to start test server: ${error.message}`); + throw error; + } +}); + +// Stop the server after tests +after(async () => { + try { + await server.stop(); + } catch (error) { + console.error(`Failed to stop test server: ${error.message}`); + } +}); + +describe("Screenshot sourceIntegration preservation", function () { + this.timeout(0); + + const tempDir = path.resolve("./test/temp-screenshot-tests"); + + beforeEach(function () { + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + } + }); + + afterEach(function () { + // Cleanup temp files + if (fs.existsSync(tempDir)) { + const files = fs.readdirSync(tempDir); + for (const file of files) { + fs.unlinkSync(path.join(tempDir, file)); + } + fs.rmdirSync(tempDir); + } + }); + + it("preserves sourceIntegration for new screenshots", async function () { + const screenshotPath = path.join(tempDir, "new-screenshot.png"); + + const testSpec = { + tests: [ + { + steps: [ + { + goTo: "http://localhost:8093", + }, + { + screenshot: { + path: screenshotPath, + sourceIntegration: { + type: "heretto", + integrationName: "test-integration", + filePath: "new-screenshot.png", + contentPath: "/content/topic.dita", + }, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.join(tempDir, "test-spec.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(testSpec, null, 2)); + + const result = await runTests({ input: tempFilePath, logLevel: "silent" }); + + // Find the screenshot step + const screenshotStep = result.specs[0].tests[0].contexts[0].steps[1]; + + // Verify sourceIntegration is preserved + assert.ok(screenshotStep.outputs.sourceIntegration, "sourceIntegration should be present"); + assert.equal(screenshotStep.outputs.sourceIntegration.type, "heretto"); + assert.equal(screenshotStep.outputs.sourceIntegration.integrationName, "test-integration"); + assert.equal(screenshotStep.outputs.sourceIntegration.filePath, "new-screenshot.png"); + assert.equal(screenshotStep.outputs.sourceIntegration.contentPath, "/content/topic.dita"); + + // Verify changed is true for new screenshots + assert.equal(screenshotStep.outputs.changed, true, "changed should be true for new screenshots"); + }); + + it("preserves sourceIntegration when variation exceeds threshold", async function () { + const screenshotPath = path.join(tempDir, "variation-screenshot.png"); + + // First, create an initial screenshot + const initialSpec = { + tests: [ + { + steps: [ + { + goTo: "http://localhost:8093", + }, + { + screenshot: { + path: screenshotPath, + maxVariation: 0.05, + overwrite: "false", + }, + }, + ], + }, + ], + }; + + const initialFilePath = path.join(tempDir, "initial-spec.json"); + fs.writeFileSync(initialFilePath, JSON.stringify(initialSpec, null, 2)); + await runTests({ input: initialFilePath, logLevel: "silent" }); + + // Now run with a different page to trigger variation warning + const variationSpec = { + tests: [ + { + steps: [ + { + goTo: "http://localhost:8093/drag-drop-test.html", // Different page + }, + { + screenshot: { + path: screenshotPath, + maxVariation: 0.05, + overwrite: "aboveVariation", + sourceIntegration: { + type: "heretto", + integrationName: "variation-test", + filePath: "variation-screenshot.png", + contentPath: "/content/variation-topic.dita", + }, + }, + }, + ], + }, + ], + }; + + const variationFilePath = path.join(tempDir, "variation-spec.json"); + fs.writeFileSync(variationFilePath, JSON.stringify(variationSpec, null, 2)); + + const result = await runTests({ input: variationFilePath, logLevel: "silent" }); + + const screenshotStep = result.specs[0].tests[0].contexts[0].steps[1]; + + // Verify the step is a WARNING (variation exceeded) + assert.equal(screenshotStep.result, "WARNING"); + + // Verify sourceIntegration is preserved + assert.ok(screenshotStep.outputs.sourceIntegration, "sourceIntegration should be present"); + assert.equal(screenshotStep.outputs.sourceIntegration.type, "heretto"); + assert.equal(screenshotStep.outputs.sourceIntegration.integrationName, "variation-test"); + + // Verify changed is true + assert.equal(screenshotStep.outputs.changed, true, "changed should be true when variation exceeds threshold"); + }); + + it("preserves sourceIntegration when screenshot is within variation", async function () { + const screenshotPath = path.join(tempDir, "same-screenshot.png"); + + // First, create an initial screenshot + const initialSpec = { + tests: [ + { + steps: [ + { + goTo: "http://localhost:8093", + }, + { + screenshot: { + path: screenshotPath, + maxVariation: 0.05, + overwrite: "false", + }, + }, + ], + }, + ], + }; + + const initialFilePath = path.join(tempDir, "initial-spec.json"); + fs.writeFileSync(initialFilePath, JSON.stringify(initialSpec, null, 2)); + await runTests({ input: initialFilePath, logLevel: "silent" }); + + // Now run with the same page (should be within variation) + const samePageSpec = { + tests: [ + { + steps: [ + { + goTo: "http://localhost:8093", // Same page + }, + { + screenshot: { + path: screenshotPath, + maxVariation: 0.95, // High threshold to ensure within variation + overwrite: "aboveVariation", + sourceIntegration: { + type: "heretto", + integrationName: "same-page-test", + filePath: "same-screenshot.png", + contentPath: "/content/same-topic.dita", + }, + }, + }, + ], + }, + ], + }; + + const sameFilePath = path.join(tempDir, "same-spec.json"); + fs.writeFileSync(sameFilePath, JSON.stringify(samePageSpec, null, 2)); + + const result = await runTests({ input: sameFilePath, logLevel: "silent" }); + + const screenshotStep = result.specs[0].tests[0].contexts[0].steps[1]; + + // Verify the step passed (within variation) + assert.equal(screenshotStep.result, "PASS"); + + // Verify sourceIntegration is preserved + assert.ok(screenshotStep.outputs.sourceIntegration, "sourceIntegration should be present"); + assert.equal(screenshotStep.outputs.sourceIntegration.type, "heretto"); + assert.equal(screenshotStep.outputs.sourceIntegration.integrationName, "same-page-test"); + + // Verify changed is false (within variation, no update) + assert.equal(screenshotStep.outputs.changed, false, "changed should be false when within variation"); + }); + + it("does not set sourceIntegration when not provided", async function () { + const screenshotPath = path.join(tempDir, "no-integration-screenshot.png"); + + const testSpec = { + tests: [ + { + steps: [ + { + goTo: "http://localhost:8093", + }, + { + screenshot: { + path: screenshotPath, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.join(tempDir, "test-spec.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(testSpec, null, 2)); + + const result = await runTests({ input: tempFilePath, logLevel: "silent" }); + + const screenshotStep = result.specs[0].tests[0].contexts[0].steps[1]; + + // Verify sourceIntegration is NOT set + assert.equal(screenshotStep.outputs.sourceIntegration, undefined, "sourceIntegration should not be set when not provided"); + }); +}); diff --git a/core/test/upload.test.js b/core/test/upload.test.js new file mode 100644 index 0000000..23401f3 --- /dev/null +++ b/core/test/upload.test.js @@ -0,0 +1,650 @@ +const assert = require("assert").strict; +const { + collectChangedFiles, + getIntegrationConfig, + getUploader, +} = require("../src/integrations"); + +describe("Upload module", function () { + describe("collectChangedFiles", function () { + it("returns empty array for null report", function () { + const result = collectChangedFiles(null); + assert.deepEqual(result, []); + }); + + it("returns empty array for report without specs", function () { + const result = collectChangedFiles({}); + assert.deepEqual(result, []); + }); + + it("returns empty array for report with empty specs", function () { + const result = collectChangedFiles({ specs: [] }); + assert.deepEqual(result, []); + }); + + it("extracts file when changed is true and sourceIntegration present", function () { + const report = { + specs: [ + { + specId: "spec-1", + tests: [ + { + testId: "test-1", + contexts: [ + { + steps: [ + { + stepId: "step-1", + screenshot: { path: "test.png" }, + outputs: { + changed: true, + screenshotPath: "/path/to/test.png", + sourceIntegration: { + type: "heretto", + integrationName: "example", + filePath: "test.png", + contentPath: "/content/topic.dita", + }, + }, + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const result = collectChangedFiles(report); + + assert.equal(result.length, 1); + assert.equal(result[0].localPath, "/path/to/test.png"); + assert.equal(result[0].sourceIntegration.type, "heretto"); + assert.equal(result[0].sourceIntegration.integrationName, "example"); + assert.equal(result[0].stepId, "step-1"); + assert.equal(result[0].testId, "test-1"); + assert.equal(result[0].specId, "spec-1"); + }); + + it("ignores step when changed is false", function () { + const report = { + specs: [ + { + specId: "spec-1", + tests: [ + { + testId: "test-1", + contexts: [ + { + steps: [ + { + stepId: "step-1", + screenshot: { path: "test.png" }, + outputs: { + changed: false, + screenshotPath: "/path/to/test.png", + sourceIntegration: { + type: "heretto", + integrationName: "example", + }, + }, + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const result = collectChangedFiles(report); + assert.equal(result.length, 0); + }); + + it("ignores step when sourceIntegration is missing", function () { + const report = { + specs: [ + { + specId: "spec-1", + tests: [ + { + testId: "test-1", + contexts: [ + { + steps: [ + { + stepId: "step-1", + screenshot: { path: "test.png" }, + outputs: { + changed: true, + screenshotPath: "/path/to/test.png", + }, + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const result = collectChangedFiles(report); + assert.equal(result.length, 0); + }); + + it("ignores step without screenshot property", function () { + const report = { + specs: [ + { + specId: "spec-1", + tests: [ + { + testId: "test-1", + contexts: [ + { + steps: [ + { + stepId: "step-1", + goTo: "http://example.com", + outputs: { + changed: true, + sourceIntegration: { + type: "heretto", + integrationName: "example", + }, + }, + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const result = collectChangedFiles(report); + assert.equal(result.length, 0); + }); + + it("collects multiple changed files from different tests", function () { + const report = { + specs: [ + { + specId: "spec-1", + tests: [ + { + testId: "test-1", + contexts: [ + { + steps: [ + { + stepId: "step-1", + screenshot: { path: "screenshot1.png" }, + outputs: { + changed: true, + screenshotPath: "/path/to/screenshot1.png", + sourceIntegration: { + type: "heretto", + integrationName: "example", + }, + }, + }, + ], + }, + ], + }, + { + testId: "test-2", + contexts: [ + { + steps: [ + { + stepId: "step-2", + screenshot: { path: "screenshot2.png" }, + outputs: { + changed: true, + screenshotPath: "/path/to/screenshot2.png", + sourceIntegration: { + type: "heretto", + integrationName: "another", + }, + }, + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const result = collectChangedFiles(report); + + assert.equal(result.length, 2); + assert.equal(result[0].localPath, "/path/to/screenshot1.png"); + assert.equal(result[1].localPath, "/path/to/screenshot2.png"); + }); + }); + + describe("getIntegrationConfig", function () { + it("returns null for missing sourceIntegration type", function () { + const config = { integrations: { heretto: [{ name: "example" }] } }; + const result = getIntegrationConfig(config, { integrationName: "example" }); + assert.equal(result, null); + }); + + it("returns null for missing integrationName", function () { + const config = { integrations: { heretto: [{ name: "example" }] } }; + const result = getIntegrationConfig(config, { type: "heretto" }); + assert.equal(result, null); + }); + + it("returns heretto config when found", function () { + const herettoConfig = { + name: "example", + organizationId: "test-org", + username: "user", + apiToken: "token", + }; + const config = { integrations: { heretto: [herettoConfig] } }; + const sourceIntegration = { type: "heretto", integrationName: "example" }; + + const result = getIntegrationConfig(config, sourceIntegration); + + assert.deepEqual(result, herettoConfig); + }); + + it("returns null when heretto config not found by name", function () { + const config = { + integrations: { + heretto: [{ name: "other", organizationId: "org" }], + }, + }; + const sourceIntegration = { type: "heretto", integrationName: "example" }; + + const result = getIntegrationConfig(config, sourceIntegration); + assert.equal(result, undefined); + }); + + it("returns null for unknown integration type", function () { + const config = { integrations: { heretto: [{ name: "example" }] } }; + const sourceIntegration = { type: "unknown", integrationName: "example" }; + + const result = getIntegrationConfig(config, sourceIntegration); + assert.equal(result, null); + }); + }); + + describe("getUploader", function () { + it("returns null for null sourceIntegration", function () { + const result = getUploader(null); + assert.equal(result, null); + }); + + it("returns null for sourceIntegration without type", function () { + const result = getUploader({ integrationName: "example" }); + assert.equal(result, null); + }); + + it("returns HerettoUploader for heretto type", function () { + const result = getUploader({ type: "heretto" }); + assert.notEqual(result, null); + assert.equal(typeof result.canHandle, "function"); + assert.equal(typeof result.upload, "function"); + assert.equal(result.canHandle({ type: "heretto" }), true); + }); + + it("returns null for unknown type", function () { + const result = getUploader({ type: "unknown-cms" }); + assert.equal(result, null); + }); + }); + + describe("HerettoUploader", function () { + const HerettoUploader = getUploader({ type: "heretto" }); + + describe("resolveFromDependencies", function () { + const mockLog = () => {}; + + it("returns null when resourceDependencies is null", function () { + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies: null, + filePath: "test.png", + filename: "test.png", + log: mockLog, + }); + assert.equal(result, null); + }); + + it("returns null when resourceDependencies is undefined", function () { + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies: undefined, + filePath: "test.png", + filename: "test.png", + log: mockLog, + }); + assert.equal(result, null); + }); + + it("finds exact path match in dependencies", function () { + const resourceDependencies = { + "content/media/test.png": { uuid: "file-uuid-123", parentFolderId: "folder-uuid-456" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "content/media/test.png", + filename: "test.png", + log: mockLog, + }); + assert.deepEqual(result, { uuid: "file-uuid-123", parentFolderId: "folder-uuid-456" }); + }); + + it("finds path match with leading relative path removed", function () { + const resourceDependencies = { + "content/_media/screenshot.png": { uuid: "uuid-1", parentFolderId: "parent-1" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "../_media/screenshot.png", + filename: "screenshot.png", + log: mockLog, + }); + // Should match via filename + folder name + assert.notEqual(result, null); + }); + + it("finds filename+folder match when exact path does not match", function () { + const resourceDependencies = { + "master/content/images/logo.png": { uuid: "uuid-logo", parentFolderId: "images-folder" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "images/logo.png", + filename: "logo.png", + log: mockLog, + }); + assert.deepEqual(result, { uuid: "uuid-logo", parentFolderId: "images-folder" }); + }); + + it("finds filename-only match as last resort", function () { + const resourceDependencies = { + "totally/different/path/unique-file.png": { uuid: "uuid-unique", parentFolderId: "some-folder" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "other/path/unique-file.png", + filename: "unique-file.png", + log: mockLog, + }); + assert.deepEqual(result, { uuid: "uuid-unique", parentFolderId: "some-folder" }); + }); + + it("skips internal keys starting with underscore", function () { + const resourceDependencies = { + "_ditamapParentFolderId": "internal-folder-id", + "_ditamapPath": "some/path.ditamap", + "real/file.png": { uuid: "real-uuid", parentFolderId: "real-folder" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "_ditamapParentFolderId", + filename: "_ditamapParentFolderId", + log: mockLog, + }); + // Should not match the internal key + assert.equal(result, null); + }); + + it("returns null when no match found", function () { + const resourceDependencies = { + "path/to/other-file.png": { uuid: "uuid-other", parentFolderId: "folder-other" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "completely/different/nonexistent.png", + filename: "nonexistent.png", + log: mockLog, + }); + assert.equal(result, null); + }); + + it("normalizes Windows backslashes to forward slashes", function () { + const resourceDependencies = { + "content/_media/image.png": { uuid: "uuid-win", parentFolderId: "folder-win" }, + }; + const result = HerettoUploader.resolveFromDependencies({ + resourceDependencies, + filePath: "content\\_media\\image.png", + filename: "image.png", + log: mockLog, + }); + assert.deepEqual(result, { uuid: "uuid-win", parentFolderId: "folder-win" }); + }); + }); + + describe("findParentFolderFromDependencies", function () { + const mockLog = () => {}; + + it("returns empty result when resourceDependencies is null", function () { + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies: null, + filePath: "_media/test.png", + log: mockLog, + }); + assert.deepEqual(result, { folderId: null, targetFolderName: null, ditamapParentFolderId: null }); + }); + + it("finds parent folder from sibling file", function () { + const resourceDependencies = { + "content/_media/existing-image.png": { uuid: "sibling-uuid", parentFolderId: "media-folder-uuid" }, + }; + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies, + filePath: "_media/new-image.png", + log: mockLog, + }); + assert.equal(result.folderId, "media-folder-uuid"); + assert.equal(result.targetFolderName, "_media"); + }); + + it("finds folder by direct path match", function () { + const resourceDependencies = { + "content/_media": { uuid: "direct-folder-uuid" }, + }; + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies, + filePath: "_media/new-file.png", + log: mockLog, + }); + assert.equal(result.folderId, "direct-folder-uuid"); + assert.equal(result.targetFolderName, "_media"); + }); + + it("returns ditamap parent folder info when folder not found", function () { + const resourceDependencies = { + "_ditamapParentFolderId": "ditamap-parent-folder-uuid", + "other/path/file.dita": { uuid: "other-uuid", parentFolderId: "other-folder" }, + }; + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies, + filePath: "_nonexistent_folder/new-file.png", + log: mockLog, + }); + // folderId should be null (not found), but ditamapParentFolderId should be set for API lookup + assert.equal(result.folderId, null); + assert.equal(result.ditamapParentFolderId, "ditamap-parent-folder-uuid"); + assert.equal(result.targetFolderName, "_nonexistent_folder"); + }); + + it("prefers sibling file match over ditamap fallback", function () { + const resourceDependencies = { + "_ditamapParentFolderId": "ditamap-parent-folder-uuid", + "content/_media/sibling.png": { uuid: "sibling-uuid", parentFolderId: "correct-media-folder" }, + }; + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies, + filePath: "_media/new-file.png", + log: mockLog, + }); + assert.equal(result.folderId, "correct-media-folder"); + assert.equal(result.targetFolderName, "_media"); + }); + + it("returns null folderId when no folder found and no ditamap fallback", function () { + const resourceDependencies = { + "completely/different/path/file.png": { uuid: "some-uuid", parentFolderId: "some-folder" }, + }; + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies, + filePath: "_unknown_folder/file.png", + log: mockLog, + }); + assert.equal(result.folderId, null); + assert.equal(result.ditamapParentFolderId, null); + assert.equal(result.targetFolderName, "_unknown_folder"); + }); + + it("normalizes relative path prefixes", function () { + const resourceDependencies = { + "content/_media/file.png": { uuid: "uuid", parentFolderId: "media-folder" }, + }; + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies, + filePath: "../_media/new-file.png", + log: mockLog, + }); + assert.equal(result.folderId, "media-folder"); + assert.equal(result.targetFolderName, "_media"); + }); + }); + + describe("canHandle", function () { + it("returns true for heretto type", function () { + assert.equal(HerettoUploader.canHandle({ type: "heretto" }), true); + }); + + it("returns false for other types", function () { + assert.equal(HerettoUploader.canHandle({ type: "other" }), false); + assert.equal(HerettoUploader.canHandle({ type: "github" }), false); + }); + + it("returns false for null", function () { + assert.equal(HerettoUploader.canHandle(null), false); + }); + + it("returns false for undefined", function () { + assert.equal(HerettoUploader.canHandle(undefined), false); + }); + }); + + describe("getContentType", function () { + it("returns image/png for .png files", function () { + assert.equal(HerettoUploader.getContentType("test.png"), "image/png"); + assert.equal(HerettoUploader.getContentType("/path/to/image.PNG"), "image/png"); + }); + + it("returns image/jpeg for .jpg files", function () { + assert.equal(HerettoUploader.getContentType("photo.jpg"), "image/jpeg"); + assert.equal(HerettoUploader.getContentType("photo.JPG"), "image/jpeg"); + }); + + it("returns image/jpeg for .jpeg files", function () { + assert.equal(HerettoUploader.getContentType("photo.jpeg"), "image/jpeg"); + }); + + it("returns image/gif for .gif files", function () { + assert.equal(HerettoUploader.getContentType("animation.gif"), "image/gif"); + }); + + it("returns image/svg+xml for .svg files", function () { + assert.equal(HerettoUploader.getContentType("icon.svg"), "image/svg+xml"); + }); + + it("returns image/webp for .webp files", function () { + assert.equal(HerettoUploader.getContentType("modern.webp"), "image/webp"); + }); + + it("returns application/octet-stream for unknown extensions", function () { + assert.equal(HerettoUploader.getContentType("file.unknown"), "application/octet-stream"); + assert.equal(HerettoUploader.getContentType("noextension"), "application/octet-stream"); + }); + }); + + describe("escapeXml", function () { + it("escapes ampersand", function () { + assert.equal(HerettoUploader.escapeXml("a & b"), "a & b"); + }); + + it("escapes less than", function () { + assert.equal(HerettoUploader.escapeXml("a < b"), "a < b"); + }); + + it("escapes greater than", function () { + assert.equal(HerettoUploader.escapeXml("a > b"), "a > b"); + }); + + it("escapes double quotes", function () { + assert.equal(HerettoUploader.escapeXml('name="value"'), "name="value""); + }); + + it("escapes single quotes", function () { + assert.equal(HerettoUploader.escapeXml("it's"), "it's"); + }); + + it("handles multiple special characters", function () { + assert.equal(HerettoUploader.escapeXml('a & b'), "<tag attr="val">a & b</tag>"); + }); + + it("returns empty string for empty input", function () { + assert.equal(HerettoUploader.escapeXml(""), ""); + }); + + it("returns string unchanged when no special characters", function () { + assert.equal(HerettoUploader.escapeXml("normal text 123"), "normal text 123"); + }); + }); + + describe("upload validation", function () { + it("returns error for missing integrationConfig", async function () { + const result = await HerettoUploader.upload({ + config: {}, + integrationConfig: null, + localFilePath: "/path/to/file.png", + sourceIntegration: { type: "heretto", filePath: "file.png" }, + log: () => {}, + }); + assert.equal(result.status, "FAIL"); + assert.equal(result.description, "No Heretto integration configuration found"); + }); + + it("returns error for missing organizationId", async function () { + const result = await HerettoUploader.upload({ + config: {}, + integrationConfig: { apiToken: "token" }, + localFilePath: "/path/to/file.png", + sourceIntegration: { type: "heretto", filePath: "file.png" }, + log: () => {}, + }); + assert.equal(result.status, "FAIL"); + assert.equal(result.description, "Heretto integration missing organizationId or apiToken"); + }); + + it("returns error for missing apiToken", async function () { + const result = await HerettoUploader.upload({ + config: {}, + integrationConfig: { organizationId: "org" }, + localFilePath: "/path/to/file.png", + sourceIntegration: { type: "heretto", filePath: "file.png" }, + log: () => {}, + }); + assert.equal(result.status, "FAIL"); + assert.equal(result.description, "Heretto integration missing organizationId or apiToken"); + }); + }); + }); +}); diff --git a/docs/docs/references/integrations/heretto-screenshot-sync.md b/docs/docs/references/integrations/heretto-screenshot-sync.md new file mode 100644 index 0000000..5a25470 --- /dev/null +++ b/docs/docs/references/integrations/heretto-screenshot-sync.md @@ -0,0 +1,149 @@ +# Heretto Screenshot Sync + +This document describes the automatic screenshot synchronization feature for the Heretto CMS integration. + +## Overview + +When Doc Detective runs tests against content sourced from Heretto CMS, any screenshots that change during test execution are automatically uploaded back to Heretto. This keeps documentation screenshots in sync with the actual application state without requiring manual intervention. + +## Intent + +Documentation screenshots frequently become outdated as applications evolve. Manually updating screenshots is time-consuming and error-prone. This feature automates the process: + +1. **Run tests** - Doc Detective executes tests from Heretto-sourced content +2. **Detect changes** - Screenshots are compared against their baseline versions +3. **Upload automatically** - Changed screenshots are pushed back to Heretto + +This creates a continuous synchronization loop where running tests not only validates documentation accuracy but also updates it. + +## Architecture + +The implementation spans two modules: + +### Resolver Module + +The resolver tracks the origin of content loaded from Heretto: + +1. **Source tracking** - When parsing screenshot steps from Heretto content, the resolver attaches `sourceIntegration` metadata containing: + - `type`: Integration type ("heretto") + - `integrationName`: Name of the Heretto integration config + - `filePath`: Path of the source file in Heretto + - `contentPath`: Local path where content was downloaded + +2. **Path mapping** - The resolver maintains a mapping (`_herettoPathMapping`) that tracks which local output directories correspond to which Heretto integrations + +### Core Module + +The core module handles change detection and uploads: + +1. **Change detection** - The `saveScreenshot` action compares new screenshots against existing baselines using pixel-level comparison (pixelmatch). When the variation exceeds the configured threshold, the screenshot is marked as `changed: true` + +2. **Integration uploader abstraction** - An extensible uploader system in `core/src/integrations/` allows multiple CMS integrations to handle uploads. Each uploader implements: + - `canHandle(sourceIntegration)` - Returns true if it handles the integration type + - `upload(options)` - Performs the actual upload + +3. **Post-test orchestration** - After test execution completes, `runSpecs` calls `uploadChangedFiles()` which: + - Collects all steps with `changed: true` and `sourceIntegration` metadata + - Routes each to the appropriate uploader + - Uses best-effort error handling (continues on individual failures) + +## Data Flow + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ RESOLVER │ +├─────────────────────────────────────────────────────────────────────┤ +│ 1. Load content from Heretto API │ +│ 2. Parse DITA/markdown for screenshot steps │ +│ 3. Attach sourceIntegration metadata to each screenshot step │ +│ 4. Track path mappings for upload routing │ +└─────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ CORE │ +├─────────────────────────────────────────────────────────────────────┤ +│ 5. Execute test specs │ +│ 6. For each screenshot step: │ +│ - Capture new screenshot │ +│ - Compare against baseline │ +│ - Set changed=true if variation exceeds threshold │ +│ - Preserve sourceIntegration in outputs │ +│ 7. After all tests complete: │ +│ - Collect changed screenshots with sourceIntegration │ +│ - Upload each to its source CMS │ +│ - Report upload results │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +## Heretto API Usage + +### File Search + +When a screenshot lacks a pre-resolved file ID, the uploader searches Heretto by filename: + +``` +POST /ezdnxtgen/api/search +Content-Type: application/json +Authorization: Basic + +{ + "queryString": "", + "searchResultType": "FILES" +} +``` + +### File Upload + +Changed screenshots are uploaded via the REST API: + +``` +PUT /rest/all-files//content +Content-Type: image/png +Authorization: Basic + + +``` + +## Error Handling + +The upload process uses best-effort error handling: + +- **Individual failures don't stop other uploads** - If one screenshot fails to upload, the system continues with remaining files +- **Results are logged and reported** - Upload successes, failures, and skipped items are tracked in `report.uploadResults` +- **Missing integrations are skipped** - If no matching integration config is found, the file is skipped with a warning + +## Extensibility + +The uploader abstraction supports adding new CMS integrations: + +```javascript +const { registerUploader } = require('./integrations'); + +class MyCustomUploader { + canHandle(sourceIntegration) { + return sourceIntegration?.type === 'my-cms'; + } + + async upload({ config, integrationConfig, localFilePath, sourceIntegration, log }) { + // Upload implementation + return { status: 'PASS', description: 'Uploaded successfully' }; + } +} + +registerUploader(new MyCustomUploader()); +``` + +## Configuration + +No additional configuration is required. Screenshot sync is automatic when: + +1. Content is sourced from a Heretto integration +2. The Heretto integration has valid API credentials (`apiBaseUrl`, `username`, `apiToken`) +3. Screenshots change during test execution + +## Limitations + +- **Write access required** - The API token must have write permissions to update files in Heretto +- **File must exist in Heretto** - New screenshots (not already in Heretto) cannot be uploaded automatically +- **Search relies on filename** - If multiple files have the same name, the first match is used diff --git a/resolver/.github/workflows/integration-tests.yml b/resolver/.github/workflows/integration-tests.yml new file mode 100644 index 0000000..a372c93 --- /dev/null +++ b/resolver/.github/workflows/integration-tests.yml @@ -0,0 +1,119 @@ +name: Integration Tests + +on: + push: + branches: + - main + - heretto + paths: + - 'src/heretto*.js' + - '.github/workflows/integration-tests.yml' + pull_request: + branches: + - main + paths: + - 'src/heretto*.js' + - '.github/workflows/integration-tests.yml' + workflow_dispatch: + # Allow manual triggering for testing + schedule: + # Run daily at 6:00 AM UTC to catch any API changes + - cron: '0 6 * * *' + +jobs: + heretto-integration-tests: + runs-on: ubuntu-latest + timeout-minutes: 15 + # Only run if secrets are available (not available on fork PRs) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'npm' + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Run integration tests + env: + CI: 'true' + HERETTO_ORGANIZATION_ID: ${{ secrets.HERETTO_ORGANIZATION_ID }} + HERETTO_USERNAME: ${{ secrets.HERETTO_USERNAME }} + HERETTO_API_TOKEN: ${{ secrets.HERETTO_API_TOKEN }} + HERETTO_SCENARIO_NAME: ${{ secrets.HERETTO_SCENARIO_NAME || 'Doc Detective' }} + run: npm run test:integration + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: integration-test-results + path: | + test-results/ + *.log + retention-days: 7 + + notify-on-failure: + runs-on: ubuntu-latest + needs: heretto-integration-tests + if: failure() && github.event_name == 'schedule' + steps: + - name: Create issue on failure + uses: actions/github-script@v7 + with: + script: | + const title = '🚨 Heretto Integration Tests Failed'; + const body = ` + ## Integration Test Failure + + The scheduled Heretto integration tests have failed. + + **Workflow Run:** [View Details](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + **Triggered:** ${{ github.event_name }} + **Branch:** ${{ github.ref_name }} + + Please investigate and fix the issue. + + ### Possible Causes + - Heretto API changes + - Expired or invalid API credentials + - Network connectivity issues + - Changes in test scenario configuration + + /cc @${{ github.repository_owner }} + `; + + // Check if an open issue already exists + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'integration-test-failure' + }); + + const existingIssue = issues.data.find(issue => issue.title === title); + + if (!existingIssue) { + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: body, + labels: ['bug', 'integration-test-failure', 'automated'] + }); + } else { + // Add a comment to the existing issue + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number, + body: `Another failure detected on ${new Date().toISOString()}\n\n[Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})` + }); + } diff --git a/resolver/package-lock.json b/resolver/package-lock.json index 1b40437..173a859 100644 --- a/resolver/package-lock.json +++ b/resolver/package-lock.json @@ -13,8 +13,9 @@ "adm-zip": "^0.5.16", "ajv": "^8.17.1", "axios": "^1.13.2", - "doc-detective-common": "3.6.0-dev.1", + "doc-detective-common": "file:../common", "dotenv": "^17.2.3", + "fast-xml-parser": "^5.3.3", "json-schema-faker": "^0.5.9", "posthog-node": "^5.17.2" }, @@ -29,6 +30,25 @@ "yaml": "^2.8.2" } }, + "../common": { + "name": "doc-detective-common", + "version": "3.6.0-dev.1", + "license": "AGPL-3.0-only", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^15.1.3", + "ajv": "^8.17.1", + "ajv-errors": "^3.0.0", + "ajv-formats": "^3.0.1", + "ajv-keywords": "^5.1.0", + "axios": "^1.13.2", + "yaml": "^2.8.2" + }, + "devDependencies": { + "chai": "^6.2.1", + "mocha": "^11.7.5", + "sinon": "^21.0.0" + } + }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "15.1.3", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-15.1.3.tgz", @@ -232,7 +252,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -244,44 +263,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -713,19 +694,8 @@ } }, "node_modules/doc-detective-common": { - "version": "3.6.0-dev.1", - "resolved": "https://registry.npmjs.org/doc-detective-common/-/doc-detective-common-3.6.0-dev.1.tgz", - "integrity": "sha512-e+3FNyqjhPUZRq+4A1t7G+au07RZockzCHdQ6LDaQQySPtAiNSO42v48ylbHIu4ZOn06SO933rVJe/b+e1GVdw==", - "license": "AGPL-3.0-only", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^15.1.3", - "ajv": "^8.17.1", - "ajv-errors": "^3.0.0", - "ajv-formats": "^3.0.1", - "ajv-keywords": "^5.1.0", - "axios": "^1.13.2", - "yaml": "^2.8.2" - } + "resolved": "../common", + "link": true }, "node_modules/dotenv": { "version": "17.2.3", @@ -953,6 +923,24 @@ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, + "node_modules/fast-xml-parser": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz", + "integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", @@ -2339,6 +2327,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -2588,6 +2588,7 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/resolver/package.json b/resolver/package.json index b6e7b26..2aa74f4 100644 --- a/resolver/package.json +++ b/resolver/package.json @@ -4,7 +4,9 @@ "description": "Detect and resolve docs into Doc Detective tests.", "main": "src/index.js", "scripts": { - "test": "mocha src/*.test.js", + "test": "mocha src/*.test.js --ignore src/*.integration.test.js", + "test:integration": "mocha src/*.integration.test.js --timeout 600000", + "test:all": "mocha src/*.test.js --timeout 600000", "dev": "node dev" }, "repository": { @@ -28,8 +30,9 @@ "adm-zip": "^0.5.16", "ajv": "^8.17.1", "axios": "^1.13.2", - "doc-detective-common": "3.6.0-dev.1", + "doc-detective-common": "file:../common", "dotenv": "^17.2.3", + "fast-xml-parser": "^5.3.3", "json-schema-faker": "^0.5.9", "posthog-node": "^5.17.2" }, diff --git a/resolver/src/config.js b/resolver/src/config.js index 8bacb3a..b363a2e 100644 --- a/resolver/src/config.js +++ b/resolver/src/config.js @@ -88,6 +88,7 @@ let defaultFileTypes = { step: [ "<\\?doc-detective\\s+step\\s+([\\s\\S]*?)\\s*\\?>", "", + '([\\s\\S]*?)<\\/data>', ], }, markup: [ @@ -124,7 +125,7 @@ let defaultFileTypes = { { name: "runShellCmdWithCodeblock", regex: [ - "(?:[Rr]un|[Ee]xecute)\\s+(?:the\\s+)?(?:following\\s+)?(?:command)[^<]*<\\/cmd>\\s*\\s*]*outputclass=\"(?:shell|bash)\"[^>]*>([\\s\\S]*?)<\\/codeblock>", + '(?:[Rr]un|[Ee]xecute)\\s+(?:the\\s+)?(?:following\\s+)?(?:command)[^<]*<\\/cmd>\\s*\\s*]*outputclass="(?:shell|bash)"[^>]*>([\\s\\S]*?)<\\/codeblock>', ], actions: [ { @@ -133,27 +134,21 @@ let defaultFileTypes = { }, }, ], - }, + }, // Inline Elements - for finding UI elements and text { name: "findUiControl", - regex: [ - "([^<]+)<\\/uicontrol>", - ], + regex: ["([^<]+)<\\/uicontrol>"], actions: ["find"], }, { name: "verifyWindowTitle", - regex: [ - "([^<]+)<\\/wintitle>", - ], + regex: ["([^<]+)<\\/wintitle>"], actions: ["find"], }, { name: "EnterKey", - regex: [ - "(?:[Pp]ress)\\s+Enter<\\/shortcut>", - ], + regex: ["(?:[Pp]ress)\\s+Enter<\\/shortcut>"], actions: [ { type: { @@ -164,9 +159,7 @@ let defaultFileTypes = { }, { name: "executeCmdName", - regex: [ - "(?:[Ee]xecute|[Rr]un)\\s+([^<]+)<\\/cmdname>", - ], + regex: ["(?:[Ee]xecute|[Rr]un)\\s+([^<]+)<\\/cmdname>"], actions: [ { runShell: { @@ -175,7 +168,7 @@ let defaultFileTypes = { }, ], }, - + // Links and References - for link validation { name: "checkExternalXref", @@ -187,24 +180,20 @@ let defaultFileTypes = { }, { name: "checkHyperlink", - regex: [ - ']*>', - ], + regex: [']*>'], actions: ["checkLink"], }, { name: "checkLinkElement", - regex: [ - ']*>', - ], + regex: [']*>'], actions: ["checkLink"], }, - + // Code Execution { name: "runShellCodeblock", regex: [ - "]*outputclass=\"(?:shell|bash)\"[^>]*>([\\s\\S]*?)<\\/codeblock>", + ']*outputclass="(?:shell|bash)"[^>]*>([\\s\\S]*?)<\\/codeblock>', ], actions: [ { @@ -217,7 +206,7 @@ let defaultFileTypes = { { name: "runCode", regex: [ - "]*outputclass=\"(python|py|javascript|js)\"[^>]*>([\\s\\S]*?)<\\/codeblock>", + ']*outputclass="(python|py|javascript|js)"[^>]*>([\\s\\S]*?)<\\/codeblock>', ], actions: [ { @@ -231,7 +220,7 @@ let defaultFileTypes = { }, ], }, - + // Legacy patterns for compatibility with existing tests { name: "clickOnscreenText", @@ -255,8 +244,10 @@ let defaultFileTypes = { { name: "screenshotImage", regex: [ + ']*outputclass="[^"]*screenshot[^"]*href="([^"]+)"[^>]*"[^>]*\\/>', ']*href="([^"]+)"[^>]*outputclass="[^"]*screenshot[^"]*"[^>]*\\/>', - ], + ']*outputclass="[^"]*screenshot[^"]*"[^>]*href="([^"]+)"[\\s\\S]*?<\\/image>', + ']*href="([^"]+)"[^>]*outputclass="[^"]*screenshot[^"]*"[\\s\\S]*?<\\/image>', ], actions: ["screenshot"], }, { @@ -267,7 +258,7 @@ let defaultFileTypes = { { name: "httpRequestFormat", regex: [ - "]*outputclass=\"http\"[^>]*>\\s*([A-Z]+)\\s+([^\\s]+)(?:\\s+HTTP\\/[\\d.]+)?\\s*(?:\\r?\\n| )((?:[^\\s<]+:\\s+[^\\r\\n<]+(?:\\r?\\n| ))*)(?:\\s*(?:\\r?\\n| )([\\s\\S]*?))?\\s*<\\/codeblock>", + ']*outputclass="http"[^>]*>\\s*([A-Z]+)\\s+([^\\s]+)(?:\\s+HTTP\\/[\\d.]+)?\\s*(?:\\r?\\n| )((?:[^\\s<]+:\\s+[^\\r\\n<]+(?:\\r?\\n| ))*)(?:\\s*(?:\\r?\\n| )([\\s\\S]*?))?\\s*<\\/codeblock>', ], actions: [ { @@ -285,7 +276,7 @@ let defaultFileTypes = { { name: "runCode", regex: [ - "]*outputclass=\"(bash|python|py|javascript|js)\"[^>]*>([\\s\\S]*?)<\\/codeblock>", + ']*outputclass="(bash|python|py|javascript|js)"[^>]*>([\\s\\S]*?)<\\/codeblock>', ], actions: [ { @@ -462,7 +453,7 @@ defaultFileTypes = { /** * Resolves the concurrentRunners configuration value from various input formats * to a concrete integer for the core execution engine. - * + * * @param {Object} config - The configuration object * @returns {number} The resolved concurrent runners value */ @@ -665,10 +656,10 @@ async function setConfig({ config }) { // Detect current environment. config.environment = getEnvironment(); - + // Resolve concurrent runners configuration config.concurrentRunners = resolveConcurrentRunners(config); - + // TODO: Revise loadDescriptions() so it doesn't mutate the input but instead returns an updated object await loadDescriptions(config); diff --git a/resolver/src/heretto.integration.test.js b/resolver/src/heretto.integration.test.js new file mode 100644 index 0000000..a26e987 --- /dev/null +++ b/resolver/src/heretto.integration.test.js @@ -0,0 +1,232 @@ +/** + * Heretto Integration Tests + * + * These tests run against the real Heretto API and are designed to only + * execute in CI environments (GitHub Actions) where credentials are available. + * + * Required environment variables: + * - HERETTO_ORGANIZATION_ID: The Heretto organization ID + * - HERETTO_USERNAME: The Heretto username (email) + * - HERETTO_API_TOKEN: The Heretto API token + * + * These tests are skipped when: + * - Running locally without CI=true environment variable + * - Required environment variables are not set + */ + +const heretto = require("./heretto"); + +before(async function () { + const { expect } = await import("chai"); + global.expect = expect; +}); + +/** + * Check if we're running in CI and have required credentials + */ +const isCI = process.env.CI === "true"; +const hasCredentials = + process.env.HERETTO_ORGANIZATION_ID && + process.env.HERETTO_USERNAME && + process.env.HERETTO_API_TOKEN; + +const shouldRunIntegrationTests = isCI && hasCredentials; + +// Helper to skip tests when not in CI or missing credentials +const describeIntegration = shouldRunIntegrationTests ? describe : describe.skip; + +// Log why tests are being skipped +if (!shouldRunIntegrationTests) { + console.log("\n⏭️ Heretto integration tests skipped:"); + if (!isCI) { + console.log(" - Not running in CI environment (CI !== 'true')"); + } + if (!hasCredentials) { + console.log(" - Missing required environment variables:"); + if (!process.env.HERETTO_ORGANIZATION_ID) + console.log(" - HERETTO_ORGANIZATION_ID"); + if (!process.env.HERETTO_USERNAME) console.log(" - HERETTO_USERNAME"); + if (!process.env.HERETTO_API_TOKEN) console.log(" - HERETTO_API_TOKEN"); + } + console.log(""); +} + +describeIntegration("Heretto Integration Tests (CI Only)", function () { + // These tests interact with real APIs, so allow longer timeouts + this.timeout(120000); // 2 minutes per test + + let client; + let herettoConfig; + const mockLog = (...args) => { + if (process.env.DEBUG) { + console.log(...args); + } + }; + const mockConfig = { logLevel: process.env.DEBUG ? "debug" : "info" }; + + before(function () { + herettoConfig = { + name: "integration-test", + organizationId: process.env.HERETTO_ORGANIZATION_ID, + username: process.env.HERETTO_USERNAME, + apiToken: process.env.HERETTO_API_TOKEN, + scenarioName: process.env.HERETTO_SCENARIO_NAME || "Doc Detective", + }; + + client = heretto.createApiClient(herettoConfig); + }); + + describe("API Client Creation", function () { + it("should create a valid API client", function () { + expect(client).to.not.be.null; + expect(client).to.have.property("get"); + expect(client).to.have.property("post"); + }); + + it("should configure correct base URL", function () { + const expectedBaseUrl = `https://${herettoConfig.organizationId}.heretto.com/ezdnxtgen/api/v2`; + expect(client.defaults.baseURL).to.equal(expectedBaseUrl); + }); + }); + + describe("findScenario", function () { + it("should find an existing scenario with correct configuration", async function () { + const result = await heretto.findScenario( + client, + mockLog, + mockConfig, + herettoConfig.scenarioName + ); + + // The scenario should exist and have required properties + expect(result).to.not.be.null; + expect(result).to.have.property("scenarioId"); + expect(result).to.have.property("fileId"); + expect(result.scenarioId).to.be.a("string"); + expect(result.fileId).to.be.a("string"); + }); + + it("should return null for non-existent scenario", async function () { + const result = await heretto.findScenario( + client, + mockLog, + mockConfig, + "NonExistent Scenario That Should Not Exist 12345" + ); + + expect(result).to.be.null; + }); + }); + + describe("Full Publishing Workflow", function () { + let scenarioInfo; + let jobId; + + before(async function () { + // Find the scenario first + scenarioInfo = await heretto.findScenario( + client, + mockLog, + mockConfig, + herettoConfig.scenarioName + ); + + if (!scenarioInfo) { + this.skip(); + } + }); + + it("should trigger a publishing job", async function () { + const job = await heretto.triggerPublishingJob( + client, + scenarioInfo.fileId, + scenarioInfo.scenarioId + ); + + expect(job).to.not.be.null; + expect(job).to.have.property("id"); + jobId = job.id; + }); + + it("should poll job status until completion", async function () { + // This test may take a while as it waits for the job to complete + this.timeout(360000); // 6 minutes + + const completedJob = await heretto.pollJobStatus( + client, + scenarioInfo.fileId, + jobId, + mockLog, + mockConfig + ); + + expect(completedJob).to.not.be.null; + expect(completedJob).to.have.property("status"); + expect(completedJob.status).to.have.property("status"); + + // Job should be in a completed state + const completedStates = ["COMPLETED", "FAILED", "DONE"]; + expect(completedStates).to.include(completedJob.status.status); + }); + + it("should fetch job asset details", async function () { + const assets = await heretto.getJobAssetDetails( + client, + scenarioInfo.fileId, + jobId + ); + + expect(assets).to.be.an("array"); + expect(assets.length).to.be.greaterThan(0); + + // Should contain at least some DITA files + const hasDitaFiles = assets.some( + (path) => path.endsWith(".dita") || path.endsWith(".ditamap") + ); + expect(hasDitaFiles).to.be.true; + }); + + it("should validate ditamap exists in assets", async function () { + const assets = await heretto.getJobAssetDetails( + client, + scenarioInfo.fileId, + jobId + ); + + const hasValidDitamap = heretto.validateDitamapInAssets(assets); + expect(hasValidDitamap).to.be.true; + }); + + it("should download and extract output", async function () { + const outputPath = await heretto.downloadAndExtractOutput( + client, + scenarioInfo.fileId, + jobId, + herettoConfig.name, + mockLog, + mockConfig + ); + + expect(outputPath).to.not.be.null; + expect(outputPath).to.be.a("string"); + expect(outputPath).to.include("heretto_"); + }); + }); + + describe("loadHerettoContent (End-to-End)", function () { + it("should load content from Heretto successfully", async function () { + // This is the full end-to-end test + this.timeout(600000); // 10 minutes for full workflow + + const outputPath = await heretto.loadHerettoContent( + herettoConfig, + mockLog, + mockConfig + ); + + expect(outputPath).to.not.be.null; + expect(outputPath).to.be.a("string"); + expect(outputPath).to.include("heretto_"); + }); + }); +}); diff --git a/resolver/src/heretto.js b/resolver/src/heretto.js index c535e33..cedf1a2 100644 --- a/resolver/src/heretto.js +++ b/resolver/src/heretto.js @@ -4,6 +4,7 @@ const path = require("path"); const os = require("os"); const crypto = require("crypto"); const AdmZip = require("adm-zip"); +const { XMLParser } = require("fast-xml-parser"); // Internal constants - not exposed to users const POLLING_INTERVAL_MS = 5000; @@ -11,6 +12,8 @@ const POLLING_TIMEOUT_MS = 300000; // 5 minutes const API_REQUEST_TIMEOUT_MS = 30000; // 30 seconds for individual API requests const DOWNLOAD_TIMEOUT_MS = 300000; // 5 minutes for downloads const DEFAULT_SCENARIO_NAME = "Doc Detective"; +// Base URL for REST API (different from publishing API) +const REST_API_PATH = "/rest/all-files"; /** * Creates a Base64-encoded Basic Auth header from username and API token. @@ -52,6 +55,26 @@ function createApiClient(herettoConfig) { }); } +/** + * Creates an axios instance configured for Heretto REST API requests (different base URL). + * @param {Object} herettoConfig - Heretto integration configuration + * @returns {Object} Configured axios instance for REST API + */ +function createRestApiClient(herettoConfig) { + const authHeader = createAuthHeader( + herettoConfig.username, + herettoConfig.apiToken + ); + return axios.create({ + baseURL: `https://${herettoConfig.organizationId}.heretto.com`, + timeout: API_REQUEST_TIMEOUT_MS, + headers: { + Authorization: `Basic ${authHeader}`, + Accept: "application/xml, text/xml, */*", + }, + }); +} + /** * Fetches all available publishing scenarios from Heretto. * @param {Object} client - Configured axios instance @@ -151,7 +174,10 @@ async function findScenario(client, log, config, scenarioName) { "debug", `Found existing "${scenarioName}" scenario: ${foundScenario.id}` ); - return { scenarioId: foundScenario.id, fileId: fileUuidPickerParam.value }; + return { + scenarioId: foundScenario.id, + fileId: fileUuidPickerParam.value, + }; } catch (error) { log( config, @@ -191,8 +217,64 @@ async function getJobStatus(client, fileId, jobId) { return response.data; } +/** + * Gets all asset file paths from a completed publishing job. + * Handles pagination to retrieve all assets. + * @param {Object} client - Configured axios instance + * @param {string} fileId - UUID of the DITA map + * @param {string} jobId - ID of the publishing job + * @returns {Promise>} Array of asset file paths + */ +async function getJobAssetDetails(client, fileId, jobId) { + const allAssets = []; + let page = 0; + const pageSize = 100; + let hasMorePages = true; + + while (hasMorePages) { + const response = await client.get( + `/files/${fileId}/publishes/${jobId}/assets`, + { + params: { + page, + size: pageSize, + }, + } + ); + + const data = response.data; + const content = data.content || []; + + for (const asset of content) { + if (asset.filePath) { + allAssets.push(asset.filePath); + } + } + + // Check if there are more pages + const totalPages = data.totalPages || 1; + page++; + hasMorePages = page < totalPages; + } + + return allAssets; +} + +/** + * Validates that a .ditamap file exists in the job assets. + * Checks for any .ditamap file in the ot-output/dita/ directory. + * @param {Array} assets - Array of asset file paths + * @returns {boolean} True if a .ditamap is found in ot-output/dita/ + */ +function validateDitamapInAssets(assets) { + return assets.some((assetPath) => + assetPath.startsWith("ot-output/dita/") && assetPath.endsWith(".ditamap") + ); +} + /** * Polls a publishing job until completion or timeout. + * After job completes, validates that a .ditamap file exists in the output. * @param {Object} client - Configured axios instance * @param {string} fileId - UUID of the DITA map * @param {string} jobId - ID of the publishing job @@ -208,17 +290,46 @@ async function pollJobStatus(client, fileId, jobId, log, config) { const job = await getJobStatus(client, fileId, jobId); log(config, "debug", `Job ${jobId} status: ${job?.status?.status}`); - if (job?.status?.result === "SUCCESS") { - return job; - } - - if (job?.status?.result === "FAIL") { + // Check if job has reached a terminal state (result is set) + if (job?.status?.result) { log( config, - "warning", - `Publishing job ${jobId} failed.` + "debug", + `Job ${jobId} completed with result: ${job.status.result}` ); - return null; + + // Validate that a .ditamap file exists in the output + try { + const assets = await getJobAssetDetails(client, fileId, jobId); + log( + config, + "debug", + `Job ${jobId} has ${assets.length} assets` + ); + + if (validateDitamapInAssets(assets)) { + log( + config, + "debug", + `Found .ditamap file in ot-output/dita/` + ); + return job; + } + + log( + config, + "warning", + `Publishing job ${jobId} completed but no .ditamap file found in ot-output/dita/` + ); + return null; + } catch (assetError) { + log( + config, + "warning", + `Failed to validate job assets: ${assetError.message}` + ); + return null; + } } // Wait before next poll @@ -333,6 +444,140 @@ async function downloadAndExtractOutput( } } +/** + * Retrieves resource dependencies (all files) for a ditamap from Heretto REST API. + * This provides the complete file structure with UUIDs and paths. + * @param {Object} restClient - Configured axios instance for REST API + * @param {string} ditamapId - UUID of the ditamap file + * @param {Function} log - Logging function + * @param {Object} config - Doc Detective config for logging + * @returns {Promise} Object mapping relative paths to UUIDs and parent folder info + */ +async function getResourceDependencies(restClient, ditamapId, log, config) { + const pathToUuidMap = {}; + + const xmlParser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "@_", + }); + + // First, try to get the ditamap's own info (this is more reliable than the dependencies endpoint) + try { + log(config, "debug", `Fetching ditamap info for: ${ditamapId}`); + const ditamapInfo = await restClient.get(`${REST_API_PATH}/${ditamapId}`); + const ditamapParsed = xmlParser.parse(ditamapInfo.data); + + const ditamapUri = ditamapParsed.resource?.["xmldb-uri"] || ditamapParsed["@_uri"]; + const ditamapName = ditamapParsed.resource?.name || ditamapParsed["@_name"]; + const ditamapParentFolder = ditamapParsed.resource?.["folder-uuid"] || + ditamapParsed.resource?.["@_folder-uuid"] || + ditamapParsed["@_folder-uuid"]; + + log(config, "debug", `Ditamap info: uri=${ditamapUri}, name=${ditamapName}, parentFolder=${ditamapParentFolder}`); + + if (ditamapUri) { + let relativePath = ditamapUri; + const orgPathMatch = relativePath?.match(/\/db\/organizations\/[^/]+\/(.+)/); + if (orgPathMatch) { + relativePath = orgPathMatch[1]; + } + + pathToUuidMap[relativePath] = { + uuid: ditamapId, + fullPath: ditamapUri, + name: ditamapName, + parentFolderId: ditamapParentFolder, + isDitamap: true, + }; + + // Store the ditamap info as reference points for creating new files + pathToUuidMap._ditamapPath = relativePath; + pathToUuidMap._ditamapId = ditamapId; + pathToUuidMap._ditamapParentFolderId = ditamapParentFolder; + + log(config, "debug", `Ditamap path: ${relativePath}, parent folder: ${ditamapParentFolder}`); + } + } catch (ditamapError) { + log(config, "warning", `Could not get ditamap info: ${ditamapError.message}`); + } + + // Then try to get the full dependencies list (this endpoint may not be available) + try { + log(config, "debug", `Fetching resource dependencies for ditamap: ${ditamapId}`); + + const response = await restClient.get(`${REST_API_PATH}/${ditamapId}/dependencies`); + const xmlData = response.data; + + const parsed = xmlParser.parse(xmlData); + + // Extract dependencies from the response + // Response format: ...... + const extractDependencies = (obj, parentPath = "") => { + if (!obj) return; + + // Handle single dependency or array of dependencies + let dependencies = obj.dependencies?.dependency || obj.dependency; + if (!dependencies) { + // Try to extract from root-level response + if (obj["@_id"] && obj["@_uri"]) { + dependencies = [obj]; + } else if (Array.isArray(obj)) { + dependencies = obj; + } + } + + if (!dependencies) return; + if (!Array.isArray(dependencies)) { + dependencies = [dependencies]; + } + + for (const dep of dependencies) { + const uuid = dep["@_id"] || dep["@_uuid"] || dep.id || dep.uuid; + const uri = dep["@_uri"] || dep["@_path"] || dep.uri || dep.path || dep["xmldb-uri"]; + const name = dep["@_name"] || dep.name; + const parentFolderId = dep["@_folder-uuid"] || dep["@_parent"] || dep["folder-uuid"]; + + if (uuid && (uri || name)) { + // Extract the relative path from the full URI + // URI format: /db/organizations/{org}/{path} + let relativePath = uri || name; + const orgPathMatch = relativePath?.match(/\/db\/organizations\/[^/]+\/(.+)/); + if (orgPathMatch) { + relativePath = orgPathMatch[1]; + } + + pathToUuidMap[relativePath] = { + uuid, + fullPath: uri, + name: name || path.basename(relativePath || ""), + parentFolderId, + }; + + log(config, "debug", `Mapped: ${relativePath} -> ${uuid}`); + } + + // Recursively process nested dependencies + if (dep.dependencies || dep.dependency) { + extractDependencies(dep); + } + } + }; + + extractDependencies(parsed); + + log(config, "info", `Retrieved ${Object.keys(pathToUuidMap).length} resource dependencies from Heretto`); + + } catch (error) { + // Log more details about the error for debugging + const statusCode = error.response?.status; + const responseData = error.response?.data; + log(config, "debug", `Dependencies endpoint not available (${statusCode}), will use ditamap info as fallback`); + // Continue with ditamap info only - the fallback will create files in the ditamap's parent folder + } + + return pathToUuidMap; +} + /** * Main function to load content from a Heretto CMS instance. * Triggers a publishing job, waits for completion, and downloads the output. @@ -350,10 +595,16 @@ async function loadHerettoContent(herettoConfig, log, config) { try { const client = createApiClient(herettoConfig); + const restClient = createRestApiClient(herettoConfig); // Find the Doc Detective publishing scenario const scenarioName = herettoConfig.scenarioName || DEFAULT_SCENARIO_NAME; - const scenario = await findScenario(client, log, config, scenarioName); + const scenario = await findScenario( + client, + log, + config, + scenarioName + ); if (!scenario) { log( config, @@ -363,6 +614,19 @@ async function loadHerettoContent(herettoConfig, log, config) { return null; } + // Fetch resource dependencies to build path-to-UUID mapping + // This gives us the complete file structure with UUIDs before we even run the job + if (herettoConfig.uploadOnChange) { + log(config, "debug", `Fetching resource dependencies for ditamap ${scenario.fileId}...`); + const resourceDependencies = await getResourceDependencies( + restClient, + scenario.fileId, + log, + config + ); + herettoConfig.resourceDependencies = resourceDependencies; + } + // Trigger publishing job log( config, @@ -404,6 +668,17 @@ async function loadHerettoContent(herettoConfig, log, config) { config ); + // Build file mapping from extracted content (legacy approach, still useful as fallback) + if (outputPath && herettoConfig.uploadOnChange) { + const fileMapping = await buildFileMapping( + outputPath, + herettoConfig, + log, + config + ); + herettoConfig.fileMapping = fileMapping; + } + return outputPath; } catch (error) { log( @@ -415,14 +690,358 @@ async function loadHerettoContent(herettoConfig, log, config) { } } +/** + * Builds a mapping of local file paths to Heretto file metadata. + * Parses DITA files to extract file references and attempts to resolve UUIDs. + * @param {string} outputPath - Path to extracted Heretto content + * @param {Object} herettoConfig - Heretto integration configuration + * @param {Function} log - Logging function + * @param {Object} config - Doc Detective config for logging + * @returns {Promise} Mapping of local paths to {fileId, filePath} + */ +async function buildFileMapping(outputPath, herettoConfig, log, config) { + const fileMapping = {}; + const xmlParser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "@_", + }); + + try { + // Recursively find all DITA/XML files + const ditaFiles = findFilesWithExtensions(outputPath, [ + ".dita", + ".ditamap", + ".xml", + ]); + + for (const ditaFile of ditaFiles) { + try { + const content = fs.readFileSync(ditaFile, "utf-8"); + const parsed = xmlParser.parse(content); + + // Extract image references from DITA content + const imageRefs = extractImageReferences(parsed); + + for (const imageRef of imageRefs) { + // Resolve relative path to absolute local path + const absoluteLocalPath = path.resolve( + path.dirname(ditaFile), + imageRef + ); + + if (!fileMapping[absoluteLocalPath]) { + fileMapping[absoluteLocalPath] = { + filePath: imageRef, + sourceFile: ditaFile, + }; + } + } + } catch (parseError) { + log( + config, + "debug", + `Failed to parse ${ditaFile} for file mapping: ${parseError.message}` + ); + } + } + + log( + config, + "debug", + `Built file mapping with ${Object.keys(fileMapping).length} entries` + ); + } catch (error) { + log(config, "warning", `Failed to build file mapping: ${error.message}`); + } + + return fileMapping; +} + +/** + * Recursively finds files with specified extensions. + * @param {string} dir - Directory to search + * @param {Array} extensions - File extensions to match (e.g., ['.dita', '.xml']) + * @returns {Array} Array of matching file paths + */ +function findFilesWithExtensions(dir, extensions) { + const results = []; + + try { + const items = fs.readdirSync(dir); + + for (const item of items) { + const fullPath = path.join(dir, item); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + results.push(...findFilesWithExtensions(fullPath, extensions)); + } else if ( + extensions.some((ext) => fullPath.toLowerCase().endsWith(ext)) + ) { + results.push(fullPath); + } + } + } catch (error) { + // Ignore read errors for inaccessible directories + } + + return results; +} + +/** + * Extracts image references from parsed DITA XML content. + * Looks for elements with href attributes. + * @param {Object} parsedXml - Parsed XML object + * @returns {Array} Array of image href values + */ +function extractImageReferences(parsedXml) { + const refs = []; + + function traverse(obj) { + if (!obj || typeof obj !== "object") return; + + // Check for image elements + if (obj.image) { + const images = Array.isArray(obj.image) ? obj.image : [obj.image]; + for (const img of images) { + if (img["@_href"]) { + refs.push(img["@_href"]); + } + } + } + + // Recursively traverse all properties + for (const key of Object.keys(obj)) { + if (typeof obj[key] === "object") { + traverse(obj[key]); + } + } + } + + traverse(parsedXml); + return refs; +} + +/** + * Searches for a file in Heretto by filename. + * @param {Object} herettoConfig - Heretto integration configuration + * @param {string} filename - Name of the file to search for + * @param {string} folderPath - Optional folder path to search within + * @param {Function} log - Logging function + * @param {Object} config - Doc Detective config for logging + * @returns {Promise} File info with ID and URI, or null if not found + */ +async function searchFileByName( + herettoConfig, + filename, + folderPath, + log, + config +) { + const client = createApiClient(herettoConfig); + + try { + const searchBody = { + queryString: filename, + foldersToSearch: {}, + startOffset: 0, + endOffset: 10, + searchResultType: "FILES_ONLY", + addPrefixAndFuzzy: false, + }; + + // If folderPath provided, search within that folder; otherwise search root + if (folderPath) { + searchBody.foldersToSearch[folderPath] = true; + } else { + // Search in organization root + searchBody.foldersToSearch[ + `/db/organizations/${herettoConfig.organizationId}/` + ] = true; + } + + const response = await client.post( + "/ezdnxtgen/api/search", + searchBody, + { + baseURL: `https://${herettoConfig.organizationId}.heretto.com`, + headers: { "Content-Type": "application/json" }, + } + ); + + if (response.data?.hits?.length > 0) { + // Find exact filename match + const exactMatch = response.data.hits.find( + (hit) => hit.fileEntity?.name === filename + ); + + if (exactMatch) { + return { + fileId: exactMatch.fileEntity.ID, + filePath: exactMatch.fileEntity.URI, + name: exactMatch.fileEntity.name, + }; + } + } + + return null; + } catch (error) { + log( + config, + "debug", + `Failed to search for file "${filename}": ${error.message}` + ); + return null; + } +} + +/** + * Uploads a file to Heretto CMS. + * @param {Object} herettoConfig - Heretto integration configuration + * @param {string} fileId - UUID of the file to update + * @param {string} localFilePath - Local path to the file to upload + * @param {Function} log - Logging function + * @param {Object} config - Doc Detective config for logging + * @returns {Promise} Result object with status and description + */ +async function uploadFile(herettoConfig, fileId, localFilePath, log, config) { + const client = createRestApiClient(herettoConfig); + + try { + // Read file as binary + const fileBuffer = fs.readFileSync(localFilePath); + + // Determine content type from file extension + const ext = path.extname(localFilePath).toLowerCase(); + let contentType = "application/octet-stream"; + if (ext === ".png") contentType = "image/png"; + else if (ext === ".jpg" || ext === ".jpeg") contentType = "image/jpeg"; + else if (ext === ".gif") contentType = "image/gif"; + else if (ext === ".svg") contentType = "image/svg+xml"; + else if (ext === ".webp") contentType = "image/webp"; + + log(config, "debug", `Uploading ${localFilePath} to Heretto file ${fileId}`); + + const response = await client.put( + `${REST_API_PATH}/${fileId}/content`, + fileBuffer, + { + headers: { + "Content-Type": contentType, + "Content-Length": fileBuffer.length, + }, + maxBodyLength: Infinity, + maxContentLength: Infinity, + } + ); + + if (response.status === 200 || response.status === 201) { + log( + config, + "info", + `Successfully uploaded ${path.basename(localFilePath)} to Heretto` + ); + return { + status: "PASS", + description: `File uploaded successfully to Heretto`, + }; + } + + return { + status: "FAIL", + description: `Unexpected response status: ${response.status}`, + }; + } catch (error) { + const errorMessage = error.response?.data || error.message; + log( + config, + "warning", + `Failed to upload file to Heretto: ${errorMessage}` + ); + return { + status: "FAIL", + description: `Failed to upload: ${errorMessage}`, + }; + } +} + +/** + * Resolves a local file path to a Heretto file ID. + * First checks file mapping, then searches by filename if needed. + * @param {Object} herettoConfig - Heretto integration configuration + * @param {string} localFilePath - Local path to the file + * @param {Object} sourceIntegration - Source integration metadata from step + * @param {Function} log - Logging function + * @param {Object} config - Doc Detective config for logging + * @returns {Promise} Heretto file ID or null if not found + */ +async function resolveFileId( + herettoConfig, + localFilePath, + sourceIntegration, + log, + config +) { + // If fileId is already known, use it + if (sourceIntegration?.fileId) { + return sourceIntegration.fileId; + } + + // Check file mapping + if (herettoConfig.fileMapping && herettoConfig.fileMapping[localFilePath]) { + const mapping = herettoConfig.fileMapping[localFilePath]; + if (mapping.fileId) { + return mapping.fileId; + } + } + + // Search by filename + const filename = path.basename(localFilePath); + const searchResult = await searchFileByName( + herettoConfig, + filename, + null, + log, + config + ); + + if (searchResult?.fileId) { + // Cache the result in file mapping + if (!herettoConfig.fileMapping) { + herettoConfig.fileMapping = {}; + } + herettoConfig.fileMapping[localFilePath] = { + fileId: searchResult.fileId, + filePath: searchResult.filePath, + }; + return searchResult.fileId; + } + + log( + config, + "warning", + `Could not resolve Heretto file ID for ${localFilePath}` + ); + return null; +} + module.exports = { createAuthHeader, createApiClient, + createRestApiClient, findScenario, triggerPublishingJob, + getJobStatus, + getJobAssetDetails, + validateDitamapInAssets, pollJobStatus, downloadAndExtractOutput, loadHerettoContent, + buildFileMapping, + searchFileByName, + uploadFile, + resolveFileId, + getResourceDependencies, // Export constants for testing POLLING_INTERVAL_MS, POLLING_TIMEOUT_MS, diff --git a/resolver/src/heretto.test.js b/resolver/src/heretto.test.js index c89ea82..c47a5a8 100644 --- a/resolver/src/heretto.test.js +++ b/resolver/src/heretto.test.js @@ -100,9 +100,11 @@ describe("Heretto Integration", function () { const result = await heretto.findScenario(mockClient, mockLog, mockConfig, "Doc Detective"); - expect(result).to.deep.equal({ scenarioId: "scenario-123", fileId: "file-uuid-456" }); + expect(result).to.deep.equal({ + scenarioId: "scenario-123", + fileId: "file-uuid-456", + }); expect(mockClient.get.calledTwice).to.be.true; - expect(mockClient.post.called).to.be.false; }); it("should return null if scenario is not found", async function () { @@ -114,7 +116,6 @@ describe("Heretto Integration", function () { expect(result).to.be.null; expect(mockClient.get.calledOnce).to.be.true; - expect(mockClient.post.called).to.be.false; }); it("should return null if scenario fetch fails", async function () { @@ -228,6 +229,157 @@ describe("Heretto Integration", function () { }); }); + describe("getJobAssetDetails", function () { + it("should return all asset file paths from single page", async function () { + const assetsResponse = { + content: [ + { filePath: "ot-output/dita/my-guide.ditamap" }, + { filePath: "ot-output/dita/topic1.dita" }, + { filePath: "ot-output/dita/topic2.dita" }, + ], + totalPages: 1, + number: 0, + size: 100, + }; + + mockClient.get.resolves({ data: assetsResponse }); + + const result = await heretto.getJobAssetDetails(mockClient, "file-uuid", "job-123"); + + expect(result).to.deep.equal([ + "ot-output/dita/my-guide.ditamap", + "ot-output/dita/topic1.dita", + "ot-output/dita/topic2.dita", + ]); + expect(mockClient.get.calledOnce).to.be.true; + expect(mockClient.get.firstCall.args[0]).to.equal("/files/file-uuid/publishes/job-123/assets"); + }); + + it("should handle pagination and aggregate all assets", async function () { + const page1Response = { + content: [ + { filePath: "ot-output/dita/topic1.dita" }, + { filePath: "ot-output/dita/topic2.dita" }, + ], + totalPages: 2, + number: 0, + size: 100, + }; + + const page2Response = { + content: [ + { filePath: "ot-output/dita/topic3.dita" }, + { filePath: "ot-output/dita/my-guide.ditamap" }, + ], + totalPages: 2, + number: 1, + size: 100, + }; + + mockClient.get + .onFirstCall().resolves({ data: page1Response }) + .onSecondCall().resolves({ data: page2Response }); + + const result = await heretto.getJobAssetDetails(mockClient, "file-uuid", "job-123"); + + expect(result).to.deep.equal([ + "ot-output/dita/topic1.dita", + "ot-output/dita/topic2.dita", + "ot-output/dita/topic3.dita", + "ot-output/dita/my-guide.ditamap", + ]); + expect(mockClient.get.calledTwice).to.be.true; + }); + + it("should return empty array when no assets", async function () { + const assetsResponse = { + content: [], + totalPages: 1, + number: 0, + size: 100, + }; + + mockClient.get.resolves({ data: assetsResponse }); + + const result = await heretto.getJobAssetDetails(mockClient, "file-uuid", "job-123"); + + expect(result).to.deep.equal([]); + }); + + it("should skip assets without filePath", async function () { + const assetsResponse = { + content: [ + { filePath: "ot-output/dita/topic1.dita" }, + { otherField: "no-path" }, + { filePath: "ot-output/dita/topic2.dita" }, + ], + totalPages: 1, + }; + + mockClient.get.resolves({ data: assetsResponse }); + + const result = await heretto.getJobAssetDetails(mockClient, "file-uuid", "job-123"); + + expect(result).to.deep.equal([ + "ot-output/dita/topic1.dita", + "ot-output/dita/topic2.dita", + ]); + }); + }); + + describe("validateDitamapInAssets", function () { + it("should return true when ditamap is in ot-output/dita/", function () { + const assets = [ + "ot-output/dita/topic1.dita", + "ot-output/dita/my-guide.ditamap", + "ot-output/dita/topic2.dita", + ]; + + const result = heretto.validateDitamapInAssets(assets); + + expect(result).to.be.true; + }); + + it("should return false when no ditamap is present", function () { + const assets = [ + "ot-output/dita/topic1.dita", + "ot-output/dita/topic2.dita", + ]; + + const result = heretto.validateDitamapInAssets(assets); + + expect(result).to.be.false; + }); + + it("should return false when ditamap is in wrong directory", function () { + const assets = [ + "ot-output/other/my-guide.ditamap", + "ot-output/dita/topic1.dita", + ]; + + const result = heretto.validateDitamapInAssets(assets); + + expect(result).to.be.false; + }); + + it("should return true when any ditamap is in correct directory", function () { + const assets = [ + "ot-output/dita/different-guide.ditamap", + "ot-output/dita/topic1.dita", + ]; + + const result = heretto.validateDitamapInAssets(assets); + + expect(result).to.be.true; + }); + + it("should return false when assets array is empty", function () { + const result = heretto.validateDitamapInAssets([]); + + expect(result).to.be.false; + }); + }); + describe("pollJobStatus", function () { const mockLog = sinon.stub(); const mockConfig = { logLevel: "info" }; @@ -236,40 +388,91 @@ describe("Heretto Integration", function () { mockLog.reset(); }); - it("should return completed job when status.result is SUCCESS", async function () { + it("should return completed job when status.result is SUCCESS and ditamap is present", async function () { const completedJob = { id: "job-123", status: { status: "COMPLETED", result: "SUCCESS" }, }; - mockClient.get.resolves({ data: completedJob }); + const assetsResponse = { + content: [ + { filePath: "ot-output/dita/my-guide.ditamap" }, + { filePath: "ot-output/dita/topic1.dita" }, + ], + totalPages: 1, + }; + + mockClient.get + .onFirstCall().resolves({ data: completedJob }) + .onSecondCall().resolves({ data: assetsResponse }); const result = await heretto.pollJobStatus(mockClient, "file-uuid", "job-123", mockLog, mockConfig); expect(result).to.deep.equal(completedJob); }); - it("should return null when status.result is FAIL", async function () { + it("should return completed job when status.result is FAIL but ditamap is present", async function () { const failedJob = { id: "job-123", status: { status: "FAILED", result: "FAIL" }, }; - mockClient.get.resolves({ data: failedJob }); + const assetsResponse = { + content: [ + { filePath: "ot-output/dita/my-guide.ditamap" }, + { filePath: "ot-output/dita/topic1.dita" }, + ], + totalPages: 1, + }; + + mockClient.get + .onFirstCall().resolves({ data: failedJob }) + .onSecondCall().resolves({ data: assetsResponse }); + + const result = await heretto.pollJobStatus(mockClient, "file-uuid", "job-123", mockLog, mockConfig); + + expect(result).to.deep.equal(failedJob); + }); + + it("should return null when job completes but ditamap is missing", async function () { + const completedJob = { + id: "job-123", + status: { status: "COMPLETED", result: "SUCCESS" }, + }; + + const assetsResponse = { + content: [ + { filePath: "ot-output/dita/topic1.dita" }, + { filePath: "ot-output/dita/topic2.dita" }, + ], + totalPages: 1, + }; + + mockClient.get + .onFirstCall().resolves({ data: completedJob }) + .onSecondCall().resolves({ data: assetsResponse }); const result = await heretto.pollJobStatus(mockClient, "file-uuid", "job-123", mockLog, mockConfig); expect(result).to.be.null; }); - it("should poll until completion", async function () { + it("should poll until completion then validate assets", async function () { // Use fake timers to avoid waiting for real POLLING_INTERVAL_MS delays const clock = sinon.useFakeTimers(); + const assetsResponse = { + content: [ + { filePath: "ot-output/dita/my-guide.ditamap" }, + ], + totalPages: 1, + }; + mockClient.get - .onFirstCall().resolves({ data: { id: "job-123", status: { status: "PENDING", result: null } } }) - .onSecondCall().resolves({ data: { id: "job-123", status: { status: "PROCESSING", result: null } } }) - .onThirdCall().resolves({ data: { id: "job-123", status: { status: "COMPLETED", result: "SUCCESS" } } }); + .onCall(0).resolves({ data: { id: "job-123", status: { status: "PENDING", result: null } } }) + .onCall(1).resolves({ data: { id: "job-123", status: { status: "PROCESSING", result: null } } }) + .onCall(2).resolves({ data: { id: "job-123", status: { status: "COMPLETED", result: "SUCCESS" } } }) + .onCall(3).resolves({ data: assetsResponse }); const pollPromise = heretto.pollJobStatus(mockClient, "file-uuid", "job-123", mockLog, mockConfig); @@ -281,7 +484,7 @@ describe("Heretto Integration", function () { const result = await pollPromise; expect(result.status.result).to.equal("SUCCESS"); - expect(mockClient.get.callCount).to.equal(3); + expect(mockClient.get.callCount).to.equal(4); // 3 status polls + 1 assets call clock.restore(); }); @@ -313,6 +516,21 @@ describe("Heretto Integration", function () { expect(result).to.be.null; }); + + it("should return null when asset validation fails", async function () { + const completedJob = { + id: "job-123", + status: { status: "COMPLETED", result: "SUCCESS" }, + }; + + mockClient.get + .onFirstCall().resolves({ data: completedJob }) + .onSecondCall().rejects(new Error("Failed to fetch assets")); + + const result = await heretto.pollJobStatus(mockClient, "file-uuid", "job-123", mockLog, mockConfig); + + expect(result).to.be.null; + }); }); describe("loadHerettoContent", function () { diff --git a/resolver/src/utils.js b/resolver/src/utils.js index 4c32bb9..8517ad3 100644 --- a/resolver/src/utils.js +++ b/resolver/src/utils.js @@ -26,6 +26,28 @@ exports.cleanTemp = cleanTemp; exports.calculatePercentageDifference = calculatePercentageDifference; exports.fetchFile = fetchFile; exports.isRelativeUrl = isRelativeUrl; +exports.findHerettoIntegration = findHerettoIntegration; + +/** + * Finds which Heretto integration a file belongs to based on its path. + * @param {Object} config - Doc Detective config with _herettoPathMapping + * @param {string} filePath - Path to check + * @returns {string|null} Heretto integration name or null if not from Heretto + */ +function findHerettoIntegration(config, filePath) { + if (!config._herettoPathMapping) return null; + + const normalizedFilePath = path.resolve(filePath); + + for (const [outputPath, integrationName] of Object.entries(config._herettoPathMapping)) { + const normalizedOutputPath = path.resolve(outputPath); + if (normalizedFilePath.startsWith(normalizedOutputPath)) { + return integrationName; + } + } + + return null; +} function isRelativeUrl(url) { try { @@ -38,6 +60,33 @@ function isRelativeUrl(url) { } } +/** + * Generates a unique specId from a file path that is safe for storage/URLs. + * Uses relative path from cwd when possible to provide uniqueness while + * avoiding collisions from files with the same basename in different directories. + * @param {string} filePath - Absolute or relative file path + * @returns {string} A safe specId derived from the file path + */ +function generateSpecId(filePath) { + const absolutePath = path.resolve(filePath); + const cwd = process.cwd(); + + let relativePath; + if (absolutePath.startsWith(cwd)) { + relativePath = path.relative(cwd, absolutePath); + } else { + relativePath = absolutePath; + } + + const normalizedPath = relativePath + .split(path.sep) + .join("/") + .replace(/^\.\//, "") + .replace(/[^a-zA-Z0-9._\-\/]/g, "_"); + + return normalizedPath; +} + // Parse XML-style attributes to an object // Example: 'wait=500' becomes { wait: 500 } // Example: 'testId="myTestId" detectSteps=false' becomes { testId: "myTestId", detectSteps: false } @@ -230,6 +279,11 @@ async function qualifyFiles({ config }) { } const ignoredDitaMaps = []; + + // Track Heretto output paths for sourceIntegration metadata + if (!config._herettoPathMapping) { + config._herettoPathMapping = {}; + } for (let source of sequence) { log(config, "debug", `source: ${source}`); @@ -256,6 +310,8 @@ async function qualifyFiles({ config }) { const outputPath = await loadHerettoContent(herettoConfig, log, config); if (outputPath) { herettoConfig.outputPath = outputPath; + // Store mapping from output path to Heretto integration name + config._herettoPathMapping[outputPath] = herettoName; log(config, "debug", `Adding Heretto output path: ${outputPath}`); // Insert the output path into the sequence for processing const currentIndex = sequence.indexOf(source); @@ -728,10 +784,47 @@ async function parseContent({ config, content, filePath, fileType }) { ) { step[action].origin = config.origin; } + // Attach sourceIntegration metadata for screenshot steps from Heretto + if (action === "screenshot" && config._herettoPathMapping) { + const herettoIntegration = findHerettoIntegration(config, filePath); + if (herettoIntegration) { + // Convert simple screenshot value to object with sourceIntegration + const screenshotPath = step[action]; + step[action] = { + path: screenshotPath, + sourceIntegration: { + type: "heretto", + integrationName: herettoIntegration, + filePath: screenshotPath, + contentPath: filePath, + }, + }; + } + } } else { // Substitute variables $n with match[n] // TODO: Make key substitution recursive step = replaceNumericVariables(action, statement); + + // Attach sourceIntegration metadata for screenshot steps from Heretto + if (step.screenshot && config._herettoPathMapping) { + const herettoIntegration = findHerettoIntegration(config, filePath); + if (herettoIntegration) { + // Ensure screenshot is an object + if (typeof step.screenshot === "string") { + step.screenshot = { path: step.screenshot }; + } else if (typeof step.screenshot === "boolean") { + step.screenshot = {}; + } + // Attach sourceIntegration + step.screenshot.sourceIntegration = { + type: "heretto", + integrationName: herettoIntegration, + filePath: step.screenshot.path || "", + contentPath: filePath, + }; + } + } } // Normalize step field formats @@ -912,7 +1005,9 @@ async function parseTests({ config, files }) { specs.push(content); } else { // Process non-object - let id = `${crypto.randomUUID()}`; + // Generate a specId that includes more of the file path to avoid collisions + // when different files share the same basename + let id = generateSpecId(file); let spec = { specId: id, contentPath: file, tests: [] }; const fileType = config.fileTypes.find((fileType) => fileType.extensions.includes(extension)