diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 7992eb1..53ea48c 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -2,7 +2,7 @@ name: build-kit PR on: pull_request: - types: [opened, synchronize, reopened] + types: [ opened, synchronize, reopened ] jobs: ci: @@ -28,5 +28,5 @@ jobs: - name: Lint run: yarn nx affected --target=lint --base=remotes/origin/main - - name: Test - run: yarn nx affected --target=test --base=remotes/origin/main + - name: Test (non-eval) + run: yarn nx affected --target=test --base=remotes/origin/main --testPathIgnorePatterns=".*\\.eval\\.spec\\.ts" diff --git a/package.json b/package.json index 69735df..193335d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@types/js-yaml": "^4.0.9", "@types/luxon": "^3.7.1", "@types/node": "^22", + "@types/pdfkit": "^0.17.5", "@types/randomstring": "^1.3.0", "@typescript-eslint/eslint-plugin": "^8", "@typescript-eslint/parser": "^8", @@ -36,12 +37,16 @@ "jest-environment-node": "^29", "lerna": "^9.0.7", "nx": "^22.6.4", + "pdf-parse": "1.1.1", + "pdfjs-dist": "4.4.168", + "pdfkit": "^0.18.0", "pg": "^8.20.0", "prettier": "^3.8.1", "rxjs": "^7.8.2", "testcontainers": "^11.13.0", "ts-jest": "^29", "ts-node": "^10.9.2", - "typescript": "~5.8" + "typescript": "~5.8", + "unpdf": "^1.4.0" } } diff --git a/packages/agent-experiments/.eslintrc.json b/packages/agent-experiments/.eslintrc.json new file mode 100644 index 0000000..f1ff0d9 --- /dev/null +++ b/packages/agent-experiments/.eslintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "rules": {} + } + ] +} diff --git a/packages/agent-experiments/assets/config.yaml b/packages/agent-experiments/assets/config.yaml new file mode 100644 index 0000000..197bd31 --- /dev/null +++ b/packages/agent-experiments/assets/config.yaml @@ -0,0 +1,7 @@ +local: false +port: 3000 + +aiAgent: + provider: ollama + ollamaBaseUrl: http://localhost:11434 + defaultModel: "qwen2.5:latest" diff --git a/packages/agent-experiments/index.ts b/packages/agent-experiments/index.ts new file mode 100644 index 0000000..dc8efdc --- /dev/null +++ b/packages/agent-experiments/index.ts @@ -0,0 +1,2 @@ +export * from "./src/salary-extraction/salary-extraction-agent"; +export * from "./src/salary-extraction/salary-extraction-module"; diff --git a/packages/agent-experiments/jest.config.ts b/packages/agent-experiments/jest.config.ts new file mode 100644 index 0000000..0734800 --- /dev/null +++ b/packages/agent-experiments/jest.config.ts @@ -0,0 +1,11 @@ +export default { + displayName: "agent-experiments", + preset: "../../jest.preset.js", + testEnvironment: "node", + transform: { + "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }] + }, + transformIgnorePatterns: ["node_modules/(?!unpdf)"], + moduleFileExtensions: ["ts", "js", "mjs", "html"], + coverageDirectory: "../../coverage/packages/agent-experiments" +}; diff --git a/packages/agent-experiments/package.json b/packages/agent-experiments/package.json new file mode 100644 index 0000000..96aaef3 --- /dev/null +++ b/packages/agent-experiments/package.json @@ -0,0 +1,20 @@ +{ + "name": "@zeroshotbuilders/agent-experiments", + "version": "0.0.0", + "private": true, + "type": "commonjs", + "main": "./src/index.ts", + "dependencies": { + "pdfkit": "^0.16.0", + "unpdf": "^1.4.0" + }, + "devDependencies": { + "@zeroshotbuilders/commons-testing": "*" + }, + "peerDependencies": { + "@nestjs/common": ">=10.0.0", + "@nestjs/core": ">=10.0.0", + "@zeroshotbuilders/agentic-workflows": ">=0.0.1", + "@zeroshotbuilders/commons": ">=0.0.1" + } +} diff --git a/packages/agent-experiments/project.json b/packages/agent-experiments/project.json new file mode 100644 index 0000000..1d8b3c5 --- /dev/null +++ b/packages/agent-experiments/project.json @@ -0,0 +1,41 @@ +{ + "name": "agent-experiments", + "sourceRoot": "packages/agent-experiments/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nx/js:tsc", + "options": { + "outputPath": "dist/packages/agent-experiments", + "main": "packages/agent-experiments/src/index.ts", + "tsConfig": "packages/agent-experiments/tsconfig.lib.json", + "assets": [ + { + "glob": "**/*.md", + "input": "packages/agent-experiments/src", + "output": "src" + }, + { + "glob": "**/*", + "input": "packages/agent-experiments/assets", + "output": "assets" + } + ] + } + }, + "test": { + "executor": "@nx/jest:jest", + "options": { + "jestConfig": "packages/agent-experiments/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "options": { + "lintFilePatterns": ["packages/agent-experiments/**/*.ts"] + } + } + }, + "tags": [] +} diff --git a/packages/agent-experiments/src/index.ts b/packages/agent-experiments/src/index.ts new file mode 100644 index 0000000..aa7705a --- /dev/null +++ b/packages/agent-experiments/src/index.ts @@ -0,0 +1,2 @@ +export * from "./salary-extraction/salary-extraction-agent"; +export * from "./salary-extraction/salary-extraction-module"; diff --git a/packages/agent-experiments/src/salary-extraction/prompts/calculateSalary.md b/packages/agent-experiments/src/salary-extraction/prompts/calculateSalary.md new file mode 100644 index 0000000..f772ee4 --- /dev/null +++ b/packages/agent-experiments/src/salary-extraction/prompts/calculateSalary.md @@ -0,0 +1,39 @@ +You are a salary calculation agent. Your job is to determine an employee's annual salary from multiple extracted document data points. + +## Input + +You will receive a JSON array of extracted pay data from multiple documents. Each entry contains fields like: +- `documentType`, `employeeName`, `employerName` +- `payFrequency`, `grossPayThisPeriod`, `grossPayYtd` +- `annualSalaryStated`, `hourlyRate`, `hoursWorked` + +## Task + +Analyze all the extracted data and compute the annual salary. Use the following strategies: + +1. **Direct statement**: If any document explicitly states an annual salary (offer letter, W-2 Box 1, employment verification), use that as a strong signal. + +2. **Period-to-annual calculation**: For paystubs, multiply the gross pay per period by the number of periods per year: + - Weekly: multiply by 52 + - Biweekly: multiply by 26 + - Semi-monthly: multiply by 24 + - Monthly: multiply by 12 + +3. **Hourly calculation**: If hourly rate is given, calculate as: hourlyRate * hoursWorked * periodsPerYear + +4. **YTD extrapolation**: Use YTD gross and the pay period date to extrapolate (less reliable). + +5. **Cross-validation**: If multiple documents reference the same employee/employer, cross-validate the figures. Flag discrepancies. + +If documents are for different employees at different companies, pick the one with the most supporting evidence or calculate for each separately. If there are multiple employees, focus on the one with the most documents. + +## Output + +Return a JSON object with: +- `annualSalary`: the computed annual salary (best estimate) +- `confidence`: 0-1 confidence score +- `methodology`: brief description of how you arrived at the number +- `employeeName`: the employee name +- `employerName`: the employer name +- `documentsAnalyzed`: number of documents used +- `breakdown`: array of objects with `source` (filename), `derivedAnnualSalary` (if calculable), and `notes` diff --git a/packages/agent-experiments/src/salary-extraction/prompts/classifyDocument.md b/packages/agent-experiments/src/salary-extraction/prompts/classifyDocument.md new file mode 100644 index 0000000..e5d223b --- /dev/null +++ b/packages/agent-experiments/src/salary-extraction/prompts/classifyDocument.md @@ -0,0 +1,25 @@ +You are a document classification agent. Your job is to identify the type of employment/income document you are given. + +## Input + +You will receive a filename and the extracted text content of a document. + +## Task + +Classify the document into one of these types: +- **paystub** — A pay statement or earnings statement showing periodic compensation +- **w2** — A W-2 Wage and Tax Statement (annual tax form) +- **offer_letter** — An employment offer letter stating compensation terms +- **employment_verification** — A letter verifying employment status and/or salary +- **tax_return** — A tax return or related tax document +- **unknown** — Cannot determine the document type + +Also extract the employee name and employer name from the document. + +## Output + +Return a JSON object with: +- `documentType`: one of the types listed above +- `employeeName`: the full name of the employee +- `employerName`: the name of the employer/company +- `confidence`: a number between 0 and 1 indicating how confident you are in the classification diff --git a/packages/agent-experiments/src/salary-extraction/prompts/extractPayData.md b/packages/agent-experiments/src/salary-extraction/prompts/extractPayData.md new file mode 100644 index 0000000..24f901c --- /dev/null +++ b/packages/agent-experiments/src/salary-extraction/prompts/extractPayData.md @@ -0,0 +1,47 @@ +You are a financial data extraction agent. Your job is to extract compensation and pay information from employment documents. + +## Input + +You will receive: +- The document type (e.g., paystub, w2, offer_letter, employment_verification) +- The filename +- The extracted text content of the document + +## Task + +Extract all relevant compensation data from the document. The specific fields you can extract depend on the document type: + +### For paystubs: +- Pay period dates +- Pay frequency (weekly, biweekly, semi_monthly, monthly) +- Gross pay for the current period +- Year-to-date gross pay +- Hourly rate (if listed) +- Hours worked (if listed) + +### For W-2 forms: +- Annual wages (Box 1) +- This IS the annual salary stated directly + +### For offer letters: +- Annual salary (the stated annual compensation) +- Pay frequency + +### For employment verification letters: +- Annual salary (if stated) + +## Output + +Return a JSON object with: +- `documentType`: the type of document +- `employeeName`: the employee's name +- `employerName`: the employer's name +- `payPeriod`: the pay period (if applicable) +- `payFrequency`: one of "weekly", "biweekly", "semi_monthly", "monthly", "annual", "unknown" +- `grossPayThisPeriod`: gross pay for the current period (if applicable) +- `grossPayYtd`: year-to-date gross pay (if applicable) +- `annualSalaryStated`: explicitly stated annual salary (if available) +- `hourlyRate`: hourly rate (if available) +- `hoursWorked`: hours worked in the period (if available) + +Only include fields where you have data. Use null for fields you cannot determine. diff --git a/packages/agent-experiments/src/salary-extraction/salary-extraction-agent.ts b/packages/agent-experiments/src/salary-extraction/salary-extraction-agent.ts new file mode 100644 index 0000000..b11f3c0 --- /dev/null +++ b/packages/agent-experiments/src/salary-extraction/salary-extraction-agent.ts @@ -0,0 +1,188 @@ +import { + AgenticWorkflow, + Agent, + AgentRunResult, + AiAgentService, + AI_AGENT_SERVICE +} from "@zeroshotbuilders/agentic-workflows"; +import { Inject, Injectable } from "@nestjs/common"; +import { createLogger, transports } from "winston"; +import { z } from "zod"; + +// ── Output Schemas ────────────────────────────────────────────────────────── + +export const DocumentClassificationSchema = z.object({ + documentType: z.enum([ + "paystub", + "w2", + "offer_letter", + "employment_verification", + "tax_return", + "unknown" + ]), + employeeName: z.string(), + employerName: z.string(), + confidence: z.number().min(0).max(1) +}); + +export type DocumentClassification = z.infer< + typeof DocumentClassificationSchema +>; + +export const PayDataExtractionSchema = z.object({ + documentType: z.string(), + employeeName: z.string(), + employerName: z.string(), + payPeriod: z.string().nullable(), + payFrequency: z + .enum(["weekly", "biweekly", "semi_monthly", "monthly", "annual", "unknown"]) + .nullable(), + grossPayThisPeriod: z.number().nullable(), + grossPayYtd: z.number().nullable(), + annualSalaryStated: z.number().nullable(), + hourlyRate: z.number().nullable(), + hoursWorked: z.number().nullable() +}); + +export type PayDataExtraction = z.infer; + +export const SalaryCalculationSchema = z.object({ + annualSalary: z.number(), + confidence: z.number().min(0).max(1), + methodology: z.string(), + employeeName: z.string(), + employerName: z.string(), + documentsAnalyzed: z.number(), + breakdown: z.array( + z.object({ + source: z.string(), + derivedAnnualSalary: z.number().nullable(), + notes: z.string() + }) + ) +}); + +export type SalaryCalculation = z.infer; + +// ── Agent Pipeline ────────────────────────────────────────────────────────── + +@AgenticWorkflow({ + promptsDirectory: `${__dirname}/prompts` +}) +@Injectable() +export class SalaryExtractionAgent { + private readonly logger = createLogger({ + transports: [new transports.Console()] + }); + + constructor( + @Inject(AI_AGENT_SERVICE) + private readonly aiAgentService: AiAgentService + ) {} + + /** + * Main pipeline entry point: takes an array of document texts and returns + * the computed annual salary. + */ + async extractSalary( + documents: Array<{ filename: string; text: string }> + ): Promise { + this.logger.info( + `Starting salary extraction pipeline with ${documents.length} documents` + ); + + // Phase 1: Classify all documents + const classifications = await Promise.all( + documents.map(async (doc) => { + const result = await this.classifyDocument(doc.filename, doc.text); + if (!result.success) { + this.logger.warn( + `Classification failed for ${doc.filename}: ${result.error}` + ); + } + return { ...doc, classification: result.output }; + }) + ); + + this.logger.info( + "Classifications complete", + classifications.map((c) => ({ + file: c.filename, + type: c.classification?.documentType + })) + ); + + // Phase 2: Extract pay data from each document + const extractions = await Promise.all( + classifications.map(async (doc) => { + const result = await this.extractPayData( + doc.classification?.documentType ?? "unknown", + doc.filename, + doc.text + ); + if (!result.success) { + this.logger.warn( + `Extraction failed for ${doc.filename}: ${result.error}` + ); + } + return { filename: doc.filename, extraction: result.output }; + }) + ); + + this.logger.info("Extractions complete"); + + // Phase 3: Calculate annual salary from all extractions + const extractionSummary = JSON.stringify( + extractions.map((e) => ({ + filename: e.filename, + ...e.extraction + })), + null, + 2 + ); + + const result = await this.calculateSalary(extractionSummary); + if (!result.success) { + throw new Error(`Salary calculation failed: ${result.error}`); + } + + this.logger.info("Salary calculation complete", { + annualSalary: result.output.annualSalary, + confidence: result.output.confidence + }); + + return result.output; + } + + // ── Agent Methods (intercepted by decorator) ───────────────────────────── + + @Agent({ + outputSchema: DocumentClassificationSchema + }) + private async classifyDocument( + filename: string, + documentText: string + ): Promise> { + return null as any; + } + + @Agent({ + outputSchema: PayDataExtractionSchema + }) + private async extractPayData( + documentType: string, + filename: string, + documentText: string + ): Promise> { + return null as any; + } + + @Agent({ + outputSchema: SalaryCalculationSchema + }) + private async calculateSalary( + extractedData: string + ): Promise> { + return null as any; + } +} diff --git a/packages/agent-experiments/src/salary-extraction/salary-extraction-module.ts b/packages/agent-experiments/src/salary-extraction/salary-extraction-module.ts new file mode 100644 index 0000000..dfbe945 --- /dev/null +++ b/packages/agent-experiments/src/salary-extraction/salary-extraction-module.ts @@ -0,0 +1,19 @@ +import { AiAgentModule } from "@zeroshotbuilders/agentic-workflows"; +import { ConfigModule } from "@zeroshotbuilders/commons"; +import { DynamicModule, Module } from "@nestjs/common"; +import { SalaryExtractionAgent } from "./salary-extraction-agent"; + +@Module({}) +export class SalaryExtractionModule { + static forApplicationRoot(applicationRoot: string): DynamicModule { + return { + module: SalaryExtractionModule, + providers: [SalaryExtractionAgent], + imports: [ + ConfigModule.forApplicationRoot(applicationRoot), + AiAgentModule.forApplicationRoot(applicationRoot) + ], + exports: [SalaryExtractionAgent] + }; + } +} diff --git a/packages/agent-experiments/test/assets/extract-pdf-text.mjs b/packages/agent-experiments/test/assets/extract-pdf-text.mjs new file mode 100644 index 0000000..3d6b1a7 --- /dev/null +++ b/packages/agent-experiments/test/assets/extract-pdf-text.mjs @@ -0,0 +1,7 @@ +import { extractText } from "unpdf"; +import { readFileSync } from "fs"; + +const filePath = process.argv[2]; +const buffer = readFileSync(filePath); +const result = await extractText(new Uint8Array(buffer)); +process.stdout.write(result.text.join("\n")); diff --git a/packages/agent-experiments/test/assets/fixtures/employment-verification-techforward.pdf b/packages/agent-experiments/test/assets/fixtures/employment-verification-techforward.pdf new file mode 100644 index 0000000..2a5326f Binary files /dev/null and b/packages/agent-experiments/test/assets/fixtures/employment-verification-techforward.pdf differ diff --git a/packages/agent-experiments/test/assets/fixtures/offer-letter-acme.pdf b/packages/agent-experiments/test/assets/fixtures/offer-letter-acme.pdf new file mode 100644 index 0000000..f3d7a27 Binary files /dev/null and b/packages/agent-experiments/test/assets/fixtures/offer-letter-acme.pdf differ diff --git a/packages/agent-experiments/test/assets/fixtures/paystub-acme-biweekly.pdf b/packages/agent-experiments/test/assets/fixtures/paystub-acme-biweekly.pdf new file mode 100644 index 0000000..1669b2b Binary files /dev/null and b/packages/agent-experiments/test/assets/fixtures/paystub-acme-biweekly.pdf differ diff --git a/packages/agent-experiments/test/assets/fixtures/paystub-greenleaf-weekly.pdf b/packages/agent-experiments/test/assets/fixtures/paystub-greenleaf-weekly.pdf new file mode 100644 index 0000000..e8db276 Binary files /dev/null and b/packages/agent-experiments/test/assets/fixtures/paystub-greenleaf-weekly.pdf differ diff --git a/packages/agent-experiments/test/assets/fixtures/paystub-techforward-monthly.pdf b/packages/agent-experiments/test/assets/fixtures/paystub-techforward-monthly.pdf new file mode 100644 index 0000000..a759da9 Binary files /dev/null and b/packages/agent-experiments/test/assets/fixtures/paystub-techforward-monthly.pdf differ diff --git a/packages/agent-experiments/test/assets/fixtures/w2-acme-2024.pdf b/packages/agent-experiments/test/assets/fixtures/w2-acme-2024.pdf new file mode 100644 index 0000000..291b32f Binary files /dev/null and b/packages/agent-experiments/test/assets/fixtures/w2-acme-2024.pdf differ diff --git a/packages/agent-experiments/test/assets/generate-fixtures.ts b/packages/agent-experiments/test/assets/generate-fixtures.ts new file mode 100644 index 0000000..b120e49 --- /dev/null +++ b/packages/agent-experiments/test/assets/generate-fixtures.ts @@ -0,0 +1,335 @@ +import PDFDocument from "pdfkit"; +import fs from "fs"; +import path from "path"; + +const FIXTURES_DIR = path.join(__dirname, "fixtures"); + +function ensureDir(dir: string) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +function writePdf( + filename: string, + buildFn: (doc: PDFKit.PDFDocument) => void +): Promise { + return new Promise((resolve, reject) => { + const doc = new PDFDocument({ size: "LETTER", margin: 50 }); + const stream = fs.createWriteStream(path.join(FIXTURES_DIR, filename)); + doc.pipe(stream); + buildFn(doc); + doc.end(); + stream.on("finish", resolve); + stream.on("error", reject); + }); +} + +// ── Paystub 1: Biweekly, $95,000/year ────────────────────────────────────── + +function generatePaystub1(doc: PDFKit.PDFDocument) { + doc.fontSize(18).text("ACME CORPORATION", { align: "center" }); + doc.fontSize(10).text("123 Business Ave, Suite 400, San Francisco, CA 94105", { + align: "center" + }); + doc.moveDown(); + doc.fontSize(14).text("EARNINGS STATEMENT", { align: "center" }); + doc.moveDown(); + + doc.fontSize(10); + doc.text("Employee: Sarah J. Mitchell"); + doc.text("Employee ID: EMP-20412"); + doc.text("SSN: XXX-XX-4829"); + doc.text("Pay Period: 01/01/2025 - 01/15/2025"); + doc.text("Pay Date: 01/17/2025"); + doc.text("Pay Frequency: Biweekly"); + doc.moveDown(); + + doc.fontSize(12).text("EARNINGS", { underline: true }); + doc.fontSize(10); + doc.text("Regular Hours: 80.00 hrs x $45.6731/hr = $3,653.85"); + doc.moveDown(0.5); + doc.text("Gross Pay (This Period): $3,653.85"); + doc.text("Gross Pay (YTD): $3,653.85"); + doc.moveDown(); + + doc.fontSize(12).text("DEDUCTIONS", { underline: true }); + doc.fontSize(10); + doc.text("Federal Income Tax: $548.08"); + doc.text("State Income Tax (CA): $232.87"); + doc.text("Social Security (OASDI): $226.54"); + doc.text("Medicare: $52.98"); + doc.text("401(k) Employee (6%): $219.23"); + doc.text("Health Insurance: $187.50"); + doc.text("Dental Insurance: $22.00"); + doc.moveDown(0.5); + doc.text("Total Deductions: $1,489.20"); + doc.moveDown(); + + doc.fontSize(12).text("NET PAY", { underline: true }); + doc.fontSize(10); + doc.text("Net Pay (This Period): $2,164.65"); + doc.text("Net Pay (YTD): $2,164.65"); + doc.moveDown(); + doc.text("Direct Deposit: Chase Bank ****7721 $2,164.65"); +} + +// ── Paystub 2: Monthly, $120,000/year ─────────────────────────────────────── + +function generatePaystub2(doc: PDFKit.PDFDocument) { + doc.fontSize(16).text("TechForward Inc.", { align: "center" }); + doc.fontSize(9).text("One Market Street, Floor 22, San Francisco, CA 94111", { + align: "center" + }); + doc.moveDown(); + doc.fontSize(13).text("Pay Statement", { align: "center" }); + doc.moveDown(); + + doc.fontSize(10); + doc.text("Name: James R. Chen"); + doc.text("Department: Engineering"); + doc.text("Title: Senior Software Engineer"); + doc.text("Period: March 1, 2025 - March 31, 2025"); + doc.text("Check Date: March 31, 2025"); + doc.text("Pay Schedule: Monthly"); + doc.moveDown(); + + doc.fontSize(11).text("Compensation", { underline: true }); + doc.fontSize(10); + doc.text("Base Salary: $10,000.00"); + doc.moveDown(0.5); + doc.text("Current Gross: $10,000.00"); + doc.text("YTD Gross: $30,000.00"); + doc.moveDown(); + + doc.fontSize(11).text("Withholdings & Deductions", { underline: true }); + doc.fontSize(10); + doc.text("Federal Tax: $1,665.00"); + doc.text("CA State Tax: $730.00"); + doc.text("Social Security: $620.00"); + doc.text("Medicare: $145.00"); + doc.text("Medical (PPO): $275.00"); + doc.text("Vision: $12.50"); + doc.text("401(k) (8%): $800.00"); + doc.text("Roth 401(k) (2%): $200.00"); + doc.moveDown(0.5); + doc.text("Total Deductions: $4,447.50"); + doc.moveDown(); + + doc.fontSize(11).text("Net Pay", { underline: true }); + doc.fontSize(10); + doc.text("Take-Home Pay: $5,552.50"); +} + +// ── Paystub 3: Weekly, $78,000/year ───────────────────────────────────────── + +function generatePaystub3(doc: PDFKit.PDFDocument) { + doc.fontSize(16).text("GREENLEAF SERVICES LLC", { align: "center" }); + doc.fontSize(9).text("800 Oak Blvd, Portland, OR 97201", { align: "center" }); + doc.moveDown(); + doc.fontSize(12).text("PAYROLL CHECK STUB", { align: "center" }); + doc.moveDown(); + + doc.fontSize(10); + doc.text("Employee Name: Maria L. Torres"); + doc.text("Employee #: GL-0893"); + doc.text("Week Ending: 02/07/2025"); + doc.text("Check #: 44219"); + doc.text("Frequency: Weekly"); + doc.moveDown(); + + doc.text("─".repeat(60)); + doc.text("HOURS AND EARNINGS"); + doc.text("Regular 40.00 hrs $37.50/hr $1,500.00"); + doc.text("─".repeat(60)); + doc.text("GROSS PAY $1,500.00"); + doc.text("YTD GROSS $9,000.00"); + doc.moveDown(); + + doc.text("TAX WITHHOLDINGS"); + doc.text("Federal Income Tax $195.00"); + doc.text("OR State Income Tax $120.00"); + doc.text("Social Security $93.00"); + doc.text("Medicare $21.75"); + doc.moveDown(); + doc.text("OTHER DEDUCTIONS"); + doc.text("Health Insurance $92.31"); + doc.text("HSA Contribution $50.00"); + doc.text("─".repeat(60)); + doc.text("TOTAL DEDUCTIONS $572.06"); + doc.text("NET PAY $927.94"); +} + +// ── W-2 Form (Acme Corp for Sarah Mitchell, same as paystub 1) ──────────── + +function generateW2(doc: PDFKit.PDFDocument) { + doc.fontSize(16).text("Form W-2 Wage and Tax Statement 2024", { + align: "center" + }); + doc.moveDown(); + + doc.fontSize(10); + doc.text("a Employee's social security number: XXX-XX-4829"); + doc.moveDown(0.5); + doc.text("b Employer identification number (EIN): 94-3218765"); + doc.moveDown(0.5); + doc.text("c Employer's name, address, and ZIP code:"); + doc.text(" ACME CORPORATION"); + doc.text(" 123 Business Ave, Suite 400"); + doc.text(" San Francisco, CA 94105"); + doc.moveDown(0.5); + doc.text("e Employee's first name and initial: Sarah J."); + doc.text(" Last name: Mitchell"); + doc.text(" Address: 456 Residential Lane, Apt 7B"); + doc.text(" San Francisco, CA 94102"); + doc.moveDown(); + + doc.fontSize(11).text("Wage and Tax Data", { underline: true }); + doc.fontSize(10); + doc.text("1 Wages, tips, other compensation: $95,000.00"); + doc.text("2 Federal income tax withheld: $14,250.00"); + doc.text("3 Social security wages: $95,000.00"); + doc.text("4 Social security tax withheld: $5,890.00"); + doc.text("5 Medicare wages and tips: $95,000.00"); + doc.text("6 Medicare tax withheld: $1,377.50"); + doc.moveDown(0.5); + doc.text("12a Code D - 401(k): $5,700.00"); + doc.text("12b Code DD - Health insurance: $4,875.00"); + doc.moveDown(0.5); + doc.text("16 State wages, tips, etc.: $95,000.00"); + doc.text("17 State income tax: $6,055.00"); + doc.text("18 Local wages, tips, etc.: $95,000.00"); +} + +// ── Offer Letter ($95,000/year for Sarah Mitchell) ───────────────────────── + +function generateOfferLetter(doc: PDFKit.PDFDocument) { + doc.fontSize(16).text("ACME CORPORATION", { align: "center" }); + doc.fontSize(10).text("123 Business Ave, Suite 400, San Francisco, CA 94105", { + align: "center" + }); + doc.moveDown(2); + + doc.fontSize(10); + doc.text("November 15, 2024"); + doc.moveDown(); + doc.text("Sarah J. Mitchell"); + doc.text("456 Residential Lane, Apt 7B"); + doc.text("San Francisco, CA 94102"); + doc.moveDown(); + + doc.text("Dear Sarah,"); + doc.moveDown(); + doc.text( + "We are pleased to offer you the position of Product Manager at ACME Corporation. " + + "We believe your experience and skills will be a valuable addition to our team.", + { lineGap: 4 } + ); + doc.moveDown(); + + doc.text("The details of your offer are as follows:", { lineGap: 4 }); + doc.moveDown(); + doc.text("Position: Product Manager"); + doc.text("Department: Product"); + doc.text("Reports To: VP of Product, David Park"); + doc.text("Start Date: January 2, 2025"); + doc.text("Employment Type: Full-time, Exempt"); + doc.moveDown(); + + doc.fontSize(11).text("Compensation", { underline: true }); + doc.fontSize(10); + doc.text( + "Your annual base salary will be $95,000.00, paid on a biweekly basis " + + "(26 pay periods per year). Your per-period gross pay will be approximately $3,653.85." + ); + doc.moveDown(); + + doc.text( + "You will be eligible for an annual performance bonus of up to 10% of your base salary, " + + "subject to company and individual performance targets." + ); + doc.moveDown(); + + doc.fontSize(11).text("Benefits", { underline: true }); + doc.fontSize(10); + doc.text("- Medical, dental, and vision insurance (employee contribution applies)"); + doc.text("- 401(k) retirement plan with 4% company match"); + doc.text("- 15 days PTO + 10 company holidays"); + doc.text("- $1,500 annual professional development stipend"); + doc.moveDown(2); + + doc.text("Sincerely,"); + doc.moveDown(); + doc.text("Jennifer Wu"); + doc.text("Director of People Operations"); + doc.text("ACME CORPORATION"); +} + +// ── Employment Verification Letter (TechForward for James Chen) ──────────── + +function generateEmploymentVerification(doc: PDFKit.PDFDocument) { + doc.fontSize(16).text("TechForward Inc.", { align: "center" }); + doc.fontSize(9).text("One Market Street, Floor 22, San Francisco, CA 94111", { + align: "center" + }); + doc.moveDown(2); + + doc.fontSize(10); + doc.text("February 20, 2025"); + doc.moveDown(); + doc.text("To Whom It May Concern,"); + doc.moveDown(); + doc.text( + "This letter is to confirm that James R. Chen is currently employed at TechForward Inc. " + + "as a Senior Software Engineer in the Engineering department.", + { lineGap: 4 } + ); + doc.moveDown(); + doc.text("Employment details are as follows:"); + doc.moveDown(); + doc.text("Employee Name: James R. Chen"); + doc.text("Title: Senior Software Engineer"); + doc.text("Department: Engineering"); + doc.text("Date of Hire: June 15, 2022"); + doc.text("Employment Status: Full-time"); + doc.text("Current Annual Salary: $120,000.00"); + doc.moveDown(); + doc.text( + "If you require any additional information, please do not hesitate to contact our " + + "Human Resources department at hr@techforward.com or (415) 555-0192.", + { lineGap: 4 } + ); + doc.moveDown(2); + doc.text("Best regards,"); + doc.moveDown(); + doc.text("Amanda Liu"); + doc.text("HR Business Partner"); + doc.text("TechForward Inc."); +} + +export async function generateAllFixtures(): Promise { + ensureDir(FIXTURES_DIR); + + const files = [ + { name: "paystub-acme-biweekly.pdf", fn: generatePaystub1 }, + { name: "paystub-techforward-monthly.pdf", fn: generatePaystub2 }, + { name: "paystub-greenleaf-weekly.pdf", fn: generatePaystub3 }, + { name: "w2-acme-2024.pdf", fn: generateW2 }, + { name: "offer-letter-acme.pdf", fn: generateOfferLetter }, + { name: "employment-verification-techforward.pdf", fn: generateEmploymentVerification } + ]; + + for (const file of files) { + await writePdf(file.name, file.fn); + } + + return files.map((f) => path.join(FIXTURES_DIR, f.name)); +} + +// Allow running standalone +if (require.main === module) { + generateAllFixtures().then((files) => { + console.log("Generated fixtures:"); + files.forEach((f) => console.log(` ${f}`)); + }); +} diff --git a/packages/agent-experiments/test/salary-extraction.eval.spec.ts b/packages/agent-experiments/test/salary-extraction.eval.spec.ts new file mode 100644 index 0000000..39a953d --- /dev/null +++ b/packages/agent-experiments/test/salary-extraction.eval.spec.ts @@ -0,0 +1,115 @@ +import { + SalaryExtractionAgent, + SalaryExtractionModule +} from "@zeroshotbuilders/agent-experiments"; +import { NestFactory } from "@nestjs/core"; +import { execFileSync } from "child_process"; +import path from "path"; +import { generateAllFixtures } from "./assets/generate-fixtures"; + +const FIXTURES_DIR = path.join(__dirname, "assets", "fixtures"); +const EXTRACT_SCRIPT = path.join(__dirname, "assets", "extract-pdf-text.mjs"); +const APPLICATION_ROOT = path.join(__dirname, "..", "assets"); + +function readPdf(filePath: string): string { + return execFileSync("node", [EXTRACT_SCRIPT, filePath], { + encoding: "utf-8", + timeout: 15_000 + }); +} + +function loadDocuments( + filenames: string[] +): Array<{ filename: string; text: string }> { + return filenames.map((filename) => ({ + filename, + text: readPdf(path.join(FIXTURES_DIR, filename)) + })); +} + +jest.setTimeout(300_000); + +describe("Salary Extraction Pipeline", () => { + let agent: SalaryExtractionAgent; + let app: any; + + beforeAll(async () => { + await generateAllFixtures(); + + app = await NestFactory.createApplicationContext( + SalaryExtractionModule.forApplicationRoot(APPLICATION_ROOT) + ); + agent = app.get(SalaryExtractionAgent); + }); + + afterAll(async () => { + if (app) { + await app.close(); + } + }); + + it("should extract salary from ACME paystub + W-2 + offer letter", async () => { + const documents = loadDocuments([ + "paystub-acme-biweekly.pdf", + "w2-acme-2024.pdf", + "offer-letter-acme.pdf" + ]); + + const result = await agent.extractSalary(documents); + + console.log("ACME Result:", JSON.stringify(result, null, 2)); + + expect(result.annualSalary).toBeGreaterThan(90_000); + expect(result.annualSalary).toBeLessThan(100_000); + expect(result.employeeName).toContain("Mitchell"); + expect(result.confidence).toBeGreaterThan(0.5); + expect(result.documentsAnalyzed).toBe(3); + }); + + it("should extract salary from TechForward monthly paystub + verification letter", async () => { + const documents = loadDocuments([ + "paystub-techforward-monthly.pdf", + "employment-verification-techforward.pdf" + ]); + + const result = await agent.extractSalary(documents); + + console.log("TechForward Result:", JSON.stringify(result, null, 2)); + + expect(result.annualSalary).toBeGreaterThan(115_000); + expect(result.annualSalary).toBeLessThan(125_000); + expect(result.employeeName).toContain("Chen"); + expect(result.confidence).toBeGreaterThan(0.5); + }); + + it("should extract salary from Greenleaf weekly paystub alone", async () => { + const documents = loadDocuments(["paystub-greenleaf-weekly.pdf"]); + + const result = await agent.extractSalary(documents); + + console.log("Greenleaf Result:", JSON.stringify(result, null, 2)); + + expect(result.annualSalary).toBeGreaterThan(74_000); + expect(result.annualSalary).toBeLessThan(82_000); + expect(result.employeeName).toContain("Torres"); + }); + + it("should handle all documents together and pick best estimate", async () => { + const documents = loadDocuments([ + "paystub-acme-biweekly.pdf", + "w2-acme-2024.pdf", + "offer-letter-acme.pdf", + "paystub-techforward-monthly.pdf", + "employment-verification-techforward.pdf", + "paystub-greenleaf-weekly.pdf" + ]); + + const result = await agent.extractSalary(documents); + + console.log("All Documents Result:", JSON.stringify(result, null, 2)); + + expect(result.annualSalary).toBeGreaterThan(0); + expect(result.breakdown.length).toBeGreaterThan(0); + expect(result.methodology).toBeTruthy(); + }); +}); diff --git a/packages/agent-experiments/tsconfig.json b/packages/agent-experiments/tsconfig.json new file mode 100644 index 0000000..46167d5 --- /dev/null +++ b/packages/agent-experiments/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc" + }, + "files": [], + "include": [], + "references": [ + { "path": "./tsconfig.lib.json" }, + { "path": "./tsconfig.spec.json" } + ] +} diff --git a/packages/agent-experiments/tsconfig.lib.json b/packages/agent-experiments/tsconfig.lib.json new file mode 100644 index 0000000..e927de6 --- /dev/null +++ b/packages/agent-experiments/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node", "reflect-metadata"] + }, + "include": ["index.ts", "src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/packages/agent-experiments/tsconfig.spec.json b/packages/agent-experiments/tsconfig.spec.json new file mode 100644 index 0000000..3835753 --- /dev/null +++ b/packages/agent-experiments/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node", "reflect-metadata"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts", + "test/**/*.ts" + ] +} diff --git a/packages/commons-testing/index.ts b/packages/commons-testing/index.ts index 44df151..2139886 100644 --- a/packages/commons-testing/index.ts +++ b/packages/commons-testing/index.ts @@ -1,6 +1,5 @@ export * from "./src/postgres/postgres-container"; export * from "./src/redis/redis-container"; -export * from "./src/ollama/ollama-fixture"; export * from "./src/async/async-test-utils"; export * from "./src/booter/booter-configuration"; export * from "./src/exceptions/jest-exception"; diff --git a/packages/commons-testing/src/ollama/ollama-fixture.ts b/packages/commons-testing/src/ollama/ollama-fixture.ts deleted file mode 100644 index b296533..0000000 --- a/packages/commons-testing/src/ollama/ollama-fixture.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { GenericContainer, StartedTestContainer, Wait } from "testcontainers"; -import { createLogger, transports } from "winston"; - -export enum OllamaMode { - LOCAL = "LOCAL", - CONTAINER = "CONTAINER" -} - -export interface OllamaFixtureConfig { - mode: OllamaMode; - localHost?: string; - localPort?: number; -} - -/** - * OllamaFixture provides a unified interface for running Ollama either locally - * (for development on Mac with Apple Silicon) or via Docker container (for CI/testing). - * - * Local mode assumes Ollama is installed natively on the host machine. - * Container mode uses the official ollama/ollama Docker image. - * - * For local mode on Mac, install Ollama via: - * brew install ollama - * ollama serve - */ -export class OllamaFixture { - public static readonly PORT = 11434; - public static readonly IMAGE = "ollama/ollama:latest"; - public static readonly DEFAULT_LOCAL_HOST = "localhost"; - - private readonly logger = createLogger({ - transports: [new transports.Console()] - }); - - private readonly mode: OllamaMode; - private readonly localHost: string; - private readonly localPort: number; - - private container: GenericContainer | undefined; - private startedContainer: StartedTestContainer | undefined; - - constructor(config: OllamaFixtureConfig = { mode: OllamaMode.CONTAINER }) { - this.mode = config.mode; - this.localHost = config.localHost ?? OllamaFixture.DEFAULT_LOCAL_HOST; - this.localPort = config.localPort ?? OllamaFixture.PORT; - - if (this.mode === OllamaMode.CONTAINER) { - this.container = new GenericContainer(OllamaFixture.IMAGE) - .withWaitStrategy(Wait.forLogMessage(new RegExp(".*Listening on.*"), 1)) - .withExposedPorts(OllamaFixture.PORT); - } - } - - public async start(): Promise { - if (this.mode === OllamaMode.CONTAINER) { - if (!this.container) { - throw new Error("Container not initialized"); - } - this.logger.info("Starting Ollama container..."); - this.startedContainer = await this.container.start(); - this.logger.info(`Ollama container started at ${this.getBaseUrl()}`); - } else { - this.logger.info(`Using local Ollama instance at ${this.getBaseUrl()}`); - } - } - - public async stop(): Promise { - if (this.mode === OllamaMode.CONTAINER && this.startedContainer) { - this.logger.info("Stopping Ollama container..."); - await this.startedContainer.stop(); - this.startedContainer = undefined; - } - } - - public getHost(): string { - if (this.mode === OllamaMode.CONTAINER) { - if (!this.startedContainer) { - throw new Error("Container not started"); - } - return this.startedContainer.getHost(); - } - return this.localHost; - } - - public getPort(): number { - if (this.mode === OllamaMode.CONTAINER) { - if (!this.startedContainer) { - throw new Error("Container not started"); - } - return this.startedContainer.getMappedPort(OllamaFixture.PORT); - } - return this.localPort; - } - - public getBaseUrl(): string { - return `http://${this.getHost()}:${this.getPort()}`; - } - - public getMode(): OllamaMode { - return this.mode; - } - - public isLocal(): boolean { - return this.mode === OllamaMode.LOCAL; - } - - public isContainer(): boolean { - return this.mode === OllamaMode.CONTAINER; - } -} diff --git a/tsconfig.base.json b/tsconfig.base.json index 1aeeb12..571d702 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -29,7 +29,8 @@ "@zeroshotbuilders/agentic-workflows": ["packages/agentic-workflows/index.ts"], "@zeroshotbuilders/openai-utils": ["packages/openai-utils/index.ts"], "@zeroshotbuilders/sql-decorators": ["packages/sql-decorators/index.ts"], - "@zeroshotbuilders/tavily-utils": ["packages/tavily-utils/index.ts"] + "@zeroshotbuilders/tavily-utils": ["packages/tavily-utils/index.ts"], + "@zeroshotbuilders/agent-experiments": ["packages/agent-experiments/index.ts"] } }, "exclude": ["node_modules"] diff --git a/yarn.lock b/yarn.lock index e108d2b..a18fec8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2468,6 +2468,25 @@ __metadata: languageName: node linkType: hard +"@mapbox/node-pre-gyp@npm:^1.0.0": + version: 1.0.11 + resolution: "@mapbox/node-pre-gyp@npm:1.0.11" + dependencies: + detect-libc: "npm:^2.0.0" + https-proxy-agent: "npm:^5.0.0" + make-dir: "npm:^3.1.0" + node-fetch: "npm:^2.6.7" + nopt: "npm:^5.0.0" + npmlog: "npm:^5.0.1" + rimraf: "npm:^3.0.2" + semver: "npm:^7.3.5" + tar: "npm:^6.1.11" + bin: + node-pre-gyp: bin/node-pre-gyp + checksum: 10c0/2b24b93c31beca1c91336fa3b3769fda98e202fb7f9771f0f4062588d36dcc30fcf8118c36aa747fa7f7610d8cf601872bdaaf62ce7822bb08b545d1bbe086cc + languageName: node + linkType: hard + "@modelcontextprotocol/sdk@npm:^1.25.2": version: 1.29.0 resolution: "@modelcontextprotocol/sdk@npm:1.29.0" @@ -2650,6 +2669,20 @@ __metadata: languageName: node linkType: hard +"@noble/ciphers@npm:^1.0.0": + version: 1.3.0 + resolution: "@noble/ciphers@npm:1.3.0" + checksum: 10c0/3ba6da645ce45e2f35e3b2e5c87ceba86b21dfa62b9466ede9edfb397f8116dae284f06652c0cd81d99445a2262b606632e868103d54ecc99fd946ae1af8cd37 + languageName: node + linkType: hard + +"@noble/hashes@npm:^1.6.0": + version: 1.8.0 + resolution: "@noble/hashes@npm:1.8.0" + checksum: 10c0/06a0b52c81a6fa7f04d67762e08b2c476a00285858150caeaaff4037356dd5e119f45b2a530f638b77a5eeca013168ec1b655db41bae3236cb2e9d511484fc77 + languageName: node + linkType: hard + "@npmcli/agent@npm:^4.0.0": version: 4.0.0 resolution: "@npmcli/agent@npm:4.0.0" @@ -3586,6 +3619,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:^0.5.12": + version: 0.5.21 + resolution: "@swc/helpers@npm:0.5.21" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10c0/692018ec8a9f7ea5ea3fe576fea5af1a782c8bc1752fcb60f949b482fb2521609d1d3710908aebae67086f152e57d231d59b08f4653fd20a2e3e0fa4a34e6322 + languageName: node + linkType: hard + "@tavily/core@npm:^0.6.0": version: 0.6.4 resolution: "@tavily/core@npm:0.6.4" @@ -3915,6 +3957,15 @@ __metadata: languageName: node linkType: hard +"@types/pdfkit@npm:^0.17.5": + version: 0.17.5 + resolution: "@types/pdfkit@npm:0.17.5" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/4b854f5172f899684d231d45feb4018fee5806e639859b55bbdc93c0abe185c83194ac82da5f82d7e7716eb042079df4e21806d2ab628b955bb407be8dfbe2ea + languageName: node + linkType: hard + "@types/randomstring@npm:^1.3.0": version: 1.3.0 resolution: "@types/randomstring@npm:1.3.0" @@ -4290,6 +4341,21 @@ __metadata: languageName: node linkType: hard +"@zeroshotbuilders/agent-experiments@workspace:packages/agent-experiments": + version: 0.0.0-use.local + resolution: "@zeroshotbuilders/agent-experiments@workspace:packages/agent-experiments" + dependencies: + "@zeroshotbuilders/commons-testing": "npm:*" + pdfkit: "npm:^0.16.0" + unpdf: "npm:^1.4.0" + peerDependencies: + "@nestjs/common": ">=10.0.0" + "@nestjs/core": ">=10.0.0" + "@zeroshotbuilders/agentic-workflows": ">=0.0.1" + "@zeroshotbuilders/commons": ">=0.0.1" + languageName: unknown + linkType: soft + "@zeroshotbuilders/agentic-workflows@workspace:packages/agentic-workflows": version: 0.0.0-use.local resolution: "@zeroshotbuilders/agentic-workflows@workspace:packages/agentic-workflows" @@ -4303,7 +4369,7 @@ __metadata: languageName: unknown linkType: soft -"@zeroshotbuilders/commons-testing@workspace:packages/commons-testing": +"@zeroshotbuilders/commons-testing@npm:*, @zeroshotbuilders/commons-testing@workspace:packages/commons-testing": version: 0.0.0-use.local resolution: "@zeroshotbuilders/commons-testing@workspace:packages/commons-testing" dependencies: @@ -4397,6 +4463,13 @@ __metadata: languageName: node linkType: hard +"abbrev@npm:1": + version: 1.1.1 + resolution: "abbrev@npm:1.1.1" + checksum: 10c0/3f762677702acb24f65e813070e306c61fafe25d4b2583f9dfc935131f774863f3addd5741572ed576bd69cabe473c5af18e1e108b829cb7b6b4747884f726e6 + languageName: node + linkType: hard + "abbrev@npm:^3.0.0": version: 3.0.1 resolution: "abbrev@npm:3.0.1" @@ -4488,6 +4561,15 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261 + languageName: node + linkType: hard + "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.4 resolution: "agent-base@npm:7.1.4" @@ -4629,6 +4711,13 @@ __metadata: languageName: node linkType: hard +"aproba@npm:^1.0.3 || ^2.0.0": + version: 2.1.0 + resolution: "aproba@npm:2.1.0" + checksum: 10c0/ec8c1d351bac0717420c737eb062766fb63bde1552900e0f4fdad9eb064c3824fef23d1c416aa5f7a80f21ca682808e902d79b7c9ae756d342b5f1884f36932f + languageName: node + linkType: hard + "archiver-utils@npm:^5.0.0, archiver-utils@npm:^5.0.2": version: 5.0.2 resolution: "archiver-utils@npm:5.0.2" @@ -4659,6 +4748,16 @@ __metadata: languageName: node linkType: hard +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" + dependencies: + delegates: "npm:^1.0.0" + readable-stream: "npm:^3.6.0" + checksum: 10c0/375f753c10329153c8d66dc95e8f8b6c7cc2aa66e05cb0960bd69092b10dae22900cacc7d653ad11d26b3ecbdbfe1e8bfb6ccf0265ba8077a7d979970f16b99c + languageName: node + linkType: hard + "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -5068,7 +5167,14 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base64-js@npm:0.0.8": + version: 0.0.8 + resolution: "base64-js@npm:0.0.8" + checksum: 10c0/60f02a9fdbbbb251c0a1064834d4062f5a3c4237edd9f0313282d75980a80ce303316795f7a80c8e240e524169644d88445ec0697b03f81ab9f4458090979375 + languageName: node + linkType: hard + +"base64-js@npm:^1.1.2, base64-js@npm:^1.3.0, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf @@ -5209,6 +5315,15 @@ __metadata: languageName: node linkType: hard +"brotli@npm:^1.3.2": + version: 1.3.3 + resolution: "brotli@npm:1.3.3" + dependencies: + base64-js: "npm:^1.1.2" + checksum: 10c0/9d24e24f8b7eabf44af034ed5f7d5530008b835f09a107a84ac060723e86dd43c6aa68958691fe5df524f59473b35f5ce2e0854aa1152c0a254d1010f51bcf22 + languageName: node + linkType: hard + "browserslist@npm:^4.24.0, browserslist@npm:^4.28.1": version: 4.28.2 resolution: "browserslist@npm:4.28.2" @@ -5293,6 +5408,7 @@ __metadata: "@types/js-yaml": "npm:^4.0.9" "@types/luxon": "npm:^3.7.1" "@types/node": "npm:^22" + "@types/pdfkit": "npm:^0.17.5" "@types/randomstring": "npm:^1.3.0" "@typescript-eslint/eslint-plugin": "npm:^8" "@typescript-eslint/parser": "npm:^8" @@ -5304,6 +5420,9 @@ __metadata: jest-environment-node: "npm:^29" lerna: "npm:^9.0.7" nx: "npm:^22.6.4" + pdf-parse: "npm:1.1.1" + pdfjs-dist: "npm:4.4.168" + pdfkit: "npm:^0.18.0" pg: "npm:^8.20.0" prettier: "npm:^3.8.1" rxjs: "npm:^7.8.2" @@ -5311,6 +5430,7 @@ __metadata: ts-jest: "npm:^29" ts-node: "npm:^10.9.2" typescript: "npm:~5.8" + unpdf: "npm:^1.4.0" languageName: unknown linkType: soft @@ -5443,6 +5563,18 @@ __metadata: languageName: node linkType: hard +"canvas@npm:^2.11.2": + version: 2.11.2 + resolution: "canvas@npm:2.11.2" + dependencies: + "@mapbox/node-pre-gyp": "npm:^1.0.0" + nan: "npm:^2.17.0" + node-gyp: "npm:latest" + simple-get: "npm:^3.0.3" + checksum: 10c0/943368798ad1b66b18633aa34b6181e1038dac5433fc9727cd07be35f0a633f572b60d9edb95f5ff90b6a9128e86d5312035f91a2934101c73185b15d906230a + languageName: node + linkType: hard + "chalk@npm:4.1.0": version: 4.1.0 resolution: "chalk@npm:4.1.0" @@ -5484,6 +5616,13 @@ __metadata: languageName: node linkType: hard +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6 + languageName: node + linkType: hard + "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -5610,6 +5749,13 @@ __metadata: languageName: node linkType: hard +"clone@npm:^2.1.2": + version: 2.1.2 + resolution: "clone@npm:2.1.2" + checksum: 10c0/ed0601cd0b1606bc7d82ee7175b97e68d1dd9b91fd1250a3617b38d34a095f8ee0431d40a1a611122dcccb4f93295b4fdb94942aa763392b5fe44effa50c2d5e + languageName: node + linkType: hard + "cluster-key-slot@npm:1.1.2, cluster-key-slot@npm:^1.1.0": version: 1.1.2 resolution: "cluster-key-slot@npm:1.1.2" @@ -5686,7 +5832,7 @@ __metadata: languageName: node linkType: hard -"color-support@npm:1.1.3": +"color-support@npm:1.1.3, color-support@npm:^1.1.2": version: 1.1.3 resolution: "color-support@npm:1.1.3" bin: @@ -5780,7 +5926,7 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 10c0/7ab51d30b52d461412cd467721bb82afe695da78fff8f29fe6f6b9cbaac9a2328e27a22a966014df9532100f6dd85370460be8130b9c677891ba36d96a343f50 @@ -6071,6 +6217,13 @@ __metadata: languageName: node linkType: hard +"crypto-js@npm:^4.2.0": + version: 4.2.0 + resolution: "crypto-js@npm:4.2.0" + checksum: 10c0/8fbdf9d56f47aea0794ab87b0eb9833baf80b01a7c5c1b0edc7faf25f662fb69ab18dc2199e2afcac54670ff0cd9607a9045a3f7a80336cccd18d77a55b9fdf0 + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -6115,6 +6268,15 @@ __metadata: languageName: node linkType: hard +"debug@npm:^3.1.0": + version: 3.2.7 + resolution: "debug@npm:3.2.7" + dependencies: + ms: "npm:^2.1.1" + checksum: 10c0/37d96ae42cbc71c14844d2ae3ba55adf462ec89fd3a999459dec3833944cd999af6007ff29c780f1c61153bcaaf2c842d1e4ce1ec621e4fc4923244942e4a02a + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -6132,6 +6294,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^4.2.0": + version: 4.2.1 + resolution: "decompress-response@npm:4.2.1" + dependencies: + mimic-response: "npm:^2.0.0" + checksum: 10c0/5e4821be332e80e3639acee2441c41d245fc07ac3ee85a6f28893c10c079d66d9bf09e8d84bffeae5656a4625e09e9b93fb4a5705adbe6b07202eea64fae1c8d + languageName: node + linkType: hard + "dedent@npm:1.5.3": version: 1.5.3 resolution: "dedent@npm:1.5.3" @@ -6193,6 +6364,13 @@ __metadata: languageName: node linkType: hard +"delegates@npm:^1.0.0": + version: 1.0.0 + resolution: "delegates@npm:1.0.0" + checksum: 10c0/ba05874b91148e1db4bf254750c042bf2215febd23a6d3cda2e64896aef79745fbd4b9996488bd3cafb39ce19dbce0fd6e3b6665275638befffe1c9b312b91b5 + languageName: node + linkType: hard + "denque@npm:^2.1.0": version: 2.1.0 resolution: "denque@npm:2.1.0" @@ -6221,7 +6399,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.1": +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1": version: 2.1.2 resolution: "detect-libc@npm:2.1.2" checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 @@ -6248,6 +6426,13 @@ __metadata: languageName: node linkType: hard +"dfa@npm:^1.2.0": + version: 1.2.0 + resolution: "dfa@npm:1.2.0" + checksum: 10c0/ad12f0bc73b530876672e0a9dfbaa350eeff0c876580042734a004e462eca86d7749b9dedf6b067ba54f346137ab23d16615826bbfa424a3e01ab0e2786fad3c + languageName: node + linkType: hard + "diff-sequences@npm:^29.6.3": version: 29.6.3 resolution: "diff-sequences@npm:29.6.3" @@ -7171,6 +7356,23 @@ __metadata: languageName: node linkType: hard +"fontkit@npm:^2.0.4": + version: 2.0.4 + resolution: "fontkit@npm:2.0.4" + dependencies: + "@swc/helpers": "npm:^0.5.12" + brotli: "npm:^1.3.2" + clone: "npm:^2.1.2" + dfa: "npm:^1.2.0" + fast-deep-equal: "npm:^3.1.3" + restructure: "npm:^3.0.0" + tiny-inflate: "npm:^1.0.3" + unicode-properties: "npm:^1.4.0" + unicode-trie: "npm:^2.0.0" + checksum: 10c0/e68940a0801daa53a4bd160fc49814eeea5eab4dc67225b43064548d35939be9f14de17213bc1a88064adf81b6dfbdb53bda7189df1d07a3ad044482e7fd55e4 + languageName: node + linkType: hard + "foreground-child@npm:^3.1.0, foreground-child@npm:^3.3.1": version: 3.3.1 resolution: "foreground-child@npm:3.3.1" @@ -7259,6 +7461,15 @@ __metadata: languageName: node linkType: hard +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004 + languageName: node + linkType: hard + "fs-minipass@npm:^3.0.0": version: 3.0.3 resolution: "fs-minipass@npm:3.0.3" @@ -7301,6 +7512,23 @@ __metadata: languageName: node linkType: hard +"gauge@npm:^3.0.0": + version: 3.0.2 + resolution: "gauge@npm:3.0.2" + dependencies: + aproba: "npm:^1.0.3 || ^2.0.0" + color-support: "npm:^1.1.2" + console-control-strings: "npm:^1.0.0" + has-unicode: "npm:^2.0.1" + object-assign: "npm:^4.1.1" + signal-exit: "npm:^3.0.0" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wide-align: "npm:^1.1.2" + checksum: 10c0/75230ccaf216471e31025c7d5fcea1629596ca20792de50c596eb18ffb14d8404f927cd55535aab2eeecd18d1e11bd6f23ec3c2e9878d2dda1dc74bccc34b913 + languageName: node + linkType: hard + "generic-pool@npm:3.9.0": version: 3.9.0 resolution: "generic-pool@npm:3.9.0" @@ -7597,7 +7825,7 @@ __metadata: languageName: node linkType: hard -"has-unicode@npm:2.0.1": +"has-unicode@npm:2.0.1, has-unicode@npm:^2.0.1": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" checksum: 10c0/ebdb2f4895c26bb08a8a100b62d362e49b2190bcfd84b76bc4be1a3bd4d254ec52d0dd9f2fbcc093fc5eb878b20c52146f9dfd33e2686ed28982187be593b47c @@ -7691,6 +7919,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1 + languageName: node + linkType: hard + "https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" @@ -9043,6 +9281,20 @@ __metadata: languageName: node linkType: hard +"jpeg-exif@npm:^1.1.4": + version: 1.1.4 + resolution: "jpeg-exif@npm:1.1.4" + checksum: 10c0/0f9225b2423184d60c66b3d7361176801c17ede92fc9b3c044fcf00f379a5a1d424b360ecf0027dda47d405d253c7b62bf5b353fb08b2589e3650f38cc575e82 + languageName: node + linkType: hard + +"js-md5@npm:^0.8.3": + version: 0.8.3 + resolution: "js-md5@npm:0.8.3" + checksum: 10c0/f7e41e95f8e5eb5eeb43085bec3832ae3dfe0020c42fcca5a4efe571213391a9e9594db31bd34624b7280af4f1f12c751b6a50074a15346ecf40a0d54115d77f + languageName: node + linkType: hard + "js-tiktoken@npm:^1.0.14": version: 1.0.21 resolution: "js-tiktoken@npm:1.0.21" @@ -9383,6 +9635,16 @@ __metadata: languageName: node linkType: hard +"linebreak@npm:^1.1.0": + version: 1.1.0 + resolution: "linebreak@npm:1.1.0" + dependencies: + base64-js: "npm:0.0.8" + unicode-trie: "npm:^2.0.0" + checksum: 10c0/b350c90d7b10db30345ed56cdb869548110ce73ccdc4337100eaee50755eed78e9823490e6f2d7ed0adde14f7ed2a12d8583015e072c54f34dc70b316fde133d + languageName: node + linkType: hard + "lines-and-columns@npm:2.0.3": version: 2.0.3 resolution: "lines-and-columns@npm:2.0.3" @@ -9575,6 +9837,15 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^3.1.0": + version: 3.1.0 + resolution: "make-dir@npm:3.1.0" + dependencies: + semver: "npm:^6.0.0" + checksum: 10c0/56aaafefc49c2dfef02c5c95f9b196c4eb6988040cf2c712185c7fe5c99b4091591a7fc4d4eafaaefa70ff763a26f6ab8c3ff60b9e75ea19876f49b18667ecaa + languageName: node + linkType: hard + "make-dir@npm:^4.0.0": version: 4.0.0 resolution: "make-dir@npm:4.0.0" @@ -9779,6 +10050,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^2.0.0": + version: 2.1.0 + resolution: "mimic-response@npm:2.1.0" + checksum: 10c0/717475c840f20deca87a16cb2f7561f9115f5de225ea2377739e09890c81aec72f43c81fd4984650c4044e66be5a846fa7a517ac7908f01009e1e624e19864d5 + languageName: node + linkType: hard + "min-indent@npm:^1.0.0": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -9942,6 +10220,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462 + languageName: node + linkType: hard + "minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3": version: 7.1.3 resolution: "minipass@npm:7.1.3" @@ -9949,6 +10234,16 @@ __metadata: languageName: node linkType: hard +"minizlib@npm:^2.1.1": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: "npm:^3.0.0" + yallist: "npm:^4.0.0" + checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78 + languageName: node + linkType: hard + "minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" @@ -9976,7 +10271,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^1.0.4": +"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" bin: @@ -10096,7 +10391,7 @@ __metadata: languageName: node linkType: hard -"nan@npm:^2.19.0, nan@npm:^2.23.0": +"nan@npm:^2.17.0, nan@npm:^2.19.0, nan@npm:^2.23.0": version: 2.26.2 resolution: "nan@npm:2.26.2" dependencies: @@ -10185,6 +10480,13 @@ __metadata: languageName: node linkType: hard +"node-ensure@npm:^0.0.0": + version: 0.0.0 + resolution: "node-ensure@npm:0.0.0" + checksum: 10c0/7af391aee024a8b7df77c239ed8b90417e3f2539824fa06b60f243ce14c75ee455766464c7c3ba9407d5b1e4d1d74ed5cf5f8af10c67b0fc05aa6e29f5d2462b + languageName: node + linkType: hard + "node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" @@ -10257,6 +10559,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:^5.0.0": + version: 5.0.0 + resolution: "nopt@npm:5.0.0" + dependencies: + abbrev: "npm:1" + bin: + nopt: bin/nopt.js + checksum: 10c0/fc5c4f07155cb455bf5fc3dd149fac421c1a40fd83c6bfe83aa82b52f02c17c5e88301321318adaa27611c8a6811423d51d29deaceab5fa158b585a61a551061 + languageName: node + linkType: hard + "nopt@npm:^8.0.0": version: 8.1.0 resolution: "nopt@npm:8.1.0" @@ -10481,6 +10794,18 @@ __metadata: languageName: node linkType: hard +"npmlog@npm:^5.0.1": + version: 5.0.1 + resolution: "npmlog@npm:5.0.1" + dependencies: + are-we-there-yet: "npm:^2.0.0" + console-control-strings: "npm:^1.1.0" + gauge: "npm:^3.0.0" + set-blocking: "npm:^2.0.0" + checksum: 10c0/489ba519031013001135c463406f55491a17fc7da295c18a04937fe3a4d523fd65e88dd418a28b967ab743d913fdeba1e29838ce0ad8c75557057c481f7d49fa + languageName: node + linkType: hard + "nx@npm:22.6.4, nx@npm:>=21.5.3 < 23.0.0, nx@npm:^22.6.4": version: 22.6.4 resolution: "nx@npm:22.6.4" @@ -10901,6 +11226,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:^0.2.5": + version: 0.2.9 + resolution: "pako@npm:0.2.9" + checksum: 10c0/79c1806ebcf325b60ae599e4d7227c2e346d7b829dc20f5cf24cef07c934079dc3a61c5b3c8278a2f7a190c4a613e343ea11e5302dbe252efd11712df4b6b041 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -11060,6 +11392,65 @@ __metadata: languageName: node linkType: hard +"path2d@npm:^0.2.0": + version: 0.2.2 + resolution: "path2d@npm:0.2.2" + checksum: 10c0/1bb76c7f275d07f1bc7ca12171d828e91bf8a12596f0765a52e9d4d47fe1a428455dc1dd4c9002924a9bc554f6ac25e09a6c22eaecf32e5e33fba2985b5168f8 + languageName: node + linkType: hard + +"pdf-parse@npm:1.1.1": + version: 1.1.1 + resolution: "pdf-parse@npm:1.1.1" + dependencies: + debug: "npm:^3.1.0" + node-ensure: "npm:^0.0.0" + checksum: 10c0/cba2b6ddfbfa73d94ff0cd342cbe8ef2ef0501863ada687eddf99487a4d06766e00fc44525c40cef3b01f04376cb99d5873ab789bd3e2379a28c3ae5377f3298 + languageName: node + linkType: hard + +"pdfjs-dist@npm:4.4.168": + version: 4.4.168 + resolution: "pdfjs-dist@npm:4.4.168" + dependencies: + canvas: "npm:^2.11.2" + path2d: "npm:^0.2.0" + dependenciesMeta: + canvas: + optional: true + path2d: + optional: true + checksum: 10c0/61bad19fe0aae8261631d425bd96368cf0b8803c3a4446615456dddd867ef3e5ba15c11a1caabb8033bce9538e3ad8262c24a56fa68b150c6ee0eb9e26a9f3a7 + languageName: node + linkType: hard + +"pdfkit@npm:^0.16.0": + version: 0.16.0 + resolution: "pdfkit@npm:0.16.0" + dependencies: + crypto-js: "npm:^4.2.0" + fontkit: "npm:^2.0.4" + jpeg-exif: "npm:^1.1.4" + linebreak: "npm:^1.1.0" + png-js: "npm:^1.0.0" + checksum: 10c0/7238451e0638589ea544463f4e2a1d7f0cd2f02560cb9edad85fe74d13935bcfdfc57b7d390165a92336e5e26a91533b69b5e35b34f63143f6608efd3f556e9f + languageName: node + linkType: hard + +"pdfkit@npm:^0.18.0": + version: 0.18.0 + resolution: "pdfkit@npm:0.18.0" + dependencies: + "@noble/ciphers": "npm:^1.0.0" + "@noble/hashes": "npm:^1.6.0" + fontkit: "npm:^2.0.4" + js-md5: "npm:^0.8.3" + linebreak: "npm:^1.1.0" + png-js: "npm:^1.0.0" + checksum: 10c0/09a0e2eb704269603db397a199bcbb0bcae90e3e4a548b5f5009acf8baefb237a6adf524146805d10e96ed53abac34f7db4f534fdfc870c93a72d967660624ae + languageName: node + linkType: hard + "pg-cloudflare@npm:^1.3.0": version: 1.3.0 resolution: "pg-cloudflare@npm:1.3.0" @@ -11199,6 +11590,13 @@ __metadata: languageName: node linkType: hard +"png-js@npm:^1.0.0": + version: 1.0.0 + resolution: "png-js@npm:1.0.0" + checksum: 10c0/b591107bb888c66cb1799327774f73f2865a366b5992ed7335c81610eab0e9cb4f13fb396e3c00e28781ada6ccde170b566209e9df833a2abe71d11da55aea85 + languageName: node + linkType: hard + "postcss-selector-parser@npm:^7.0.0": version: 7.1.1 resolution: "postcss-selector-parser@npm:7.1.1" @@ -11641,7 +12039,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.2": +"readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -11849,6 +12247,13 @@ __metadata: languageName: node linkType: hard +"restructure@npm:^3.0.0": + version: 3.0.2 + resolution: "restructure@npm:3.0.2" + checksum: 10c0/f13536c094ba40a9af704e6a9fc030afd48d6112e9a3bec5f9cf5bad50416a22a7cf9aaece542bbac8c82204ad4901bf455e6204613abedbc075bc221ea6bdef + languageName: node + linkType: hard + "retry-as-promised@npm:^7.0.4": version: 7.1.1 resolution: "retry-as-promised@npm:7.1.1" @@ -11863,6 +12268,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^3.0.2": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: bin.js + checksum: 10c0/9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8 + languageName: node + linkType: hard + "router@npm:^2.2.0": version: 2.2.0 resolution: "router@npm:2.2.0" @@ -11947,7 +12363,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.3.0, semver@npm:^6.3.1": +"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -12070,6 +12486,13 @@ __metadata: languageName: node linkType: hard +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 10c0/9f8c1b2d800800d0b589de1477c753492de5c1548d4ade52f57f1d1f5e04af5481554d75ce5e5c43d4004b80a3eb714398d6907027dc0534177b7539119f4454 + languageName: node + linkType: hard + "setprototypeof@npm:1.2.0, setprototypeof@npm:~1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" @@ -12141,7 +12564,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:3.0.7, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"signal-exit@npm:3.0.7, signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 @@ -12169,6 +12592,24 @@ __metadata: languageName: node linkType: hard +"simple-concat@npm:^1.0.0": + version: 1.0.1 + resolution: "simple-concat@npm:1.0.1" + checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 + languageName: node + linkType: hard + +"simple-get@npm:^3.0.3": + version: 3.1.1 + resolution: "simple-get@npm:3.1.1" + dependencies: + decompress-response: "npm:^4.2.0" + once: "npm:^1.3.1" + simple-concat: "npm:^1.0.0" + checksum: 10c0/438c78844ea1b1e7268d13ee0b3a39c7d644183367aec916aed3b676b45d3037a61d9f975c200a49b42eb851f29f03745118af1e13c01e60a7b4044f2fd60be7 + languageName: node + linkType: hard + "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -12629,6 +13070,20 @@ __metadata: languageName: node linkType: hard +"tar@npm:^6.1.11": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: "npm:^2.0.0" + fs-minipass: "npm:^2.0.0" + minipass: "npm:^5.0.0" + minizlib: "npm:^2.1.1" + mkdirp: "npm:^1.0.3" + yallist: "npm:^4.0.0" + checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 + languageName: node + linkType: hard + "tar@npm:^7.4.3, tar@npm:^7.5.4": version: 7.5.13 resolution: "tar@npm:7.5.13" @@ -12748,6 +13203,13 @@ __metadata: languageName: node linkType: hard +"tiny-inflate@npm:^1.0.0, tiny-inflate@npm:^1.0.3": + version: 1.0.3 + resolution: "tiny-inflate@npm:1.0.3" + checksum: 10c0/fab687537254f6ec44c9a2e880048fe70da3542aba28f73cda3e74c95cabf342a339372f2a6c032e322324f01accc03ca26c04ba2bad9b3eb8cf3ee99bba7f9b + languageName: node + linkType: hard + "tinyglobby@npm:0.2.12": version: 0.2.12 resolution: "tinyglobby@npm:0.2.12" @@ -12965,7 +13427,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.8.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": +"tslib@npm:2.8.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -13195,6 +13657,16 @@ __metadata: languageName: node linkType: hard +"unicode-properties@npm:^1.4.0": + version: 1.4.1 + resolution: "unicode-properties@npm:1.4.1" + dependencies: + base64-js: "npm:^1.3.0" + unicode-trie: "npm:^2.0.0" + checksum: 10c0/1d140b7945664fb0ef53de955170821e077b949eef377c6e4905902f07e339039271bfa2a005e4f4c6074b080d3420b486c52dc905e11f924949a04d1fb47ffd + languageName: node + linkType: hard + "unicode-property-aliases-ecmascript@npm:^2.0.0": version: 2.2.0 resolution: "unicode-property-aliases-ecmascript@npm:2.2.0" @@ -13202,6 +13674,16 @@ __metadata: languageName: node linkType: hard +"unicode-trie@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-trie@npm:2.0.0" + dependencies: + pako: "npm:^0.2.5" + tiny-inflate: "npm:^1.0.0" + checksum: 10c0/2422368645249f315640a1c9e9506046aa7738fc9c5d59e15c207cdd6ec66101c35b0b9f75dc3ac28fe7be19aaf1efc898bbea074fa1e8e295ef736aeb7904bb + languageName: node + linkType: hard + "universal-user-agent@npm:^6.0.0": version: 6.0.1 resolution: "universal-user-agent@npm:6.0.1" @@ -13216,6 +13698,18 @@ __metadata: languageName: node linkType: hard +"unpdf@npm:^1.4.0": + version: 1.4.0 + resolution: "unpdf@npm:1.4.0" + peerDependencies: + "@napi-rs/canvas": ^0.1.69 + peerDependenciesMeta: + "@napi-rs/canvas": + optional: true + checksum: 10c0/dd248e26c79b12d8a2e68f3f8d959f42d6d0bb14878928d2601bbb56da6d0ba0f207d14dffe64028cdd9afc228b8c11b3af08655dad19717a8cec7072b8942c8 + languageName: node + linkType: hard + "unpipe@npm:~1.0.0": version: 1.0.0 resolution: "unpipe@npm:1.0.0" @@ -13499,7 +13993,7 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:1.1.5": +"wide-align@npm:1.1.5, wide-align@npm:^1.1.2": version: 1.1.5 resolution: "wide-align@npm:1.1.5" dependencies: