diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 6063c1c..e18e390 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -7,6 +7,7 @@ import { BootstrapModule } from "./bootstrap/bootstrap.module.js"; import { CompaniesModule } from "./companies/companies.module.js"; import { UsersModule } from "./users/users.module.js"; import { RolesModule } from "./roles/roles.module.js"; +import { ProductsModule } from "./products/products.module.js"; @Module({ imports: [ @@ -17,6 +18,7 @@ import { RolesModule } from "./roles/roles.module.js"; CompaniesModule, UsersModule, RolesModule, + ProductsModule, ], controllers: [HealthController], }) diff --git a/apps/api/src/products/products.service.ts b/apps/api/src/products/products.service.ts index ef537cc..4cf0a31 100644 --- a/apps/api/src/products/products.service.ts +++ b/apps/api/src/products/products.service.ts @@ -1,17 +1,5 @@ import { Injectable, NotFoundException } from "@nestjs/common"; -import { auditRepo } from "@eden/database"; -// Deliberately deep-imported rather than added to the shared @eden/database -// barrel (packages/database/src/index.ts) — that file is off limits for -// this module per the integration boundary (avoids merge conflicts with -// sibling agents editing the same barrel concurrently). Whoever wires -// modules together should add -// export * as productsRepo from "./repositories/products.js"; -// export type { Product } from "./repositories/products.js"; -// to the barrel for parity with how companiesRepo is consumed; until then -// this deep path is functionally equivalent (requires @eden/database to be -// built first, same as the barrel import would). -import * as productsRepo from "@eden/database/dist/repositories/products.js"; -import type { Product } from "@eden/database/dist/repositories/products.js"; +import { auditRepo, productsRepo, type Product } from "@eden/database"; import type { CreateProductDto } from "./dto/create-product.dto.js"; import type { UpdateProductDto } from "./dto/update-product.dto.js"; diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index 9e7eacd..34eff15 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -4,5 +4,7 @@ export * as usersRepo from "./repositories/users.js"; export * as sessionsRepo from "./repositories/sessions.js"; export * as auditRepo from "./repositories/audit.js"; export * as companiesRepo from "./repositories/companies.js"; +export * as productsRepo from "./repositories/products.js"; export type * from "./repositories/types.js"; export type { Company } from "./repositories/companies.js"; +export type { Product, CreateProductInput, UpdateProductInput } from "./repositories/products.js"; diff --git a/packages/integrations/package.json b/packages/integrations/package.json index d2355e8..55d26b9 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -10,7 +10,13 @@ "typecheck": "tsc -p tsconfig.json --noEmit", "lint": "echo 'no linter configured yet'" }, + "dependencies": { + "@aws-sdk/client-s3": "^3.1096.0", + "@eden/config": "workspace:*", + "nodemailer": "^9.1.1" + }, "devDependencies": { + "@types/nodemailer": "^8.0.1", "typescript": "^5.7.3" } } diff --git a/packages/integrations/src/index.ts b/packages/integrations/src/index.ts index 30464da..01d7896 100644 --- a/packages/integrations/src/index.ts +++ b/packages/integrations/src/index.ts @@ -1,7 +1,8 @@ /** * External adapters (Focus NFe, SaperX, Control iD, S3, SMTP) — each - * isolated behind a port per Master Prompt §13. Empty placeholder; the - * first real adapter (S3 or SMTP) lands with the rest of Fase 1's - * platform bootstrap. + * isolated behind a port per Master Prompt §13. S3 and SMTP adapters land + * first (platform bootstrap); the others follow per-domain as their + * modules are built. */ -export const INTEGRATIONS_PACKAGE_VERSION = "0.1.0"; +export * from "./s3/client.js"; +export * from "./smtp/mailer.js"; diff --git a/packages/integrations/src/s3/client.ts b/packages/integrations/src/s3/client.ts new file mode 100644 index 0000000..2cee17d --- /dev/null +++ b/packages/integrations/src/s3/client.ts @@ -0,0 +1,128 @@ +/** + * S3 adapter — port of the legacy `server/src/lib/s3.js` (eden.md §5 + * "Armazenamento S3"). Compatible with any S3-like endpoint (MinIO, AWS S3), + * not exclusive to AWS. Storage failures are never swallowed: every + * function throws a clear error when S3 isn't configured, unlike the SMTP + * adapter (see `../smtp/mailer.ts`) which fails silently by design. + */ +import { + DeleteObjectCommand, + GetObjectCommand, + PutObjectCommand, + S3Client, +} from "@aws-sdk/client-s3"; +import type { Readable } from "node:stream"; +import { loadEnv } from "@eden/config"; + +const NOT_CONFIGURED_MESSAGE = "S3 não configurado (S3_ENDPOINT ausente)"; + +interface S3Config { + endpoint: string; + region: string; + forcePathStyle: boolean; + accessKeyId: string | undefined; + secretAccessKey: string | undefined; + bucket: string | undefined; +} + +let client: S3Client | undefined; +let config: S3Config | undefined; + +function loadConfig(): S3Config | undefined { + const env = loadEnv(); + if (!env.S3_ENDPOINT) { + return undefined; + } + return { + endpoint: env.S3_ENDPOINT, + region: env.S3_REGION, + forcePathStyle: env.S3_FORCE_PATH_STYLE, + accessKeyId: env.S3_ACCESS_KEY_ID, + secretAccessKey: env.S3_SECRET_ACCESS_KEY, + bucket: env.S3_BUCKET, + }; +} + +function requireConfig(): S3Config { + if (!config) { + config = loadConfig(); + } + if (!config) { + throw new Error(NOT_CONFIGURED_MESSAGE); + } + return config; +} + +/** + * Raw S3 client, exported for callers (e.g. a future Backup module) that + * need multipart streaming upload via `@aws-sdk/lib-storage`'s `Upload` + * class directly instead of `uploadObject` (which buffers the whole file + * in memory — fine for small files, not for a Postgres dump). + */ +export function getClient(): S3Client { + const cfg = requireConfig(); + if (!client) { + client = new S3Client({ + endpoint: cfg.endpoint, + region: cfg.region, + forcePathStyle: cfg.forcePathStyle, + ...(cfg.accessKeyId && cfg.secretAccessKey + ? { + credentials: { + accessKeyId: cfg.accessKeyId, + secretAccessKey: cfg.secretAccessKey, + }, + } + : {}), + }); + } + return client; +} + +/** Single bucket used for all data (no bucket-per-data-type). */ +export function getBucket(): string { + const cfg = requireConfig(); + return cfg.bucket ?? ""; +} + +/** + * Simple PUT, whole buffer in memory — adequate for small/medium files + * (images, PDFs, attachments), not for large streaming uploads. + */ +export async function uploadObject( + key: string, + buffer: Buffer, + mimeType: string, +): Promise { + const s3 = getClient(); + const bucket = getBucket(); + await s3.send( + new PutObjectCommand({ + Bucket: bucket, + Key: key, + Body: buffer, + ContentType: mimeType, + }), + ); +} + +/** + * GET, returning the object body as a Node stream — used both to serve a + * download and to pipe directly to an HTTP response. On Node.js, the SDK + * v3 response `.Body` is already a `Readable` (with extra helper mixins); + * this just types it correctly for callers. + */ +export async function getObjectStream(key: string): Promise { + const s3 = getClient(); + const bucket = getBucket(); + const response = await s3.send( + new GetObjectCommand({ Bucket: bucket, Key: key }), + ); + return response.Body as Readable; +} + +export async function deleteObject(key: string): Promise { + const s3 = getClient(); + const bucket = getBucket(); + await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key })); +} diff --git a/packages/integrations/src/smtp/mailer.ts b/packages/integrations/src/smtp/mailer.ts new file mode 100644 index 0000000..76b4074 --- /dev/null +++ b/packages/integrations/src/smtp/mailer.ts @@ -0,0 +1,76 @@ +/** + * SMTP adapter — port of the legacy `server/src/lib/mailer.js` (eden.md §4 + * "Envio de e-mail"). Generic SMTP via `nodemailer`, not a direct + * integration with SES/SendGrid/etc. + * + * Deliberately fail-soft, unlike the S3 adapter (`../s3/client.ts`): no + * business flow may ever fail because of email. If SMTP isn't configured, + * or if sending fails, `sendMail` logs and returns normally — it never + * throws into the caller. + */ +import nodemailer, { type Transporter } from "nodemailer"; +import { loadEnv } from "@eden/config"; + +export interface SendMailInput { + to: string; + subject: string; + html: string; + from?: string; +} + +let transporter: Transporter | undefined; +let transporterBuilt = false; + +function buildTransporter(): Transporter | undefined { + const env = loadEnv(); + if (!env.SMTP_HOST) { + return undefined; + } + return nodemailer.createTransport({ + host: env.SMTP_HOST, + port: env.SMTP_PORT, + // secure: true automatically when the port is 465 (implicit SMTPS). + secure: env.SMTP_PORT === 465, + ...(env.SMTP_USER ? { auth: { user: env.SMTP_USER, pass: env.SMTP_PASS } } : {}), + }); +} + +function getTransporter(): Transporter | undefined { + // Built once, lazily, on first use — not rebuilt afterwards even if env + // vars change at runtime (simplification acceptable for this MVP). + if (!transporterBuilt) { + transporter = buildTransporter(); + transporterBuilt = true; + } + return transporter; +} + +function defaultFrom(): string | undefined { + const env = loadEnv(); + return env.SMTP_FROM ?? env.SMTP_USER; +} + +export async function sendMail({ + to, + subject, + html, + from, +}: SendMailInput): Promise { + const t = getTransporter(); + if (!t) { + console.log( + `[mailer] SMTP não configurado — e-mail para ${to} não enviado`, + ); + return; + } + try { + await t.sendMail({ + from: from ?? defaultFrom(), + to, + subject, + html, + }); + } catch (err) { + console.error(`[mailer] falha ao enviar e-mail para ${to}:`, err); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 688e693..16ff2ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -248,7 +248,20 @@ importers: version: 5.9.3 packages/integrations: + dependencies: + '@aws-sdk/client-s3': + specifier: ^3.1096.0 + version: 3.1124.0 + '@eden/config': + specifier: workspace:* + version: link:../config + nodemailer: + specifier: ^9.1.1 + version: 9.1.1 devDependencies: + '@types/nodemailer': + specifier: ^8.0.1 + version: 8.0.1 typescript: specifier: ^5.7.3 version: 5.9.3 @@ -284,6 +297,78 @@ packages: resolution: {integrity: sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==} engines: {node: '>=10'} + '@aws-sdk/checksums@3.1000.29': + resolution: {integrity: sha512-Dtu0gr4dnATZAPwEYbpCsG+MpLM7OAliy2gTepEFQwl1vZ6DL3QMH2FveMa3HLvPsOdhJsPRB3KtxVhph9T75A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1124.0': + resolution: {integrity: sha512-f20BksgVlXufcf/mijde1LAjwM/iSDdG3mGtN3yRFOUzXtlFEOjxNi1Rf2Kb+rSgoiOSpO0hUOna9Bs+J1gX2A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.977.9': + resolution: {integrity: sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.70': + resolution: {integrity: sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.72': + resolution: {integrity: sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.973.15': + resolution: {integrity: sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.77': + resolution: {integrity: sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.82': + resolution: {integrity: sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.70': + resolution: {integrity: sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.973.14': + resolution: {integrity: sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.76': + resolution: {integrity: sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.75': + resolution: {integrity: sha512-wMIsNumRVKaNMKhvU/s9VrdEwE8S6gSzXp4RygFG5BEMnGkkXf8cjh8zf7cKJBpUDpqTWqwbz5isEgp9rH6Lng==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.997.44': + resolution: {integrity: sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.46': + resolution: {integrity: sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1116.0': + resolution: {integrity: sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.974.5': + resolution: {integrity: sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.40': + resolution: {integrity: sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -995,6 +1080,30 @@ packages: '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + '@smithy/core@3.33.3': + resolution: {integrity: sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.5.2': + resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.7.2': + resolution: {integrity: sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.12.0': + resolution: {integrity: sha512-0mq1pHadfyXCYCqm2cNpbjNIT+fbaUpNxewZb/YNr2L0IrEVMOb8gM/Fl4K6XvHCW3uSNDFwPl/+iKm0bx9jYg==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.7.3': + resolution: {integrity: sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.17.2': + resolution: {integrity: sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA==} + engines: {node: '>=18.0.0'} + '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} engines: {node: '>=18'} @@ -1071,6 +1180,9 @@ packages: '@types/node@22.20.1': resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + '@types/nodemailer@8.0.1': + resolution: {integrity: sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==} + '@types/pg@8.23.1': resolution: {integrity: sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==} @@ -1172,6 +1284,9 @@ packages: resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + boxen@5.1.2: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} engines: {node: '>=10'} @@ -1729,6 +1844,10 @@ packages: resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} engines: {node: '>=18'} + nodemailer@9.1.1: + resolution: {integrity: sha512-izw9mVKFix6YSnC9eLgV6g1opl9DUlRio9ZNcq+Wu9Ujn2UwF+8Nl0B8nz22kEC+CTZCvinkxwJ0DeFbb6NwcQ==} + engines: {node: '>=6.0.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2246,6 +2365,171 @@ snapshots: '@alloc/quick-lru@5.3.0': {} + '@aws-sdk/checksums@3.1000.29': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1124.0': + dependencies: + '@aws-sdk/checksums': 3.1000.29 + '@aws-sdk/core': 3.977.9 + '@aws-sdk/credential-provider-node': 3.972.82 + '@aws-sdk/middleware-sdk-s3': 3.972.75 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/fetch-http-handler': 5.7.2 + '@smithy/node-http-handler': 4.12.0 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/core@3.977.9': + dependencies: + '@aws-sdk/types': 3.974.5 + '@aws-sdk/xml-builder': 3.972.40 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.33.3 + '@smithy/signature-v4': 5.7.3 + '@smithy/types': 4.17.2 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.70': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.72': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/fetch-http-handler': 5.7.2 + '@smithy/node-http-handler': 4.12.0 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.973.15': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/credential-provider-env': 3.972.70 + '@aws-sdk/credential-provider-http': 3.972.72 + '@aws-sdk/credential-provider-login': 3.972.77 + '@aws-sdk/credential-provider-process': 3.972.70 + '@aws-sdk/credential-provider-sso': 3.973.14 + '@aws-sdk/credential-provider-web-identity': 3.972.76 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.77': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.82': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.70 + '@aws-sdk/credential-provider-http': 3.972.72 + '@aws-sdk/credential-provider-ini': 3.973.15 + '@aws-sdk/credential-provider-process': 3.972.70 + '@aws-sdk/credential-provider-sso': 3.973.14 + '@aws-sdk/credential-provider-web-identity': 3.972.76 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.70': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.973.14': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/token-providers': 3.1116.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.76': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.75': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.44': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/fetch-http-handler': 5.7.2 + '@smithy/node-http-handler': 4.12.0 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.46': + dependencies: + '@aws-sdk/types': 3.974.5 + '@smithy/signature-v4': 5.7.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1116.0': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/types@3.974.5': + dependencies: + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.40': + dependencies: + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.3.0': {} + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -2719,6 +3003,39 @@ snapshots: '@scarf/scarf@1.4.0': {} + '@smithy/core@3.33.3': + dependencies: + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.5.2': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.7.2': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.12.0': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/signature-v4@5.7.3': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/types@4.17.2': + dependencies: + tslib: 2.8.1 + '@tokenizer/inflate@0.2.7(supports-color@7.2.0)': dependencies: debug: 4.4.3(supports-color@7.2.0) @@ -2805,6 +3122,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/nodemailer@8.0.1': + dependencies: + '@types/node': 22.20.1 + '@types/pg@8.23.1': dependencies: '@types/node': 22.20.1 @@ -2934,6 +3255,8 @@ snapshots: transitivePeerDependencies: - supports-color + bowser@2.14.1: {} + boxen@5.1.2: dependencies: ansi-align: 3.0.1 @@ -3547,6 +3870,8 @@ snapshots: node-releases@2.0.54: {} + nodemailer@9.1.1: {} + normalize-path@3.0.0: {} object-assign@4.1.1: {}