Adding LibreOffice conversion support (WIP)

This commit is contained in:
Saud Fatayerji 2023-11-13 02:46:50 +03:00
parent c7dd18695d
commit e625a415fd
18 changed files with 659 additions and 104 deletions

150
README.md
View File

@ -88,66 +88,136 @@ If you are interested in learning about this, take a look at the Example workflo
Current functions of spdf and their progress in this repo. Current functions of spdf and their progress in this repo.
#### Page Operations
| Status | Feature | Description | | Status | Feature | Description |
| ------ | ------------------------ | ----------- | | ------ | ------------------------ | ----------- |
| ✔️ | Merge | | | 🚧A | Merge | |
| ✔️ | Split | | | 🚧A | Split | |
| ✔️ | Rotate | | | 🚧A | Organize | |
| ✔️ | Multi-Page-Layout | | | 🚧S | Rotate | |
| ✔️ | Adjust page size/scale | | | 🚧A | Remove Pages | |
| ✔️ | Organize | | | 🚧A | Multi-Page Layout | |
| ✔️ | Change Metadata | | | ❌ | Adjust page size/scale | |
| ✔️ | Auto Rename | | | 🚧A | Auto Split Pages | |
| ❌ | Add Watermark | | | ❌ | Adjust Colours/Contrast | |
| ❌ | Crop | |
| 🚧A | Extract Pages | |
| ❌ | PDF to Single large Page | | | ❌ | PDF to Single large Page | |
| ❌ | Auto Redact | |
| ❌ | Remove Pages | |
| Status | Feature | Description |
| ------ | ------------------ | ----------- |
| ✔️ | Remove Blank Pages | |
| ✔️ | Auto Split Pages | |
| Status | Feature | Description | #### Convert
| ------ | ------------ | ----------- | | Status | Feature | Description |
| ❌ | Repair | | | ------ | ------------------- | ----------- |
| ❌ | Compress | | | ❌ | Image to PDF | |
| ❌ | Flatten | | | 🚧S | Convert file to PDF | |
| ❌ | Compare/Diff | | | ❌ | URL to PDF | |
| ❌ | Sanitize | | | ❌ | HTML to PDF | |
| ❌ | Get info | | | ❌ | Markdown to PDF | |
| ❌ | Show JS | | | ❌ | PDF to Image | |
| ❌ | PDF to Word | |
| ❌ | PDF to Presentation | |
| ❌ | PDF to Text/RTF | |
| ❌ | PDF to HTML | |
| ❌ | PDF to PDF/A | |
#### Security
| Status | Feature | Description | | Status | Feature | Description |
| ------ | --------------------- | ----------- | | ------ | --------------------- | ----------- |
| ❌ | Sign | |
| ❌ | Sign with Certificate | |
| ❌ | Add Password | | | ❌ | Add Password | |
| ❌ | Remove Password | | | ❌ | Remove Password | |
| ❌ | Change Permissions | | | ❌ | Change Permissions | |
| ❌ | Add Watermark | |
| ❌ | Sign with Certificate | |
| ❌ | Sanitize | |
| ❌ | Auto Redact | |
#### Miscellaneous
| Status | Feature | Description | | Status | Feature | Description |
| ------ | --------------------------- | ----------- | | ------ | --------------------------- | ----------- |
| ❌ | Image to PDF | |
| ❌ | Add image | |
| ❌ | Extract Images | |
| ❌ | PDF to Image | |
| ❌ | OCR | | | ❌ | OCR | |
| ❌ | Detect/Split Scanned photos | | | ❌ | Add image | |
| ❌ | Compress | |
| ❌ | Extract Images | |
| 🚧S | Change Metadata | |
| 🚧A | Detect/Split Scanned photos | |
| ❌ | Sign | |
| ❌ | Flatten | |
| ❌ | Repair | |
| 🚧A | Remove Blank Pages | |
| ❌ | Compare/Diff | |
| ❌ | Add Page Numbers | |
| ❌ | Auto Rename | |
| ❌ | Get info | |
| ❌ | Show JS | |
| Status | Feature | Description |
| ------ | ------------------------ | ----------- |
| ❌ | Convert file to PDF | |
| ❌ | PDF to Text/RTF | |
| ❌ | PDF to HTML | |
| ❌ | PDF to XML | |
| ❌ | URL to PDF | |
| ❌ | HTML to PDF | |
| ❌ | Markdown to PDF | |
✔️: Done, 🚧: Started Developement, ❌: Planned Feature ✔️: Done, 🚧: Started Developement, ❌: Planned Feature
A: Available in the internal API, S: Available on the node server, C: Available in the client
## Contribute ## Contribute
For initial instructions look at [CONTRIBUTE.md](./CONTRIBUTE.md) For initial instructions look at [CONTRIBUTE.md](./CONTRIBUTE.md)
/*
///// CONVERT 2 pdf
file2pdf
url2pdf
html2pdf
md2pdf
image2pdf
///// CONVERT from pdf
pdf2image
flatten
pdf2pdf/a
pdf2word
pdf2presentation
pdf2rtf
pdf2html
pdf2xml
///// SINGLE
merge
rotate
crop
pageNumbers
colours/contrast
addPassword
removePassword
compress
changeMetadata
change Permissions
OCR
sanitise
repair
compare
extract images
signWith certificate
impose
adjust page size/scale
auto rename
getAllInfo
showJS
redact
pdf2singleLargePage
///// SPLITTING
split
auto split
detect/split scanned
///// REARRANGE
- organise pages (remove/re-arrange)
- removePages
- removeBlank
- extractPages
///// ADD OBJECTS
add image
add watermark
sign
*/

View File

@ -17,13 +17,15 @@
"i18next": "^23.6.0", "i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0", "i18next-browser-languagedetector": "^7.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"pdfjs-dist": "^4.0.189",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.9.1", "react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.3.1", "react-i18next": "^13.3.1",
"react-icons": "^4.11.0", "react-icons": "^4.11.0",
"react-router-bootstrap": "^0.26.2", "react-router-bootstrap": "^0.26.2",
"react-router-dom": "^6.18.0" "react-router-dom": "^6.18.0",
"vite-plugin-top-level-await": "^1.3.1"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1.5.0", "@tauri-apps/cli": "^1.5.0",

View File

@ -1711,6 +1711,16 @@ dependencies = [
"windows-sys 0.42.0", "windows-sys 0.42.0",
] ]
[[package]]
name = "os_pipe"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177"
dependencies = [
"libc",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -2436,6 +2446,16 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shared_child"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.7" version = "0.3.7"
@ -2693,6 +2713,7 @@ dependencies = [
"objc", "objc",
"once_cell", "once_cell",
"open", "open",
"os_pipe",
"percent-encoding", "percent-encoding",
"rand 0.8.5", "rand 0.8.5",
"raw-window-handle", "raw-window-handle",
@ -2703,6 +2724,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"serialize-to-javascript", "serialize-to-javascript",
"shared_child",
"state", "state",
"tar", "tar",
"tauri-macros", "tauri-macros",

View File

@ -13,7 +13,7 @@ edition = "2021"
tauri-build = { version = "1.5", features = [] } tauri-build = { version = "1.5", features = [] }
[dependencies] [dependencies]
tauri = { version = "1.5", features = [ "fs-write-file", "fs-read-file", "dialog-save", "dialog-open", "shell-open"] } tauri = { version = "1.5", features = [ "fs-remove-dir", "fs-create-dir", "shell-all", "fs-write-file", "fs-read-file", "dialog-save", "dialog-open"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View File

@ -14,8 +14,19 @@
"allowlist": { "allowlist": {
"all": false, "all": false,
"shell": { "shell": {
"all": false, "all": true,
"open": true "open": true,
"scope": [
{
"name": "libreoffice-version",
"cmd": "libreoffice",
"args": ["--version"]
},{
"name": "libreoffice-convert",
"cmd": "libreoffice",
"args": ["--headless","--convert-to",{ "validator": "\\S+" },{ "validator": "\\S+" },"--outdir",{ "validator": "\\S+" }]
}
]
}, },
"dialog": { "dialog": {
"all": false, "all": false,
@ -31,8 +42,8 @@
"writeFile": true, "writeFile": true,
"readDir": false, "readDir": false,
"copyFile": false, "copyFile": false,
"createDir": false, "createDir": true,
"removeDir": false, "removeDir": true,
"removeFile": false, "removeFile": false,
"renameFile": false, "renameFile": false,
"exists": false "exists": false

View File

@ -4,6 +4,7 @@ import { Routes, Route, Outlet } from "react-router-dom";
import Home from "./pages/Home"; import Home from "./pages/Home";
import About from "./pages/About"; import About from "./pages/About";
import Dashboard from "./pages/Dashboard"; import Dashboard from "./pages/Dashboard";
import ToPdf from "./pages/convert/ToPdf"
import NoMatch from "./pages/NoMatch"; import NoMatch from "./pages/NoMatch";
import NavBar from "./components/NavBar"; import NavBar from "./components/NavBar";
@ -38,6 +39,7 @@ export default function App() {
<Route index element={<Home />} /> <Route index element={<Home />} />
<Route path="about" element={<About />} /> <Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />} /> <Route path="dashboard" element={<Dashboard />} />
<Route path="to-pdf" element={<ToPdf />} />
{/* Using path="*"" means "match anything", so this route {/* Using path="*"" means "match anything", so this route
acts like a catch-all for URLs that we don't have explicit acts like a catch-all for URLs that we don't have explicit

View File

@ -86,7 +86,7 @@ function NavBar() {
]}, ]},
{displayText: t('navbar.convert'), icon: BsArrowLeftRight, sublist: [ {displayText: t('navbar.convert'), icon: BsArrowLeftRight, sublist: [
{ displayText: t('home.imageToPdf.title'), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t('home.imageToPdf.desc') }, { displayText: t('home.imageToPdf.title'), icon: BsFileEarmarkImage, dest: "/dashboard", tooltip: t('home.imageToPdf.desc') },
{ displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/nothing-here", tooltip: t('home.fileToPDF.desc') }, { displayText: t('home.fileToPDF.title'), icon: BsFileEarmark, dest: "/to-pdf", tooltip: t('home.fileToPDF.desc') },
{ displayText: t('home.HTMLToPDF.title'), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t('home.HTMLToPDF.desc') }, { displayText: t('home.HTMLToPDF.title'), icon: BsFiletypeHtml, dest: "/nothing-here", tooltip: t('home.HTMLToPDF.desc') },
{ displayText: t('home.URLToPDF.title'), icon: BsLink, dest: "/nothing-here", tooltip: t('home.URLToPDF.desc') }, { displayText: t('home.URLToPDF.title'), icon: BsLink, dest: "/nothing-here", tooltip: t('home.URLToPDF.desc') },
{ displayText: t('home.MarkdownToPDF.title'), icon: BsFiletypeMd, dest: "/nothing-here", tooltip: t('home.MarkdownToPDF.desc') }, { displayText: t('home.MarkdownToPDF.title'), icon: BsFiletypeMd, dest: "/nothing-here", tooltip: t('home.MarkdownToPDF.desc') },

View File

@ -0,0 +1,16 @@
import { isLibreOfficeInstalled } from "../../utils/libre-office-utils";
const hasLibreOffice = await isLibreOfficeInstalled();
console.log(hasLibreOffice)
function About() {
return (
<div>
<h2>Convert to PDF</h2>
{"hasLibreOffice: "+hasLibreOffice}
</div>
);
}
export default About;

View File

@ -0,0 +1,46 @@
import { readBinaryFile, writeBinaryFile, removeDir, BaseDirectory } from '@tauri-apps/api/fs';
import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
import { runShell } from './tauri-wrapper';
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
const randUuid = crypto.randomUUID();
const tempDir = "StirlingPDF/"+randUuid;
const srcFile = tempDir+"/"+filename;
await writeBinaryFile(srcFile, byteArray);
await writeBinaryFile(srcFile, new Uint8Array([]), { dir: BaseDirectory.Temp });
const messageList: string[] = [];
await runShell("libreoffice-convert", ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir], (message, stream) => {
if (stream === "stdout") {
messageList.push(message);
}
console.debug(`${stream}, ${randUuid}: ${message}`);
});
const lastMessage = messageList[messageList.length-1]
const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
const outputFilePathSplit = outputFilePath.toString().split("[\\/]")
const outputFileName = outputFilePathSplit[outputFilePathSplit.length-1];
const outputBytes = await readBinaryFile(outputFilePath);
await removeDir(tempDir);
return fromUint8Array(outputBytes, outputFileName);
}
export async function isLibreOfficeInstalled() {
const messageList: string[] = [];
try {
await runShell("libreoffice-version", ["--version"], (message, stream) => {
if (stream === "stdout") {
messageList.push(message);
}
});
} catch (error) {
return false;
}
console.log("messageList", messageList)
const result = messageList[0].match("LibreOffice ([0-9]+\.){4}.*");
return result ? true : false;
}

View File

@ -1,6 +1,7 @@
import { open, save } from '@tauri-apps/api/dialog'; import { open, save } from '@tauri-apps/api/dialog';
import { readBinaryFile, writeBinaryFile } from '@tauri-apps/api/fs'; import { readBinaryFile, writeBinaryFile } from '@tauri-apps/api/fs';
import { Command } from '@tauri-apps/api/shell'
export type TauriBrowserFile = { export type TauriBrowserFile = {
name: string, name: string,
@ -52,7 +53,7 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows
selected = [selected]; selected = [selected];
} }
const files:TauriBrowserFile[] = []; const files: TauriBrowserFile[] = [];
for (const s of selected) { for (const s of selected) {
const contents = await readBinaryFile(s); const contents = await readBinaryFile(s);
const res = byteArrayToFile(contents, s); const res = byteArrayToFile(contents, s);
@ -73,7 +74,7 @@ export function openFiles(options: SelectFilesDialogOptions): Promise<TauriBrows
input.onchange = async () => { input.onchange = async () => {
if (input.files && input.files.length) { if (input.files && input.files.length) {
console.log("input.files", input.files) console.log("input.files", input.files)
const files:TauriBrowserFile[] = []; const files: TauriBrowserFile[] = [];
for (const f of input.files) { for (const f of input.files) {
const contents = new Uint8Array(await f.arrayBuffer()); const contents = new Uint8Array(await f.arrayBuffer());
const res = byteArrayToFile(contents, f.name); const res = byteArrayToFile(contents, f.name);
@ -138,4 +139,31 @@ export async function downloadFile(fileData: Uint8Array, options: DownloadFilesD
downloadLink.click(); downloadLink.click();
} }
} }
} }
/**
* Dont forget to whitelist the Command in src-tauri/tauri.conf.json! (tauri.allowlist.shell.scope)
* @param commandName The name of the command to run. Must be defined in tauri.allowlist.shell.scope[].name
* @param args The args to pass into the command
* @param callback A callback function that is called when output is logged
* @returns A log of all the outputs logged
*/
export function runShell(commandName: string, args: string[], callback: (message: any, stream:"stdout"|"stderr"|"error") => void): Promise<void> {
return new Promise(async (resolve, reject) => {
const comm = new Command(commandName, args);
comm.on('close', data => {
if (data.code === 0) {
resolve();
} else {
reject(new Error(`Command failed with exit code ${data.code} and signal ${data.signal}`));
}
});
comm.on('error', error => callback(error, "error"));
comm.stdout.on('data', line => callback(line, "stdout"));
comm.stderr.on('data', line => callback(line, "stderr"));
const child = await comm.spawn();
console.debug(`Started child process with pid: ${child.pid}`)
});
}

View File

@ -1,9 +1,18 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import topLevelAwait from "vite-plugin-top-level-await";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(async () => ({ export default defineConfig(async () => ({
plugins: [react()], plugins: [
react(),
topLevelAwait({
// The export name of top-level await promise for each chunk module
promiseExportName: "__tla",
// The function to generate import names of top-level await promise in each chunk module
promiseImportName: i => `__tla_${i}`
}),
],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
// //

299
package-lock.json generated
View File

@ -74,13 +74,15 @@
"i18next": "^23.6.0", "i18next": "^23.6.0",
"i18next-browser-languagedetector": "^7.1.0", "i18next-browser-languagedetector": "^7.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"pdfjs-dist": "^4.0.189",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.9.1", "react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.3.1", "react-i18next": "^13.3.1",
"react-icons": "^4.11.0", "react-icons": "^4.11.0",
"react-router-bootstrap": "^0.26.2", "react-router-bootstrap": "^0.26.2",
"react-router-dom": "^6.18.0" "react-router-dom": "^6.18.0",
"vite-plugin-top-level-await": "^1.3.1"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^1.5.0", "@tauri-apps/cli": "^1.5.0",
@ -2579,7 +2581,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
@ -2595,7 +2596,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
@ -2611,7 +2611,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"android" "android"
@ -2627,7 +2626,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@ -2643,7 +2641,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
@ -2659,7 +2656,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"freebsd" "freebsd"
@ -2675,7 +2671,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"freebsd" "freebsd"
@ -2691,7 +2686,6 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2707,7 +2701,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2723,7 +2716,6 @@
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2739,7 +2731,6 @@
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2755,7 +2746,6 @@
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2771,7 +2761,6 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2787,7 +2776,6 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2803,7 +2791,6 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2819,7 +2806,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -2835,7 +2821,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"netbsd" "netbsd"
@ -2851,7 +2836,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"openbsd" "openbsd"
@ -2867,7 +2851,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"sunos" "sunos"
@ -2883,7 +2866,6 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@ -2899,7 +2881,6 @@
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@ -2915,7 +2896,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@ -3605,7 +3585,7 @@
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
"dev": true, "devOptional": true,
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.0.1", "@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/sourcemap-codec": "^1.4.10",
@ -3619,7 +3599,7 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true, "devOptional": true,
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
@ -3628,7 +3608,7 @@
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true, "devOptional": true,
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
@ -3637,7 +3617,7 @@
"version": "0.3.5", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
"dev": true, "devOptional": true,
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.9"
@ -3647,13 +3627,13 @@
"version": "1.4.15", "version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true "devOptional": true
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.20", "version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
"dev": true, "devOptional": true,
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
@ -3833,6 +3813,22 @@
"react": ">=16.14.0" "react": ">=16.14.0"
} }
}, },
"node_modules/@rollup/plugin-virtual": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz",
"integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==",
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@sideway/address": { "node_modules/@sideway/address": {
"version": "4.1.4", "version": "4.1.4",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
@ -3906,6 +3902,198 @@
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"node_modules/@swc/core": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.96.tgz",
"integrity": "sha512-zwE3TLgoZwJfQygdv2SdCK9mRLYluwDOM53I+dT6Z5ZvrgVENmY3txvWDvduzkV+/8IuvrRbVezMpxcojadRdQ==",
"hasInstallScript": true,
"dependencies": {
"@swc/counter": "^0.1.1",
"@swc/types": "^0.1.5"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.3.96",
"@swc/core-darwin-x64": "1.3.96",
"@swc/core-linux-arm-gnueabihf": "1.3.96",
"@swc/core-linux-arm64-gnu": "1.3.96",
"@swc/core-linux-arm64-musl": "1.3.96",
"@swc/core-linux-x64-gnu": "1.3.96",
"@swc/core-linux-x64-musl": "1.3.96",
"@swc/core-win32-arm64-msvc": "1.3.96",
"@swc/core-win32-ia32-msvc": "1.3.96",
"@swc/core-win32-x64-msvc": "1.3.96"
},
"peerDependencies": {
"@swc/helpers": "^0.5.0"
},
"peerDependenciesMeta": {
"@swc/helpers": {
"optional": true
}
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.96.tgz",
"integrity": "sha512-8hzgXYVd85hfPh6mJ9yrG26rhgzCmcLO0h1TIl8U31hwmTbfZLzRitFQ/kqMJNbIBCwmNH1RU2QcJnL3d7f69A==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.96.tgz",
"integrity": "sha512-mFp9GFfuPg+43vlAdQZl0WZpZSE8sEzqL7sr/7Reul5McUHP0BaLsEzwjvD035ESfkY8GBZdLpMinblIbFNljQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.96.tgz",
"integrity": "sha512-8UEKkYJP4c8YzYIY/LlbSo8z5Obj4hqcv/fUTHiEePiGsOddgGf7AWjh56u7IoN/0uEmEro59nc1ChFXqXSGyg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.96.tgz",
"integrity": "sha512-c/IiJ0s1y3Ymm2BTpyC/xr6gOvoqAVETrivVXHq68xgNms95luSpbYQ28rqaZC8bQC8M5zdXpSc0T8DJu8RJGw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.96.tgz",
"integrity": "sha512-i5/UTUwmJLri7zhtF6SAo/4QDQJDH2fhYJaBIUhrICmIkRO/ltURmpejqxsM/ye9Jqv5zG7VszMC0v/GYn/7BQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.96.tgz",
"integrity": "sha512-USdaZu8lTIkm4Yf9cogct/j5eqtdZqTgcTib4I+NloUW0E/hySou3eSyp3V2UAA1qyuC72ld1otXuyKBna0YKQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.96.tgz",
"integrity": "sha512-QYErutd+G2SNaCinUVobfL7jWWjGTI0QEoQ6hqTp7PxCJS/dmKmj3C5ZkvxRYcq7XcZt7ovrYCTwPTHzt6lZBg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.96.tgz",
"integrity": "sha512-hjGvvAduA3Un2cZ9iNP4xvTXOO4jL3G9iakhFsgVhpkU73SGmK7+LN8ZVBEu4oq2SUcHO6caWvnZ881cxGuSpg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.96.tgz",
"integrity": "sha512-Far2hVFiwr+7VPCM2GxSmbh3ikTpM3pDombE+d69hkedvYHYZxtTF+2LTKl/sXtpbUnsoq7yV/32c9R/xaaWfw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.3.96",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.96.tgz",
"integrity": "sha512-4VbSAniIu0ikLf5mBX81FsljnfqjoVGleEkCQv4+zRlyZtO3FHoDPkeLVoy6WRlj7tyrRcfUJ4mDdPkbfTO14g==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/counter": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz",
"integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw=="
},
"node_modules/@swc/helpers": { "node_modules/@swc/helpers": {
"version": "0.5.3", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz",
@ -3919,6 +4107,11 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
}, },
"node_modules/@swc/types": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz",
"integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw=="
},
"node_modules/@swiftcarrot/color-fns": { "node_modules/@swiftcarrot/color-fns": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/@swiftcarrot/color-fns/-/color-fns-3.2.0.tgz", "resolved": "https://registry.npmjs.org/@swiftcarrot/color-fns/-/color-fns-3.2.0.tgz",
@ -4281,7 +4474,7 @@
"version": "18.18.7", "version": "18.18.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.7.tgz",
"integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==", "integrity": "sha512-bw+lEsxis6eqJYW8Ql6+yTqkE6RuFtsQPSe5JxXbqYRFQEER5aJA9a5UH9igqDWm3X4iLHIKOHlnAXLM4mi7uQ==",
"dev": true, "devOptional": true,
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
@ -4749,7 +4942,7 @@
"version": "8.10.0", "version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"dev": true, "devOptional": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -6751,7 +6944,6 @@
"version": "0.18.20", "version": "0.18.20",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
@ -10093,7 +10285,6 @@
"version": "3.3.6", "version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -10810,8 +11001,7 @@
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
"dev": true
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
@ -10862,7 +11052,6 @@
"version": "8.4.31", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -11639,7 +11828,6 @@
"version": "3.29.4", "version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
"dev": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
@ -12042,7 +12230,7 @@
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true, "devOptional": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -12051,7 +12239,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -12060,7 +12247,7 @@
"version": "0.5.21", "version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true, "devOptional": true,
"dependencies": { "dependencies": {
"buffer-from": "^1.0.0", "buffer-from": "^1.0.0",
"source-map": "^0.6.0" "source-map": "^0.6.0"
@ -12476,7 +12663,7 @@
"version": "5.22.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz",
"integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==", "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==",
"dev": true, "devOptional": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2", "acorn": "^8.8.2",
@ -12494,7 +12681,7 @@
"version": "2.20.3", "version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true "devOptional": true
}, },
"node_modules/text-table": { "node_modules/text-table": {
"version": "0.2.0", "version": "0.2.0",
@ -13038,7 +13225,7 @@
"version": "5.26.5", "version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true "devOptional": true
}, },
"node_modules/unicode-canonical-property-names-ecmascript": { "node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0", "version": "2.0.0",
@ -13242,7 +13429,6 @@
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
"dev": true,
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.18.10",
"postcss": "^8.4.27", "postcss": "^8.4.27",
@ -13398,6 +13584,31 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/vite-plugin-top-level-await": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.3.1.tgz",
"integrity": "sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ==",
"dependencies": {
"@rollup/plugin-virtual": "^3.0.1",
"@swc/core": "^1.3.10",
"uuid": "^9.0.0"
},
"peerDependencies": {
"vite": ">=2.8"
}
},
"node_modules/vite-plugin-top-level-await/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vitest": { "node_modules/vitest": {
"version": "0.32.4", "version": "0.32.4",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz",

View File

@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
//import workflow from './workflow-controller'; //import workflow from './workflow-controller';
import operations from './operations-controller'; import operations from './operations-controller';
import conversions from './conversions-controller';
const router = express.Router(); const router = express.Router();
@ -11,6 +12,7 @@ router.get("/", (req: Request, res: Response) => {
}); });
router.use("/operations", operations); router.use("/operations", operations);
router.use("/conversions", conversions);
//router.use("/workflow", workflow); //router.use("/workflow", workflow);
export default router; export default router;

View File

@ -0,0 +1,27 @@
import { respondWithPdfFile, response_mustHaveExactlyOneFile, response_dependencyNotConfigured } from '../../utils/endpoint-utils';
import { fileToPdf, isLibreOfficeInstalled } from '../../utils/libre-office-utils';
import express, { Request, Response } from 'express';
const router = express.Router();
import multer from 'multer';
const upload = multer();
import Joi from 'joi';
router.post('/file-to-pdf', upload.single("file"), async function(req: Request, res: Response) {
if (!req.file) {
response_mustHaveExactlyOneFile(res);
return;
}
const isInstalled = await isLibreOfficeInstalled();
if (isInstalled) {
const outputFile = await fileToPdf(req.file.buffer, req.file.originalname);
respondWithPdfFile(res, outputFile);
return;
}
response_dependencyNotConfigured(res, "LibreOffice");
});
export default router;

View File

@ -2,14 +2,18 @@
import { Response } from 'express'; import { Response } from 'express';
import { PdfFile } from '@stirling-pdf/shared-operations/wrappers/PdfFile' import { PdfFile } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
export async function respondWithFile(res: Response, bytes: Uint8Array, name: string, mimeType: string): Promise<void> {
res.writeHead(200, {
'Content-Type': mimeType,
'Content-disposition': 'attachment;filename=' + name,
'Content-Length': bytes.length
});
res.end(bytes);
}
export async function respondWithPdfFile(res: Response, file: PdfFile): Promise<void> { export async function respondWithPdfFile(res: Response, file: PdfFile): Promise<void> {
const byteFile = await file.convertToByteArrayFile(); const byteFile = await file.convertToByteArrayFile();
res.writeHead(200, { respondWithFile(res, byteFile.byteArray!, byteFile.filename, "application/pdf");
'Content-Type': "application/pdf",
'Content-disposition': 'attachment;filename=' + byteFile.filename,
'Content-Length': byteFile.byteArray?.length
});
res.end(byteFile.byteArray)
} }
export function response_mustHaveExactlyOneFile(res: Response): void { export function response_mustHaveExactlyOneFile(res: Response): void {
@ -27,3 +31,12 @@ export function response_mustHaveExactlyOneFile(res: Response): void {
} }
]); ]);
} }
export function response_dependencyNotConfigured(res: Response, dependencyName: string): void {
res.status(400).send([
{
"message": `${dependencyName} is not configured correctly on the server.`,
"type": "dependency_error",
}
]);
}

View File

@ -0,0 +1,99 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
import { exec, spawn } from 'child_process'
import { PdfFile, fromUint8Array } from '@stirling-pdf/shared-operations/wrappers/PdfFile'
export async function fileToPdf(byteArray: Uint8Array, filename: string): Promise<PdfFile> {
const parentDir = path.join(os.tmpdir(), "StirlingPDF");
fs.mkdirSync(parentDir, {recursive: true});
const tempDir = fs.mkdtempSync(parentDir+"/");
const srcFile = path.join(tempDir, filename);
const randFolderName = path.parse(tempDir).base;
await writeBytesToFile(srcFile, byteArray);
const messages = await runLibreOfficeCommand(randFolderName, ["--headless","--convert-to","pdf",srcFile,"--outdir",tempDir]);
const lastMessage = messages[messages.length-1]
const outputFilePath = lastMessage.split(" -> ")[1].split(".pdf")[0]+".pdf";
const outputFileName = path.parse(outputFilePath).base;
const outputBytes = await readBytesFromFile(outputFilePath);
fs.rmdirSync(tempDir);
return fromUint8Array(outputBytes, outputFileName);
}
export function isLibreOfficeInstalled() {
return new Promise((resolve, reject) => {
exec("libreoffice --version", (error, stdout, stderr) => {
if (error) {
resolve(false);
return;
}
if (stderr) {
resolve(false);
return;
}
const result = stdout.match("LibreOffice ([0-9]+\.){4}.*");
resolve(result ? true : false);
});
})
}
function writeBytesToFile(filePath: string, bytes: Uint8Array): Promise<void> {
return new Promise((resolve, reject) => {
fs.writeFile(filePath, bytes, function(err) {
if(err) {
reject(err)
return;
}
resolve();
});
});
}
function readBytesFromFile(filePath: string): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
reject(new Error(`Error reading file: ${err.message}`));
} else {
const uint8Array = new Uint8Array(data);
resolve(uint8Array);
}
});
});
}
function runLibreOfficeCommand(idKey: string, args: string[]): Promise<string[]> {
return new Promise(async (resolve, reject) => {
const messageList: string[] = [];
const process = spawn("libreoffice", args);
process.stdout.on('data', (data) => {
const dataStr = data.toString();
console.log(`Progress ${idKey}:`, dataStr);
messageList.push(dataStr);
});
process.stderr.on('data', (data) => {
console.error(`stderr ${idKey}:`, data.toString());
});
process.on('exit', (code) => {
if (code === 0) {
resolve(messageList);
} else {
reject(new Error(`Command failed with exit code ${code}`));
}
});
process.on('error', (err) => {
reject(err);
});
});
}

View File

@ -1,6 +1,4 @@
import { PDFDocument } from 'pdf-lib';
import { selectPages } from "./subDocumentFunctions"; import { selectPages } from "./subDocumentFunctions";
import { PdfFile } from '../wrappers/PdfFile'; import { PdfFile } from '../wrappers/PdfFile';

View File

@ -1,5 +1,4 @@
import { PDFDocument, ParseSpeeds } from 'pdf-lib';
import { PdfFile, fromPdfLib } from '../wrappers/PdfFile'; import { PdfFile, fromPdfLib } from '../wrappers/PdfFile';
export type Metadata = { export type Metadata = {