From a92a92652208e84c0611a015506fd449145ebfba Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:01:31 -0700 Subject: [PATCH 1/7] feat: upload placeholder interlinear export PDF --- package.json | 2 + .../data-access/ExportStorageRepository.ts | 96 +++++++++++++++++ .../export/jobs/exportInterlinearPdfJob.ts | 36 ++++++- src/shared/s3.ts | 102 ++++++++++++++++++ src/shared/storageEnvironment.ts | 5 + 5 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 src/modules/export/data-access/ExportStorageRepository.ts create mode 100644 src/shared/s3.ts create mode 100644 src/shared/storageEnvironment.ts diff --git a/package.json b/package.json index a4eaff52..0d7a2794 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@aws-sdk/client-s3": "3.662.0", "@aws-sdk/client-sqs": "^3.678.0", "@aws-sdk/lib-storage": "3.658.1", + "@aws-sdk/s3-request-presigner": "3.662.0", "@date-fns/utc": "2.1.1", "@eslint-react/eslint-plugin": "2.13.0", "@eslint/eslintrc": "3.3.5", @@ -79,6 +80,7 @@ "nitro": "3.0.260311-beta", "nodemailer": "6.9.15", "patch-package": "8.0.1", + "pdf-lib": "^1.17.1", "pg": "8.12.0", "pg-copy-streams": "7.0.0", "pg-cursor": "2.15.3", diff --git a/src/modules/export/data-access/ExportStorageRepository.ts b/src/modules/export/data-access/ExportStorageRepository.ts new file mode 100644 index 00000000..00744ad4 --- /dev/null +++ b/src/modules/export/data-access/ExportStorageRepository.ts @@ -0,0 +1,96 @@ +import { Upload } from "@aws-sdk/lib-storage"; +import { GetObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3"; +import { Readable } from "stream"; +import { createLogger } from "@/logging"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { getS3Client, s3BodyToUint8Array } from "@/shared/s3"; + +const EXPORT_BUCKET_PREFIX = process.env.EXPORT_BUCKET_PREFIX ?? "gbt-exports"; + +const s3Client = getS3Client(); + +export interface ExportStorageOptions { + environment: "prod" | "local"; +} + +export const exportStorageRepository = { + async uploadPdf({ + environment, + key, + stream, + }: ExportStorageOptions & { + key: string; + stream: Readable; + }): Promise { + const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; + const logger = createLogger({ bucket, key }); + + const upload = new Upload({ + client: s3Client, + params: { + Bucket: bucket, + Key: key, + Body: stream, + ContentType: "application/pdf", + }, + }); + + await upload.done(); + logger.info("Export PDF uploaded"); + + return `s3://${bucket}/${key}`; + }, + + bucketName(environment: "prod" | "local") { + return `${EXPORT_BUCKET_PREFIX}-${environment}`; + }, + + async presignPdf({ + environment, + key, + expiresInSeconds, + }: ExportStorageOptions & { + key: string; + expiresInSeconds: number; + }): Promise { + const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; + const command = new GetObjectCommand({ Bucket: bucket, Key: key }); + const url = await getSignedUrl(s3Client, command, { + expiresIn: expiresInSeconds, + }); + const publicEndpoint = process.env.EXPORT_PUBLIC_S3_ENDPOINT; + if (publicEndpoint) { + try { + const parsed = new URL(url); + const target = new URL(publicEndpoint); + parsed.protocol = target.protocol; + parsed.host = target.host; + return parsed.toString(); + } catch { + return url; + } + } + return url; + }, + + async fetchBuffer({ + environment, + key, + }: ExportStorageOptions & { key: string }): Promise { + const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; + const res = await s3Client.send( + new GetObjectCommand({ Bucket: bucket, Key: key }), + ); + return s3BodyToUint8Array(res.Body); + }, + + async deleteObject({ + environment, + key, + }: ExportStorageOptions & { key: string }): Promise { + const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; + await s3Client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key })); + }, +}; + +export default exportStorageRepository; diff --git a/src/modules/export/jobs/exportInterlinearPdfJob.ts b/src/modules/export/jobs/exportInterlinearPdfJob.ts index 53d1008d..0e12229b 100644 --- a/src/modules/export/jobs/exportInterlinearPdfJob.ts +++ b/src/modules/export/jobs/exportInterlinearPdfJob.ts @@ -1,13 +1,22 @@ -import { Job } from "@/shared/jobs/model"; +import { PDFDocument } from "pdf-lib"; +import { Readable } from "stream"; import { logger } from "@/logging"; +import { Job } from "@/shared/jobs/model"; +import { getStorageEnvironment } from "@/shared/storageEnvironment"; +import exportStorageRepository from "../data-access/ExportStorageRepository"; +import type { + ExportInterlinearPdfJobData, + ExportInterlinearPdfJobPayload, +} from "../model"; import { EXPORT_JOB_TYPES } from "./jobTypes"; -import type { ExportInterlinearPdfJobPayload } from "../model"; export async function exportInterlinearPdfJob( job: Job, -): Promise { +): Promise { const jobLogger = logger.child({ jobId: job.id, jobType: job.type }); + const environment = getStorageEnvironment(); + if (job.type !== EXPORT_JOB_TYPES.EXPORT_INTERLINEAR_PDF) { jobLogger.error( `received job type ${job.type}, expected ${EXPORT_JOB_TYPES.EXPORT_INTERLINEAR_PDF}`, @@ -17,8 +26,27 @@ export async function exportInterlinearPdfJob( ); } + const exportKey = `interlinear/${job.payload.languageCode}/${job.id}.pdf`; + try { - jobLogger.info("Interlinear export job completed (noop)"); + const placeholderPdf = await PDFDocument.create(); + placeholderPdf.addPage(); + const bytes = await placeholderPdf.save(); + await exportStorageRepository.uploadPdf({ + environment, + key: exportKey, + stream: Readable.from([bytes]), + }); + + const url = await exportStorageRepository.presignPdf({ + environment, + key: exportKey, + expiresInSeconds: 60 * 60 * 24, + }); + const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); + + jobLogger.info({ url, exportKey }, "Interlinear placeholder PDF uploaded"); + return { exportKey, downloadUrl: url, expiresAt }; } catch (error) { jobLogger.error({ err: error }, "Interlinear export job failed"); throw error; diff --git a/src/shared/s3.ts b/src/shared/s3.ts new file mode 100644 index 00000000..f65b8abd --- /dev/null +++ b/src/shared/s3.ts @@ -0,0 +1,102 @@ +import { S3Client } from "@aws-sdk/client-s3"; +import { Readable } from "stream"; + +let cachedS3Client: S3Client | undefined; + +export function getS3Client(): S3Client { + if (cachedS3Client) return cachedS3Client; + + cachedS3Client = new S3Client({ + region: process.env.AWS_REGION ?? "us-east-1", + endpoint: process.env.AWS_ENDPOINT_URL_S3, + forcePathStyle: process.env.AWS_S3_FORCE_PATH_STYLE === "true", + }); + return cachedS3Client; +} + +export function mergeUint8Arrays(parts: Uint8Array[]): Uint8Array { + const total = parts.reduce((sum, part) => sum + part.byteLength, 0); + const merged = new Uint8Array(total); + let offset = 0; + for (const part of parts) { + merged.set(part, offset); + offset += part.byteLength; + } + return merged; +} + +async function readableToUint8Array(stream: Readable): Promise { + const parts: Uint8Array[] = []; + for await (const chunk of stream) { + if (typeof chunk === "string") { + parts.push(Uint8Array.from(Buffer.from(chunk))); + } else if (chunk instanceof Uint8Array) { + parts.push(chunk); + } else { + parts.push(Uint8Array.from(chunk)); + } + } + return mergeUint8Arrays(parts); +} + +async function webStreamToUint8Array( + stream: ReadableStream, +): Promise { + const reader = stream.getReader(); + const parts: Uint8Array[] = []; + // eslint-disable-next-line no-constant-condition + while (true) { + const { done, value } = await reader.read(); + if (done) break; + if (value) parts.push(value); + } + return mergeUint8Arrays(parts); +} + +export async function s3BodyToUint8Array( + body: unknown, +): Promise { + if (!body) return undefined; + if (body instanceof Uint8Array) return body; + if (body instanceof Readable) return readableToUint8Array(body); + + if (typeof Blob !== "undefined" && body instanceof Blob) { + return new Uint8Array(await body.arrayBuffer()); + } + + if (typeof (body as ReadableStream).getReader === "function") { + return webStreamToUint8Array(body as ReadableStream); + } + + return undefined; +} + +export async function s3BodyToReadable( + body: unknown, +): Promise { + if (!body) return undefined; + if (body instanceof Readable) return body; + + if (body instanceof Uint8Array) { + return Readable.from([body]); + } + + if (typeof Blob !== "undefined" && body instanceof Blob) { + const bytes = new Uint8Array(await body.arrayBuffer()); + return Readable.from([bytes]); + } + + if (typeof (body as ReadableStream).getReader === "function") { + const webStream = body as ReadableStream; + const maybeFromWeb = ( + Readable as unknown as { fromWeb?: (s: any) => Readable } + ).fromWeb; + if (typeof maybeFromWeb === "function") { + return maybeFromWeb(webStream); + } + const bytes = await webStreamToUint8Array(webStream); + return Readable.from([bytes]); + } + + return undefined; +} diff --git a/src/shared/storageEnvironment.ts b/src/shared/storageEnvironment.ts new file mode 100644 index 00000000..87590364 --- /dev/null +++ b/src/shared/storageEnvironment.ts @@ -0,0 +1,5 @@ +export type StorageEnvironment = "prod" | "local"; + +export function getStorageEnvironment(): StorageEnvironment { + return process.env.NODE_ENV === "production" ? "prod" : "local"; +} From a1481f70ea5fcc5d7ac8e4f2f61a5ca2073f4a01 Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:31:52 -0700 Subject: [PATCH 2/7] build: update package-lock --- package-lock.json | 1031 +++++---------------------------------------- 1 file changed, 113 insertions(+), 918 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b5d89fc..1320cb85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "@aws-sdk/client-s3": "3.662.0", "@aws-sdk/client-sqs": "^3.678.0", "@aws-sdk/lib-storage": "3.658.1", + "@aws-sdk/s3-request-presigner": "3.662.0", "@date-fns/utc": "2.1.1", "@eslint-react/eslint-plugin": "2.13.0", "@eslint/eslintrc": "3.3.5", @@ -70,6 +71,7 @@ "nitro": "3.0.260311-beta", "nodemailer": "6.9.15", "patch-package": "8.0.1", + "pdf-lib": "^1.17.1", "pg": "8.12.0", "pg-copy-streams": "7.0.0", "pg-cursor": "2.15.3", @@ -91,77 +93,6 @@ "zod": "3.23.8" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz", - "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/css-calc": "^3.1.1", - "@csstools/css-color-parser": "^4.0.2", - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.6" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "optional": true, - "peer": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@asamuzakjp/dom-selector": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", - "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "optional": true, - "peer": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "dev": true, @@ -1541,6 +1472,40 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.662.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.662.0.tgz", + "integrity": "sha512-O3FXO4LGNXzIXtrWPBu+ImQcF3DxRiP87cJObdNDso3p+UZQ5rlsUnYovnD8WazFfUbBcYy6IK1+yYJDyXXQvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.662.0", + "@aws-sdk/types": "3.662.0", + "@aws-sdk/util-format-url": "3.662.0", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.3.6", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/types": { + "version": "3.662.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.662.0.tgz", + "integrity": "sha512-Ff9/KRmIm8iEzodxzISLj4/pB/0hX2nVw1RFeOBC65OuM6nHrAdWHHog/CVx25hS5JPU0uE3h6NlWRaBJ7AV5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.662.0", "dev": true, @@ -1624,6 +1589,36 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.662.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.662.0.tgz", + "integrity": "sha512-McyEyXsZMzuk/nqrVEbjCSmsKykJ7UI4lTDMdaqFdL0l5K/6VWgbFc3xOZcxEGBIvNucHiusQhqJXYHCAG65Dg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.662.0", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url/node_modules/@aws-sdk/types": { + "version": "3.662.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.662.0.tgz", + "integrity": "sha512-Ff9/KRmIm8iEzodxzISLj4/pB/0hX2nVw1RFeOBC65OuM6nHrAdWHHog/CVx25hS5JPU0uE3h6NlWRaBJ7AV5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.568.0", "dev": true, @@ -2077,173 +2072,6 @@ "node": ">=6.9.0" } }, - "node_modules/@bramus/specificity": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", - "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "css-tree": "^3.0.0" - }, - "bin": { - "specificity": "bin/cli.js" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "optional": true, - "peer": true, - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/@csstools/css-calc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", - "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", - "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/color-helpers": "^6.0.2", - "@csstools/css-calc": "^3.1.1" - }, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^4.0.0" - } - }, - "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.1.tgz", - "integrity": "sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "optional": true, - "peer": true, - "peerDependencies": { - "css-tree": "^3.2.1" - }, - "peerDependenciesMeta": { - "css-tree": { - "optional": true - } - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=20.19.0" - } - }, "node_modules/@date-fns/utc": { "version": "2.1.1", "dev": true, @@ -3060,26 +2888,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@exodus/bytes": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", - "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } - } - }, "node_modules/@faker-js/faker": { "version": "9.7.0", "dev": true, @@ -4019,16 +3827,6 @@ "node": ">=20.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@oxc-project/types": { "version": "0.120.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz", @@ -4039,6 +3837,26 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -8058,18 +7876,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "require-from-string": "^2.0.2" - } - }, "node_modules/bignumber.js": { "version": "9.1.2", "dev": true, @@ -8692,22 +8498,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/css-what": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", @@ -9106,34 +8896,6 @@ "node": ">= 12" } }, - "node_modules/data-urls": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/data-urls/node_modules/whatwg-mimetype": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=20" - } - }, "node_modules/date-fns": { "version": "4.1.0", "dev": true, @@ -9196,13 +8958,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -11442,21 +11197,6 @@ "dev": true, "license": "MIT" }, - "node_modules/html-encoding-sniffer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@exodus/bytes": "^1.6.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -11839,13 +11579,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/is-stream": { "version": "2.0.1", "dev": true, @@ -11962,103 +11695,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdom": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", - "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@asamuzakjp/css-color": "^5.0.1", - "@asamuzakjp/dom-selector": "^7.0.3", - "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.1", - "@exodus/bytes": "^1.15.0", - "css-tree": "^3.2.1", - "data-urls": "^7.0.0", - "decimal.js": "^10.6.0", - "html-encoding-sniffer": "^6.0.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7", - "parse5": "^8.0.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.1", - "undici": "^7.24.5", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^8.0.1", - "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.1", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/jsdom/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "optional": true, - "peer": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/jsdom/node_modules/parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=20" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -12222,36 +11858,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lightningcss": { - "version": "1.30.2", - "dev": true, - "license": "MPL-2.0", - "optional": true, - "peer": true, - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" - } - }, "node_modules/lightningcss-android-arm64": { "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", @@ -12399,46 +12005,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.2", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.2", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", @@ -12481,204 +12047,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lightningcss/node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-darwin-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", - "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-darwin-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-freebsd-x64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/linkify-it": { "version": "5.0.0", "dev": true, @@ -13084,15 +12452,6 @@ "node": ">= 0.4" } }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "dev": true, - "license": "CC0-1.0", - "optional": true, - "peer": true - }, "node_modules/mdurl": { "version": "2.0.0", "dev": true, @@ -13278,52 +12637,6 @@ } } }, - "node_modules/nitro/node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/nitro/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "dev": true, - "license": "BlueOak-1.0.0", - "optional": true, - "peer": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/nitro/node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/nitro/node_modules/unstorage": { "version": "2.0.0-alpha.7", "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-2.0.0-alpha.7.tgz", @@ -13729,6 +13042,13 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13930,6 +13250,26 @@ "dev": true, "license": "MIT" }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf-lib/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, "node_modules/pg": { "version": "8.12.0", "dev": true, @@ -15202,18 +14542,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", @@ -15556,19 +14884,6 @@ "dev": true, "license": "ISC" }, - "node_modules/saxes": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/scheduler": { "version": "0.27.0", "dev": true, @@ -16005,13 +15320,6 @@ "node": ">=0.8.0" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tabbable": { "version": "6.4.0", "dev": true, @@ -16165,30 +15473,6 @@ "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", - "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tldts-core": "^7.0.27" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", - "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tmp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", @@ -16210,36 +15494,6 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^7.0.5" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=20" - } - }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -17422,19 +16676,6 @@ "dev": true, "license": "MIT" }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/watchpack": { "version": "2.4.2", "dev": true, @@ -17457,18 +16698,6 @@ "node": ">= 8" } }, - "node_modules/webidl-conversions": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=20" - } - }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -17495,23 +16724,6 @@ "node": ">=18" } }, - "node_modules/whatwg-url": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", - "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@exodus/bytes": "^1.11.0", - "tr46": "^6.0.0", - "webidl-conversions": "^8.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -17611,16 +16823,6 @@ "dev": true, "license": "ISC" }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/xml2js": { "version": "0.6.2", "dev": true, @@ -17657,13 +16859,6 @@ "node": ">=20.0" } }, - "node_modules/xmlchars": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/xtend": { "version": "4.0.2", "dev": true, From a4188c26596c65c2754c5eb0b0f5dc3b97980f5e Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Sat, 4 Apr 2026 17:44:27 -0700 Subject: [PATCH 3/7] fix: test failure after rebase --- package-lock.json | 122 +++++++++++++++++++++++++++++++--------------- package.json | 2 + 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1320cb85..1f2bfbb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "aws-lambda": "1.0.7", "chart.js": "4.4.4", "chartjs-adapter-date-fns": "3.0.0", + "chokidar": "5.0.0", "d3": "7.9.0", "date-fns": "4.1.0", "dompurify": "3.3.3", @@ -68,6 +69,7 @@ "kysely": "0.28.2", "lint-staged": "15.2.11", "lodash": "4.17.21", + "lru-cache": "11.2.7", "nitro": "3.0.260311-beta", "nodemailer": "6.9.15", "patch-package": "8.0.1", @@ -1802,6 +1804,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -6054,6 +6066,57 @@ } } }, + "node_modules/@tanstack/router-plugin/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@tanstack/router-plugin/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tanstack/router-plugin/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/@tanstack/router-plugin/node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", @@ -8207,41 +8270,19 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^5.0.0" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/ci-info": { @@ -12402,13 +12443,13 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" } }, "node_modules/lz-string": { @@ -14495,16 +14536,17 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", "dev": true, "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/real-require": { diff --git a/package.json b/package.json index 0d7a2794..afef6397 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "aws-lambda": "1.0.7", "chart.js": "4.4.4", "chartjs-adapter-date-fns": "3.0.0", + "chokidar": "5.0.0", "d3": "7.9.0", "date-fns": "4.1.0", "dompurify": "3.3.3", @@ -76,6 +77,7 @@ "husky": "9.1.7", "kysely": "0.28.2", "lint-staged": "15.2.11", + "lru-cache": "11.2.7", "lodash": "4.17.21", "nitro": "3.0.260311-beta", "nodemailer": "6.9.15", From 881a73fec7ddbe1975691d01195bce8617fa71d2 Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Sat, 4 Apr 2026 17:55:23 -0700 Subject: [PATCH 4/7] fix: test failure after rebase --- package-lock.json | 11 +++++++++++ package.json | 1 + 2 files changed, 12 insertions(+) diff --git a/package-lock.json b/package-lock.json index 1f2bfbb7..89305639 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@gracious.tech/fetch-client": "0.8.9", "@headlessui/react": "2.2.9", "@octokit/rest": "22.0.1", + "@opentelemetry/api": "^1.9.0", "@tailwindcss/vite": "4.2.0", "@tanstack/eslint-plugin-query": "5.91.5", "@tanstack/eslint-plugin-router": "1.161.6", @@ -3839,6 +3840,16 @@ "node": ">=20.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@oxc-project/types": { "version": "0.120.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz", diff --git a/package.json b/package.json index afef6397..9c40e0b2 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@types/react": "19.2.10", "@types/react-dom": "19.2.3", "@vitejs/plugin-react": "5.1.4", + "@opentelemetry/api": "^1.9.0", "aws-lambda": "1.0.7", "chart.js": "4.4.4", "chartjs-adapter-date-fns": "3.0.0", From a1f9332e78c0901c81e7d7c564bc02776d430c16 Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:56:00 -0700 Subject: [PATCH 5/7] fix: test failure after rebase --- package-lock.json | 401 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 396 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89305639..5549e0d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,6 +67,7 @@ "fuzzysort": "3.0.2", "googleapis": "148.0.0", "husky": "9.1.7", + "jsdom": "^26.1.0", "kysely": "0.28.2", "lint-staged": "15.2.11", "lodash": "4.17.21", @@ -96,6 +97,27 @@ "zod": "3.23.8" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "dev": true, @@ -2085,6 +2107,121 @@ "node": ">=6.9.0" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@date-fns/utc": { "version": "2.1.1", "dev": true, @@ -7665,12 +7802,11 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -8563,6 +8699,20 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.2.3", "dev": true, @@ -8948,6 +9098,20 @@ "node": ">= 12" } }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/date-fns": { "version": "4.1.0", "dev": true, @@ -9010,6 +9174,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -11249,6 +11420,19 @@ "dev": true, "license": "MIT" }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -11323,11 +11507,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -11631,6 +11817,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-stream": { "version": "2.0.1", "dev": true, @@ -11747,6 +11940,46 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -12914,6 +13147,13 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "dev": true, @@ -14871,6 +15111,13 @@ "dev": true, "license": "MIT" }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -14937,6 +15184,19 @@ "dev": true, "license": "ISC" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "dev": true, @@ -15373,6 +15633,13 @@ "node": ">=0.8.0" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tabbable": { "version": "6.4.0", "dev": true, @@ -15526,6 +15793,26 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", @@ -15547,6 +15834,32 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -16729,6 +17042,19 @@ "dev": true, "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/watchpack": { "version": "2.4.2", "dev": true, @@ -16751,6 +17077,16 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -16777,6 +17113,20 @@ "node": ">=18" } }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -16876,6 +17226,38 @@ "dev": true, "license": "ISC" }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/xml2js": { "version": "0.6.2", "dev": true, @@ -16912,6 +17294,13 @@ "node": ">=20.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "dev": true, diff --git a/package.json b/package.json index 9c40e0b2..8a74428e 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "fuzzysort": "3.0.2", "googleapis": "148.0.0", "husky": "9.1.7", + "jsdom": "^26.1.0", "kysely": "0.28.2", "lint-staged": "15.2.11", "lru-cache": "11.2.7", From 058d9928dfc025e9a56b445a00cb54f4fb7ae891 Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:33:14 -0700 Subject: [PATCH 6/7] fix: persist interlinear export data --- .../data-access/ExportStorageRepository.ts | 55 +----------- .../export/jobs/exportInterlinearPdfJob.ts | 14 ++- .../jobs/exportInterlinearPdfJob.unit.ts | 59 +++++++++++++ src/shared/s3.ts | 88 ------------------- 4 files changed, 65 insertions(+), 151 deletions(-) create mode 100644 src/modules/export/jobs/exportInterlinearPdfJob.unit.ts diff --git a/src/modules/export/data-access/ExportStorageRepository.ts b/src/modules/export/data-access/ExportStorageRepository.ts index 00744ad4..4875755c 100644 --- a/src/modules/export/data-access/ExportStorageRepository.ts +++ b/src/modules/export/data-access/ExportStorageRepository.ts @@ -1,9 +1,7 @@ import { Upload } from "@aws-sdk/lib-storage"; -import { GetObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3"; import { Readable } from "stream"; import { createLogger } from "@/logging"; -import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; -import { getS3Client, s3BodyToUint8Array } from "@/shared/s3"; +import { getS3Client } from "@/shared/s3"; const EXPORT_BUCKET_PREFIX = process.env.EXPORT_BUCKET_PREFIX ?? "gbt-exports"; @@ -40,57 +38,6 @@ export const exportStorageRepository = { return `s3://${bucket}/${key}`; }, - - bucketName(environment: "prod" | "local") { - return `${EXPORT_BUCKET_PREFIX}-${environment}`; - }, - - async presignPdf({ - environment, - key, - expiresInSeconds, - }: ExportStorageOptions & { - key: string; - expiresInSeconds: number; - }): Promise { - const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; - const command = new GetObjectCommand({ Bucket: bucket, Key: key }); - const url = await getSignedUrl(s3Client, command, { - expiresIn: expiresInSeconds, - }); - const publicEndpoint = process.env.EXPORT_PUBLIC_S3_ENDPOINT; - if (publicEndpoint) { - try { - const parsed = new URL(url); - const target = new URL(publicEndpoint); - parsed.protocol = target.protocol; - parsed.host = target.host; - return parsed.toString(); - } catch { - return url; - } - } - return url; - }, - - async fetchBuffer({ - environment, - key, - }: ExportStorageOptions & { key: string }): Promise { - const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; - const res = await s3Client.send( - new GetObjectCommand({ Bucket: bucket, Key: key }), - ); - return s3BodyToUint8Array(res.Body); - }, - - async deleteObject({ - environment, - key, - }: ExportStorageOptions & { key: string }): Promise { - const bucket = `${EXPORT_BUCKET_PREFIX}-${environment}`; - await s3Client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key })); - }, }; export default exportStorageRepository; diff --git a/src/modules/export/jobs/exportInterlinearPdfJob.ts b/src/modules/export/jobs/exportInterlinearPdfJob.ts index 0e12229b..521ffec7 100644 --- a/src/modules/export/jobs/exportInterlinearPdfJob.ts +++ b/src/modules/export/jobs/exportInterlinearPdfJob.ts @@ -2,6 +2,7 @@ import { PDFDocument } from "pdf-lib"; import { Readable } from "stream"; import { logger } from "@/logging"; import { Job } from "@/shared/jobs/model"; +import jobRepository from "@/shared/jobs/data-access/jobRepository"; import { getStorageEnvironment } from "@/shared/storageEnvironment"; import exportStorageRepository from "../data-access/ExportStorageRepository"; import type { @@ -12,7 +13,7 @@ import { EXPORT_JOB_TYPES } from "./jobTypes"; export async function exportInterlinearPdfJob( job: Job, -): Promise { +): Promise { const jobLogger = logger.child({ jobId: job.id, jobType: job.type }); const environment = getStorageEnvironment(); @@ -38,15 +39,10 @@ export async function exportInterlinearPdfJob( stream: Readable.from([bytes]), }); - const url = await exportStorageRepository.presignPdf({ - environment, - key: exportKey, - expiresInSeconds: 60 * 60 * 24, - }); - const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); + const data: ExportInterlinearPdfJobData = { exportKey }; + await jobRepository.updateData(job.id, data); - jobLogger.info({ url, exportKey }, "Interlinear placeholder PDF uploaded"); - return { exportKey, downloadUrl: url, expiresAt }; + jobLogger.info({ exportKey }, "Interlinear placeholder PDF uploaded"); } catch (error) { jobLogger.error({ err: error }, "Interlinear export job failed"); throw error; diff --git a/src/modules/export/jobs/exportInterlinearPdfJob.unit.ts b/src/modules/export/jobs/exportInterlinearPdfJob.unit.ts new file mode 100644 index 00000000..f0fb480b --- /dev/null +++ b/src/modules/export/jobs/exportInterlinearPdfJob.unit.ts @@ -0,0 +1,59 @@ +import { Readable } from "stream"; +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { JobStatus } from "@/shared/jobs/model"; +import jobRepository from "@/shared/jobs/data-access/jobRepository"; +import exportStorageRepository from "../data-access/ExportStorageRepository"; +import { exportInterlinearPdfJob } from "./exportInterlinearPdfJob"; +import { EXPORT_JOB_TYPES } from "./jobTypes"; + +const { uploadPdfMock, updateDataMock } = vi.hoisted(() => ({ + uploadPdfMock: vi.fn(), + updateDataMock: vi.fn(), +})); + +vi.mock("../data-access/ExportStorageRepository", () => { + const repository = { + uploadPdf: uploadPdfMock, + }; + return { __esModule: true, default: repository }; +}); + +vi.mock("@/shared/jobs/data-access/jobRepository", () => ({ + __esModule: true, + default: { + updateData: updateDataMock, + }, +})); + +describe("exportInterlinearPdfJob", () => { + beforeEach(() => { + uploadPdfMock.mockReset(); + updateDataMock.mockReset(); + }); + + test("uploads a placeholder PDF and records the export key", async () => { + const job = { + id: "job-1", + type: EXPORT_JOB_TYPES.EXPORT_INTERLINEAR_PDF, + status: JobStatus.Pending, + payload: { + languageId: "language-1", + languageCode: "spa", + requestedBy: "user-1", + }, + createdAt: new Date("2026-04-09T00:00:00.000Z"), + updatedAt: new Date("2026-04-09T00:00:00.000Z"), + }; + + await expect(exportInterlinearPdfJob(job)).resolves.toBeUndefined(); + + expect(exportStorageRepository.uploadPdf).toHaveBeenCalledExactlyOnceWith({ + environment: "local", + key: "interlinear/spa/job-1.pdf", + stream: expect.any(Readable), + }); + expect(jobRepository.updateData).toHaveBeenCalledExactlyOnceWith("job-1", { + exportKey: "interlinear/spa/job-1.pdf", + }); + }); +}); diff --git a/src/shared/s3.ts b/src/shared/s3.ts index f65b8abd..35307c39 100644 --- a/src/shared/s3.ts +++ b/src/shared/s3.ts @@ -1,5 +1,4 @@ import { S3Client } from "@aws-sdk/client-s3"; -import { Readable } from "stream"; let cachedS3Client: S3Client | undefined; @@ -13,90 +12,3 @@ export function getS3Client(): S3Client { }); return cachedS3Client; } - -export function mergeUint8Arrays(parts: Uint8Array[]): Uint8Array { - const total = parts.reduce((sum, part) => sum + part.byteLength, 0); - const merged = new Uint8Array(total); - let offset = 0; - for (const part of parts) { - merged.set(part, offset); - offset += part.byteLength; - } - return merged; -} - -async function readableToUint8Array(stream: Readable): Promise { - const parts: Uint8Array[] = []; - for await (const chunk of stream) { - if (typeof chunk === "string") { - parts.push(Uint8Array.from(Buffer.from(chunk))); - } else if (chunk instanceof Uint8Array) { - parts.push(chunk); - } else { - parts.push(Uint8Array.from(chunk)); - } - } - return mergeUint8Arrays(parts); -} - -async function webStreamToUint8Array( - stream: ReadableStream, -): Promise { - const reader = stream.getReader(); - const parts: Uint8Array[] = []; - // eslint-disable-next-line no-constant-condition - while (true) { - const { done, value } = await reader.read(); - if (done) break; - if (value) parts.push(value); - } - return mergeUint8Arrays(parts); -} - -export async function s3BodyToUint8Array( - body: unknown, -): Promise { - if (!body) return undefined; - if (body instanceof Uint8Array) return body; - if (body instanceof Readable) return readableToUint8Array(body); - - if (typeof Blob !== "undefined" && body instanceof Blob) { - return new Uint8Array(await body.arrayBuffer()); - } - - if (typeof (body as ReadableStream).getReader === "function") { - return webStreamToUint8Array(body as ReadableStream); - } - - return undefined; -} - -export async function s3BodyToReadable( - body: unknown, -): Promise { - if (!body) return undefined; - if (body instanceof Readable) return body; - - if (body instanceof Uint8Array) { - return Readable.from([body]); - } - - if (typeof Blob !== "undefined" && body instanceof Blob) { - const bytes = new Uint8Array(await body.arrayBuffer()); - return Readable.from([bytes]); - } - - if (typeof (body as ReadableStream).getReader === "function") { - const webStream = body as ReadableStream; - const maybeFromWeb = ( - Readable as unknown as { fromWeb?: (s: any) => Readable } - ).fromWeb; - if (typeof maybeFromWeb === "function") { - return maybeFromWeb(webStream); - } - const bytes = await webStreamToUint8Array(webStream); - return Readable.from([bytes]); - } - - return undefined; -} From 5f450636e54eb19b072a9ca4024d806c4c96da27 Mon Sep 17 00:00:00 2001 From: delgado-jacob <29643013+delgado-jacob@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:54:41 -0700 Subject: [PATCH 7/7] chore: remove unused export dependencies --- package-lock.json | 134 ++++++---------------------------------------- package.json | 3 -- 2 files changed, 16 insertions(+), 121 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5549e0d2..a737b28c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "@aws-sdk/client-s3": "3.662.0", "@aws-sdk/client-sqs": "^3.678.0", "@aws-sdk/lib-storage": "3.658.1", - "@aws-sdk/s3-request-presigner": "3.662.0", "@date-fns/utc": "2.1.1", "@eslint-react/eslint-plugin": "2.13.0", "@eslint/eslintrc": "3.3.5", @@ -55,7 +54,6 @@ "aws-lambda": "1.0.7", "chart.js": "4.4.4", "chartjs-adapter-date-fns": "3.0.0", - "chokidar": "5.0.0", "d3": "7.9.0", "date-fns": "4.1.0", "dompurify": "3.3.3", @@ -71,7 +69,6 @@ "kysely": "0.28.2", "lint-staged": "15.2.11", "lodash": "4.17.21", - "lru-cache": "11.2.7", "nitro": "3.0.260311-beta", "nodemailer": "6.9.15", "patch-package": "8.0.1", @@ -111,13 +108,6 @@ "lru-cache": "^10.4.3" } }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "dev": true, @@ -1497,40 +1487,6 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.662.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.662.0.tgz", - "integrity": "sha512-O3FXO4LGNXzIXtrWPBu+ImQcF3DxRiP87cJObdNDso3p+UZQ5rlsUnYovnD8WazFfUbBcYy6IK1+yYJDyXXQvw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/signature-v4-multi-region": "3.662.0", - "@aws-sdk/types": "3.662.0", - "@aws-sdk/util-format-url": "3.662.0", - "@smithy/middleware-endpoint": "^3.1.4", - "@smithy/protocol-http": "^4.1.4", - "@smithy/smithy-client": "^3.3.6", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/s3-request-presigner/node_modules/@aws-sdk/types": { - "version": "3.662.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.662.0.tgz", - "integrity": "sha512-Ff9/KRmIm8iEzodxzISLj4/pB/0hX2nVw1RFeOBC65OuM6nHrAdWHHog/CVx25hS5JPU0uE3h6NlWRaBJ7AV5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.662.0", "dev": true, @@ -1614,36 +1570,6 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/util-format-url": { - "version": "3.662.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.662.0.tgz", - "integrity": "sha512-McyEyXsZMzuk/nqrVEbjCSmsKykJ7UI4lTDMdaqFdL0l5K/6VWgbFc3xOZcxEGBIvNucHiusQhqJXYHCAG65Dg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.662.0", - "@smithy/querystring-builder": "^3.0.7", - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/util-format-url/node_modules/@aws-sdk/types": { - "version": "3.662.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.662.0.tgz", - "integrity": "sha512-Ff9/KRmIm8iEzodxzISLj4/pB/0hX2nVw1RFeOBC65OuM6nHrAdWHHog/CVx25hS5JPU0uE3h6NlWRaBJ7AV5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^3.5.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.568.0", "dev": true, @@ -8416,22 +8342,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -12687,14 +12597,11 @@ } }, "node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } + "license": "ISC" }, "node_modules/lz-string": { "version": "1.5.0", @@ -12922,6 +12829,18 @@ } } }, + "node_modules/nitro/node_modules/lru-cache": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.3.tgz", + "integrity": "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/nitro/node_modules/unstorage": { "version": "2.0.0-alpha.7", "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-2.0.0-alpha.7.tgz", @@ -13520,13 +13439,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -14786,20 +14698,6 @@ "node": ">= 6" } }, - "node_modules/readdirp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", - "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/real-require": { "version": "0.2.0", "dev": true, diff --git a/package.json b/package.json index 8a74428e..4a37a1c0 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "@aws-sdk/client-s3": "3.662.0", "@aws-sdk/client-sqs": "^3.678.0", "@aws-sdk/lib-storage": "3.658.1", - "@aws-sdk/s3-request-presigner": "3.662.0", "@date-fns/utc": "2.1.1", "@eslint-react/eslint-plugin": "2.13.0", "@eslint/eslintrc": "3.3.5", @@ -64,7 +63,6 @@ "aws-lambda": "1.0.7", "chart.js": "4.4.4", "chartjs-adapter-date-fns": "3.0.0", - "chokidar": "5.0.0", "d3": "7.9.0", "date-fns": "4.1.0", "dompurify": "3.3.3", @@ -79,7 +77,6 @@ "jsdom": "^26.1.0", "kysely": "0.28.2", "lint-staged": "15.2.11", - "lru-cache": "11.2.7", "lodash": "4.17.21", "nitro": "3.0.260311-beta", "nodemailer": "6.9.15",