From 9299f61fc3123ed19cac248ce222cfef6c598e36 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Thu, 18 Dec 2025 11:49:17 -0800 Subject: [PATCH 01/11] feat: Implement Heretto CMS uploader for automatic screenshot synchronization - Added HerettoUploader class to handle file uploads to Heretto CMS. - Integrated uploader into the existing upload process for changed screenshots. - Enhanced screenshot saving logic to track changes and source integration metadata. - Created documentation for Heretto screenshot sync feature, detailing the upload process and architecture. - Updated resolver to build file mappings and handle Heretto-specific configurations. - Introduced utility functions to find Heretto integrations based on file paths. - Added error handling and logging for upload operations to improve reliability. --- .../schemas/src_schemas/config_v3.schema.json | 18 + .../src_schemas/screenshot_v3.schema.json | 5 + .../sourceIntegration_v3.schema.json | 45 +++ core/src/integrations/heretto.js | 260 ++++++++++++ core/src/integrations/index.js | 231 +++++++++++ core/src/tests.js | 19 + core/src/tests/saveScreenshot.js | 24 +- .../integrations/heretto-screenshot-sync.md | 149 +++++++ resolver/package-lock.json | 33 +- resolver/package.json | 1 + resolver/src/heretto.js | 373 ++++++++++++++++++ resolver/src/utils.js | 66 ++++ 12 files changed, 1221 insertions(+), 3 deletions(-) create mode 100644 common/src/schemas/src_schemas/sourceIntegration_v3.schema.json create mode 100644 core/src/integrations/heretto.js create mode 100644 core/src/integrations/index.js create mode 100644 docs/docs/references/integrations/heretto-screenshot-sync.md diff --git a/common/src/schemas/src_schemas/config_v3.schema.json b/common/src/schemas/src_schemas/config_v3.schema.json index 5b5be5c..769ff3c 100644 --- a/common/src/schemas/src_schemas/config_v3.schema.json +++ b/common/src/schemas/src_schemas/config_v3.schema.json @@ -258,6 +258,24 @@ "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." + } + } + } } }, "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..c801222 100644 --- a/common/src/schemas/src_schemas/screenshot_v3.schema.json +++ b/common/src/schemas/src_schemas/screenshot_v3.schema.json @@ -68,6 +68,11 @@ "$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.", + "readOnly": true, + "$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/src/integrations/heretto.js b/core/src/integrations/heretto.js new file mode 100644 index 0000000..3393590 --- /dev/null +++ b/core/src/integrations/heretto.js @@ -0,0 +1,260 @@ +/** + * 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.apiBaseUrl || !integrationConfig.apiToken) { + result.description = "Heretto integration missing apiBaseUrl or apiToken"; + return result; + } + + // Resolve the file ID + let fileId = sourceIntegration.fileId; + + if (!fileId) { + log(config, "debug", `No fileId found, searching for file by path: ${sourceIntegration.filePath}`); + + try { + fileId = await this.searchFileByName({ + apiBaseUrl: integrationConfig.apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + filename: path.basename(sourceIntegration.filePath), + log: (level, msg) => log(config, level, msg), + }); + + if (!fileId) { + result.description = `Could not find file in Heretto: ${sourceIntegration.filePath}`; + return result; + } + } catch (error) { + result.description = `Error searching for 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: integrationConfig.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; + } + + /** + * 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", + }); + + 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 { + 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(); + }); + } + + /** + * 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..fa55e7e 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; @@ -362,11 +364,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 +387,15 @@ async function saveScreenshot({ config, step, driver }) { } } + // Set output path for new screenshots + if (!result.outputs.screenshotPath) { + result.outputs.screenshotPath = filePath; + // Preserve sourceIntegration metadata + if (step.screenshot.sourceIntegration) { + result.outputs.sourceIntegration = step.screenshot.sourceIntegration; + } + } + // PASS return result; } 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/package-lock.json b/resolver/package-lock.json index 1b40437..abff311 100644 --- a/resolver/package-lock.json +++ b/resolver/package-lock.json @@ -15,6 +15,7 @@ "axios": "^1.13.2", "doc-detective-common": "3.6.0-dev.1", "dotenv": "^17.2.3", + "fast-xml-parser": "^5.3.3", "json-schema-faker": "^0.5.9", "posthog-node": "^5.17.2" }, @@ -232,7 +233,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", @@ -953,6 +953,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", @@ -1383,7 +1401,6 @@ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 10.16.0" } @@ -2339,6 +2356,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", diff --git a/resolver/package.json b/resolver/package.json index b6e7b26..f74d3f3 100644 --- a/resolver/package.json +++ b/resolver/package.json @@ -30,6 +30,7 @@ "axios": "^1.13.2", "doc-detective-common": "3.6.0-dev.1", "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/heretto.js b/resolver/src/heretto.js index c535e33..968b8bb 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,25 @@ 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}`, + }, + }); +} + /** * Fetches all available publishing scenarios from Heretto. * @param {Object} client - Configured axios instance @@ -404,6 +426,17 @@ async function loadHerettoContent(herettoConfig, log, config) { config ); + // Build file mapping from extracted content + if (outputPath && herettoConfig.uploadOnChange) { + const fileMapping = await buildFileMapping( + outputPath, + herettoConfig, + log, + config + ); + herettoConfig.fileMapping = fileMapping; + } + return outputPath; } catch (error) { log( @@ -415,14 +448,354 @@ 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".replace("/ezdnxtgen/api/v2", ""), + 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, pollJobStatus, downloadAndExtractOutput, loadHerettoContent, + buildFileMapping, + searchFileByName, + uploadFile, + resolveFileId, // Export constants for testing POLLING_INTERVAL_MS, POLLING_TIMEOUT_MS, diff --git a/resolver/src/utils.js b/resolver/src/utils.js index 4c32bb9..57acea8 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 { @@ -230,6 +252,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 +283,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 +757,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 From 5bb9381716917f382dd8bb2287fb22e4bafb2a1d Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Fri, 19 Dec 2025 18:23:03 -0800 Subject: [PATCH 02/11] Fix build --- common/dist/schemas/config_v3.schema.json | 218 +++ common/dist/schemas/report_v3.schema.json | 200 +++ .../dist/schemas/resolvedTests_v3.schema.json | 418 +++++ common/dist/schemas/screenshot_v3.schema.json | 100 ++ .../schemas/sourceIntegration_v3.schema.json | 50 + common/dist/schemas/spec_v3.schema.json | 200 +++ common/dist/schemas/step_v3.schema.json | 100 ++ common/dist/schemas/test_v3.schema.json | 200 +++ common/src/schemas/dereferenceSchemas.js | 1 + .../output_schemas/config_v3.schema.json | 218 +++ .../output_schemas/report_v3.schema.json | 200 +++ .../resolvedTests_v3.schema.json | 418 +++++ .../output_schemas/screenshot_v3.schema.json | 100 ++ .../sourceIntegration_v3.schema.json | 50 + .../output_schemas/spec_v3.schema.json | 200 +++ .../output_schemas/step_v3.schema.json | 100 ++ .../output_schemas/test_v3.schema.json | 200 +++ common/src/schemas/schemas.json | 1486 +++++++++++++++++ .../src_schemas/screenshot_v3.schema.json | 1 - 19 files changed, 4459 insertions(+), 1 deletion(-) create mode 100644 common/dist/schemas/sourceIntegration_v3.schema.json create mode 100644 common/src/schemas/output_schemas/sourceIntegration_v3.schema.json diff --git a/common/dist/schemas/config_v3.schema.json b/common/dist/schemas/config_v3.schema.json index f9e967a..ac890fd 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,24 @@ "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." + } + } + } } }, "title": "Heretto CMS integration" @@ -13538,6 +13656,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 +13902,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..ddd8a29 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,24 @@ "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." + } + } + } } }, "title": "Heretto CMS integration" @@ -13551,6 +13669,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 +13915,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 +22461,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 +22707,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 +30038,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 +30284,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/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..ac890fd 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,24 @@ "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." + } + } + } } }, "title": "Heretto CMS integration" @@ -13538,6 +13656,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 +13902,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..ddd8a29 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,24 @@ "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." + } + } + } } }, "title": "Heretto CMS integration" @@ -13551,6 +13669,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 +13915,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 +22461,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 +22707,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 +30038,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 +30284,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..ef22892 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,24 @@ "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." + } + } + } } }, "title": "Heretto CMS integration" @@ -13936,6 +14054,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 +14300,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 +26570,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 +26816,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 +29613,24 @@ "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." + } + } + } } }, "title": "Heretto CMS integration" @@ -34306,6 +34642,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 +34888,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 +43434,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 +43680,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 +51011,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 +51257,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 +60298,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 +60544,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 +67875,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 +68121,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 +72001,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 +72247,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 +72493,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 +78544,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 +78790,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 +86121,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 +86367,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 +93978,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 +94224,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 +101910,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 +102156,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 +109487,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 +109733,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/screenshot_v3.schema.json b/common/src/schemas/src_schemas/screenshot_v3.schema.json index c801222..a231415 100644 --- a/common/src/schemas/src_schemas/screenshot_v3.schema.json +++ b/common/src/schemas/src_schemas/screenshot_v3.schema.json @@ -71,7 +71,6 @@ }, "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.", - "readOnly": true, "$ref": "sourceIntegration_v3.schema.json#" } }, From c6323659b935fff87af26b9eed3b1b8c6f03fbf4 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Fri, 19 Dec 2025 18:23:20 -0800 Subject: [PATCH 03/11] Add -based detection --- resolver/src/config.js | 53 ++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 31 deletions(-) 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); From 02ad4c338b10435bf42cea46f9979f0e8de60f7f Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Fri, 19 Dec 2025 18:24:21 -0800 Subject: [PATCH 04/11] Set spec ID based on file name --- resolver/src/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resolver/src/utils.js b/resolver/src/utils.js index 57acea8..099feb0 100644 --- a/resolver/src/utils.js +++ b/resolver/src/utils.js @@ -978,7 +978,7 @@ async function parseTests({ config, files }) { specs.push(content); } else { // Process non-object - let id = `${crypto.randomUUID()}`; + let id = path.basename(file); let spec = { specId: id, contentPath: file, tests: [] }; const fileType = config.fileTypes.find((fileType) => fileType.extensions.includes(extension) From bd8466b4aaa2bbf3fd85a50a3a113ba5d047ba5d Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Mon, 22 Dec 2025 12:41:30 -0800 Subject: [PATCH 05/11] feat: Add Heretto integration tests and enhance job asset handling - Create integration tests for Heretto API interactions. - Implement job asset retrieval with pagination support. - Validate presence of .ditamap files in job assets. - Update package.json scripts for integration testing. - Modify Heretto functions to improve error handling and logging. --- .../.github/workflows/integration-tests.yml | 119 +++++++++ resolver/package.json | 6 +- resolver/src/heretto.integration.test.js | 232 +++++++++++++++++ resolver/src/heretto.js | 116 ++++++++- resolver/src/heretto.test.js | 242 +++++++++++++++++- 5 files changed, 691 insertions(+), 24 deletions(-) create mode 100644 resolver/.github/workflows/integration-tests.yml create mode 100644 resolver/src/heretto.integration.test.js 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.json b/resolver/package.json index f74d3f3..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,7 +30,7 @@ "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", 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 968b8bb..b64513f 100644 --- a/resolver/src/heretto.js +++ b/resolver/src/heretto.js @@ -173,7 +173,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, @@ -213,8 +216,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 @@ -230,17 +289,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 @@ -375,7 +463,12 @@ async function loadHerettoContent(herettoConfig, log, config) { // 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, @@ -789,6 +882,9 @@ module.exports = { createRestApiClient, findScenario, triggerPublishingJob, + getJobStatus, + getJobAssetDetails, + validateDitamapInAssets, pollJobStatus, downloadAndExtractOutput, loadHerettoContent, 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 () { From 566ad87dfb074e773419f8dfe9594af950087f6b Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Mon, 22 Dec 2025 15:40:40 -0800 Subject: [PATCH 06/11] Implement screenshot handling and upload functionality - Update `saveScreenshot.js` to mark new screenshots as changed for upload. - Add a new test file `screenshot.test.js` to verify sourceIntegration preservation and changed status for screenshots. - Create a new test file `upload.test.js` to validate the upload module, including collecting changed files and integration configuration. - Modify `package-lock.json` to link to local `doc-detective-common` instead of a versioned package. - Enhance `heretto.js` to retrieve resource dependencies from the Heretto REST API, including detailed logging and XML parsing. --- common/dist/schemas/config_v3.schema.json | 31 + .../dist/schemas/resolvedTests_v3.schema.json | 31 + .../output_schemas/config_v3.schema.json | 31 + .../resolvedTests_v3.schema.json | 31 + common/src/schemas/schemas.json | 62 ++ .../schemas/src_schemas/config_v3.schema.json | 31 + core/dev/index.js | 2 + core/package-lock.json | 777 ++++-------------- core/package.json | 4 +- core/src/integrations/heretto.js | 469 ++++++++++- core/src/tests/saveScreenshot.js | 2 + core/test/screenshot.test.js | 273 ++++++ core/test/upload.test.js | 641 +++++++++++++++ resolver/package-lock.json | 76 +- resolver/src/heretto.js | 152 +++- 15 files changed, 1919 insertions(+), 694 deletions(-) create mode 100644 core/test/screenshot.test.js create mode 100644 core/test/upload.test.js diff --git a/common/dist/schemas/config_v3.schema.json b/common/dist/schemas/config_v3.schema.json index ac890fd..02711e4 100644 --- a/common/dist/schemas/config_v3.schema.json +++ b/common/dist/schemas/config_v3.schema.json @@ -8645,6 +8645,37 @@ } } } + }, + "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/dist/schemas/resolvedTests_v3.schema.json b/common/dist/schemas/resolvedTests_v3.schema.json index ddd8a29..8126dfc 100644 --- a/common/dist/schemas/resolvedTests_v3.schema.json +++ b/common/dist/schemas/resolvedTests_v3.schema.json @@ -8658,6 +8658,37 @@ } } } + }, + "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/output_schemas/config_v3.schema.json b/common/src/schemas/output_schemas/config_v3.schema.json index ac890fd..02711e4 100644 --- a/common/src/schemas/output_schemas/config_v3.schema.json +++ b/common/src/schemas/output_schemas/config_v3.schema.json @@ -8645,6 +8645,37 @@ } } } + }, + "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/output_schemas/resolvedTests_v3.schema.json b/common/src/schemas/output_schemas/resolvedTests_v3.schema.json index ddd8a29..8126dfc 100644 --- a/common/src/schemas/output_schemas/resolvedTests_v3.schema.json +++ b/common/src/schemas/output_schemas/resolvedTests_v3.schema.json @@ -8658,6 +8658,37 @@ } } } + }, + "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/schemas.json b/common/src/schemas/schemas.json index ef22892..50f923a 100644 --- a/common/src/schemas/schemas.json +++ b/common/src/schemas/schemas.json @@ -9043,6 +9043,37 @@ } } } + }, + "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" @@ -29631,6 +29662,37 @@ } } } + }, + "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/config_v3.schema.json b/common/src/schemas/src_schemas/config_v3.schema.json index 769ff3c..84d8ee5 100644 --- a/common/src/schemas/src_schemas/config_v3.schema.json +++ b/common/src/schemas/src_schemas/config_v3.schema.json @@ -276,6 +276,37 @@ } } } + }, + "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/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 index 3393590..bf00176 100644 --- a/core/src/integrations/heretto.js +++ b/core/src/integrations/heretto.js @@ -43,32 +43,124 @@ class HerettoUploader { return result; } - if (!integrationConfig.apiBaseUrl || !integrationConfig.apiToken) { - result.description = "Heretto integration missing apiBaseUrl or apiToken"; + if (!integrationConfig.organizationId || !integrationConfig.apiToken) { + result.description = "Heretto integration missing organizationId or apiToken"; return result; } - // Resolve the file ID + // 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, searching for file by path: ${sourceIntegration.filePath}`); try { fileId = await this.searchFileByName({ - apiBaseUrl: integrationConfig.apiBaseUrl, + apiBaseUrl, apiToken: integrationConfig.apiToken, username: integrationConfig.username || "", - filename: path.basename(sourceIntegration.filePath), + filename, log: (level, msg) => log(config, level, msg), }); + // If file not found, try to create it in the parent folder if (!fileId) { - result.description = `Could not find file in Heretto: ${sourceIntegration.filePath}`; - return result; + // Try to find parent folder from resource dependencies + if (!parentFolderId && integrationConfig.resourceDependencies) { + parentFolderId = this.findParentFolderFromDependencies({ + resourceDependencies: integrationConfig.resourceDependencies, + filePath: relativeFilePath, + 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", `File not found, searching for parent folder: ${folderName}`); + + parentFolderId = await this.searchFolderByName({ + apiBaseUrl, + apiToken: integrationConfig.apiToken, + username: integrationConfig.username || "", + folderName, + log: (level, msg) => log(config, level, msg), + }); + } + } + + if (parentFolderId) { + log(config, "debug", `Creating new document in folder: ${parentFolderId}`); + + 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) { + // File already exists in folder - get its ID + log(config, "debug", `File already exists in folder, 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) { + log(config, "info", `Found existing document in folder with ID: ${fileId}`); + } else { + 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 { + result.description = `Could not find file or parent folder in Heretto: ${sourceIntegration.filePath}`; + return result; + } } } catch (error) { - result.description = `Error searching for file: ${error.message}`; + result.description = `Error searching/creating file: ${error.message}`; return result; } } @@ -85,7 +177,7 @@ class HerettoUploader { // Upload to Heretto try { await this.uploadFile({ - apiBaseUrl: integrationConfig.apiBaseUrl, + apiBaseUrl, apiToken: integrationConfig.apiToken, username: integrationConfig.username || "", documentId: fileId, @@ -103,6 +195,356 @@ class HerettoUploader { 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. + * @param {Object} options - Resolution options + * @returns {string|null} Parent folder UUID or null if not found + */ + findParentFolderFromDependencies({ resourceDependencies, filePath, log }) { + if (!resourceDependencies) return null; + + // Normalize path and get parent directory + const normalizedPath = filePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\.\.\//, ""); + const parentDir = path.dirname(normalizedPath); + const targetFolderName = path.basename(parentDir); + + 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}`); + return info.parentFolderId; + } + } + + // 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}`); + return info.uuid; + } + } + + // Fallback: use the ditamap's parent folder ID if available + // This is useful when the target folder doesn't exist yet - we can create it + // as a sibling to the ditamap + if (resourceDependencies._ditamapParentFolderId) { + log("debug", `Using ditamap parent folder as fallback: ${resourceDependencies._ditamapParentFolderId}`); + return resourceDependencies._ditamapParentFolderId; + } + + log("debug", `Could not find parent folder '${targetFolderName}' in dependencies`); + return null; + } + + /** + * 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 @@ -113,7 +555,7 @@ class HerettoUploader { const searchBody = JSON.stringify({ queryString: filename, - searchResultType: "FILES", + searchResultType: "FILES_ONLY", }); return new Promise((resolve, reject) => { @@ -142,6 +584,13 @@ class HerettoUploader { 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) { diff --git a/core/src/tests/saveScreenshot.js b/core/src/tests/saveScreenshot.js index fa55e7e..44f5258 100644 --- a/core/src/tests/saveScreenshot.js +++ b/core/src/tests/saveScreenshot.js @@ -390,6 +390,8 @@ 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; 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..bf2500f --- /dev/null +++ b/core/test/upload.test.js @@ -0,0 +1,641 @@ +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 null when resourceDependencies is null", function () { + const result = HerettoUploader.findParentFolderFromDependencies({ + resourceDependencies: null, + filePath: "_media/test.png", + log: mockLog, + }); + assert.equal(result, 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, "media-folder-uuid"); + }); + + 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, "direct-folder-uuid"); + }); + + it("uses ditamap parent folder as fallback", 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, + }); + assert.equal(result, "ditamap-parent-folder-uuid"); + }); + + 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, "correct-media-folder"); + }); + + it("returns null 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, null); + }); + + 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, "media-folder"); + }); + }); + + 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/resolver/package-lock.json b/resolver/package-lock.json index abff311..173a859 100644 --- a/resolver/package-lock.json +++ b/resolver/package-lock.json @@ -13,7 +13,7 @@ "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", @@ -30,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", @@ -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", @@ -1401,6 +1371,7 @@ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -2617,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/src/heretto.js b/resolver/src/heretto.js index b64513f..a5d9052 100644 --- a/resolver/src/heretto.js +++ b/resolver/src/heretto.js @@ -70,6 +70,7 @@ function createRestApiClient(herettoConfig) { timeout: API_REQUEST_TIMEOUT_MS, headers: { Authorization: `Basic ${authHeader}`, + Accept: "application/xml, text/xml, */*", }, }); } @@ -443,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. @@ -460,6 +595,7 @@ 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; @@ -478,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, @@ -519,7 +668,7 @@ async function loadHerettoContent(herettoConfig, log, config) { config ); - // Build file mapping from extracted content + // Build file mapping from extracted content (legacy approach, still useful as fallback) if (outputPath && herettoConfig.uploadOnChange) { const fileMapping = await buildFileMapping( outputPath, @@ -892,6 +1041,7 @@ module.exports = { searchFileByName, uploadFile, resolveFileId, + getResourceDependencies, // Export constants for testing POLLING_INTERVAL_MS, POLLING_TIMEOUT_MS, From 198859ffc8dccb2638a3d1f2003eeec5e7436549 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Mon, 29 Dec 2025 19:25:38 -0800 Subject: [PATCH 07/11] feat: Enhance HerettoUploader with improved folder resolution and add integration tests - Refactor folder resolution logic to ensure correct target folder is identified before file upload. - Implement integration tests for Heretto upload functionality, covering document retrieval and upload processes. - Update existing tests to reflect changes in folder resolution behavior. --- core/src/integrations/heretto.js | 341 ++++++++++++++++++++---- core/test/heretto-upload.test.js | 438 +++++++++++++++++++++++++++++++ core/test/upload.test.js | 29 +- 3 files changed, 744 insertions(+), 64 deletions(-) create mode 100644 core/test/heretto-upload.test.js diff --git a/core/src/integrations/heretto.js b/core/src/integrations/heretto.js index bf00176..382d7d2 100644 --- a/core/src/integrations/heretto.js +++ b/core/src/integrations/heretto.js @@ -76,47 +76,69 @@ class HerettoUploader { } if (!fileId) { - log(config, "debug", `No fileId found, searching for file by path: ${sourceIntegration.filePath}`); + log(config, "debug", `No fileId found, resolving correct folder for: ${sourceIntegration.filePath}`); try { - fileId = await this.searchFileByName({ - apiBaseUrl, - apiToken: integrationConfig.apiToken, - username: integrationConfig.username || "", - filename, - log: (level, msg) => log(config, level, msg), - }); - - // If file not found, try to create it in the parent folder - if (!fileId) { - // Try to find parent folder from resource dependencies - if (!parentFolderId && integrationConfig.resourceDependencies) { - parentFolderId = this.findParentFolderFromDependencies({ - resourceDependencies: integrationConfig.resourceDependencies, - filePath: relativeFilePath, + // 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", `File not found, searching for parent folder: ${folderName}`); - - parentFolderId = await this.searchFolderByName({ - apiBaseUrl, - apiToken: integrationConfig.apiToken, - username: integrationConfig.username || "", - folderName, - 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), + }); } + } - if (parentFolderId) { - log(config, "debug", `Creating new document in folder: ${parentFolderId}`); + // 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({ @@ -133,8 +155,8 @@ class HerettoUploader { fileId = createResult.documentId; log(config, "info", `Created new document in Heretto with ID: ${fileId}`); } else if (createResult.existsInFolder) { - // File already exists in folder - get its ID - log(config, "debug", `File already exists in folder, searching for its ID`); + // 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, @@ -144,9 +166,7 @@ class HerettoUploader { log: (level, msg) => log(config, level, msg), }); - if (fileId) { - log(config, "info", `Found existing document in folder with ID: ${fileId}`); - } else { + if (!fileId) { result.description = `File exists in folder but could not get its ID: ${filename}`; return result; } @@ -154,7 +174,19 @@ class HerettoUploader { result.description = `Failed to create document in Heretto: ${filename}`; return result; } - } else { + } + } 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; } @@ -256,17 +288,27 @@ class HerettoUploader { /** * 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 {string|null} Parent folder UUID or null if not found + * @returns {Object} { folderId, targetFolderName, ditamapParentFolderId } */ findParentFolderFromDependencies({ resourceDependencies, filePath, log }) { - if (!resourceDependencies) return null; + 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 @@ -279,7 +321,8 @@ class HerettoUploader { // 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}`); - return info.parentFolderId; + result.folderId = info.parentFolderId; + return result; } } @@ -290,20 +333,80 @@ class HerettoUploader { // 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}`); - return info.uuid; + result.folderId = info.uuid; + return result; } } - // Fallback: use the ditamap's parent folder ID if available - // This is useful when the target folder doesn't exist yet - we can create it - // as a sibling to the ditamap - if (resourceDependencies._ditamapParentFolderId) { - log("debug", `Using ditamap parent folder as fallback: ${resourceDependencies._ditamapParentFolderId}`); - return resourceDependencies._ditamapParentFolderId; - } - - log("debug", `Could not find parent folder '${targetFolderName}' in dependencies`); - return null; + 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(); + }); } /** @@ -677,6 +780,136 @@ class HerettoUploader { }); } + /** + * 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 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/upload.test.js b/core/test/upload.test.js index bf2500f..23401f3 100644 --- a/core/test/upload.test.js +++ b/core/test/upload.test.js @@ -430,13 +430,13 @@ describe("Upload module", function () { describe("findParentFolderFromDependencies", function () { const mockLog = () => {}; - it("returns null when resourceDependencies is null", function () { + it("returns empty result when resourceDependencies is null", function () { const result = HerettoUploader.findParentFolderFromDependencies({ resourceDependencies: null, filePath: "_media/test.png", log: mockLog, }); - assert.equal(result, null); + assert.deepEqual(result, { folderId: null, targetFolderName: null, ditamapParentFolderId: null }); }); it("finds parent folder from sibling file", function () { @@ -448,7 +448,8 @@ describe("Upload module", function () { filePath: "_media/new-image.png", log: mockLog, }); - assert.equal(result, "media-folder-uuid"); + assert.equal(result.folderId, "media-folder-uuid"); + assert.equal(result.targetFolderName, "_media"); }); it("finds folder by direct path match", function () { @@ -460,10 +461,11 @@ describe("Upload module", function () { filePath: "_media/new-file.png", log: mockLog, }); - assert.equal(result, "direct-folder-uuid"); + assert.equal(result.folderId, "direct-folder-uuid"); + assert.equal(result.targetFolderName, "_media"); }); - it("uses ditamap parent folder as fallback", function () { + 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" }, @@ -473,7 +475,10 @@ describe("Upload module", function () { filePath: "_nonexistent_folder/new-file.png", log: mockLog, }); - assert.equal(result, "ditamap-parent-folder-uuid"); + // 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 () { @@ -486,10 +491,11 @@ describe("Upload module", function () { filePath: "_media/new-file.png", log: mockLog, }); - assert.equal(result, "correct-media-folder"); + assert.equal(result.folderId, "correct-media-folder"); + assert.equal(result.targetFolderName, "_media"); }); - it("returns null when no folder found and no ditamap fallback", function () { + 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" }, }; @@ -498,7 +504,9 @@ describe("Upload module", function () { filePath: "_unknown_folder/file.png", log: mockLog, }); - assert.equal(result, null); + assert.equal(result.folderId, null); + assert.equal(result.ditamapParentFolderId, null); + assert.equal(result.targetFolderName, "_unknown_folder"); }); it("normalizes relative path prefixes", function () { @@ -510,7 +518,8 @@ describe("Upload module", function () { filePath: "../_media/new-file.png", log: mockLog, }); - assert.equal(result, "media-folder"); + assert.equal(result.folderId, "media-folder"); + assert.equal(result.targetFolderName, "_media"); }); }); From 3d99825cd99cf6b6f50b57e03ddab1f62a9c7b39 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Tue, 30 Dec 2025 11:17:30 -0800 Subject: [PATCH 08/11] feat: Add generateSpecId function to create unique spec IDs from file paths - Implement generateSpecId to derive safe spec IDs from absolute or relative file paths. - Update parseTests to use generateSpecId for generating spec IDs, reducing collisions from files with the same basename. --- resolver/src/utils.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/resolver/src/utils.js b/resolver/src/utils.js index 099feb0..8517ad3 100644 --- a/resolver/src/utils.js +++ b/resolver/src/utils.js @@ -60,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 } @@ -978,7 +1005,9 @@ async function parseTests({ config, files }) { specs.push(content); } else { // Process non-object - let id = path.basename(file); + // 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) From e35d5e88ebe3763e57f3a4b5e47819f9c11c894e Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Tue, 30 Dec 2025 11:17:35 -0800 Subject: [PATCH 09/11] fix: Correct API endpoint in searchFileByName function --- resolver/src/heretto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resolver/src/heretto.js b/resolver/src/heretto.js index a5d9052..cedf1a2 100644 --- a/resolver/src/heretto.js +++ b/resolver/src/heretto.js @@ -861,7 +861,7 @@ async function searchFileByName( } const response = await client.post( - "/ezdnxtgen/api/search".replace("/ezdnxtgen/api/v2", ""), + "/ezdnxtgen/api/search", searchBody, { baseURL: `https://${herettoConfig.organizationId}.heretto.com`, From 2a80c3054c0efcb2cb2b4e8362156b623af05258 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Tue, 30 Dec 2025 11:22:51 -0800 Subject: [PATCH 10/11] feat: Update path resolution to support heretto: URIs - Modify resolve function to return heretto: URIs unchanged. - Update comments to reflect new URI handling in path resolution. --- common/src/resolvePaths.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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; } From ee8bbad1c2af3783e7529a799c8c80205efa6496 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Tue, 30 Dec 2025 13:40:18 -0800 Subject: [PATCH 11/11] feat: Enhance saveScreenshot function to include additional output details - Add screenshotPath to outputs when overwriting existing files - Set changed flag to true when a file is overwritten - Preserve sourceIntegration metadata in outputs if provided --- core/src/tests/saveScreenshot.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/tests/saveScreenshot.js b/core/src/tests/saveScreenshot.js index 44f5258..f8bc3d6 100644 --- a/core/src/tests/saveScreenshot.js +++ b/core/src/tests/saveScreenshot.js @@ -293,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;