From 43c04d0461b8c870bfe9155e85792789f24951ff Mon Sep 17 00:00:00 2001 From: Giulio Zanchetta Date: Sat, 1 Feb 2025 14:20:55 +0100 Subject: [PATCH 1/4] Better format --- core/dist/index.js | 358 +- core/package.json | 68 +- core/scripts/install-dependencies.js | 148 +- core/src/index.ts | 419 +-- core/src/openAI.ts | 148 +- core/src/types.ts | 73 +- core/src/utils.ts | 466 +-- core/tsconfig.json | 30 +- extractor/src/converter.js | 32 +- extractor/src/index.js | 6 +- extractor/src/main-extractor.js | 96 +- extractor/src/services/extract.js | 74 +- extractor/src/services/formatter.js | 58 +- extractor/src/services/templates.js | 30 +- extractor/src/utils/autogenerateSchema.js | 76 +- extractor/src/utils/convertToText.js | 41 +- extractor/src/utils/convertToZodSchema.js | 102 +- extractor/src/utils/fileValidator.js | 63 +- extractor/src/utils/generateMarkdown.js | 20 +- extractor/src/utils/pdfValidator.js | 30 +- extractor/src/utils/schemaValidator.js | 125 +- package-lock.json | 3980 ++++++++++----------- package.json | 88 +- 23 files changed, 3310 insertions(+), 3221 deletions(-) diff --git a/core/dist/index.js b/core/dist/index.js index 6b385f7..23664d2 100644 --- a/core/dist/index.js +++ b/core/dist/index.js @@ -1,7 +1,6 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; +var __importDefault = + (this && this.__importDefault) || + ((mod) => (mod && mod.__esModule ? mod : { default: mod })); Object.defineProperty(exports, "__esModule", { value: true }); exports.documind = void 0; const utils_1 = require("./utils"); @@ -12,172 +11,189 @@ const fs_extra_1 = __importDefault(require("fs-extra")); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const p_limit_1 = __importDefault(require("p-limit")); -const documind = async ({ cleanup = true, concurrency = 10, filePath, llmParams = {}, maintainFormat = false, model, //= ModelOptions.gpt_4o_mini, -openaiAPIKey = "", outputDir, pagesToConvertAsImages = -1, tempDir = os_1.default.tmpdir(), }) => { - const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; - const defaultModel = model ?? - (baseUrl !== "https://api.openai.com/v1" - ? types_1.ModelOptions.llava // Default for custom base URL - : types_1.ModelOptions.gpt_4o_mini); // Default for OpenAI - let inputTokenCount = 0; - let outputTokenCount = 0; - let priorPage = ""; - const aggregatedMarkdown = []; - const startTime = new Date(); - llmParams = (0, utils_2.validateLLMParams)(llmParams); - // Validators - if (!openaiAPIKey || !openaiAPIKey.length) { - throw new Error("Missing OpenAI API key"); - } - if (!filePath || !filePath.length) { - throw new Error("Missing file path"); - } - // Ensure temp directory exists + create temp folder - const rand = Math.floor(1000 + Math.random() * 9000).toString(); - const tempDirectory = path_1.default.join(tempDir || os_1.default.tmpdir(), `documind-file-${rand}`); - await fs_extra_1.default.ensureDir(tempDirectory); - // Download the PDF. Get file name. - const { extension, localPath } = await (0, utils_1.downloadFile)({ - filePath, - tempDir: tempDirectory, - }); - if (!localPath) - throw "Failed to save file to local drive"; - // Sort the `pagesToConvertAsImages` array to make sure we use the right index - // for `formattedPages` as `pdf2pic` always returns images in order - if (Array.isArray(pagesToConvertAsImages)) { - pagesToConvertAsImages.sort((a, b) => a - b); - } - // Convert file to PDF if necessary - if (extension !== ".png") { - let pdfPath; - if (extension === ".pdf") { - pdfPath = localPath; - } - else { - pdfPath = await (0, utils_1.convertFileToPdf)({ - extension, - localPath, - tempDir: tempDirectory, - }); - } - // Convert the file to a series of images - await (0, utils_1.convertPdfToImages)({ - localPath: pdfPath, - pagesToConvertAsImages, - tempDir: tempDirectory, - }); - } - const endOfPath = localPath.split("/")[localPath.split("/").length - 1]; - const rawFileName = endOfPath.split(".")[0]; - const fileName = rawFileName - .replace(/[^\w\s]/g, "") - .replace(/\s+/g, "_") - .toLowerCase() - .substring(0, 255); // Truncate file name to 255 characters to prevent ENAMETOOLONG errors - // Get list of converted images - const files = await fs_extra_1.default.readdir(tempDirectory); - const images = files.filter((file) => file.endsWith(".png")); - if (maintainFormat) { - // Use synchronous processing - for (const image of images) { - const imagePath = path_1.default.join(tempDirectory, image); - try { - const { content, inputTokens, outputTokens } = await (0, openAI_1.getCompletion)({ - apiKey: openaiAPIKey, - imagePath, - llmParams, - maintainFormat, - model: defaultModel, - priorPage, - }); - const formattedMarkdown = (0, utils_1.formatMarkdown)(content); - inputTokenCount += inputTokens; - outputTokenCount += outputTokens; - // Update prior page to result from last processing step - priorPage = formattedMarkdown; - // Add all markdown results to array - aggregatedMarkdown.push(formattedMarkdown); - } - catch (error) { - console.error(`Failed to process image ${image}:`, error); - throw error; - } - } - } - else { - // Process in parallel with a limit on concurrent pages - const processPage = async (image) => { - const imagePath = path_1.default.join(tempDirectory, image); - try { - const { content, inputTokens, outputTokens } = await (0, openAI_1.getCompletion)({ - apiKey: openaiAPIKey, - imagePath, - llmParams, - maintainFormat, - model: defaultModel, - priorPage, - }); - const formattedMarkdown = (0, utils_1.formatMarkdown)(content); - inputTokenCount += inputTokens; - outputTokenCount += outputTokens; - // Update prior page to result from last processing step - priorPage = formattedMarkdown; - // Add all markdown results to array - return formattedMarkdown; - } - catch (error) { - console.error(`Failed to process image ${image}:`, error); - throw error; - } - }; - // Function to process pages with concurrency limit - const processPagesInBatches = async (images, limit) => { - const results = []; - const promises = images.map((image, index) => limit(() => processPage(image).then((result) => { - results[index] = result; - }))); - await Promise.all(promises); - return results; - }; - const limit = (0, p_limit_1.default)(concurrency); - const results = await processPagesInBatches(images, limit); - const filteredResults = results.filter(utils_1.isString); - aggregatedMarkdown.push(...filteredResults); - } - // Write the aggregated markdown to a file - if (outputDir) { - const resultFilePath = path_1.default.join(outputDir, `${fileName}.md`); - await fs_extra_1.default.writeFile(resultFilePath, aggregatedMarkdown.join("\n\n")); - } - // Cleanup the downloaded PDF file - if (cleanup) - await fs_extra_1.default.remove(tempDirectory); - // Format JSON response - const endTime = new Date(); - const completionTime = endTime.getTime() - startTime.getTime(); - const formattedPages = aggregatedMarkdown.map((el, i) => { - let pageNumber; - // If we convert all pages, just use the array index - if (pagesToConvertAsImages === -1) { - pageNumber = i + 1; - } - // Else if we convert specific pages, use the page number from the parameter - else if (Array.isArray(pagesToConvertAsImages)) { - pageNumber = pagesToConvertAsImages[i]; - } - // Else, the parameter is a number and use it for the page number - else { - pageNumber = pagesToConvertAsImages; - } - return { content: el, page: pageNumber, contentLength: el.length }; - }); - return { - completionTime, - fileName, - inputTokens: inputTokenCount, - outputTokens: outputTokenCount, - pages: formattedPages, - }; +const documind = async ({ + cleanup = true, + concurrency = 10, + filePath, + llmParams = {}, + maintainFormat = false, + model, //= ModelOptions.gpt_4o_mini, + openaiAPIKey = "", + outputDir, + pagesToConvertAsImages = -1, + tempDir = os_1.default.tmpdir(), +}) => { + const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; + const defaultModel = + model ?? + (baseUrl !== "https://api.openai.com/v1" + ? types_1.ModelOptions.llava // Default for custom base URL + : types_1.ModelOptions.gpt_4o_mini); // Default for OpenAI + let inputTokenCount = 0; + let outputTokenCount = 0; + let priorPage = ""; + const aggregatedMarkdown = []; + const startTime = new Date(); + llmParams = (0, utils_2.validateLLMParams)(llmParams); + // Validators + if (!openaiAPIKey || !openaiAPIKey.length) { + throw new Error("Missing OpenAI API key"); + } + if (!filePath || !filePath.length) { + throw new Error("Missing file path"); + } + // Ensure temp directory exists + create temp folder + const rand = Math.floor(1000 + Math.random() * 9000).toString(); + const tempDirectory = path_1.default.join( + tempDir || os_1.default.tmpdir(), + `documind-file-${rand}`, + ); + await fs_extra_1.default.ensureDir(tempDirectory); + // Download the PDF. Get file name. + const { extension, localPath } = await (0, utils_1.downloadFile)({ + filePath, + tempDir: tempDirectory, + }); + if (!localPath) throw "Failed to save file to local drive"; + // Sort the `pagesToConvertAsImages` array to make sure we use the right index + // for `formattedPages` as `pdf2pic` always returns images in order + if (Array.isArray(pagesToConvertAsImages)) { + pagesToConvertAsImages.sort((a, b) => a - b); + } + // Convert file to PDF if necessary + if (extension !== ".png") { + let pdfPath; + if (extension === ".pdf") { + pdfPath = localPath; + } else { + pdfPath = await (0, utils_1.convertFileToPdf)({ + extension, + localPath, + tempDir: tempDirectory, + }); + } + // Convert the file to a series of images + await (0, utils_1.convertPdfToImages)({ + localPath: pdfPath, + pagesToConvertAsImages, + tempDir: tempDirectory, + }); + } + const endOfPath = localPath.split("/")[localPath.split("/").length - 1]; + const rawFileName = endOfPath.split(".")[0]; + const fileName = rawFileName + .replace(/[^\w\s]/g, "") + .replace(/\s+/g, "_") + .toLowerCase() + .substring(0, 255); // Truncate file name to 255 characters to prevent ENAMETOOLONG errors + // Get list of converted images + const files = await fs_extra_1.default.readdir(tempDirectory); + const images = files.filter((file) => file.endsWith(".png")); + if (maintainFormat) { + // Use synchronous processing + for (const image of images) { + const imagePath = path_1.default.join(tempDirectory, image); + try { + const { content, inputTokens, outputTokens } = await (0, + openAI_1.getCompletion)({ + apiKey: openaiAPIKey, + imagePath, + llmParams, + maintainFormat, + model: defaultModel, + priorPage, + }); + const formattedMarkdown = (0, utils_1.formatMarkdown)(content); + inputTokenCount += inputTokens; + outputTokenCount += outputTokens; + // Update prior page to result from last processing step + priorPage = formattedMarkdown; + // Add all markdown results to array + aggregatedMarkdown.push(formattedMarkdown); + } catch (error) { + console.error(`Failed to process image ${image}:`, error); + throw error; + } + } + } else { + // Process in parallel with a limit on concurrent pages + const processPage = async (image) => { + const imagePath = path_1.default.join(tempDirectory, image); + try { + const { content, inputTokens, outputTokens } = await (0, + openAI_1.getCompletion)({ + apiKey: openaiAPIKey, + imagePath, + llmParams, + maintainFormat, + model: defaultModel, + priorPage, + }); + const formattedMarkdown = (0, utils_1.formatMarkdown)(content); + inputTokenCount += inputTokens; + outputTokenCount += outputTokens; + // Update prior page to result from last processing step + priorPage = formattedMarkdown; + // Add all markdown results to array + return formattedMarkdown; + } catch (error) { + console.error(`Failed to process image ${image}:`, error); + throw error; + } + }; + // Function to process pages with concurrency limit + const processPagesInBatches = async (images, limit) => { + const results = []; + const promises = images.map((image, index) => + limit(() => + processPage(image).then((result) => { + results[index] = result; + }), + ), + ); + await Promise.all(promises); + return results; + }; + const limit = (0, p_limit_1.default)(concurrency); + const results = await processPagesInBatches(images, limit); + const filteredResults = results.filter(utils_1.isString); + aggregatedMarkdown.push(...filteredResults); + } + // Write the aggregated markdown to a file + if (outputDir) { + const resultFilePath = path_1.default.join(outputDir, `${fileName}.md`); + await fs_extra_1.default.writeFile( + resultFilePath, + aggregatedMarkdown.join("\n\n"), + ); + } + // Cleanup the downloaded PDF file + if (cleanup) await fs_extra_1.default.remove(tempDirectory); + // Format JSON response + const endTime = new Date(); + const completionTime = endTime.getTime() - startTime.getTime(); + const formattedPages = aggregatedMarkdown.map((el, i) => { + let pageNumber; + // If we convert all pages, just use the array index + if (pagesToConvertAsImages === -1) { + pageNumber = i + 1; + } + // Else if we convert specific pages, use the page number from the parameter + else if (Array.isArray(pagesToConvertAsImages)) { + pageNumber = pagesToConvertAsImages[i]; + } + // Else, the parameter is a number and use it for the page number + else { + pageNumber = pagesToConvertAsImages; + } + return { content: el, page: pageNumber, contentLength: el.length }; + }); + return { + completionTime, + fileName, + inputTokens: inputTokenCount, + outputTokens: outputTokenCount, + pages: formattedPages, + }; }; exports.documind = documind; diff --git a/core/package.json b/core/package.json index 5efc90a..cfa0685 100644 --- a/core/package.json +++ b/core/package.json @@ -1,36 +1,36 @@ { - "name": "core", - "version": "1.0.0", - "description": "Core package for document conversion", - "main": "dist/index.js", - "type": "commonjs", - "files": [ - "dist" - ], - "scripts": { - "build": "tsc" - }, - "dependencies": { - "axios": "^1.7.2", - "child_process": "^1.0.2", - "dotenv": "^16.4.5", - "fs-extra": "^11.2.0", - "libreoffice-convert": "^1.6.0", - "mime-types": "^2.1.35", - "openai": "^4.68.4", - "os": "^0.1.2", - "p-limit": "^3.1.0", - "path": "^0.12.7", - "pdf2pic": "^3.1.1", - "sharp": "^0.33.5", - "tesseract.js": "^5.1.1", - "util": "^0.12.5", - "zod": "^3.23.8" - }, - "devDependencies": { - "@types/fs-extra": "^11.0.4", - "@types/mime-types": "^2.1.4", - "@types/node": "^20.14.11", - "typescript": "^5.6.3" - } + "name": "core", + "version": "1.0.0", + "description": "Core package for document conversion", + "main": "dist/index.js", + "type": "commonjs", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc" + }, + "dependencies": { + "axios": "^1.7.2", + "child_process": "^1.0.2", + "dotenv": "^16.4.5", + "fs-extra": "^11.2.0", + "libreoffice-convert": "^1.6.0", + "mime-types": "^2.1.35", + "openai": "^4.68.4", + "os": "^0.1.2", + "p-limit": "^3.1.0", + "path": "^0.12.7", + "pdf2pic": "^3.1.1", + "sharp": "^0.33.5", + "tesseract.js": "^5.1.1", + "util": "^0.12.5", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/mime-types": "^2.1.4", + "@types/node": "^20.14.11", + "typescript": "^5.6.3" + } } diff --git a/core/scripts/install-dependencies.js b/core/scripts/install-dependencies.js index 4555935..4eba4d8 100644 --- a/core/scripts/install-dependencies.js +++ b/core/scripts/install-dependencies.js @@ -1,87 +1,99 @@ -const { exec } = require("child_process"); -const { promisify } = require("util"); -const fs = require("fs"); +const { exec } = require("node:child_process"); +const { promisify } = require("node:util"); +const fs = require("node:fs"); const execPromise = promisify(exec); const installPackage = async (command, packageName) => { - try { - const { stdout, stderr } = await execPromise(command); - if (stderr) { - throw new Error(`Failed to install ${packageName}: ${stderr}`); - } - return stdout; - } catch (error) { - throw new Error(`Failed to install ${packageName}: ${error.message}`); - } + try { + const { stdout, stderr } = await execPromise(command); + if (stderr) { + throw new Error(`Failed to install ${packageName}: ${stderr}`); + } + return stdout; + } catch (error) { + throw new Error(`Failed to install ${packageName}: ${error.message}`); + } }; const isSudoAvailable = async () => { - try { - // Try running a sudo command (more relevant for Unix-like systems) - await execPromise("sudo -n true"); - return true; - } catch { - return false; - } + try { + // Try running a sudo command (more relevant for Unix-like systems) + await execPromise("sudo -n true"); + return true; + } catch { + return false; + } }; const checkInstalled = async (command, appName) => { - try { - const { stdout } = await execPromise(command); - console.log(`${appName} is installed: ${stdout}`); - return true; - } catch (error) { - console.log(`${appName} is not installed. Error: ${error.message}`); - return false; - } + try { + const { stdout } = await execPromise(command); + console.log(`${appName} is installed: ${stdout}`); + return true; + } catch (error) { + console.log(`${appName} is not installed. Error: ${error.message}`); + return false; + } }; const checkAndInstall = async () => { - try { - const sudoAvailable = await isSudoAvailable(); + try { + const sudoAvailable = await isSudoAvailable(); - // Check for Ghostscript - const ghostscriptInstalled = await checkInstalled("gswin64c --version", "Ghostscript") || - await checkInstalled("gswin32c --version", "Ghostscript"); - if (!ghostscriptInstalled) { - console.log("Please install Ghostscript from https://www.ghostscript.com/download.html"); - } + // Check for Ghostscript + const ghostscriptInstalled = + (await checkInstalled("gswin64c --version", "Ghostscript")) || + (await checkInstalled("gswin32c --version", "Ghostscript")); + if (!ghostscriptInstalled) { + console.log( + "Please install Ghostscript from https://www.ghostscript.com/download.html", + ); + } - // Check for GraphicsMagick - const graphicsMagickInstalled = await checkInstalled("gm -version", "GraphicsMagick"); - if (!graphicsMagickInstalled) { - if (process.platform === "darwin") { - await installPackage("brew install graphicsmagick", "GraphicsMagick"); - } else if (process.platform === "linux") { - const command = sudoAvailable - ? "sudo apt-get update && sudo apt-get install -y graphicsmagick" - : "apt-get update && apt-get install -y graphicsmagick"; - await installPackage(command, "GraphicsMagick"); - } else { - console.log("Please install GraphicsMagick from http://www.graphicsmagick.org/download.html"); - } - } + // Check for GraphicsMagick + const graphicsMagickInstalled = await checkInstalled( + "gm -version", + "GraphicsMagick", + ); + if (!graphicsMagickInstalled) { + if (process.platform === "darwin") { + await installPackage("brew install graphicsmagick", "GraphicsMagick"); + } else if (process.platform === "linux") { + const command = sudoAvailable + ? "sudo apt-get update && sudo apt-get install -y graphicsmagick" + : "apt-get update && apt-get install -y graphicsmagick"; + await installPackage(command, "GraphicsMagick"); + } else { + console.log( + "Please install GraphicsMagick from http://www.graphicsmagick.org/download.html", + ); + } + } - // Check for LibreOffice - const libreOfficeInstalled = await checkInstalled("soffice --version", "LibreOffice"); - if (!libreOfficeInstalled) { - if (process.platform === "darwin") { - await installPackage("brew install --cask libreoffice", "LibreOffice"); - } else if (process.platform === "linux") { - const command = sudoAvailable - ? "sudo apt-get update && sudo apt-get install -y libreoffice" - : "apt-get update && apt-get install -y libreoffice"; - await installPackage(command, "LibreOffice"); - } else { - console.log("Please install LibreOffice from https://www.libreoffice.org/download/download"); - } - } - - } catch (err) { - console.error(`Error during installation: ${err.message}`); - process.exit(1); - } + // Check for LibreOffice + const libreOfficeInstalled = await checkInstalled( + "soffice --version", + "LibreOffice", + ); + if (!libreOfficeInstalled) { + if (process.platform === "darwin") { + await installPackage("brew install --cask libreoffice", "LibreOffice"); + } else if (process.platform === "linux") { + const command = sudoAvailable + ? "sudo apt-get update && sudo apt-get install -y libreoffice" + : "apt-get update && apt-get install -y libreoffice"; + await installPackage(command, "LibreOffice"); + } else { + console.log( + "Please install LibreOffice from https://www.libreoffice.org/download/download", + ); + } + } + } catch (err) { + console.error(`Error during installation: ${err.message}`); + process.exit(1); + } }; checkAndInstall(); diff --git a/core/src/index.ts b/core/src/index.ts index 574c343..f45baeb 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1,215 +1,218 @@ +import os from "os"; +import path from "path"; +import fs from "fs-extra"; +import pLimit, { type Limit } from "p-limit"; +import { getCompletion } from "./openAI"; +import { type DocumindArgs, type DocumindOutput, ModelOptions } from "./types"; import { - convertFileToPdf, - convertPdfToImages, - downloadFile, - formatMarkdown, - isString, + convertFileToPdf, + convertPdfToImages, + downloadFile, + formatMarkdown, + isString, } from "./utils"; -import { getCompletion } from "./openAI"; -import { ModelOptions, DocumindArgs, DocumindOutput } from "./types"; import { validateLLMParams } from "./utils"; -import fs from "fs-extra"; -import os from "os"; -import path from "path"; -import pLimit, { Limit } from "p-limit"; export const documind = async ({ - cleanup = true, - concurrency = 10, - filePath, - llmParams = {}, - maintainFormat = false, - model, //= ModelOptions.gpt_4o_mini, - openaiAPIKey = "", - outputDir, - pagesToConvertAsImages = -1, - tempDir = os.tmpdir(), + cleanup = true, + concurrency = 10, + filePath, + llmParams = {}, + maintainFormat = false, + model, //= ModelOptions.gpt_4o_mini, + openaiAPIKey = "", + outputDir, + pagesToConvertAsImages = -1, + tempDir = os.tmpdir(), }: DocumindArgs): Promise => { - const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; - const defaultModel = - model ?? - (baseUrl !== "https://api.openai.com/v1" - ? ModelOptions.llava // Default for custom base URL - : ModelOptions.gpt_4o_mini); // Default for OpenAI - - let inputTokenCount = 0; - let outputTokenCount = 0; - let priorPage = ""; - const aggregatedMarkdown: string[] = []; - const startTime = new Date(); - - llmParams = validateLLMParams(llmParams); - - // Validators - if (!openaiAPIKey || !openaiAPIKey.length) { - throw new Error("Missing OpenAI API key"); - } - if (!filePath || !filePath.length) { - throw new Error("Missing file path"); - } - - // Ensure temp directory exists + create temp folder - const rand = Math.floor(1000 + Math.random() * 9000).toString(); - const tempDirectory = path.join(tempDir || os.tmpdir(), `documind-file-${rand}`); - await fs.ensureDir(tempDirectory); - - // Download the PDF. Get file name. - const { extension, localPath } = await downloadFile({ - filePath, - tempDir: tempDirectory, - }); - if (!localPath) throw "Failed to save file to local drive"; - - // Sort the `pagesToConvertAsImages` array to make sure we use the right index - // for `formattedPages` as `pdf2pic` always returns images in order - if (Array.isArray(pagesToConvertAsImages)) { - pagesToConvertAsImages.sort((a, b) => a - b); - } - - // Convert file to PDF if necessary - if (extension !== ".png") { - let pdfPath: string; - if (extension === ".pdf") { - pdfPath = localPath; - } else { - pdfPath = await convertFileToPdf({ - extension, - localPath, - tempDir: tempDirectory, - }); - } - // Convert the file to a series of images - await convertPdfToImages({ - localPath: pdfPath, - pagesToConvertAsImages, - tempDir: tempDirectory, - }); - } - - const endOfPath = localPath.split("/")[localPath.split("/").length - 1]; - const rawFileName = endOfPath.split(".")[0]; - const fileName = rawFileName - .replace(/[^\w\s]/g, "") - .replace(/\s+/g, "_") - .toLowerCase() - .substring(0, 255); // Truncate file name to 255 characters to prevent ENAMETOOLONG errors - - // Get list of converted images - const files = await fs.readdir(tempDirectory); - const images = files.filter((file) => file.endsWith(".png")); - - if (maintainFormat) { - // Use synchronous processing - for (const image of images) { - const imagePath = path.join(tempDirectory, image); - try { - const { content, inputTokens, outputTokens } = await getCompletion({ - apiKey: openaiAPIKey, - imagePath, - llmParams, - maintainFormat, - model: defaultModel, - priorPage, - }); - const formattedMarkdown = formatMarkdown(content); - inputTokenCount += inputTokens; - outputTokenCount += outputTokens; - - // Update prior page to result from last processing step - priorPage = formattedMarkdown; - - // Add all markdown results to array - aggregatedMarkdown.push(formattedMarkdown); - } catch (error) { - console.error(`Failed to process image ${image}:`, error); - throw error; - } - } - } else { - // Process in parallel with a limit on concurrent pages - const processPage = async (image: string): Promise => { - const imagePath = path.join(tempDirectory, image); - try { - const { content, inputTokens, outputTokens } = await getCompletion({ - apiKey: openaiAPIKey, - imagePath, - llmParams, - maintainFormat, - model: defaultModel, - priorPage, - }); - const formattedMarkdown = formatMarkdown(content); - inputTokenCount += inputTokens; - outputTokenCount += outputTokens; - - // Update prior page to result from last processing step - priorPage = formattedMarkdown; - - // Add all markdown results to array - return formattedMarkdown; - } catch (error) { - console.error(`Failed to process image ${image}:`, error); - throw error; - } - }; - - // Function to process pages with concurrency limit - const processPagesInBatches = async (images: string[], limit: Limit) => { - const results: (string | null)[] = []; - - const promises = images.map((image, index) => - limit(() => - processPage(image).then((result) => { - results[index] = result; - }) - ) - ); - - await Promise.all(promises); - return results; - }; - - const limit = pLimit(concurrency); - const results = await processPagesInBatches(images, limit); - const filteredResults = results.filter(isString); - aggregatedMarkdown.push(...filteredResults); - } - - // Write the aggregated markdown to a file - if (outputDir) { - const resultFilePath = path.join(outputDir, `${fileName}.md`); - await fs.writeFile(resultFilePath, aggregatedMarkdown.join("\n\n")); - } - - // Cleanup the downloaded PDF file - if (cleanup) await fs.remove(tempDirectory); - - // Format JSON response - const endTime = new Date(); - const completionTime = endTime.getTime() - startTime.getTime(); - const formattedPages = aggregatedMarkdown.map((el, i) => { - let pageNumber; - // If we convert all pages, just use the array index - if (pagesToConvertAsImages === -1) { - pageNumber = i + 1; - } - // Else if we convert specific pages, use the page number from the parameter - else if (Array.isArray(pagesToConvertAsImages)) { - pageNumber = pagesToConvertAsImages[i]; - } - // Else, the parameter is a number and use it for the page number - else { - pageNumber = pagesToConvertAsImages; - } - - return { content: el, page: pageNumber, contentLength: el.length }; - }); - - return { - completionTime, - fileName, - inputTokens: inputTokenCount, - outputTokens: outputTokenCount, - pages: formattedPages, - }; + const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; + const defaultModel = + model ?? + (baseUrl !== "https://api.openai.com/v1" + ? ModelOptions.llava // Default for custom base URL + : ModelOptions.gpt_4o_mini); // Default for OpenAI + + let inputTokenCount = 0; + let outputTokenCount = 0; + let priorPage = ""; + const aggregatedMarkdown: string[] = []; + const startTime = new Date(); + + llmParams = validateLLMParams(llmParams); + + // Validators + if (!openaiAPIKey || !openaiAPIKey.length) { + throw new Error("Missing OpenAI API key"); + } + if (!filePath || !filePath.length) { + throw new Error("Missing file path"); + } + + // Ensure temp directory exists + create temp folder + const rand = Math.floor(1000 + Math.random() * 9000).toString(); + const tempDirectory = path.join( + tempDir || os.tmpdir(), + `documind-file-${rand}`, + ); + await fs.ensureDir(tempDirectory); + + // Download the PDF. Get file name. + const { extension, localPath } = await downloadFile({ + filePath, + tempDir: tempDirectory, + }); + if (!localPath) throw "Failed to save file to local drive"; + + // Sort the `pagesToConvertAsImages` array to make sure we use the right index + // for `formattedPages` as `pdf2pic` always returns images in order + if (Array.isArray(pagesToConvertAsImages)) { + pagesToConvertAsImages.sort((a, b) => a - b); + } + + // Convert file to PDF if necessary + if (extension !== ".png") { + let pdfPath: string; + if (extension === ".pdf") { + pdfPath = localPath; + } else { + pdfPath = await convertFileToPdf({ + extension, + localPath, + tempDir: tempDirectory, + }); + } + // Convert the file to a series of images + await convertPdfToImages({ + localPath: pdfPath, + pagesToConvertAsImages, + tempDir: tempDirectory, + }); + } + + const endOfPath = localPath.split("/")[localPath.split("/").length - 1]; + const rawFileName = endOfPath.split(".")[0]; + const fileName = rawFileName + .replace(/[^\w\s]/g, "") + .replace(/\s+/g, "_") + .toLowerCase() + .substring(0, 255); // Truncate file name to 255 characters to prevent ENAMETOOLONG errors + + // Get list of converted images + const files = await fs.readdir(tempDirectory); + const images = files.filter((file) => file.endsWith(".png")); + + if (maintainFormat) { + // Use synchronous processing + for (const image of images) { + const imagePath = path.join(tempDirectory, image); + try { + const { content, inputTokens, outputTokens } = await getCompletion({ + apiKey: openaiAPIKey, + imagePath, + llmParams, + maintainFormat, + model: defaultModel, + priorPage, + }); + const formattedMarkdown = formatMarkdown(content); + inputTokenCount += inputTokens; + outputTokenCount += outputTokens; + + // Update prior page to result from last processing step + priorPage = formattedMarkdown; + + // Add all markdown results to array + aggregatedMarkdown.push(formattedMarkdown); + } catch (error) { + console.error(`Failed to process image ${image}:`, error); + throw error; + } + } + } else { + // Process in parallel with a limit on concurrent pages + const processPage = async (image: string): Promise => { + const imagePath = path.join(tempDirectory, image); + try { + const { content, inputTokens, outputTokens } = await getCompletion({ + apiKey: openaiAPIKey, + imagePath, + llmParams, + maintainFormat, + model: defaultModel, + priorPage, + }); + const formattedMarkdown = formatMarkdown(content); + inputTokenCount += inputTokens; + outputTokenCount += outputTokens; + + // Update prior page to result from last processing step + priorPage = formattedMarkdown; + + // Add all markdown results to array + return formattedMarkdown; + } catch (error) { + console.error(`Failed to process image ${image}:`, error); + throw error; + } + }; + + // Function to process pages with concurrency limit + const processPagesInBatches = async (images: string[], limit: Limit) => { + const results: (string | null)[] = []; + + const promises = images.map((image, index) => + limit(() => + processPage(image).then((result) => { + results[index] = result; + }), + ), + ); + + await Promise.all(promises); + return results; + }; + + const limit = pLimit(concurrency); + const results = await processPagesInBatches(images, limit); + const filteredResults = results.filter(isString); + aggregatedMarkdown.push(...filteredResults); + } + + // Write the aggregated markdown to a file + if (outputDir) { + const resultFilePath = path.join(outputDir, `${fileName}.md`); + await fs.writeFile(resultFilePath, aggregatedMarkdown.join("\n\n")); + } + + // Cleanup the downloaded PDF file + if (cleanup) await fs.remove(tempDirectory); + + // Format JSON response + const endTime = new Date(); + const completionTime = endTime.getTime() - startTime.getTime(); + const formattedPages = aggregatedMarkdown.map((el, i) => { + let pageNumber; + // If we convert all pages, just use the array index + if (pagesToConvertAsImages === -1) { + pageNumber = i + 1; + } + // Else if we convert specific pages, use the page number from the parameter + else if (Array.isArray(pagesToConvertAsImages)) { + pageNumber = pagesToConvertAsImages[i]; + } + // Else, the parameter is a number and use it for the page number + else { + pageNumber = pagesToConvertAsImages; + } + + return { content: el, page: pageNumber, contentLength: el.length }; + }); + + return { + completionTime, + fileName, + inputTokens: inputTokenCount, + outputTokens: outputTokenCount, + pages: formattedPages, + }; }; diff --git a/core/src/openAI.ts b/core/src/openAI.ts index 8b93f36..652fe1c 100644 --- a/core/src/openAI.ts +++ b/core/src/openAI.ts @@ -1,92 +1,88 @@ -import { CompletionArgs, CompletionResponse } from "./types"; -import { convertKeysToSnakeCase, encodeImageToBase64 } from "./utils"; import axios from "axios"; +import type { CompletionArgs, CompletionResponse } from "./types"; +import { convertKeysToSnakeCase, encodeImageToBase64 } from "./utils"; export const getCompletion = async ({ - apiKey, - imagePath, - llmParams, - maintainFormat, - model, - priorPage, + apiKey, + imagePath, + llmParams, + maintainFormat, + model, + priorPage, }: CompletionArgs): Promise => { + const validModelsForCustomBaseUrl = ["llava", "llama3.2-vision"]; + const validModelsForOpenAi = ["gpt-4o", "gpt-4o-mini"]; + const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; - const validModelsForCustomBaseUrl = [ - "llava", - "llama3.2-vision", - ]; - const validModelsForOpenAi = ["gpt-4o", "gpt-4o-mini"]; - const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; - - if (baseUrl !== "https://api.openai.com/v1") { - if (!validModelsForCustomBaseUrl.includes(model as any)) { - throw new Error( - `Invalid model "${model}" for custom base URL. Valid options are: ${validModelsForCustomBaseUrl.join(", ")}.` - ); - } - } else { - if (!validModelsForOpenAi.includes(model as any)) { - throw new Error( - `Invalid model "${model}" for OpenAI. Valid options are: ${validModelsForOpenAi.join(", ")}.` - ); - } - } + if (baseUrl !== "https://api.openai.com/v1") { + if (!validModelsForCustomBaseUrl.includes(model as any)) { + throw new Error( + `Invalid model "${model}" for custom base URL. Valid options are: ${validModelsForCustomBaseUrl.join(", ")}.`, + ); + } + } else { + if (!validModelsForOpenAi.includes(model as any)) { + throw new Error( + `Invalid model "${model}" for OpenAI. Valid options are: ${validModelsForOpenAi.join(", ")}.`, + ); + } + } - const systemPrompt = ` - Convert the following image/document to markdown. + const systemPrompt = ` + Convert the following image/document to markdown. Return only the markdown with no explanation text. Do not include deliminators like '''markdown. You must include all information on the page. Do not exclude headers, footers, or subtext. `; - // Default system message. - const messages: any = [{ role: "system", content: systemPrompt }]; + // Default system message. + const messages: any = [{ role: "system", content: systemPrompt }]; - // If content has already been generated, add it to context. - // This helps maintain the same format across pages - if (maintainFormat && priorPage && priorPage.length) { - messages.push({ - role: "system", - content: `Markdown must maintain consistent formatting with the following page: \n\n """${priorPage}"""`, - }); - } + // If content has already been generated, add it to context. + // This helps maintain the same format across pages + if (maintainFormat && priorPage && priorPage.length) { + messages.push({ + role: "system", + content: `Markdown must maintain consistent formatting with the following page: \n\n """${priorPage}"""`, + }); + } - // Add Image to request - const base64Image = await encodeImageToBase64(imagePath); - messages.push({ - role: "user", - content: [ - { - type: "image_url", - image_url: { url: `data:image/png;base64,${base64Image}` }, - }, - ], - }); + // Add Image to request + const base64Image = await encodeImageToBase64(imagePath); + messages.push({ + role: "user", + content: [ + { + type: "image_url", + image_url: { url: `data:image/png;base64,${base64Image}` }, + }, + ], + }); - try { - const response = await axios.post( - `${baseUrl}/chat/completions`, - { - messages, - model, - ...convertKeysToSnakeCase(llmParams ?? null), - }, - { - headers: { - Authorization: `Bearer ${apiKey}`, - "Content-Type": "application/json", - }, - } - ); + try { + const response = await axios.post( + `${baseUrl}/chat/completions`, + { + messages, + model, + ...convertKeysToSnakeCase(llmParams ?? null), + }, + { + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + }, + ); - const data = response.data; + const data = response.data; - return { - content: data.choices[0].message.content, - inputTokens: data.usage.prompt_tokens, - outputTokens: data.usage.completion_tokens, - }; - } catch (err) { - console.error("Error in OpenAI completion", err); - throw err; - } + return { + content: data.choices[0].message.content, + inputTokens: data.usage.prompt_tokens, + outputTokens: data.usage.completion_tokens, + }; + } catch (err) { + console.error("Error in OpenAI completion", err); + throw err; + } }; diff --git a/core/src/types.ts b/core/src/types.ts index eaf9437..8479327 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -1,56 +1,57 @@ export interface DocumindArgs { - cleanup?: boolean; - concurrency?: number; - filePath: string; - llmParams?: LLMParams; - maintainFormat?: boolean; - model?: ModelOptions; - openaiAPIKey?: string; - outputDir?: string; - pagesToConvertAsImages?: number | number[]; - tempDir?: string; + cleanup?: boolean; + concurrency?: number; + filePath: string; + llmParams?: LLMParams; + maintainFormat?: boolean; + model?: ModelOptions; + openaiAPIKey?: string; + outputDir?: string; + pagesToConvertAsImages?: number | number[]; + tempDir?: string; } export enum ModelOptions { - gpt_4o = "gpt-4o", - gpt_4o_mini = "gpt-4o-mini", - llava = "llava", - llama3_2_vision = "llama3.2-vision", + gpt_o3_mini = "o3-mini", + gpt_4o = "gpt-4o", + gpt_4o_mini = "gpt-4o-mini", + llava = "llava", + llama3_2_vision = "llama3.2-vision", } export interface Page { - content: string; - contentLength: number; - page: number; + content: string; + contentLength: number; + page: number; } export interface DocumindOutput { - completionTime: number; - fileName: string; - inputTokens: number; - outputTokens: number; - pages: Page[]; + completionTime: number; + fileName: string; + inputTokens: number; + outputTokens: number; + pages: Page[]; } export interface CompletionResponse { - content: string; - inputTokens: number; - outputTokens: number; + content: string; + inputTokens: number; + outputTokens: number; } export interface CompletionArgs { - apiKey: string; - imagePath: string; - llmParams?: LLMParams; - maintainFormat: boolean; - model: ModelOptions; - priorPage: string; + apiKey: string; + imagePath: string; + llmParams?: LLMParams; + maintainFormat: boolean; + model: ModelOptions; + priorPage: string; } export interface LLMParams { - frequencyPenalty?: number; - maxTokens?: number; - presencePenalty?: number; - temperature?: number; - topP?: number; + frequencyPenalty?: number; + maxTokens?: number; + presencePenalty?: number; + temperature?: number; + topP?: number; } diff --git a/core/src/utils.ts b/core/src/utils.ts index c865ef2..5d40dc3 100644 --- a/core/src/utils.ts +++ b/core/src/utils.ts @@ -1,294 +1,294 @@ -import { convert } from "libreoffice-convert"; -import { fromPath } from "pdf2pic"; -import { LLMParams } from "./types"; -import { pipeline } from "stream/promises"; +import { pipeline } from "node:stream/promises"; +import path from "path"; import { promisify } from "util"; -import * as Tesseract from "tesseract.js"; import axios from "axios"; import fs from "fs-extra"; +import { convert } from "libreoffice-convert"; import mime from "mime-types"; -import path from "path"; +import { fromPath } from "pdf2pic"; import sharp from "sharp"; +import * as Tesseract from "tesseract.js"; +import type { LLMParams } from "./types"; const convertAsync = promisify(convert); const defaultLLMParams: LLMParams = { - frequencyPenalty: 0, // OpenAI defaults to 0 - maxTokens: 2000, - presencePenalty: 0, // OpenAI defaults to 0 - temperature: 0, - topP: 1, // OpenAI defaults to 1 + frequencyPenalty: 0, // OpenAI defaults to 0 + maxTokens: 2000, + presencePenalty: 0, // OpenAI defaults to 0 + temperature: 0, + topP: 1, // OpenAI defaults to 1 }; export const validateLLMParams = (params: Partial): LLMParams => { - const validKeys = Object.keys(defaultLLMParams); - - for (const [key, value] of Object.entries(params)) { - if (!validKeys.includes(key)) { - throw new Error(`Invalid LLM parameter: ${key}`); - } - if (typeof value !== "number") { - throw new Error(`Value for '${key}' must be a number`); - } - } - - return { ...defaultLLMParams, ...params }; + const validKeys = Object.keys(defaultLLMParams); + + for (const [key, value] of Object.entries(params)) { + if (!validKeys.includes(key)) { + throw new Error(`Invalid LLM parameter: ${key}`); + } + if (typeof value !== "number") { + throw new Error(`Value for '${key}' must be a number`); + } + } + + return { ...defaultLLMParams, ...params }; }; export const encodeImageToBase64 = async (imagePath: string) => { - const imageBuffer = await fs.readFile(imagePath); - return imageBuffer.toString("base64"); + const imageBuffer = await fs.readFile(imagePath); + return imageBuffer.toString("base64"); }; // Strip out the ```markdown wrapper export const formatMarkdown = (text: string) => { - let formattedMarkdown = text?.trim(); - let loopCount = 0; - const maxLoops = 3; - - const startsWithMarkdown = formattedMarkdown.startsWith("```markdown"); - while (startsWithMarkdown && loopCount < maxLoops) { - const endsWithClosing = formattedMarkdown.endsWith("```"); - - if (startsWithMarkdown && endsWithClosing) { - const outermostBlockRegex = /^```markdown\n([\s\S]*?)\n```$/; - const match = outermostBlockRegex.exec(formattedMarkdown); - - if (match) { - formattedMarkdown = match[1].trim(); - loopCount++; - } else { - break; - } - } else { - break; - } - } - - return formattedMarkdown; + let formattedMarkdown = text?.trim(); + let loopCount = 0; + const maxLoops = 3; + + const startsWithMarkdown = formattedMarkdown.startsWith("```markdown"); + while (startsWithMarkdown && loopCount < maxLoops) { + const endsWithClosing = formattedMarkdown.endsWith("```"); + + if (startsWithMarkdown && endsWithClosing) { + const outermostBlockRegex = /^```markdown\n([\s\S]*?)\n```$/; + const match = outermostBlockRegex.exec(formattedMarkdown); + + if (match) { + formattedMarkdown = match[1].trim(); + loopCount++; + } else { + break; + } + } else { + break; + } + } + + return formattedMarkdown; }; export const isString = (value: string | null): value is string => { - return value !== null; + return value !== null; }; export const isValidUrl = (string: string): boolean => { - let url; - try { - url = new URL(string); - } catch (_) { - return false; - } - return url.protocol === "http:" || url.protocol === "https:"; + let url; + try { + url = new URL(string); + } catch (_) { + return false; + } + return url.protocol === "http:" || url.protocol === "https:"; }; // Save file to local tmp directory export const downloadFile = async ({ - filePath, - tempDir, + filePath, + tempDir, }: { - filePath: string; - tempDir: string; + filePath: string; + tempDir: string; }): Promise<{ extension: string; localPath: string }> => { - // Shorten the file name by removing URL parameters - const baseFileName = path.basename(filePath.split("?")[0]); - const localPath = path.join(tempDir, baseFileName); - let mimetype; - - // Check if filePath is a URL - if (isValidUrl(filePath)) { - const writer = fs.createWriteStream(localPath); - - const response = await axios({ - url: filePath, - method: "GET", - responseType: "stream", - }); - - if (response.status !== 200) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - mimetype = response.headers?.["content-type"]; - await pipeline(response.data, writer); - } else { - // If filePath is a local file, copy it to the temp directory - await fs.copyFile(filePath, localPath); - } - - if (!mimetype) { - mimetype = mime.lookup(localPath); - } - - let extension = mime.extension(mimetype) || ""; - if (!extension) { - if (mimetype === "binary/octet-stream") { - extension = ".bin"; - } else { - throw new Error("File extension missing"); - } - } - - if (!extension.startsWith(".")) { - extension = `.${extension}`; - } - - return { extension, localPath }; + // Shorten the file name by removing URL parameters + const baseFileName = path.basename(filePath.split("?")[0]); + const localPath = path.join(tempDir, baseFileName); + let mimetype; + + // Check if filePath is a URL + if (isValidUrl(filePath)) { + const writer = fs.createWriteStream(localPath); + + const response = await axios({ + url: filePath, + method: "GET", + responseType: "stream", + }); + + if (response.status !== 200) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + mimetype = response.headers?.["content-type"]; + await pipeline(response.data, writer); + } else { + // If filePath is a local file, copy it to the temp directory + await fs.copyFile(filePath, localPath); + } + + if (!mimetype) { + mimetype = mime.lookup(localPath); + } + + let extension = mime.extension(mimetype) || ""; + if (!extension) { + if (mimetype === "binary/octet-stream") { + extension = ".bin"; + } else { + throw new Error("File extension missing"); + } + } + + if (!extension.startsWith(".")) { + extension = `.${extension}`; + } + + return { extension, localPath }; }; // Extract text confidence from image buffer using Tesseract export const getTextFromImage = async ( - buffer: Buffer + buffer: Buffer, ): Promise<{ confidence: number }> => { - try { - // Get image and metadata - const image = sharp(buffer); - const metadata = await image.metadata(); - - // Crop to a 150px wide column in the center of the document. - // This section produced the highest confidence/speed tradeoffs. - const cropWidth = 150; - const cropHeight = metadata.height || 0; - const left = Math.max(0, Math.floor((metadata.width! - cropWidth) / 2)); - const top = 0; - - // Extract the cropped image - const croppedBuffer = await image - .extract({ left, top, width: cropWidth, height: cropHeight }) - .toBuffer(); - - // Pass the croppedBuffer to Tesseract.recognize - // @TODO: How can we generalize this to non eng languages? - const { - data: { confidence }, - } = await Tesseract.recognize(croppedBuffer, "eng"); - - return { confidence }; - } catch (error) { - console.error("Error during OCR:", error); - return { confidence: 0 }; - } + try { + // Get image and metadata + const image = sharp(buffer); + const metadata = await image.metadata(); + + // Crop to a 150px wide column in the center of the document. + // This section produced the highest confidence/speed tradeoffs. + const cropWidth = 150; + const cropHeight = metadata.height || 0; + const left = Math.max(0, Math.floor((metadata.width! - cropWidth) / 2)); + const top = 0; + + // Extract the cropped image + const croppedBuffer = await image + .extract({ left, top, width: cropWidth, height: cropHeight }) + .toBuffer(); + + // Pass the croppedBuffer to Tesseract.recognize + // @TODO: How can we generalize this to non eng languages? + const { + data: { confidence }, + } = await Tesseract.recognize(croppedBuffer, "eng"); + + return { confidence }; + } catch (error) { + console.error("Error during OCR:", error); + return { confidence: 0 }; + } }; // Correct image orientation based on OCR confidence // Run Tesseract on 4 different orientations of the image and compare the output const correctImageOrientation = async (buffer: Buffer): Promise => { - const image = sharp(buffer); - const rotations = [0, 90, 180, 270]; - - const results = await Promise.all( - rotations.map(async (rotation) => { - const rotatedImageBuffer = await image - .clone() - .rotate(rotation) - .toBuffer(); - const { confidence } = await getTextFromImage(rotatedImageBuffer); - return { rotation, confidence }; - }) - ); - - // Find the rotation with the best confidence score - const bestResult = results.reduce((best, current) => - current.confidence > best.confidence ? current : best - ); - - if (bestResult.rotation !== 0) { - console.log( - `Reorienting image ${bestResult.rotation} degrees (Confidence: ${bestResult.confidence}%).` - ); - } - - // Rotate the image to the best orientation - const correctedImageBuffer = await image - .rotate(bestResult.rotation) - .toBuffer(); - - return correctedImageBuffer; + const image = sharp(buffer); + const rotations = [0, 90, 180, 270]; + + const results = await Promise.all( + rotations.map(async (rotation) => { + const rotatedImageBuffer = await image + .clone() + .rotate(rotation) + .toBuffer(); + const { confidence } = await getTextFromImage(rotatedImageBuffer); + return { rotation, confidence }; + }), + ); + + // Find the rotation with the best confidence score + const bestResult = results.reduce((best, current) => + current.confidence > best.confidence ? current : best, + ); + + if (bestResult.rotation !== 0) { + console.log( + `Reorienting image ${bestResult.rotation} degrees (Confidence: ${bestResult.confidence}%).`, + ); + } + + // Rotate the image to the best orientation + const correctedImageBuffer = await image + .rotate(bestResult.rotation) + .toBuffer(); + + return correctedImageBuffer; }; // Convert each page to a png, correct orientation, and save that image to tmp export const convertPdfToImages = async ({ - localPath, - pagesToConvertAsImages, - tempDir, + localPath, + pagesToConvertAsImages, + tempDir, }: { - localPath: string; - pagesToConvertAsImages: number | number[]; - tempDir: string; + localPath: string; + pagesToConvertAsImages: number | number[]; + tempDir: string; }) => { - const options = { - density: 300, - format: "png", - height: 1056, - preserveAspectRatio: true, - saveFilename: path.basename(localPath, path.extname(localPath)), - savePath: tempDir, - }; - const storeAsImage = fromPath(localPath, options); - - try { - const convertResults = await storeAsImage.bulk(pagesToConvertAsImages, { - responseType: "buffer", - }); - await Promise.all( - convertResults.map(async (result) => { - if (!result || !result.buffer) { - throw new Error("Could not convert page to image buffer"); - } - if (!result.page) throw new Error("Could not identify page data"); - const paddedPageNumber = result.page.toString().padStart(5, "0"); - - // Correct the image orientation - const correctedBuffer = await correctImageOrientation(result.buffer); - - const imagePath = path.join( - tempDir, - `${options.saveFilename}_page_${paddedPageNumber}.png` - ); - await fs.writeFile(imagePath, correctedBuffer); - }) - ); - return convertResults; - } catch (err) { - console.error("Error during PDF conversion:", err); - throw err; - } + const options = { + density: 300, + format: "png", + height: 1056, + preserveAspectRatio: true, + saveFilename: path.basename(localPath, path.extname(localPath)), + savePath: tempDir, + }; + const storeAsImage = fromPath(localPath, options); + + try { + const convertResults = await storeAsImage.bulk(pagesToConvertAsImages, { + responseType: "buffer", + }); + await Promise.all( + convertResults.map(async (result) => { + if (!result || !result.buffer) { + throw new Error("Could not convert page to image buffer"); + } + if (!result.page) throw new Error("Could not identify page data"); + const paddedPageNumber = result.page.toString().padStart(5, "0"); + + // Correct the image orientation + const correctedBuffer = await correctImageOrientation(result.buffer); + + const imagePath = path.join( + tempDir, + `${options.saveFilename}_page_${paddedPageNumber}.png`, + ); + await fs.writeFile(imagePath, correctedBuffer); + }), + ); + return convertResults; + } catch (err) { + console.error("Error during PDF conversion:", err); + throw err; + } }; // Convert each page (from other formats like docx) to a png and save that image to tmp export const convertFileToPdf = async ({ - extension, - localPath, - tempDir, + extension, + localPath, + tempDir, }: { - extension: string; - localPath: string; - tempDir: string; + extension: string; + localPath: string; + tempDir: string; }): Promise => { - const inputBuffer = await fs.readFile(localPath); - const outputFilename = path.basename(localPath, extension) + ".pdf"; - const outputPath = path.join(tempDir, outputFilename); - - try { - const pdfBuffer = await convertAsync(inputBuffer, ".pdf", undefined); - await fs.writeFile(outputPath, pdfBuffer); - return outputPath; - } catch (err) { - console.error(`Error converting ${extension} to .pdf:`, err); - throw err; - } + const inputBuffer = await fs.readFile(localPath); + const outputFilename = path.basename(localPath, extension) + ".pdf"; + const outputPath = path.join(tempDir, outputFilename); + + try { + const pdfBuffer = await convertAsync(inputBuffer, ".pdf", undefined); + await fs.writeFile(outputPath, pdfBuffer); + return outputPath; + } catch (err) { + console.error(`Error converting ${extension} to .pdf:`, err); + throw err; + } }; const camelToSnakeCase = (str: string) => - str.replace(/[A-Z]/g, (letter: string) => `_${letter.toLowerCase()}`); + str.replace(/[A-Z]/g, (letter: string) => `_${letter.toLowerCase()}`); export const convertKeysToSnakeCase = ( - obj: Record | null + obj: Record | null, ): Record => { - if (typeof obj !== "object" || obj === null) { - return obj ?? {}; - } + if (typeof obj !== "object" || obj === null) { + return obj ?? {}; + } - return Object.fromEntries( - Object.entries(obj).map(([key, value]) => [camelToSnakeCase(key), value]) - ); + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [camelToSnakeCase(key), value]), + ); }; diff --git a/core/tsconfig.json b/core/tsconfig.json index b962015..4cfb3be 100644 --- a/core/tsconfig.json +++ b/core/tsconfig.json @@ -1,14 +1,18 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "CommonJS", - "declaration": true, - "outDir": "./dist", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] - } - \ No newline at end of file + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/extractor/src/converter.js b/extractor/src/converter.js index 3326fe1..69ff1ba 100644 --- a/extractor/src/converter.js +++ b/extractor/src/converter.js @@ -1,22 +1,22 @@ -import { documind } from 'core'; -import { generateMarkdownDocument } from './utils/generateMarkdown.js'; +import { documind } from "core"; +import { generateMarkdownDocument } from "./utils/generateMarkdown.js"; export const convertFile = async (filePath, model) => { - try { - const result = await documind({ - filePath, - model, - openaiAPIKey: process.env.OPENAI_API_KEY, - }); + try { + const result = await documind({ + filePath, + model, + openaiAPIKey: process.env.OPENAI_API_KEY, + }); - const { pages, fileName } = result; - const totalPages = pages.length; + const { pages, fileName } = result; + const totalPages = pages.length; - const markdown = await generateMarkdownDocument(pages); - console.log('Markdown generated', markdown); + const markdown = await generateMarkdownDocument(pages); + console.log("Markdown generated", markdown); - return { markdown, totalPages, fileName }; - } catch (error) { - console.error('Error running documind core:', error); - } + return { markdown, totalPages, fileName }; + } catch (error) { + console.error("Error running documind core:", error); + } }; diff --git a/extractor/src/index.js b/extractor/src/index.js index 91c90db..92b9e65 100644 --- a/extractor/src/index.js +++ b/extractor/src/index.js @@ -1,3 +1,3 @@ -export { extract } from './services/extract.js'; -export { templates } from './services/templates.js'; -export { formatter } from './services/formatter.js'; +export { extract } from "./services/extract.js"; +export { templates } from "./services/templates.js"; +export { formatter } from "./services/formatter.js"; diff --git a/extractor/src/main-extractor.js b/extractor/src/main-extractor.js index 7301b67..a9c73c2 100644 --- a/extractor/src/main-extractor.js +++ b/extractor/src/main-extractor.js @@ -1,14 +1,19 @@ import OpenAI from "openai"; import { zodResponseFormat } from "openai/helpers/zod"; -import { convertToZodSchema } from './utils/convertToZodSchema.js'; +import { convertFile } from "./converter.js"; import { autogenerateSchema } from "./utils/autogenerateSchema.js"; -import { convertFile } from './converter.js'; +import { convertToZodSchema } from "./utils/convertToZodSchema.js"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); -export const extractData = async (pdfFilePath, schemaDefinition, model, autoSchema) => { - - const prompt = ` +export const extractData = async ( + pdfFilePath, + schemaDefinition, + model, + parseModel, + autoSchema, +) => { + const prompt = ` You are an expert in structured data extraction. Your task is to extract information from unstructured content and transform it into the specified structure. Follow these rules strictly: 1. Handle Missing or Undetermined Data: @@ -16,43 +21,46 @@ export const extractData = async (pdfFilePath, schemaDefinition, model, autoSche - **Do not use substitutes such as "unknown," "missing," or any other placeholder for missing or unknown data. The value **must** always be explicitly null. `; - try { - const { markdown, totalPages, fileName } = await convertFile(pdfFilePath, model); - - // Determine which schema to use - let finalSchema = schemaDefinition; - if (autoSchema) { - finalSchema = await autogenerateSchema(markdown); - - if (!finalSchema) { - throw new Error("Failed to auto-generate schema."); - } - } - - // Convert the schema (whether generated or passed) to Zod - const dynamicZodSchema = convertToZodSchema(finalSchema); - - const completion = await openai.beta.chat.completions.parse({ - model: "gpt-4o-2024-08-06", - messages: [ - { role: "system", content: prompt }, - { - role: "user", - content: markdown, - }, - ], - response_format: zodResponseFormat(dynamicZodSchema, "event"), - }); - - const event = completion.choices[0].message.parsed; - - return { - event, - totalPages, - fileName, - }; - } catch (error) { - console.error("Error running OpenAI API call:", error); - throw error; - } + try { + const { markdown, totalPages, fileName } = await convertFile( + pdfFilePath, + model, + ); + + // Determine which schema to use + let finalSchema = schemaDefinition; + if (autoSchema) { + finalSchema = await autogenerateSchema(markdown); + + if (!finalSchema) { + throw new Error("Failed to auto-generate schema."); + } + } + + // Convert the schema (whether generated or passed) to Zod + const dynamicZodSchema = convertToZodSchema(finalSchema); + + const completion = await openai.beta.chat.completions.parse({ + model: parseModel ?? "gpt-4o-2024-08-06", + messages: [ + { role: "system", content: prompt }, + { + role: "user", + content: markdown, + }, + ], + response_format: zodResponseFormat(dynamicZodSchema, "event"), + }); + + const event = completion.choices[0].message.parsed; + + return { + event, + totalPages, + fileName, + }; + } catch (error) { + console.error("Error running OpenAI API call:", error); + throw error; + } }; diff --git a/extractor/src/services/extract.js b/extractor/src/services/extract.js index c8b9852..dd710c1 100644 --- a/extractor/src/services/extract.js +++ b/extractor/src/services/extract.js @@ -1,7 +1,7 @@ -import { extractData } from '../main-extractor.js'; -import { isValidFile } from '../utils/fileValidator.js'; -import { validateSchema } from '../utils/schemaValidator.js'; -import { getTemplate } from './templates.js'; +import { extractData } from "../main-extractor.js"; +import { isValidFile } from "../utils/fileValidator.js"; +import { validateSchema } from "../utils/schemaValidator.js"; +import { getTemplate } from "./templates.js"; /** * Extracts data from a document based on a provided schema. @@ -14,39 +14,43 @@ import { getTemplate } from './templates.js'; * @returns {Promise} - The result of the extraction, including pages, extracted data, and file name. */ export async function extract({ file, schema, template, model, autoSchema }) { - try { - if (!file) { - throw new Error('File is required.'); - } + try { + if (!file) { + throw new Error("File is required."); + } - if (!isValidFile(file)) { - throw new Error('File must be a valid format: PDF, PNG, JPG, TXT, DOCX, or HTML.'); - } + if (!isValidFile(file)) { + throw new Error( + "File must be a valid format: PDF, PNG, JPG, TXT, DOCX, or HTML.", + ); + } - let finalSchema = null; - if (template) { - finalSchema = getTemplate(template); // Use pre-defined template - } else if (schema) { - const { isValid, errors } = validateSchema(schema); - if (!isValid) { - throw new Error(`Invalid schema format: ${errors.join(', ')}`); - } - finalSchema = schema; // Use custom schema - } else if (!autoSchema) { - // If neither schema nor template is provided and autoSchema is not enabled, throw an error. - throw new Error('You must provide a schema, template, or enable autoSchema.'); - } + let finalSchema = null; + if (template) { + finalSchema = getTemplate(template); // Use pre-defined template + } else if (schema) { + const { isValid, errors } = validateSchema(schema); + if (!isValid) { + throw new Error(`Invalid schema format: ${errors.join(", ")}`); + } + finalSchema = schema; // Use custom schema + } else if (!autoSchema) { + // If neither schema nor template is provided and autoSchema is not enabled, throw an error. + throw new Error( + "You must provide a schema, template, or enable autoSchema.", + ); + } - const result = await extractData(file, finalSchema, model, autoSchema); + const result = await extractData(file, finalSchema, model, autoSchema); - return { - success: true, - pages: result.totalPages, - data: result.event, - fileName: result.fileName - }; - } catch (error) { - console.error('Error processing document:', error); - throw new Error(`Failed to process document: ${error.message}`); - } + return { + success: true, + pages: result.totalPages, + data: result.event, + fileName: result.fileName, + }; + } catch (error) { + console.error("Error processing document:", error); + throw new Error(`Failed to process document: ${error.message}`); + } } diff --git a/extractor/src/services/formatter.js b/extractor/src/services/formatter.js index 07c395b..900cdd5 100644 --- a/extractor/src/services/formatter.js +++ b/extractor/src/services/formatter.js @@ -1,6 +1,6 @@ -import { convertFile } from '../converter.js'; -import { isPdfFile } from '../utils/pdfValidator.js'; -import { convertToText } from '../utils/convertToText.js'; +import { convertFile } from "../converter.js"; +import { convertToText } from "../utils/convertToText.js"; +import { isPdfFile } from "../utils/pdfValidator.js"; /** * Extracts markdown content from a PDF. @@ -10,26 +10,26 @@ import { convertToText } from '../utils/convertToText.js'; * @returns {Promise} - The markdown content. */ const getMarkdown = async ({ file, model }) => { - try { - if (!file) { - throw new Error('File is required.'); - } - - if (!isPdfFile(file)) { - throw new Error('File must be a PDF.'); - } + try { + if (!file) { + throw new Error("File is required."); + } - const { markdown } = await convertFile(file, model); + if (!isPdfFile(file)) { + throw new Error("File must be a PDF."); + } - if (!markdown) { - throw new Error("Failed to extract markdown."); - } + const { markdown } = await convertFile(file, model); - return markdown; - } catch (error) { - console.error("Error extracting markdown:", error); - throw error; - } + if (!markdown) { + throw new Error("Failed to extract markdown."); + } + + return markdown; + } catch (error) { + console.error("Error extracting markdown:", error); + throw error; + } }; /** @@ -40,19 +40,19 @@ const getMarkdown = async ({ file, model }) => { * @returns {Promise} - The plain text content. */ const getPlainText = async ({ file, model }) => { - try { - const markdown = await getMarkdown({ file, model }); - return convertToText(markdown); - } catch (error) { - console.error("Error extracting plain text:", error); - throw error; - } + try { + const markdown = await getMarkdown({ file, model }); + return convertToText(markdown); + } catch (error) { + console.error("Error extracting plain text:", error); + throw error; + } }; /** * Formatter object for various formats. */ export const formatter = { - markdown: getMarkdown, - plaintext: getPlainText, + markdown: getMarkdown, + plaintext: getPlainText, }; diff --git a/extractor/src/services/templates.js b/extractor/src/services/templates.js index dea34b5..95b3f86 100644 --- a/extractor/src/services/templates.js +++ b/extractor/src/services/templates.js @@ -1,21 +1,21 @@ -import fs from 'fs'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; +import fs from "node:fs"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const templatesDirectory = path.resolve(__dirname, '../templates'); +const templatesDirectory = path.resolve(__dirname, "../templates"); /** * Lists all available templates. * @returns {string[]} - Array of template names. */ export function listTemplates() { - return fs - .readdirSync(templatesDirectory) - .filter((file) => file.endsWith('.json')) - .map((file) => file.replace('.json', '')); + return fs + .readdirSync(templatesDirectory) + .filter((file) => file.endsWith(".json")) + .map((file) => file.replace(".json", "")); } /** @@ -25,17 +25,17 @@ export function listTemplates() { * @throws {Error} - If the template is not found. */ export function getTemplate(name) { - const templatePath = path.join(templatesDirectory, `${name}.json`); - if (!fs.existsSync(templatePath)) { - throw new Error(`Template "${name}" not found`); - } - return JSON.parse(fs.readFileSync(templatePath, 'utf8')); + const templatePath = path.join(templatesDirectory, `${name}.json`); + if (!fs.existsSync(templatePath)) { + throw new Error(`Template "${name}" not found`); + } + return JSON.parse(fs.readFileSync(templatePath, "utf8")); } /** * Exports available templates. */ export const templates = { - list: listTemplates, - get: getTemplate, + list: listTemplates, + get: getTemplate, }; diff --git a/extractor/src/utils/autogenerateSchema.js b/extractor/src/utils/autogenerateSchema.js index 4fca4d0..c1d6eef 100644 --- a/extractor/src/utils/autogenerateSchema.js +++ b/extractor/src/utils/autogenerateSchema.js @@ -10,16 +10,16 @@ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); * @returns {Array} - Cleaned schema fields. */ const cleanSchemaFields = (fields) => { - return fields.map((field) => { - if (field.children && field.children.length === 0) { - // Remove empty children arrays - delete field.children; - } else if (field.children) { - // Recursively clean nested children - field.children = cleanSchemaFields(field.children); - } - return field; - }); + return fields.map((field) => { + if (field.children && field.children.length === 0) { + // Remove empty children arrays + field.children = undefined; + } else if (field.children) { + // Recursively clean nested children + field.children = cleanSchemaFields(field.children); + } + return field; + }); }; /** @@ -28,42 +28,42 @@ const cleanSchemaFields = (fields) => { * @returns {Promise} - The auto-generated schema. */ export const autogenerateSchema = async (markdown) => { - const prompt = ` + const prompt = ` Read the following markdown content and generate a schema of useful structured data that can be extracted from it. Follow these rules strictly: - The \`children\` field **must only be present if the \`type\` is \`object\` or \`array\`. It should never exist for other types. - \`description\` fields should be concise, no longer than one sentence. """${markdown}""" `; - try { - // Define schema validation using Zod - const SchemaField = z.lazy(() => - z.object({ - name: z.string(), - type: z.enum(["string", "number", "array", "object"]), - description: z.string().optional(), - children: z.array(SchemaField).optional(), - }) - ); + try { + // Define schema validation using Zod + const SchemaField = z.lazy(() => + z.object({ + name: z.string(), + type: z.enum(["string", "number", "array", "object"]), + description: z.string().optional(), + children: z.array(SchemaField).optional(), + }), + ); - const Schema = z.object({ - fields: z.array(SchemaField), - }); + const Schema = z.object({ + fields: z.array(SchemaField), + }); - // Call OpenAI to generate schema - const completion = await openai.beta.chat.completions.parse({ - model: "gpt-4o-2024-08-06", // Use the appropriate model - messages: [{ role: "user", content: prompt }], - response_format: zodResponseFormat(Schema, "event"), - }); + // Call OpenAI to generate schema + const completion = await openai.beta.chat.completions.parse({ + model: "gpt-4o-2024-08-06", // Use the appropriate model + messages: [{ role: "user", content: prompt }], + response_format: zodResponseFormat(Schema, "event"), + }); - // Parse and clean the schema - const event = completion.choices[0].message.parsed; - const cleanedFields = cleanSchemaFields(event.fields); + // Parse and clean the schema + const event = completion.choices[0].message.parsed; + const cleanedFields = cleanSchemaFields(event.fields); - return cleanedFields; - } catch (error) { - console.error("Error auto generating schema:", error); - throw error; - } + return cleanedFields; + } catch (error) { + console.error("Error auto generating schema:", error); + throw error; + } }; diff --git a/extractor/src/utils/convertToText.js b/extractor/src/utils/convertToText.js index 8366f31..6c13ea1 100644 --- a/extractor/src/utils/convertToText.js +++ b/extractor/src/utils/convertToText.js @@ -1,22 +1,21 @@ export const convertToText = (markdown) => { - if (!markdown || typeof markdown !== "string") { - throw new Error("Valid markdown content is required."); - } - - // Strip markdown syntax and handle tables - const plainText = markdown - .replace(/(\*\*|__)(.*?)\1/g, "$2") // Bold - .replace(/(\*|_)(.*?)\1/g, "$2") // Italic - .replace(/(#+\s)/g, "") // Headings - .replace(/\[(.*?)\]\(.*?\)/g, "$1") // Links - .replace(/!\[(.*?)\]\(.*?\)/g, "$1") // Images - .replace(/(```.*?\n[\s\S]*?\n```|`.*?`)/g, "") // Code blocks/inline - .replace(/>+/g, "") // Blockquotes - .replace(/\n{2,}/g, "\n") // Excess newlines - .replace(/\|([^|]*)\|/g, (_, row) => row.trim()) // Table rows - .replace(/-+/g, "") // Table dividers (---|---) - .trim(); - - return plainText; - }; - \ No newline at end of file + if (!markdown || typeof markdown !== "string") { + throw new Error("Valid markdown content is required."); + } + + // Strip markdown syntax and handle tables + const plainText = markdown + .replace(/(\*\*|__)(.*?)\1/g, "$2") // Bold + .replace(/(\*|_)(.*?)\1/g, "$2") // Italic + .replace(/(#+\s)/g, "") // Headings + .replace(/\[(.*?)\]\(.*?\)/g, "$1") // Links + .replace(/!\[(.*?)\]\(.*?\)/g, "$1") // Images + .replace(/(```.*?\n[\s\S]*?\n```|`.*?`)/g, "") // Code blocks/inline + .replace(/>+/g, "") // Blockquotes + .replace(/\n{2,}/g, "\n") // Excess newlines + .replace(/\|([^|]*)\|/g, (_, row) => row.trim()) // Table rows + .replace(/-+/g, "") // Table dividers (---|---) + .trim(); + + return plainText; +}; diff --git a/extractor/src/utils/convertToZodSchema.js b/extractor/src/utils/convertToZodSchema.js index 6c9029c..24d7099 100644 --- a/extractor/src/utils/convertToZodSchema.js +++ b/extractor/src/utils/convertToZodSchema.js @@ -1,4 +1,4 @@ -import { z } from 'zod'; +import { z } from "zod"; /** * Converts an array of type definitions into a Zod schema. @@ -6,56 +6,62 @@ import { z } from 'zod'; * @returns {ZodObject} - A Zod object schema. */ export const convertToZodSchema = (object) => { - const createZodSchema = (obj) => { - const schema = {}; + const createZodSchema = (obj) => { + const schema = {}; - obj.forEach((item) => { - let zodType; - switch (item.type) { - case 'string': - zodType = z.string(); - break; - case 'number': - zodType = z.number(); - break; - case 'boolean': - zodType = z.boolean(); - break; - case 'array': - if (item.children && item.children.length > 0) { - const arraySchema = createZodSchema(item.children); - zodType = z.array(z.object(arraySchema)); - } else { - throw new Error(`Invalid or unsupported "array" type definition for ${item.name}`); - } - break; - case 'object': - if (item.children) { - zodType = z.object(createZodSchema(item.children)); - } else { - throw new Error(`Invalid "object" type definition for ${item.name}`); - } - break; - case 'enum': - if (item.values && Array.isArray(item.values)) { - zodType = z.enum(item.values); - } else { - throw new Error(`Invalid "enum" type definition for ${item.name}`); - } - break; - default: - throw new Error(`Unsupported type "${item.type}" for field ${item.name}`); - } + for (const item of obj) { + let zodType; + switch (item.type) { + case "string": + zodType = z.string(); + break; + case "number": + zodType = z.number(); + break; + case "boolean": + zodType = z.boolean(); + break; + case "array": + if (item.children && item.children.length > 0) { + const arraySchema = createZodSchema(item.children); + zodType = z.array(z.object(arraySchema)); + } else { + throw new Error( + `Invalid or unsupported "array" type definition for ${item.name}`, + ); + } + break; + case "object": + if (item.children) { + zodType = z.object(createZodSchema(item.children)); + } else { + throw new Error( + `Invalid "object" type definition for ${item.name}`, + ); + } + break; + case "enum": + if (item.values && Array.isArray(item.values)) { + zodType = z.enum(item.values); + } else { + throw new Error(`Invalid "enum" type definition for ${item.name}`); + } + break; + default: + throw new Error( + `Unsupported type "${item.type}" for field ${item.name}`, + ); + } - if (item.description) { - zodType = zodType.describe(item.description); - } + if (item.description) { + zodType = zodType.describe(item.description); + } - schema[item.name] = zodType; - }); + schema[item.name] = zodType; + } - return schema; - }; + return schema; + }; - return z.object(createZodSchema(object)); + return z.object(createZodSchema(object)); }; diff --git a/extractor/src/utils/fileValidator.js b/extractor/src/utils/fileValidator.js index 65fd347..60cdfd9 100644 --- a/extractor/src/utils/fileValidator.js +++ b/extractor/src/utils/fileValidator.js @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios from "axios"; /** * Function to check if a file is valid based on its URL or MIME type @@ -6,31 +6,44 @@ import axios from 'axios'; * @returns {Promise} - Resolves to true if the file is valid, false otherwise */ export async function isValidFile(file) { - const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg', 'txt', 'docx', 'html']; - const allowedMimeTypes = { - pdf: 'application/pdf', - png: 'image/png', - jpg: 'image/jpeg', - jpeg: 'image/jpeg', - txt: 'text/plain', - docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - html: 'text/html', - }; + const allowedExtensions = [ + "pdf", + "png", + "jpg", + "jpeg", + "txt", + "docx", + "html", + ]; + const allowedMimeTypes = { + pdf: "application/pdf", + png: "image/png", + jpg: "image/jpeg", + jpeg: "image/jpeg", + txt: "text/plain", + docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + html: "text/html", + }; - const urlPath = new URL(file).pathname; - const extensionRegex = new RegExp(`\\.(${allowedExtensions.join('|')})$`, 'i'); + const urlPath = new URL(file).pathname; + const extensionRegex = new RegExp( + `\\.(${allowedExtensions.join("|")})$`, + "i", + ); - if (!extensionRegex.test(urlPath)) { - return false; - } + if (!extensionRegex.test(urlPath)) { + return false; + } - // Optional: Check the MIME type if query parameters are used - try { - const response = await axios.head(file); - const contentType = response.headers['content-type']; - return Object.values(allowedMimeTypes).some(mime => contentType.startsWith(mime)); - } catch (error) { - console.error('Error checking MIME type:', error); - return false; - } + // Optional: Check the MIME type if query parameters are used + try { + const response = await axios.head(file); + const contentType = response.headers["content-type"]; + return Object.values(allowedMimeTypes).some((mime) => + contentType.startsWith(mime), + ); + } catch (error) { + console.error("Error checking MIME type:", error); + return false; + } } diff --git a/extractor/src/utils/generateMarkdown.js b/extractor/src/utils/generateMarkdown.js index 91a9a84..c2d6508 100644 --- a/extractor/src/utils/generateMarkdown.js +++ b/extractor/src/utils/generateMarkdown.js @@ -1,12 +1,14 @@ export const generateMarkdownDocument = async (pages) => { - try { - // Combine all markdown pages into a single string - const markdownContent = pages.map((page) => page.content).join("\n\n---\n\n"); + try { + // Combine all markdown pages into a single string + const markdownContent = pages + .map((page) => page.content) + .join("\n\n---\n\n"); - // Return the combined markdown string directly - return markdownContent; - } catch (error) { - console.error('Error generating markdown:', error); - throw error; - } + // Return the combined markdown string directly + return markdownContent; + } catch (error) { + console.error("Error generating markdown:", error); + throw error; + } }; diff --git a/extractor/src/utils/pdfValidator.js b/extractor/src/utils/pdfValidator.js index f1dc67e..ddfc65f 100644 --- a/extractor/src/utils/pdfValidator.js +++ b/extractor/src/utils/pdfValidator.js @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios from "axios"; /** * Function to check if a file is a PDF based on its URL or MIME type @@ -6,19 +6,19 @@ import axios from 'axios'; * @returns {Promise} - Resolves to true if the file is a PDF, false otherwise */ export async function isPdfFile(file) { - const urlPath = new URL(file).pathname; - const pdfExtensionRegex = /\.pdf$/i; - if (pdfExtensionRegex.test(urlPath)) { - return true; - } + const urlPath = new URL(file).pathname; + const pdfExtensionRegex = /\.pdf$/i; + if (pdfExtensionRegex.test(urlPath)) { + return true; + } - // Optional: Check the MIME type if query parameters are used - try { - const response = await axios.head(file); - const contentType = response.headers['content-type']; - return contentType === 'application/pdf'; - } catch (error) { - console.error('Error checking MIME type:', error); - return false; - } + // Optional: Check the MIME type if query parameters are used + try { + const response = await axios.head(file); + const contentType = response.headers["content-type"]; + return contentType === "application/pdf"; + } catch (error) { + console.error("Error checking MIME type:", error); + return false; + } } diff --git a/extractor/src/utils/schemaValidator.js b/extractor/src/utils/schemaValidator.js index c1d0f98..aa90c32 100644 --- a/extractor/src/utils/schemaValidator.js +++ b/extractor/src/utils/schemaValidator.js @@ -4,55 +4,80 @@ * @returns {Object} - { isValid: boolean, errors: Array } */ export function validateSchema(schema) { - const validTypes = ["string", "number", "array", "object", "boolean", "enum"]; - let errors = []; - - if (!Array.isArray(schema)) { - errors.push("Schema must be an array."); - return { isValid: false, errors }; - } - - function validateField(field, path) { - if (!field.hasOwnProperty("name") || typeof field.name !== "string" || field.name.trim() === "") { - errors.push(`"name" is required and should be a non-empty string at ${path}`); - } - - if (!field.hasOwnProperty("type") || !validTypes.includes(field.type)) { - errors.push(`"type" is required and must be one of ${validTypes.join(", ")} at ${path}`); - } - - if (!field.hasOwnProperty("description") || typeof field.description !== "string" || field.description.trim() === "") { - errors.push(`"description" is required and should be a non-empty string at ${path}`); - } - - // Additional checks for arrays - if (field.type === "array") { - if (!field.hasOwnProperty("children")) { - errors.push(`"children" property is required for arrays at ${path}`); - } else if (!Array.isArray(field.children) || field.children.length === 0) { - errors.push(`"children" must be a non-empty array at ${path}`); - } else { - // Recursively validate each child - field.children.forEach((child, index) => validateField(child, `${path}.children[${index}]`)); - } - } + const validTypes = ["string", "number", "array", "object", "boolean", "enum"]; + const errors = []; - // Additional checks for enum - if (field.type === "enum") { - if (!field.hasOwnProperty("values") || !Array.isArray(field.values) || field.values.length === 0) { - errors.push(`"values" is required and must be a non-empty array for enum at ${path}`); - } else if (!field.values.every((value) => typeof value === "string")) { - errors.push(`"values" for enum at ${path} must be an array of strings`); - } - } + if (!Array.isArray(schema)) { + errors.push("Schema must be an array."); + return { isValid: false, errors }; + } - } - - schema.forEach((field, index) => validateField(field, `schema[${index}]`)); - - return { - isValid: errors.length === 0, - errors, - }; - } - \ No newline at end of file + function validateField(field, path) { + if ( + !Object.prototype.hasOwnProperty.call(field, "name") || + typeof field.name !== "string" || + field.name.trim() === "" + ) { + errors.push( + `"name" is required and should be a non-empty string at ${path}`, + ); + } + + if ( + !Object.prototype.hasOwnProperty.call(field, "type") || + !validTypes.includes(field.type) + ) { + errors.push( + `"type" is required and must be one of ${validTypes.join(", ")} at ${path}`, + ); + } + if ( + !Object.prototype.hasOwnProperty.call(field, "description") || + typeof field.description !== "string" || + field.description.trim() === "" + ) { + errors.push( + `"description" is required and should be a non-empty string at ${path}`, + ); + } + + // Additional checks for arrays + if (field.type === "array") { + if (!Object.prototype.hasOwnProperty.call(field, "children")) { + errors.push(`"children" property is required for arrays at ${path}`); + } else if ( + !Array.isArray(field.children) || + field.children.length === 0 + ) { + errors.push(`"children" must be a non-empty array at ${path}`); + } else { + // Recursively validate each child + field.children.forEach((child, index) => + validateField(child, `${path}.children[${index}]`), + ); + } + } + + // Additional checks for enum + if (field.type === "enum") { + if ( + !Object.prototype.hasOwnProperty.call(field, "values") || + !Array.isArray(field.values) || + field.values.length === 0 + ) { + errors.push( + `"values" is required and must be a non-empty array for enum at ${path}`, + ); + } else if (!field.values.every((value) => typeof value === "string")) { + errors.push(`"values" for enum at ${path} must be an array of strings`); + } + } + } + + schema.forEach((field, index) => validateField(field, `schema[${index}]`)); + + return { + isValid: errors.length === 0, + errors, + }; +} diff --git a/package-lock.json b/package-lock.json index 32d4bbd..a2b0afe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1992 +1,1992 @@ { - "name": "documind", - "version": "1.0.12", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "documind", - "version": "1.0.12", - "license": "AGPL-3.0", - "workspaces": [ - "core", - "extractor" - ], - "dependencies": { - "axios": "^1.7.7", - "child_process": "^1.0.2", - "core": "file:./core", - "dotenv": "^16.4.5", - "fs-extra": "^11.2.0", - "libreoffice-convert": "^1.6.0", - "mime-types": "^2.1.35", - "openai": "^4.68.4", - "os": "^0.1.2", - "p-limit": "^3.1.0", - "path": "^0.12.7", - "pdf-lib": "^1.17.1", - "pdf2pic": "^3.1.1", - "sharp": "^0.33.5", - "tesseract.js": "^5.1.1", - "util": "^0.12.5", - "uuid": "^11.0.2", - "zod": "^3.23.8" - } - }, - "core": { - "version": "1.0.0", - "dependencies": { - "axios": "^1.7.2", - "child_process": "^1.0.2", - "dotenv": "^16.4.5", - "fs-extra": "^11.2.0", - "libreoffice-convert": "^1.6.0", - "mime-types": "^2.1.35", - "openai": "^4.68.4", - "os": "^0.1.2", - "p-limit": "^3.1.0", - "path": "^0.12.7", - "pdf2pic": "^3.1.1", - "sharp": "^0.33.5", - "tesseract.js": "^5.1.1", - "util": "^0.12.5", - "zod": "^3.23.8" - }, - "devDependencies": { - "@types/fs-extra": "^11.0.4", - "@types/mime-types": "^2.1.4", - "@types/node": "^20.14.11", - "typescript": "^5.6.3" - } - }, - "core/node_modules/@types/node": { - "version": "20.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", - "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "extractor": { - "version": "1.0.0", - "dependencies": { - "axios": "^1.7.7", - "core": "*", - "dotenv": "^16.4.5", - "openai": "^4.68.4", - "pdf-lib": "^1.17.1", - "uuid": "^11.0.2", - "zod": "^3.23.8" - }, - "devDependencies": { - "nodemon": "^3.1.7" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@pdf-lib/standard-fonts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", - "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", - "license": "MIT", - "dependencies": { - "pako": "^1.0.6" - } - }, - "node_modules/@pdf-lib/upng": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", - "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", - "license": "MIT", - "dependencies": { - "pako": "^1.0.10" - } - }, - "node_modules/@types/fs-extra": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", - "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/jsonfile": "*", - "@types/node": "*" - } - }, - "node_modules/@types/jsonfile": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", - "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.8" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-parallel": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", - "integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w==", - "license": "MIT" - }, - "node_modules/array-series": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", - "integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg==", - "license": "MIT" - }, - "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" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bmp-js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", - "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", - "license": "MIT" - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", - "license": "ISC" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core": { - "resolved": "core", - "link": true - }, - "node_modules/cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", - "license": "MIT", - "dependencies": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "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", - "engines": { - "node": ">=6" - } - }, - "node_modules/extractor": { - "resolved": "extractor", - "link": true - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "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", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gm": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/gm/-/gm-1.25.0.tgz", - "integrity": "sha512-4kKdWXTtgQ4biIo7hZA396HT062nDVVHPjQcurNZ3o/voYN+o5FUC5kOwuORbpExp3XbTJ3SU7iRipiIhQtovw==", - "license": "MIT", - "dependencies": { - "array-parallel": "~0.1.3", - "array-series": "~0.1.5", - "cross-spawn": "^4.0.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "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" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/idb-keyval": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", - "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", - "license": "Apache-2.0" - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-electron": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", - "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", - "license": "MIT" - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/libreoffice-convert": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/libreoffice-convert/-/libreoffice-convert-1.6.0.tgz", - "integrity": "sha512-hb8EaqIBYnwXAOYeqjVA1JysOGI1QwzUvf0qSgLtH7Rnc9mZxGNB5IBxVO81FvAA1Hj+/6ItdeEwAV/sGgid1Q==", - "license": "MIT", - "dependencies": { - "async": "^3.2.3", - "tmp": "^0.2.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "license": "ISC", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/nodemon": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", - "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/nodemon/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/openai": { - "version": "4.72.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.72.0.tgz", - "integrity": "sha512-hFqG9BWCs7L7ifrhJXw7mJXmUBr7d9N6If3J9563o0jfwVA4wFANFDDaOIWFdgDdwgCXg5emf0Q+LoLCGszQYA==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.64", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz", - "integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - }, - "node_modules/opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "license": "MIT", - "bin": { - "opencollective-postinstall": "index.js" - } - }, - "node_modules/os": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", - "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==", - "license": "MIT" - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", - "license": "MIT", - "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, - "node_modules/path/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "license": "ISC" - }, - "node_modules/path/node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "license": "MIT", - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/pdf-lib": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", - "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", - "license": "MIT", - "dependencies": { - "@pdf-lib/standard-fonts": "^1.0.0", - "@pdf-lib/upng": "^1.0.1", - "pako": "^1.0.11", - "tslib": "^1.11.1" - } - }, - "node_modules/pdf2pic": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/pdf2pic/-/pdf2pic-3.1.3.tgz", - "integrity": "sha512-KbW4Qb7iHw2fBRWtA9FTc4pZg9cokiFIzc6cE7dzelTrhXWolfQuG1fYVC0E2BRmK/w7xfBjQ+OEsPZPO3QEew==", - "license": "MIT", - "dependencies": { - "gm": "^1.25.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "paypal", - "url": "https://www.paypal.me/yakovmeister" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/proxy-from-env": { - "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" - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "license": "ISC" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tesseract.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-5.1.1.tgz", - "integrity": "sha512-lzVl/Ar3P3zhpUT31NjqeCo1f+D5+YfpZ5J62eo2S14QNVOmHBTtbchHm/YAbOOOzCegFnKf4B3Qih9LuldcYQ==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "bmp-js": "^0.1.0", - "idb-keyval": "^6.2.0", - "is-electron": "^2.2.2", - "is-url": "^1.2.4", - "node-fetch": "^2.6.9", - "opencollective-postinstall": "^2.0.3", - "regenerator-runtime": "^0.13.3", - "tesseract.js-core": "^5.1.1", - "wasm-feature-detect": "^1.2.11", - "zlibjs": "^0.3.1" - } - }, - "node_modules/tesseract.js-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-5.1.1.tgz", - "integrity": "sha512-KX3bYSU5iGcO1XJa+QGPbi+Zjo2qq6eBhNjSGR5E5q0JtzkoipJKOUQD7ph8kFyteCEfEQ0maWLu8MCXtvX5uQ==", - "license": "Apache-2.0" - }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/uuid": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", - "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/wasm-feature-detect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", - "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", - "license": "Apache-2.0" - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zlibjs": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", - "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } + "name": "documind", + "version": "1.0.12", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "documind", + "version": "1.0.12", + "license": "AGPL-3.0", + "workspaces": [ + "core", + "extractor" + ], + "dependencies": { + "axios": "^1.7.7", + "child_process": "^1.0.2", + "core": "file:./core", + "dotenv": "^16.4.5", + "fs-extra": "^11.2.0", + "libreoffice-convert": "^1.6.0", + "mime-types": "^2.1.35", + "openai": "^4.68.4", + "os": "^0.1.2", + "p-limit": "^3.1.0", + "path": "^0.12.7", + "pdf-lib": "^1.17.1", + "pdf2pic": "^3.1.1", + "sharp": "^0.33.5", + "tesseract.js": "^5.1.1", + "util": "^0.12.5", + "uuid": "^11.0.2", + "zod": "^3.23.8" + } + }, + "core": { + "version": "1.0.0", + "dependencies": { + "axios": "^1.7.2", + "child_process": "^1.0.2", + "dotenv": "^16.4.5", + "fs-extra": "^11.2.0", + "libreoffice-convert": "^1.6.0", + "mime-types": "^2.1.35", + "openai": "^4.68.4", + "os": "^0.1.2", + "p-limit": "^3.1.0", + "path": "^0.12.7", + "pdf2pic": "^3.1.1", + "sharp": "^0.33.5", + "tesseract.js": "^5.1.1", + "util": "^0.12.5", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/mime-types": "^2.1.4", + "@types/node": "^20.14.11", + "typescript": "^5.6.3" + } + }, + "core/node_modules/@types/node": { + "version": "20.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", + "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "extractor": { + "version": "1.0.0", + "dependencies": { + "axios": "^1.7.7", + "core": "*", + "dotenv": "^16.4.5", + "openai": "^4.68.4", + "pdf-lib": "^1.17.1", + "uuid": "^11.0.2", + "zod": "^3.23.8" + }, + "devDependencies": { + "nodemon": "^3.1.7" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-parallel": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", + "integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w==", + "license": "MIT" + }, + "node_modules/array-series": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", + "integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg==", + "license": "MIT" + }, + "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" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", + "license": "ISC" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core": { + "resolved": "core", + "link": true + }, + "node_modules/cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "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", + "engines": { + "node": ">=6" + } + }, + "node_modules/extractor": { + "resolved": "extractor", + "link": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gm": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/gm/-/gm-1.25.0.tgz", + "integrity": "sha512-4kKdWXTtgQ4biIo7hZA396HT062nDVVHPjQcurNZ3o/voYN+o5FUC5kOwuORbpExp3XbTJ3SU7iRipiIhQtovw==", + "license": "MIT", + "dependencies": { + "array-parallel": "~0.1.3", + "array-series": "~0.1.5", + "cross-spawn": "^4.0.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "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" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "license": "Apache-2.0" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-electron": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", + "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/libreoffice-convert": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/libreoffice-convert/-/libreoffice-convert-1.6.0.tgz", + "integrity": "sha512-hb8EaqIBYnwXAOYeqjVA1JysOGI1QwzUvf0qSgLtH7Rnc9mZxGNB5IBxVO81FvAA1Hj+/6ItdeEwAV/sGgid1Q==", + "license": "MIT", + "dependencies": { + "async": "^3.2.3", + "tmp": "^0.2.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/openai": { + "version": "4.72.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.72.0.tgz", + "integrity": "sha512-hFqG9BWCs7L7ifrhJXw7mJXmUBr7d9N6If3J9563o0jfwVA4wFANFDDaOIWFdgDdwgCXg5emf0Q+LoLCGszQYA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.64", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.64.tgz", + "integrity": "sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/os": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", + "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "license": "MIT", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/path/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "license": "ISC" + }, + "node_modules/path/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "license": "MIT", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf2pic": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pdf2pic/-/pdf2pic-3.1.3.tgz", + "integrity": "sha512-KbW4Qb7iHw2fBRWtA9FTc4pZg9cokiFIzc6cE7dzelTrhXWolfQuG1fYVC0E2BRmK/w7xfBjQ+OEsPZPO3QEew==", + "license": "MIT", + "dependencies": { + "gm": "^1.25.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "paypal", + "url": "https://www.paypal.me/yakovmeister" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proxy-from-env": { + "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" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "license": "ISC" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tesseract.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-5.1.1.tgz", + "integrity": "sha512-lzVl/Ar3P3zhpUT31NjqeCo1f+D5+YfpZ5J62eo2S14QNVOmHBTtbchHm/YAbOOOzCegFnKf4B3Qih9LuldcYQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-electron": "^2.2.2", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^5.1.1", + "wasm-feature-detect": "^1.2.11", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-5.1.1.tgz", + "integrity": "sha512-KX3bYSU5iGcO1XJa+QGPbi+Zjo2qq6eBhNjSGR5E5q0JtzkoipJKOUQD7ph8kFyteCEfEQ0maWLu8MCXtvX5uQ==", + "license": "Apache-2.0" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/uuid": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "license": "Apache-2.0" + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } } diff --git a/package.json b/package.json index 4329b71..6c693b0 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,46 @@ { - "name": "documind", - "version": "1.0.12", - "description": "AI-powered document extraction.", - "scripts": { - "start": "npm run start --workspace=extractor", - "dev": "npm run dev --workspace=extractor" - }, - "workspaces": [ - "core", - "extractor" - ], - "main": "./extractor/src/index.js", - "keywords": [ - "document", - "extraction", - "llm", - "ai" - ], - "author": "Tami", - "license": "AGPL-3.0", - "repository": { - "type": "git", - "url": "https://github.com/DocumindHQ/documind.git" - }, - "dependencies": { - "axios": "^1.7.7", - "child_process": "^1.0.2", - "core": "file:./core", - "dotenv": "^16.4.5", - "fs-extra": "^11.2.0", - "libreoffice-convert": "^1.6.0", - "mime-types": "^2.1.35", - "openai": "^4.68.4", - "os": "^0.1.2", - "p-limit": "^3.1.0", - "path": "^0.12.7", - "pdf-lib": "^1.17.1", - "pdf2pic": "^3.1.1", - "sharp": "^0.33.5", - "tesseract.js": "^5.1.1", - "util": "^0.12.5", - "uuid": "^11.0.2", - "zod": "^3.23.8" - } + "name": "documind", + "version": "1.0.12", + "description": "AI-powered document extraction.", + "scripts": { + "start": "npm run start --workspace=extractor", + "dev": "npm run dev --workspace=extractor" + }, + "workspaces": [ + "core", + "extractor" + ], + "main": "./extractor/src/index.js", + "keywords": [ + "document", + "extraction", + "llm", + "ai" + ], + "author": "Tami", + "license": "AGPL-3.0", + "repository": { + "type": "git", + "url": "https://github.com/DocumindHQ/documind.git" + }, + "dependencies": { + "axios": "^1.7.7", + "child_process": "^1.0.2", + "core": "file:./core", + "dotenv": "^16.4.5", + "fs-extra": "^11.2.0", + "libreoffice-convert": "^1.6.0", + "mime-types": "^2.1.35", + "openai": "^4.68.4", + "os": "^0.1.2", + "p-limit": "^3.1.0", + "path": "^0.12.7", + "pdf-lib": "^1.17.1", + "pdf2pic": "^3.1.1", + "sharp": "^0.33.5", + "tesseract.js": "^5.1.1", + "util": "^0.12.5", + "uuid": "^11.0.2", + "zod": "^3.23.8" + } } From 33e8a7be9310332cf4a0a08ac15b9d0a263b91be Mon Sep 17 00:00:00 2001 From: Giulio Zanchetta Date: Sat, 1 Feb 2025 14:25:42 +0100 Subject: [PATCH 2/4] Add parseModel option to extraction function - Introduced a new parameter `parseModel` for better data parsing. - Updated the function signature to include the new parameter. - Modified the call to `extractData` to pass along `parseModel`. --- extractor/src/services/extract.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/extractor/src/services/extract.js b/extractor/src/services/extract.js index dd710c1..bcd0826 100644 --- a/extractor/src/services/extract.js +++ b/extractor/src/services/extract.js @@ -10,10 +10,18 @@ import { getTemplate } from "./templates.js"; * @param {object} options.schema - The schema definition for data extraction. * @param {string} [options.template] - Name of a pre-defined template. * @param {string} [options.model] - The llm model to use if a base url is set. + * @param {string} [options.parseModel] - The llm model to use for parsing the data. * @param {boolean} [options.autoSchema] - Option to auto-generate the schema. * @returns {Promise} - The result of the extraction, including pages, extracted data, and file name. */ -export async function extract({ file, schema, template, model, autoSchema }) { +export async function extract({ + file, + schema, + template, + model, + parseModel, + autoSchema, +}) { try { if (!file) { throw new Error("File is required."); @@ -41,7 +49,13 @@ export async function extract({ file, schema, template, model, autoSchema }) { ); } - const result = await extractData(file, finalSchema, model, autoSchema); + const result = await extractData( + file, + finalSchema, + model, + parseModel, + autoSchema, + ); return { success: true, From 6b301c216bd10eb4b2d8ed1671c78c5d4926d358 Mon Sep 17 00:00:00 2001 From: Giulio Zanchetta Date: Sat, 1 Feb 2025 14:39:52 +0100 Subject: [PATCH 3/4] Add additional prompt support for data extraction - Introduced `additionalPrompt` parameter for custom prompts. - Updated extraction rules to preserve language and terminology. - Modified schema generation to accept a model parameter. - Added validation for the `additionalPrompt` type. --- extractor/src/main-extractor.js | 13 +++++++++++-- extractor/src/services/extract.js | 7 +++++++ extractor/src/utils/autogenerateSchema.js | 5 +++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/extractor/src/main-extractor.js b/extractor/src/main-extractor.js index a9c73c2..2294ad8 100644 --- a/extractor/src/main-extractor.js +++ b/extractor/src/main-extractor.js @@ -12,6 +12,7 @@ export const extractData = async ( model, parseModel, autoSchema, + additionalPrompt, ) => { const prompt = ` You are an expert in structured data extraction. Your task is to extract information from unstructured content and transform it into the specified structure. Follow these rules strictly: @@ -19,7 +20,15 @@ export const extractData = async ( 1. Handle Missing or Undetermined Data: - If any field's information is missing, unknown, or cannot be determined, return its value as null. - **Do not use substitutes such as "unknown," "missing," or any other placeholder for missing or unknown data. The value **must** always be explicitly null. -`; + + 2. Language and Text: + - Preserve the original language of the content + - Maintain original terminology and technical terms + - Keep proper nouns unchanged + + + ${additionalPrompt} + `; try { const { markdown, totalPages, fileName } = await convertFile( @@ -30,7 +39,7 @@ export const extractData = async ( // Determine which schema to use let finalSchema = schemaDefinition; if (autoSchema) { - finalSchema = await autogenerateSchema(markdown); + finalSchema = await autogenerateSchema(markdown, parseModel); if (!finalSchema) { throw new Error("Failed to auto-generate schema."); diff --git a/extractor/src/services/extract.js b/extractor/src/services/extract.js index bcd0826..d5ba0d0 100644 --- a/extractor/src/services/extract.js +++ b/extractor/src/services/extract.js @@ -12,6 +12,7 @@ import { getTemplate } from "./templates.js"; * @param {string} [options.model] - The llm model to use if a base url is set. * @param {string} [options.parseModel] - The llm model to use for parsing the data. * @param {boolean} [options.autoSchema] - Option to auto-generate the schema. + * @param {string} [options.additionalPrompt] - Additional prompt to use for the extraction. * @returns {Promise} - The result of the extraction, including pages, extracted data, and file name. */ export async function extract({ @@ -21,6 +22,7 @@ export async function extract({ model, parseModel, autoSchema, + additionalPrompt, }) { try { if (!file) { @@ -49,12 +51,17 @@ export async function extract({ ); } + if (additionalPrompt && typeof additionalPrompt !== "string") { + throw new Error("Additional prompt must be a string."); + } + const result = await extractData( file, finalSchema, model, parseModel, autoSchema, + additionalPrompt, ); return { diff --git a/extractor/src/utils/autogenerateSchema.js b/extractor/src/utils/autogenerateSchema.js index c1d6eef..9868d3c 100644 --- a/extractor/src/utils/autogenerateSchema.js +++ b/extractor/src/utils/autogenerateSchema.js @@ -25,9 +25,10 @@ const cleanSchemaFields = (fields) => { /** * Generates an auto schema from markdown content. * @param {string} markdown - The markdown content to generate the schema from. + * @param {string} model - The model to use for the schema generation. * @returns {Promise} - The auto-generated schema. */ -export const autogenerateSchema = async (markdown) => { +export const autogenerateSchema = async (markdown, model) => { const prompt = ` Read the following markdown content and generate a schema of useful structured data that can be extracted from it. Follow these rules strictly: - The \`children\` field **must only be present if the \`type\` is \`object\` or \`array\`. It should never exist for other types. @@ -52,7 +53,7 @@ Read the following markdown content and generate a schema of useful structured d // Call OpenAI to generate schema const completion = await openai.beta.chat.completions.parse({ - model: "gpt-4o-2024-08-06", // Use the appropriate model + model: model ?? "gpt-4o-2024-08-06", messages: [{ role: "user", content: prompt }], response_format: zodResponseFormat(Schema, "event"), }); From 0b18c41e0d97185973e384cca845097abbe309fc Mon Sep 17 00:00:00 2001 From: Giulio Zanchetta Date: Sat, 1 Feb 2025 14:41:54 +0100 Subject: [PATCH 4/4] Update build --- core/dist/index.d.ts | 2 +- core/dist/index.js | 368 ++++++++++++++++++++---------------------- core/dist/openAI.d.ts | 2 +- core/dist/openAI.js | 9 +- core/dist/types.d.ts | 1 + core/dist/types.js | 1 + core/dist/utils.d.ts | 2 +- core/dist/utils.js | 10 +- 8 files changed, 189 insertions(+), 206 deletions(-) diff --git a/core/dist/index.d.ts b/core/dist/index.d.ts index 6ec15e7..8f4c7c7 100644 --- a/core/dist/index.d.ts +++ b/core/dist/index.d.ts @@ -1,2 +1,2 @@ -import { DocumindArgs, DocumindOutput } from "./types"; +import { type DocumindArgs, type DocumindOutput } from "./types"; export declare const documind: ({ cleanup, concurrency, filePath, llmParams, maintainFormat, model, openaiAPIKey, outputDir, pagesToConvertAsImages, tempDir, }: DocumindArgs) => Promise; diff --git a/core/dist/index.js b/core/dist/index.js index 23664d2..df14c23 100644 --- a/core/dist/index.js +++ b/core/dist/index.js @@ -1,199 +1,183 @@ -var __importDefault = - (this && this.__importDefault) || - ((mod) => (mod && mod.__esModule ? mod : { default: mod })); +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.documind = void 0; -const utils_1 = require("./utils"); -const openAI_1 = require("./openAI"); -const types_1 = require("./types"); -const utils_2 = require("./utils"); -const fs_extra_1 = __importDefault(require("fs-extra")); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); +const fs_extra_1 = __importDefault(require("fs-extra")); const p_limit_1 = __importDefault(require("p-limit")); -const documind = async ({ - cleanup = true, - concurrency = 10, - filePath, - llmParams = {}, - maintainFormat = false, - model, //= ModelOptions.gpt_4o_mini, - openaiAPIKey = "", - outputDir, - pagesToConvertAsImages = -1, - tempDir = os_1.default.tmpdir(), -}) => { - const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; - const defaultModel = - model ?? - (baseUrl !== "https://api.openai.com/v1" - ? types_1.ModelOptions.llava // Default for custom base URL - : types_1.ModelOptions.gpt_4o_mini); // Default for OpenAI - let inputTokenCount = 0; - let outputTokenCount = 0; - let priorPage = ""; - const aggregatedMarkdown = []; - const startTime = new Date(); - llmParams = (0, utils_2.validateLLMParams)(llmParams); - // Validators - if (!openaiAPIKey || !openaiAPIKey.length) { - throw new Error("Missing OpenAI API key"); - } - if (!filePath || !filePath.length) { - throw new Error("Missing file path"); - } - // Ensure temp directory exists + create temp folder - const rand = Math.floor(1000 + Math.random() * 9000).toString(); - const tempDirectory = path_1.default.join( - tempDir || os_1.default.tmpdir(), - `documind-file-${rand}`, - ); - await fs_extra_1.default.ensureDir(tempDirectory); - // Download the PDF. Get file name. - const { extension, localPath } = await (0, utils_1.downloadFile)({ - filePath, - tempDir: tempDirectory, - }); - if (!localPath) throw "Failed to save file to local drive"; - // Sort the `pagesToConvertAsImages` array to make sure we use the right index - // for `formattedPages` as `pdf2pic` always returns images in order - if (Array.isArray(pagesToConvertAsImages)) { - pagesToConvertAsImages.sort((a, b) => a - b); - } - // Convert file to PDF if necessary - if (extension !== ".png") { - let pdfPath; - if (extension === ".pdf") { - pdfPath = localPath; - } else { - pdfPath = await (0, utils_1.convertFileToPdf)({ - extension, - localPath, - tempDir: tempDirectory, - }); - } - // Convert the file to a series of images - await (0, utils_1.convertPdfToImages)({ - localPath: pdfPath, - pagesToConvertAsImages, - tempDir: tempDirectory, - }); - } - const endOfPath = localPath.split("/")[localPath.split("/").length - 1]; - const rawFileName = endOfPath.split(".")[0]; - const fileName = rawFileName - .replace(/[^\w\s]/g, "") - .replace(/\s+/g, "_") - .toLowerCase() - .substring(0, 255); // Truncate file name to 255 characters to prevent ENAMETOOLONG errors - // Get list of converted images - const files = await fs_extra_1.default.readdir(tempDirectory); - const images = files.filter((file) => file.endsWith(".png")); - if (maintainFormat) { - // Use synchronous processing - for (const image of images) { - const imagePath = path_1.default.join(tempDirectory, image); - try { - const { content, inputTokens, outputTokens } = await (0, - openAI_1.getCompletion)({ - apiKey: openaiAPIKey, - imagePath, - llmParams, - maintainFormat, - model: defaultModel, - priorPage, - }); - const formattedMarkdown = (0, utils_1.formatMarkdown)(content); - inputTokenCount += inputTokens; - outputTokenCount += outputTokens; - // Update prior page to result from last processing step - priorPage = formattedMarkdown; - // Add all markdown results to array - aggregatedMarkdown.push(formattedMarkdown); - } catch (error) { - console.error(`Failed to process image ${image}:`, error); - throw error; - } - } - } else { - // Process in parallel with a limit on concurrent pages - const processPage = async (image) => { - const imagePath = path_1.default.join(tempDirectory, image); - try { - const { content, inputTokens, outputTokens } = await (0, - openAI_1.getCompletion)({ - apiKey: openaiAPIKey, - imagePath, - llmParams, - maintainFormat, - model: defaultModel, - priorPage, - }); - const formattedMarkdown = (0, utils_1.formatMarkdown)(content); - inputTokenCount += inputTokens; - outputTokenCount += outputTokens; - // Update prior page to result from last processing step - priorPage = formattedMarkdown; - // Add all markdown results to array - return formattedMarkdown; - } catch (error) { - console.error(`Failed to process image ${image}:`, error); - throw error; - } - }; - // Function to process pages with concurrency limit - const processPagesInBatches = async (images, limit) => { - const results = []; - const promises = images.map((image, index) => - limit(() => - processPage(image).then((result) => { - results[index] = result; - }), - ), - ); - await Promise.all(promises); - return results; - }; - const limit = (0, p_limit_1.default)(concurrency); - const results = await processPagesInBatches(images, limit); - const filteredResults = results.filter(utils_1.isString); - aggregatedMarkdown.push(...filteredResults); - } - // Write the aggregated markdown to a file - if (outputDir) { - const resultFilePath = path_1.default.join(outputDir, `${fileName}.md`); - await fs_extra_1.default.writeFile( - resultFilePath, - aggregatedMarkdown.join("\n\n"), - ); - } - // Cleanup the downloaded PDF file - if (cleanup) await fs_extra_1.default.remove(tempDirectory); - // Format JSON response - const endTime = new Date(); - const completionTime = endTime.getTime() - startTime.getTime(); - const formattedPages = aggregatedMarkdown.map((el, i) => { - let pageNumber; - // If we convert all pages, just use the array index - if (pagesToConvertAsImages === -1) { - pageNumber = i + 1; - } - // Else if we convert specific pages, use the page number from the parameter - else if (Array.isArray(pagesToConvertAsImages)) { - pageNumber = pagesToConvertAsImages[i]; - } - // Else, the parameter is a number and use it for the page number - else { - pageNumber = pagesToConvertAsImages; - } - return { content: el, page: pageNumber, contentLength: el.length }; - }); - return { - completionTime, - fileName, - inputTokens: inputTokenCount, - outputTokens: outputTokenCount, - pages: formattedPages, - }; +const openAI_1 = require("./openAI"); +const types_1 = require("./types"); +const utils_1 = require("./utils"); +const utils_2 = require("./utils"); +const documind = async ({ cleanup = true, concurrency = 10, filePath, llmParams = {}, maintainFormat = false, model, //= ModelOptions.gpt_4o_mini, +openaiAPIKey = "", outputDir, pagesToConvertAsImages = -1, tempDir = os_1.default.tmpdir(), }) => { + const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; + const defaultModel = model ?? + (baseUrl !== "https://api.openai.com/v1" + ? types_1.ModelOptions.llava // Default for custom base URL + : types_1.ModelOptions.gpt_4o_mini); // Default for OpenAI + let inputTokenCount = 0; + let outputTokenCount = 0; + let priorPage = ""; + const aggregatedMarkdown = []; + const startTime = new Date(); + llmParams = (0, utils_2.validateLLMParams)(llmParams); + // Validators + if (!openaiAPIKey || !openaiAPIKey.length) { + throw new Error("Missing OpenAI API key"); + } + if (!filePath || !filePath.length) { + throw new Error("Missing file path"); + } + // Ensure temp directory exists + create temp folder + const rand = Math.floor(1000 + Math.random() * 9000).toString(); + const tempDirectory = path_1.default.join(tempDir || os_1.default.tmpdir(), `documind-file-${rand}`); + await fs_extra_1.default.ensureDir(tempDirectory); + // Download the PDF. Get file name. + const { extension, localPath } = await (0, utils_1.downloadFile)({ + filePath, + tempDir: tempDirectory, + }); + if (!localPath) + throw "Failed to save file to local drive"; + // Sort the `pagesToConvertAsImages` array to make sure we use the right index + // for `formattedPages` as `pdf2pic` always returns images in order + if (Array.isArray(pagesToConvertAsImages)) { + pagesToConvertAsImages.sort((a, b) => a - b); + } + // Convert file to PDF if necessary + if (extension !== ".png") { + let pdfPath; + if (extension === ".pdf") { + pdfPath = localPath; + } + else { + pdfPath = await (0, utils_1.convertFileToPdf)({ + extension, + localPath, + tempDir: tempDirectory, + }); + } + // Convert the file to a series of images + await (0, utils_1.convertPdfToImages)({ + localPath: pdfPath, + pagesToConvertAsImages, + tempDir: tempDirectory, + }); + } + const endOfPath = localPath.split("/")[localPath.split("/").length - 1]; + const rawFileName = endOfPath.split(".")[0]; + const fileName = rawFileName + .replace(/[^\w\s]/g, "") + .replace(/\s+/g, "_") + .toLowerCase() + .substring(0, 255); // Truncate file name to 255 characters to prevent ENAMETOOLONG errors + // Get list of converted images + const files = await fs_extra_1.default.readdir(tempDirectory); + const images = files.filter((file) => file.endsWith(".png")); + if (maintainFormat) { + // Use synchronous processing + for (const image of images) { + const imagePath = path_1.default.join(tempDirectory, image); + try { + const { content, inputTokens, outputTokens } = await (0, openAI_1.getCompletion)({ + apiKey: openaiAPIKey, + imagePath, + llmParams, + maintainFormat, + model: defaultModel, + priorPage, + }); + const formattedMarkdown = (0, utils_1.formatMarkdown)(content); + inputTokenCount += inputTokens; + outputTokenCount += outputTokens; + // Update prior page to result from last processing step + priorPage = formattedMarkdown; + // Add all markdown results to array + aggregatedMarkdown.push(formattedMarkdown); + } + catch (error) { + console.error(`Failed to process image ${image}:`, error); + throw error; + } + } + } + else { + // Process in parallel with a limit on concurrent pages + const processPage = async (image) => { + const imagePath = path_1.default.join(tempDirectory, image); + try { + const { content, inputTokens, outputTokens } = await (0, openAI_1.getCompletion)({ + apiKey: openaiAPIKey, + imagePath, + llmParams, + maintainFormat, + model: defaultModel, + priorPage, + }); + const formattedMarkdown = (0, utils_1.formatMarkdown)(content); + inputTokenCount += inputTokens; + outputTokenCount += outputTokens; + // Update prior page to result from last processing step + priorPage = formattedMarkdown; + // Add all markdown results to array + return formattedMarkdown; + } + catch (error) { + console.error(`Failed to process image ${image}:`, error); + throw error; + } + }; + // Function to process pages with concurrency limit + const processPagesInBatches = async (images, limit) => { + const results = []; + const promises = images.map((image, index) => limit(() => processPage(image).then((result) => { + results[index] = result; + }))); + await Promise.all(promises); + return results; + }; + const limit = (0, p_limit_1.default)(concurrency); + const results = await processPagesInBatches(images, limit); + const filteredResults = results.filter(utils_1.isString); + aggregatedMarkdown.push(...filteredResults); + } + // Write the aggregated markdown to a file + if (outputDir) { + const resultFilePath = path_1.default.join(outputDir, `${fileName}.md`); + await fs_extra_1.default.writeFile(resultFilePath, aggregatedMarkdown.join("\n\n")); + } + // Cleanup the downloaded PDF file + if (cleanup) + await fs_extra_1.default.remove(tempDirectory); + // Format JSON response + const endTime = new Date(); + const completionTime = endTime.getTime() - startTime.getTime(); + const formattedPages = aggregatedMarkdown.map((el, i) => { + let pageNumber; + // If we convert all pages, just use the array index + if (pagesToConvertAsImages === -1) { + pageNumber = i + 1; + } + // Else if we convert specific pages, use the page number from the parameter + else if (Array.isArray(pagesToConvertAsImages)) { + pageNumber = pagesToConvertAsImages[i]; + } + // Else, the parameter is a number and use it for the page number + else { + pageNumber = pagesToConvertAsImages; + } + return { content: el, page: pageNumber, contentLength: el.length }; + }); + return { + completionTime, + fileName, + inputTokens: inputTokenCount, + outputTokens: outputTokenCount, + pages: formattedPages, + }; }; exports.documind = documind; diff --git a/core/dist/openAI.d.ts b/core/dist/openAI.d.ts index 2e390ed..6492e27 100644 --- a/core/dist/openAI.d.ts +++ b/core/dist/openAI.d.ts @@ -1,2 +1,2 @@ -import { CompletionArgs, CompletionResponse } from "./types"; +import type { CompletionArgs, CompletionResponse } from "./types"; export declare const getCompletion: ({ apiKey, imagePath, llmParams, maintainFormat, model, priorPage, }: CompletionArgs) => Promise; diff --git a/core/dist/openAI.js b/core/dist/openAI.js index 7c22118..febe8b0 100644 --- a/core/dist/openAI.js +++ b/core/dist/openAI.js @@ -4,13 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCompletion = void 0; -const utils_1 = require("./utils"); const axios_1 = __importDefault(require("axios")); +const utils_1 = require("./utils"); const getCompletion = async ({ apiKey, imagePath, llmParams, maintainFormat, model, priorPage, }) => { - const validModelsForCustomBaseUrl = [ - "llava", - "llama3.2-vision", - ]; + const validModelsForCustomBaseUrl = ["llava", "llama3.2-vision"]; const validModelsForOpenAi = ["gpt-4o", "gpt-4o-mini"]; const baseUrl = process.env.BASE_URL || "https://api.openai.com/v1"; if (baseUrl !== "https://api.openai.com/v1") { @@ -24,7 +21,7 @@ const getCompletion = async ({ apiKey, imagePath, llmParams, maintainFormat, mod } } const systemPrompt = ` - Convert the following image/document to markdown. + Convert the following image/document to markdown. Return only the markdown with no explanation text. Do not include deliminators like '''markdown. You must include all information on the page. Do not exclude headers, footers, or subtext. `; diff --git a/core/dist/types.d.ts b/core/dist/types.d.ts index f7a0b35..5640118 100644 --- a/core/dist/types.d.ts +++ b/core/dist/types.d.ts @@ -11,6 +11,7 @@ export interface DocumindArgs { tempDir?: string; } export declare enum ModelOptions { + gpt_o3_mini = "o3-mini", gpt_4o = "gpt-4o", gpt_4o_mini = "gpt-4o-mini", llava = "llava", diff --git a/core/dist/types.js b/core/dist/types.js index 1cfa6fe..e920565 100644 --- a/core/dist/types.js +++ b/core/dist/types.js @@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.ModelOptions = void 0; var ModelOptions; (function (ModelOptions) { + ModelOptions["gpt_o3_mini"] = "o3-mini"; ModelOptions["gpt_4o"] = "gpt-4o"; ModelOptions["gpt_4o_mini"] = "gpt-4o-mini"; ModelOptions["llava"] = "llava"; diff --git a/core/dist/utils.d.ts b/core/dist/utils.d.ts index aeab40c..d0188d8 100644 --- a/core/dist/utils.d.ts +++ b/core/dist/utils.d.ts @@ -1,4 +1,4 @@ -import { LLMParams } from "./types"; +import type { LLMParams } from "./types"; export declare const validateLLMParams: (params: Partial) => LLMParams; export declare const encodeImageToBase64: (imagePath: string) => Promise; export declare const formatMarkdown: (text: string) => string; diff --git a/core/dist/utils.js b/core/dist/utils.js index 69713ce..3e7b36a 100644 --- a/core/dist/utils.js +++ b/core/dist/utils.js @@ -27,16 +27,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertKeysToSnakeCase = exports.convertFileToPdf = exports.convertPdfToImages = exports.getTextFromImage = exports.downloadFile = exports.isValidUrl = exports.isString = exports.formatMarkdown = exports.encodeImageToBase64 = exports.validateLLMParams = void 0; -const libreoffice_convert_1 = require("libreoffice-convert"); -const pdf2pic_1 = require("pdf2pic"); -const promises_1 = require("stream/promises"); +const promises_1 = require("node:stream/promises"); +const path_1 = __importDefault(require("path")); const util_1 = require("util"); -const Tesseract = __importStar(require("tesseract.js")); const axios_1 = __importDefault(require("axios")); const fs_extra_1 = __importDefault(require("fs-extra")); +const libreoffice_convert_1 = require("libreoffice-convert"); const mime_types_1 = __importDefault(require("mime-types")); -const path_1 = __importDefault(require("path")); +const pdf2pic_1 = require("pdf2pic"); const sharp_1 = __importDefault(require("sharp")); +const Tesseract = __importStar(require("tesseract.js")); const convertAsync = (0, util_1.promisify)(libreoffice_convert_1.convert); const defaultLLMParams = { frequencyPenalty: 0, // OpenAI defaults to 0