From 46c07d36070ecce363a5dd7022035f67ac830529 Mon Sep 17 00:00:00 2001 From: d-klotz Date: Mon, 25 May 2026 13:59:10 -0300 Subject: [PATCH 1/8] feat(hubspot): add Tier 3 webhooks extension bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `hubspot.extensions.webhooks` matching the Tier 3 Integration Extension contract defined in friggframework/frigg#590. Integration consumers wire it in via `static Definition.extensions`: extensions: { hubspotWebhooks: { extension: hubspot.extensions.webhooks, handlers: { HUBSPOT_WEBHOOK: 'onHubSpotEvent' }, }, }, The bundle exposes one receiver route (POST /webhooks) plus two events. The default `HUBSPOT_WEBHOOK_RECEIVED` handler verifies the HubSpot v3 signature (HMAC SHA-256 over method+uri+body+timestamp, client-secret keyed, base64-encoded, 5-minute skew), iterates the inbound event array, resolves each event's portalId to a Frigg integration via the platform-neutral `findIntegrationByEntityExternalId` helper, and enqueues `HUBSPOT_WEBHOOK` jobs per matched event. A thin `findIntegrationByPortalId(integration, portalId)` wrapper lives inside the extension so HubSpot vocabulary stays in the api-module — core stays platform-neutral. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../hubspot/extensions/webhooks/handlers.js | 123 +++++ .../hubspot/extensions/webhooks/index.js | 38 ++ .../hubspot/extensions/webhooks/lookup.js | 37 ++ .../extensions/webhooks/signature-verifier.js | 181 +++++++ packages/v1-ready/hubspot/index.js | 6 +- .../hubspot/tests/extensions/webhooks.test.js | 504 ++++++++++++++++++ 6 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 packages/v1-ready/hubspot/extensions/webhooks/handlers.js create mode 100644 packages/v1-ready/hubspot/extensions/webhooks/index.js create mode 100644 packages/v1-ready/hubspot/extensions/webhooks/lookup.js create mode 100644 packages/v1-ready/hubspot/extensions/webhooks/signature-verifier.js create mode 100644 packages/v1-ready/hubspot/tests/extensions/webhooks.test.js diff --git a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js new file mode 100644 index 0000000..b887de6 --- /dev/null +++ b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js @@ -0,0 +1,123 @@ +const { verifyHubSpotSignature } = require('./signature-verifier'); +const { findIntegrationByPortalId } = require('./lookup'); + +const EVENT_NAME_DISPATCH = 'HUBSPOT_WEBHOOK'; + +/** + * Resolve the HubSpot app client secret used to sign webhook payloads. + * + * Precedence: + * 1. Definition.env.client_secret on the integration class (preferred — same + * env block the api-module already reads for OAuth) + * 2. process.env.HUBSPOT_CLIENT_SECRET (escape hatch when extensions are + * used outside a full integration class context, e.g. tests) + * + * @param {Object} integration - The IntegrationBase instance running the handler. + * @returns {string|undefined} The client secret, or undefined if neither source has it. + */ +function resolveClientSecret(integration) { + const fromDefinition = + integration && + integration.constructor && + integration.constructor.Definition && + integration.constructor.Definition.modules && + Object.values(integration.constructor.Definition.modules) + .map((m) => m && m.definition && m.definition.env) + .find((env) => env && env.client_secret); + return fromDefinition?.client_secret || process.env.HUBSPOT_CLIENT_SECRET; +} + +/** + * Receiver handler for `POST /webhooks`. + * + * Verifies HubSpot's v3 signature, iterates the inbound batch, resolves each + * event's `portalId` to a Frigg integration via the platform-neutral reverse + * lookup, and enqueues a per-event `HUBSPOT_WEBHOOK` job for the matched + * integration. Events whose portal does not map to any integration are + * silently skipped (HubSpot sends events for the whole app, not per-account). + * + * Per the Tier 3 contract, this function is bound as a plain function on the + * integration instance — `this` is the IntegrationBase instance and exposes + * `findIntegrationByEntityExternalId` and `queueWebhook`. + * + * @this {import('@friggframework/core').IntegrationBase} + * @param {Object} args + * @param {import('express').Request} args.req + * @param {import('express').Response} args.res + * @returns {Promise} + */ +async function onHubSpotWebhookReceived({ req, res }) { + const clientSecret = resolveClientSecret(this); + const verification = verifyHubSpotSignature({ req, clientSecret }); + if (!verification.valid) { + console.warn( + `[hubspot-webhooks] rejecting webhook: ${verification.reason}` + ); + res.status(401).json({ error: 'invalid signature' }); + return; + } + + const events = Array.isArray(req.body) ? req.body : []; + if (events.length === 0) { + res.status(200).json({ received: 0, queued: 0 }); + return; + } + + let queued = 0; + let skipped = 0; + for (let i = 0; i < events.length; i++) { + const evt = events[i]; + const portalId = evt && evt.portalId; + const subscriptionType = evt && evt.subscriptionType; + if (portalId === undefined || portalId === null) { + console.warn( + `[hubspot-webhooks] event[${i}] missing portalId (subscriptionType=${subscriptionType}); skipping` + ); + skipped++; + continue; + } + + // Intentionally do not catch — ambiguous resolution is a cross-tenant + // routing risk and the core helper throws by design. + const integrationId = await findIntegrationByPortalId(this, portalId); + if (!integrationId) { + skipped++; + continue; + } + + await this.queueWebhook({ + integrationId, + body: evt, + event: EVENT_NAME_DISPATCH, + }); + queued++; + } + + res.status(200).json({ + received: events.length, + queued, + skipped, + }); +} + +/** + * Default per-event handler. Integration consumers override this via + * `binding.handlers.HUBSPOT_WEBHOOK = 'methodName'`; the default is a no-op + * so a misconfigured binding fails predictably rather than crashing the + * queue worker. + * + * @this {import('@friggframework/core').IntegrationBase} + * @param {Object} args + * @param {Object} args.data + * @returns {Promise} + */ +async function onHubSpotWebhook({ data }) { + // intentional no-op — override via binding.handlers.HUBSPOT_WEBHOOK +} + +module.exports = { + onHubSpotWebhookReceived, + onHubSpotWebhook, + resolveClientSecret, + EVENT_NAME_DISPATCH, +}; diff --git a/packages/v1-ready/hubspot/extensions/webhooks/index.js b/packages/v1-ready/hubspot/extensions/webhooks/index.js new file mode 100644 index 0000000..85a5545 --- /dev/null +++ b/packages/v1-ready/hubspot/extensions/webhooks/index.js @@ -0,0 +1,38 @@ +const { + onHubSpotWebhookReceived, + onHubSpotWebhook, +} = require('./handlers'); + +/** + * HubSpot Webhooks — Tier 3 Integration Extension bundle. + * + * Contributes a single receiver route (`POST /webhooks`) plus two events: + * + * HUBSPOT_WEBHOOK_RECEIVED — bound to the route; verifies signature, + * resolves portalId → integrationId, queues + * one HUBSPOT_WEBHOOK per matched event. + * HUBSPOT_WEBHOOK — default no-op; integrations override via + * `binding.handlers.HUBSPOT_WEBHOOK`. + * + * See `EXTENSIONS.md` in the framework repo for the binding contract. + */ +module.exports = { + name: 'hubspot-webhooks', + routes: [ + { + path: '/webhooks', + method: 'POST', + event: 'HUBSPOT_WEBHOOK_RECEIVED', + }, + ], + events: { + HUBSPOT_WEBHOOK_RECEIVED: { + type: 'LIFE_CYCLE_EVENT', + handler: onHubSpotWebhookReceived, + }, + HUBSPOT_WEBHOOK: { + type: 'LIFE_CYCLE_EVENT', + handler: onHubSpotWebhook, + }, + }, +}; diff --git a/packages/v1-ready/hubspot/extensions/webhooks/lookup.js b/packages/v1-ready/hubspot/extensions/webhooks/lookup.js new file mode 100644 index 0000000..dd15c60 --- /dev/null +++ b/packages/v1-ready/hubspot/extensions/webhooks/lookup.js @@ -0,0 +1,37 @@ +const HUBSPOT_MODULE_NAME = 'hubspot'; + +/** + * HubSpot-vocabulary wrapper around the platform-neutral core helper + * {@link IntegrationBase#findIntegrationByEntityExternalId}. + * + * Lives inside the api-module per the Tier 3 contract: "portalId" is HubSpot + * vocabulary and does not belong in core. The wrapper takes the integration + * instance explicitly so it can be called from extension default handlers + * (where `this` is the integration) or from worker code that holds a handle + * to an instance. + * + * Intentionally does not catch ambiguous-resolution errors — those signal a + * cross-tenant routing risk that callers should not paper over. + * + * @param {Object} integration - An IntegrationBase instance (or any object exposing + * `findIntegrationByEntityExternalId(externalId, moduleName)`). + * @param {string|number} portalId - The HubSpot portal/hub ID from the webhook event. + * @returns {Promise} The Frigg integration id, or null if no matching + * integration exists for that portal. + */ +async function findIntegrationByPortalId(integration, portalId) { + if (!integration || typeof integration.findIntegrationByEntityExternalId !== 'function') { + throw new Error( + 'findIntegrationByPortalId: integration instance must expose findIntegrationByEntityExternalId()' + ); + } + return integration.findIntegrationByEntityExternalId( + portalId, + HUBSPOT_MODULE_NAME + ); +} + +module.exports = { + findIntegrationByPortalId, + HUBSPOT_MODULE_NAME, +}; diff --git a/packages/v1-ready/hubspot/extensions/webhooks/signature-verifier.js b/packages/v1-ready/hubspot/extensions/webhooks/signature-verifier.js new file mode 100644 index 0000000..a18a990 --- /dev/null +++ b/packages/v1-ready/hubspot/extensions/webhooks/signature-verifier.js @@ -0,0 +1,181 @@ +const crypto = require('crypto'); + +const SIGNATURE_HEADER = 'x-hubspot-signature-v3'; +const TIMESTAMP_HEADER = 'x-hubspot-request-timestamp'; +// HubSpot's documented skew window: reject if older than 5 minutes. +const MAX_TIMESTAMP_SKEW_MS = 5 * 60 * 1000; + +/** + * Read a header value case-insensitively from a request-like object. + * Express normalises header keys to lowercase, but extension authors may + * hand us raw objects in tests; tolerate both. + * + * @param {Object} headers - The headers map from the incoming request. + * @param {string} name - The header name (any case). + * @returns {string|undefined} The header value, or undefined. + */ +function getHeader(headers, name) { + if (!headers || typeof headers !== 'object') return undefined; + if (headers[name] !== undefined) return headers[name]; + const lower = name.toLowerCase(); + if (headers[lower] !== undefined) return headers[lower]; + for (const key of Object.keys(headers)) { + if (key.toLowerCase() === lower) return headers[key]; + } + return undefined; +} + +/** + * Reconstruct the request body string as it left HubSpot's servers. + * If middleware preserved the raw body bytes (req.rawBody), use those — + * any whitespace or key ordering tweak from JSON.parse → JSON.stringify + * will break the HMAC. Otherwise fall back to JSON.stringify(parsed body), + * which matches HubSpot's own Node.js sample. + * + * @param {import('express').Request|Object} req - The Express request. + * @returns {string} The body string to feed into the HMAC. + */ +function extractBodyString(req) { + if (typeof req.rawBody === 'string') return req.rawBody; + if (Buffer.isBuffer(req.rawBody)) return req.rawBody.toString('utf8'); + if (req.body === undefined || req.body === null) return ''; + if (typeof req.body === 'string') return req.body; + if (Buffer.isBuffer(req.body)) return req.body.toString('utf8'); + return JSON.stringify(req.body); +} + +/** + * Reconstruct the full request URL HubSpot signed. + * + * HubSpot signs `https://` — protocol, host, path, and + * query string. Behind a load balancer Express sees `http`, so we honour + * `X-Forwarded-Proto` and `X-Forwarded-Host` when present. Callers can + * also set `HUBSPOT_WEBHOOK_BASE_URL` to override entirely (useful for + * tunnels or custom domains). + * + * @param {import('express').Request|Object} req - The Express request. + * @returns {string} The fully-qualified URL string to feed into the HMAC. + */ +function reconstructUrl(req) { + const override = process.env.HUBSPOT_WEBHOOK_BASE_URL; + if (override) { + const trimmed = override.replace(/\/$/, ''); + return `${trimmed}${req.originalUrl || req.url || ''}`; + } + const forwardedProto = getHeader(req.headers, 'x-forwarded-proto'); + const proto = + (forwardedProto && forwardedProto.split(',')[0].trim()) || + req.protocol || + 'https'; + const forwardedHost = getHeader(req.headers, 'x-forwarded-host'); + const host = + (forwardedHost && forwardedHost.split(',')[0].trim()) || + getHeader(req.headers, 'host') || + ''; + const path = req.originalUrl || req.url || ''; + return `${proto}://${host}${path}`; +} + +/** + * Validate that two base64 signatures are equal in constant time. + * + * Decodes both into Buffers so timingSafeEqual sees same-length inputs. + * Returns false on any decode/length mismatch. + * + * @param {string} expected - The signature we computed. + * @param {string} provided - The signature HubSpot sent. + * @returns {boolean} + */ +function safeCompare(expected, provided) { + if (typeof expected !== 'string' || typeof provided !== 'string') { + return false; + } + try { + const a = Buffer.from(expected, 'base64'); + const b = Buffer.from(provided, 'base64'); + if (a.length !== b.length) return false; + return crypto.timingSafeEqual(a, b); + } catch (_err) { + return false; + } +} + +/** + * Verify a HubSpot v3 webhook signature against the configured client secret. + * + * Algorithm (per HubSpot docs): + * message = method + uri + body + timestamp + * signature = base64(HMAC-SHA256(client_secret, utf8(message))) + * + * Returns a structured result instead of throwing so the receiver can map + * different failures to the right log line and HTTP status. Never throws + * for normal "untrusted request" cases. + * + * @param {Object} args + * @param {import('express').Request|Object} args.req - The Express request. + * @param {string} args.clientSecret - The HubSpot app's client secret. + * @param {number} [args.maxSkewMs=300000] - Allowed timestamp skew in milliseconds. + * @param {Function} [args.now=Date.now] - Clock source for tests. + * @returns {{ valid: boolean, reason?: string }} + */ +function verifyHubSpotSignature({ + req, + clientSecret, + maxSkewMs = MAX_TIMESTAMP_SKEW_MS, + now = Date.now, +}) { + if (!clientSecret || typeof clientSecret !== 'string') { + return { valid: false, reason: 'missing client secret' }; + } + if (!req || typeof req !== 'object') { + return { valid: false, reason: 'missing request' }; + } + + const headers = req.headers || {}; + const signature = getHeader(headers, SIGNATURE_HEADER); + const timestamp = getHeader(headers, TIMESTAMP_HEADER); + + if (!signature) { + return { valid: false, reason: `missing ${SIGNATURE_HEADER} header` }; + } + if (!timestamp) { + return { valid: false, reason: `missing ${TIMESTAMP_HEADER} header` }; + } + + const tsNumber = Number(timestamp); + if (!Number.isFinite(tsNumber)) { + return { valid: false, reason: 'malformed timestamp header' }; + } + const skew = Math.abs(now() - tsNumber); + if (skew > maxSkewMs) { + return { + valid: false, + reason: `timestamp skew ${skew}ms exceeds max ${maxSkewMs}ms`, + }; + } + + const method = (req.method || 'POST').toUpperCase(); + const uri = reconstructUrl(req); + const body = extractBodyString(req); + const message = `${method}${uri}${body}${timestamp}`; + + const expected = crypto + .createHmac('sha256', clientSecret) + .update(message, 'utf8') + .digest('base64'); + + if (!safeCompare(expected, signature)) { + return { valid: false, reason: 'signature mismatch' }; + } + return { valid: true }; +} + +module.exports = { + verifyHubSpotSignature, + extractBodyString, + reconstructUrl, + safeCompare, + SIGNATURE_HEADER, + TIMESTAMP_HEADER, + MAX_TIMESTAMP_SKEW_MS, +}; diff --git a/packages/v1-ready/hubspot/index.js b/packages/v1-ready/hubspot/index.js index 002e1fc..caf21ec 100644 --- a/packages/v1-ready/hubspot/index.js +++ b/packages/v1-ready/hubspot/index.js @@ -1,9 +1,13 @@ const {Api} = require('./api'); const {Definition} = require('./definition'); const Config = require('./defaultConfig'); +const webhooks = require('./extensions/webhooks'); module.exports = { Api, Config, - Definition + Definition, + extensions: { + webhooks, + }, }; diff --git a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js new file mode 100644 index 0000000..a0fe8c3 --- /dev/null +++ b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js @@ -0,0 +1,504 @@ +const crypto = require('crypto'); +const webhooksExtension = require('../../extensions/webhooks'); +const { + verifyHubSpotSignature, + reconstructUrl, + extractBodyString, + safeCompare, +} = require('../../extensions/webhooks/signature-verifier'); +const { + findIntegrationByPortalId, +} = require('../../extensions/webhooks/lookup'); +const { + onHubSpotWebhookReceived, + onHubSpotWebhook, +} = require('../../extensions/webhooks/handlers'); + +const CLIENT_SECRET = 'test-client-secret'; + +const buildSignedRequest = ({ + method = 'POST', + url = '/api/hubspot-integration/webhooks', + host = 'app.example.com', + proto = 'https', + body = [{ portalId: 111, subscriptionType: 'contact.creation', objectId: 1 }], + timestamp = Date.now(), + clientSecret = CLIENT_SECRET, + extraHeaders = {}, + overrideSignature, + overrideTimestamp, + rawBody, +} = {}) => { + const bodyString = rawBody !== undefined + ? rawBody + : (typeof body === 'string' ? body : JSON.stringify(body)); + const fullUrl = `${proto}://${host}${url}`; + const message = `${method}${fullUrl}${bodyString}${timestamp}`; + const signature = crypto + .createHmac('sha256', clientSecret) + .update(message, 'utf8') + .digest('base64'); + + return { + method, + url, + originalUrl: url, + protocol: proto, + body: typeof body === 'string' ? body : body, + rawBody: rawBody, + headers: { + host, + 'x-hubspot-signature-v3': overrideSignature ?? signature, + 'x-hubspot-request-timestamp': + overrideTimestamp ?? String(timestamp), + ...extraHeaders, + }, + }; +}; + +const makeRes = () => { + const res = {}; + res.status = jest.fn((code) => { + res.statusCode = code; + return res; + }); + res.json = jest.fn((payload) => { + res.body = payload; + return res; + }); + return res; +}; + +describe('hubspot-webhooks extension bundle shape', () => { + it('conforms to the Tier 3 contract', () => { + expect(webhooksExtension).toEqual( + expect.objectContaining({ + name: 'hubspot-webhooks', + routes: expect.any(Array), + events: expect.any(Object), + }) + ); + expect(webhooksExtension.routes).toHaveLength(1); + expect(webhooksExtension.routes[0]).toEqual({ + path: '/webhooks', + method: 'POST', + event: 'HUBSPOT_WEBHOOK_RECEIVED', + }); + }); + + it('every route event references a declared event', () => { + for (const route of webhooksExtension.routes) { + expect(webhooksExtension.events).toHaveProperty(route.event); + } + }); + + it('declares both HUBSPOT_WEBHOOK_RECEIVED and HUBSPOT_WEBHOOK with function handlers', () => { + expect(typeof webhooksExtension.events.HUBSPOT_WEBHOOK_RECEIVED.handler).toBe( + 'function' + ); + expect(typeof webhooksExtension.events.HUBSPOT_WEBHOOK.handler).toBe( + 'function' + ); + }); +}); + +describe('verifyHubSpotSignature', () => { + it('returns valid:true for a correctly-signed request', () => { + const req = buildSignedRequest(); + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(true); + }); + + it('returns valid:false on signature mismatch', () => { + const req = buildSignedRequest({ overrideSignature: 'AAAA' }); + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/mismatch/); + }); + + it('returns valid:false when the signature header is missing', () => { + const req = buildSignedRequest(); + delete req.headers['x-hubspot-signature-v3']; + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/x-hubspot-signature-v3/); + }); + + it('returns valid:false when the timestamp header is missing', () => { + const req = buildSignedRequest(); + delete req.headers['x-hubspot-request-timestamp']; + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/timestamp/); + }); + + it('rejects timestamps older than the skew window', () => { + const stale = Date.now() - 10 * 60 * 1000; + const req = buildSignedRequest({ timestamp: stale }); + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/timestamp skew/); + }); + + it('rejects timestamps too far in the future', () => { + const future = Date.now() + 10 * 60 * 1000; + const req = buildSignedRequest({ timestamp: future }); + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/timestamp skew/); + }); + + it('rejects non-numeric timestamp headers', () => { + const req = buildSignedRequest({ overrideTimestamp: 'not-a-number' }); + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/malformed/); + }); + + it('returns valid:false when client secret is missing', () => { + const req = buildSignedRequest(); + const result = verifyHubSpotSignature({ req, clientSecret: undefined }); + expect(result.valid).toBe(false); + expect(result.reason).toMatch(/client secret/); + }); + + it('honours X-Forwarded-Proto/Host headers when present', () => { + const timestamp = Date.now(); + const body = [{ portalId: 1 }]; + const bodyString = JSON.stringify(body); + const fullUrl = `https://public.example.com/api/hubspot-integration/webhooks`; + const signature = crypto + .createHmac('sha256', CLIENT_SECRET) + .update(`POST${fullUrl}${bodyString}${timestamp}`, 'utf8') + .digest('base64'); + + const req = { + method: 'POST', + url: '/api/hubspot-integration/webhooks', + originalUrl: '/api/hubspot-integration/webhooks', + protocol: 'http', + body, + headers: { + host: 'internal-lb.local', + 'x-forwarded-proto': 'https', + 'x-forwarded-host': 'public.example.com', + 'x-hubspot-signature-v3': signature, + 'x-hubspot-request-timestamp': String(timestamp), + }, + }; + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(true); + }); + + it('uses HUBSPOT_WEBHOOK_BASE_URL override when set', () => { + const previous = process.env.HUBSPOT_WEBHOOK_BASE_URL; + process.env.HUBSPOT_WEBHOOK_BASE_URL = 'https://override.example.com'; + + const timestamp = Date.now(); + const body = [{ portalId: 1 }]; + const bodyString = JSON.stringify(body); + const fullUrl = `https://override.example.com/api/hubspot-integration/webhooks`; + const signature = crypto + .createHmac('sha256', CLIENT_SECRET) + .update(`POST${fullUrl}${bodyString}${timestamp}`, 'utf8') + .digest('base64'); + + const req = { + method: 'POST', + originalUrl: '/api/hubspot-integration/webhooks', + protocol: 'http', + body, + headers: { + host: 'something-else.local', + 'x-hubspot-signature-v3': signature, + 'x-hubspot-request-timestamp': String(timestamp), + }, + }; + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(true); + + if (previous === undefined) { + delete process.env.HUBSPOT_WEBHOOK_BASE_URL; + } else { + process.env.HUBSPOT_WEBHOOK_BASE_URL = previous; + } + }); + + it('uses req.rawBody when present so JSON re-serialization cannot break the HMAC', () => { + const timestamp = Date.now(); + const rawBody = '[{"portalId":1,"foo": "bar" }]'; // intentional weird spacing + const fullUrl = `https://app.example.com/api/hubspot-integration/webhooks`; + const signature = crypto + .createHmac('sha256', CLIENT_SECRET) + .update(`POST${fullUrl}${rawBody}${timestamp}`, 'utf8') + .digest('base64'); + + const req = { + method: 'POST', + originalUrl: '/api/hubspot-integration/webhooks', + protocol: 'https', + rawBody, + body: JSON.parse(rawBody), + headers: { + host: 'app.example.com', + 'x-hubspot-signature-v3': signature, + 'x-hubspot-request-timestamp': String(timestamp), + }, + }; + const result = verifyHubSpotSignature({ req, clientSecret: CLIENT_SECRET }); + expect(result.valid).toBe(true); + }); +}); + +describe('signature-verifier helpers', () => { + it('safeCompare returns false on length mismatch', () => { + // 4 chars (3 bytes) vs 8 chars (6 bytes) — clearly different decoded lengths + expect(safeCompare('AAAA', 'AAAAAAAA')).toBe(false); + }); + + it('safeCompare returns true on identical inputs', () => { + expect(safeCompare('hello', 'hello')).toBe(true); + }); + + it('safeCompare returns false on non-string inputs', () => { + expect(safeCompare(null, 'abc')).toBe(false); + expect(safeCompare('abc', undefined)).toBe(false); + }); + + it('extractBodyString returns empty string for null body', () => { + expect(extractBodyString({ body: null })).toBe(''); + }); + + it('extractBodyString prefers rawBody string', () => { + expect(extractBodyString({ rawBody: 'raw', body: { a: 1 } })).toBe('raw'); + }); + + it('extractBodyString prefers rawBody buffer', () => { + expect( + extractBodyString({ rawBody: Buffer.from('hello'), body: {} }) + ).toBe('hello'); + }); + + it('extractBodyString JSON-stringifies object bodies', () => { + expect(extractBodyString({ body: { a: 1 } })).toBe('{"a":1}'); + }); + + it('reconstructUrl falls back to req.protocol + host when no forwarded headers', () => { + const req = { + protocol: 'https', + originalUrl: '/abc', + headers: { host: 'foo.local' }, + }; + expect(reconstructUrl(req)).toBe('https://foo.local/abc'); + }); +}); + +describe('findIntegrationByPortalId wrapper', () => { + it('delegates to findIntegrationByEntityExternalId with module="hubspot"', async () => { + const integration = { + findIntegrationByEntityExternalId: jest + .fn() + .mockResolvedValue('integration-abc'), + }; + const result = await findIntegrationByPortalId(integration, 42); + expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledWith( + 42, + 'hubspot' + ); + expect(result).toBe('integration-abc'); + }); + + it('throws when the integration does not expose the helper', async () => { + await expect(findIntegrationByPortalId({}, 42)).rejects.toThrow( + /findIntegrationByEntityExternalId/ + ); + }); + + it('propagates ambiguous-resolution errors instead of swallowing them', async () => { + const ambiguous = new Error( + 'ambiguous resolution — externalId=42 matched 2 entities' + ); + const integration = { + findIntegrationByEntityExternalId: jest.fn().mockRejectedValue(ambiguous), + }; + await expect(findIntegrationByPortalId(integration, 42)).rejects.toThrow( + /ambiguous/ + ); + }); +}); + +describe('onHubSpotWebhookReceived (default receiver handler)', () => { + const makeIntegration = (overrides = {}) => { + const integration = { + findIntegrationByEntityExternalId: jest.fn(async (portalId) => { + if (portalId === 999) return null; // simulate unknown portal + return `integration-for-portal-${portalId}`; + }), + queueWebhook: jest.fn().mockResolvedValue(undefined), + constructor: { + Definition: { + name: 'hubspot', + modules: { + hubspot: { + definition: { + env: { client_secret: CLIENT_SECRET }, + }, + }, + }, + }, + }, + ...overrides, + }; + return integration; + }; + + it('returns 401 on invalid signature', async () => { + const integration = makeIntegration(); + const req = buildSignedRequest({ overrideSignature: 'AAAA' }); + const res = makeRes(); + await onHubSpotWebhookReceived.call(integration, { req, res }); + expect(res.statusCode).toBe(401); + expect(integration.queueWebhook).not.toHaveBeenCalled(); + }); + + it('returns 401 on missing signature header', async () => { + const integration = makeIntegration(); + const req = buildSignedRequest(); + delete req.headers['x-hubspot-signature-v3']; + const res = makeRes(); + await onHubSpotWebhookReceived.call(integration, { req, res }); + expect(res.statusCode).toBe(401); + }); + + it('iterates events, queues each matched event, and returns 200 with counts', async () => { + const integration = makeIntegration(); + const body = [ + { portalId: 111, subscriptionType: 'contact.creation', objectId: 1 }, + { portalId: 222, subscriptionType: 'deal.creation', objectId: 2 }, + ]; + const req = buildSignedRequest({ body }); + const res = makeRes(); + + await onHubSpotWebhookReceived.call(integration, { req, res }); + + expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledTimes( + 2 + ); + expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledWith( + 111, + 'hubspot' + ); + expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledWith( + 222, + 'hubspot' + ); + expect(integration.queueWebhook).toHaveBeenCalledTimes(2); + expect(integration.queueWebhook).toHaveBeenCalledWith({ + integrationId: 'integration-for-portal-111', + body: body[0], + event: 'HUBSPOT_WEBHOOK', + }); + expect(integration.queueWebhook).toHaveBeenCalledWith({ + integrationId: 'integration-for-portal-222', + body: body[1], + event: 'HUBSPOT_WEBHOOK', + }); + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ received: 2, queued: 2, skipped: 0 }); + }); + + it('skips events whose portalId does not resolve to an integration', async () => { + const integration = makeIntegration(); + const body = [ + { portalId: 111, subscriptionType: 'contact.creation', objectId: 1 }, + { portalId: 999, subscriptionType: 'contact.creation', objectId: 2 }, // null lookup + ]; + const req = buildSignedRequest({ body }); + const res = makeRes(); + + await onHubSpotWebhookReceived.call(integration, { req, res }); + + expect(integration.queueWebhook).toHaveBeenCalledTimes(1); + expect(integration.queueWebhook).toHaveBeenCalledWith({ + integrationId: 'integration-for-portal-111', + body: body[0], + event: 'HUBSPOT_WEBHOOK', + }); + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ received: 2, queued: 1, skipped: 1 }); + }); + + it('skips events missing a portalId entirely', async () => { + const integration = makeIntegration(); + const body = [ + { subscriptionType: 'contact.creation', objectId: 1 }, // no portalId + { portalId: 222, subscriptionType: 'deal.creation', objectId: 2 }, + ]; + const req = buildSignedRequest({ body }); + const res = makeRes(); + + await onHubSpotWebhookReceived.call(integration, { req, res }); + + expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledTimes( + 1 + ); + expect(integration.queueWebhook).toHaveBeenCalledTimes(1); + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ received: 2, queued: 1, skipped: 1 }); + }); + + it('returns 200 with zero counts on an empty event array', async () => { + const integration = makeIntegration(); + const req = buildSignedRequest({ body: [] }); + const res = makeRes(); + await onHubSpotWebhookReceived.call(integration, { req, res }); + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ received: 0, queued: 0 }); + expect(integration.queueWebhook).not.toHaveBeenCalled(); + }); + + it('propagates ambiguous-resolution errors instead of catching them', async () => { + const integration = makeIntegration({ + findIntegrationByEntityExternalId: jest + .fn() + .mockRejectedValue(new Error('ambiguous resolution')), + }); + const req = buildSignedRequest({ body: [{ portalId: 111 }] }); + const res = makeRes(); + await expect( + onHubSpotWebhookReceived.call(integration, { req, res }) + ).rejects.toThrow(/ambiguous/); + expect(integration.queueWebhook).not.toHaveBeenCalled(); + }); + + it('falls back to process.env.HUBSPOT_CLIENT_SECRET when Definition.env is absent', async () => { + const previous = process.env.HUBSPOT_CLIENT_SECRET; + process.env.HUBSPOT_CLIENT_SECRET = CLIENT_SECRET; + const integration = { + findIntegrationByEntityExternalId: jest + .fn() + .mockResolvedValue('integration-fallback'), + queueWebhook: jest.fn().mockResolvedValue(undefined), + constructor: { Definition: { name: 'hubspot' } }, + }; + const req = buildSignedRequest({ body: [{ portalId: 111 }] }); + const res = makeRes(); + await onHubSpotWebhookReceived.call(integration, { req, res }); + expect(res.statusCode).toBe(200); + expect(integration.queueWebhook).toHaveBeenCalledTimes(1); + + if (previous === undefined) { + delete process.env.HUBSPOT_CLIENT_SECRET; + } else { + process.env.HUBSPOT_CLIENT_SECRET = previous; + } + }); +}); + +describe('onHubSpotWebhook (default per-event handler)', () => { + it('is a no-op that does not throw', async () => { + await expect( + onHubSpotWebhook.call({}, { data: { body: { portalId: 1 } } }) + ).resolves.toBeUndefined(); + }); +}); From 387ef0095ca4c51c93ef5e5b106734fe571f40bd Mon Sep 17 00:00:00 2001 From: d-klotz Date: Mon, 25 May 2026 14:58:11 -0300 Subject: [PATCH 2/8] refactor(hubspot-webhooks): address PR review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four targeted fixes from Daniel's review: 1. lookup.js — source the module name from `definition.js` instead of hardcoding `'hubspot'`. A rename of the api-module now flows through automatically via `Definition.moduleName`. 2. lookup.js — switch from `integration.findIntegrationByEntityExternalId` to `integration.commands.findIntegrationByEntityExternalId`. The commands surface is the canonical access pattern for cross-cutting lookups in Frigg; the framework-side PR exposes the same use case there. Error message + tests updated to match the new contract. 3. extensions/webhooks/index.js — replace the vague "EXTENSIONS.md in the framework repo" pointer with a concrete URL to the file on the `next` branch of friggframework/frigg. 4. handlers.js — inline `'HUBSPOT_WEBHOOK'` at the single call site and drop the `EVENT_NAME_DISPATCH` const + export. Unnecessary indirection for a string used once. Tests still green (68 passing across the two affected suites). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../hubspot/extensions/webhooks/handlers.js | 5 +- .../hubspot/extensions/webhooks/index.js | 3 +- .../hubspot/extensions/webhooks/lookup.js | 37 ++++++---- .../hubspot/tests/extensions/webhooks.test.js | 73 ++++++++++++------- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js index b887de6..ffc82ea 100644 --- a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js +++ b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js @@ -1,8 +1,6 @@ const { verifyHubSpotSignature } = require('./signature-verifier'); const { findIntegrationByPortalId } = require('./lookup'); -const EVENT_NAME_DISPATCH = 'HUBSPOT_WEBHOOK'; - /** * Resolve the HubSpot app client secret used to sign webhook payloads. * @@ -88,7 +86,7 @@ async function onHubSpotWebhookReceived({ req, res }) { await this.queueWebhook({ integrationId, body: evt, - event: EVENT_NAME_DISPATCH, + event: 'HUBSPOT_WEBHOOK', }); queued++; } @@ -119,5 +117,4 @@ module.exports = { onHubSpotWebhookReceived, onHubSpotWebhook, resolveClientSecret, - EVENT_NAME_DISPATCH, }; diff --git a/packages/v1-ready/hubspot/extensions/webhooks/index.js b/packages/v1-ready/hubspot/extensions/webhooks/index.js index 85a5545..ed1cb07 100644 --- a/packages/v1-ready/hubspot/extensions/webhooks/index.js +++ b/packages/v1-ready/hubspot/extensions/webhooks/index.js @@ -14,7 +14,8 @@ const { * HUBSPOT_WEBHOOK — default no-op; integrations override via * `binding.handlers.HUBSPOT_WEBHOOK`. * - * See `EXTENSIONS.md` in the framework repo for the binding contract. + * See the binding contract at: + * https://github.com/friggframework/frigg/blob/next/packages/core/integrations/EXTENSIONS.md */ module.exports = { name: 'hubspot-webhooks', diff --git a/packages/v1-ready/hubspot/extensions/webhooks/lookup.js b/packages/v1-ready/hubspot/extensions/webhooks/lookup.js index dd15c60..aa44f63 100644 --- a/packages/v1-ready/hubspot/extensions/webhooks/lookup.js +++ b/packages/v1-ready/hubspot/extensions/webhooks/lookup.js @@ -1,8 +1,11 @@ -const HUBSPOT_MODULE_NAME = 'hubspot'; +const { Definition } = require('../../definition'); /** - * HubSpot-vocabulary wrapper around the platform-neutral core helper - * {@link IntegrationBase#findIntegrationByEntityExternalId}. + * HubSpot-vocabulary wrapper around the platform-neutral friggCommand + * `findIntegrationByEntityExternalId`. The command is the canonical access + * point for cross-cutting reverse lookups; this wrapper exists so the + * api-module's webhook code reads in HubSpot terms ("portalId") rather than + * the generic "externalId / moduleName" pair. * * Lives inside the api-module per the Tier 3 contract: "portalId" is HubSpot * vocabulary and does not belong in core. The wrapper takes the integration @@ -10,28 +13,36 @@ const HUBSPOT_MODULE_NAME = 'hubspot'; * (where `this` is the integration) or from worker code that holds a handle * to an instance. * + * The module name is sourced from `definition.js` so a rename of the api + * module flows through automatically. + * * Intentionally does not catch ambiguous-resolution errors — those signal a * cross-tenant routing risk that callers should not paper over. * - * @param {Object} integration - An IntegrationBase instance (or any object exposing - * `findIntegrationByEntityExternalId(externalId, moduleName)`). - * @param {string|number} portalId - The HubSpot portal/hub ID from the webhook event. - * @returns {Promise} The Frigg integration id, or null if no matching - * integration exists for that portal. + * @param {Object} integration - An IntegrationBase instance with `commands` + * wired (see `createFriggCommands` in @friggframework/core). + * @param {string|number} portalId - The HubSpot portal/hub ID from the + * webhook event. + * @returns {Promise} The Frigg integration id, or null if no + * matching integration exists for that portal. */ async function findIntegrationByPortalId(integration, portalId) { - if (!integration || typeof integration.findIntegrationByEntityExternalId !== 'function') { + if ( + !integration || + !integration.commands || + typeof integration.commands.findIntegrationByEntityExternalId !== + 'function' + ) { throw new Error( - 'findIntegrationByPortalId: integration instance must expose findIntegrationByEntityExternalId()' + 'findIntegrationByPortalId: integration instance must expose commands.findIntegrationByEntityExternalId() — wire up createFriggCommands in your integration constructor.' ); } - return integration.findIntegrationByEntityExternalId( + return integration.commands.findIntegrationByEntityExternalId( portalId, - HUBSPOT_MODULE_NAME + Definition.moduleName ); } module.exports = { findIntegrationByPortalId, - HUBSPOT_MODULE_NAME, }; diff --git a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js index a0fe8c3..d363b3e 100644 --- a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js +++ b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js @@ -294,24 +294,28 @@ describe('signature-verifier helpers', () => { }); describe('findIntegrationByPortalId wrapper', () => { - it('delegates to findIntegrationByEntityExternalId with module="hubspot"', async () => { + it('delegates to commands.findIntegrationByEntityExternalId with the module name from definition.js', async () => { const integration = { - findIntegrationByEntityExternalId: jest - .fn() - .mockResolvedValue('integration-abc'), + commands: { + findIntegrationByEntityExternalId: jest + .fn() + .mockResolvedValue('integration-abc'), + }, }; const result = await findIntegrationByPortalId(integration, 42); - expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledWith( - 42, - 'hubspot' - ); + expect( + integration.commands.findIntegrationByEntityExternalId + ).toHaveBeenCalledWith(42, 'hubspot'); expect(result).toBe('integration-abc'); }); - it('throws when the integration does not expose the helper', async () => { + it('throws when the integration does not expose commands.findIntegrationByEntityExternalId', async () => { await expect(findIntegrationByPortalId({}, 42)).rejects.toThrow( - /findIntegrationByEntityExternalId/ + /commands\.findIntegrationByEntityExternalId/ ); + await expect( + findIntegrationByPortalId({ commands: {} }, 42) + ).rejects.toThrow(/commands\.findIntegrationByEntityExternalId/); }); it('propagates ambiguous-resolution errors instead of swallowing them', async () => { @@ -319,21 +323,30 @@ describe('findIntegrationByPortalId wrapper', () => { 'ambiguous resolution — externalId=42 matched 2 entities' ); const integration = { - findIntegrationByEntityExternalId: jest.fn().mockRejectedValue(ambiguous), + commands: { + findIntegrationByEntityExternalId: jest + .fn() + .mockRejectedValue(ambiguous), + }, }; - await expect(findIntegrationByPortalId(integration, 42)).rejects.toThrow( - /ambiguous/ - ); + await expect( + findIntegrationByPortalId(integration, 42) + ).rejects.toThrow(/ambiguous/); }); }); describe('onHubSpotWebhookReceived (default receiver handler)', () => { const makeIntegration = (overrides = {}) => { + const commandsOverride = overrides.commands; + delete overrides.commands; const integration = { - findIntegrationByEntityExternalId: jest.fn(async (portalId) => { - if (portalId === 999) return null; // simulate unknown portal - return `integration-for-portal-${portalId}`; - }), + commands: { + findIntegrationByEntityExternalId: jest.fn(async (portalId) => { + if (portalId === 999) return null; // simulate unknown portal + return `integration-for-portal-${portalId}`; + }), + ...commandsOverride, + }, queueWebhook: jest.fn().mockResolvedValue(undefined), constructor: { Definition: { @@ -381,14 +394,14 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { await onHubSpotWebhookReceived.call(integration, { req, res }); - expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledTimes( + expect(integration.commands.findIntegrationByEntityExternalId).toHaveBeenCalledTimes( 2 ); - expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledWith( + expect(integration.commands.findIntegrationByEntityExternalId).toHaveBeenCalledWith( 111, 'hubspot' ); - expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledWith( + expect(integration.commands.findIntegrationByEntityExternalId).toHaveBeenCalledWith( 222, 'hubspot' ); @@ -439,7 +452,7 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { await onHubSpotWebhookReceived.call(integration, { req, res }); - expect(integration.findIntegrationByEntityExternalId).toHaveBeenCalledTimes( + expect(integration.commands.findIntegrationByEntityExternalId).toHaveBeenCalledTimes( 1 ); expect(integration.queueWebhook).toHaveBeenCalledTimes(1); @@ -459,9 +472,11 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { it('propagates ambiguous-resolution errors instead of catching them', async () => { const integration = makeIntegration({ - findIntegrationByEntityExternalId: jest - .fn() - .mockRejectedValue(new Error('ambiguous resolution')), + commands: { + findIntegrationByEntityExternalId: jest + .fn() + .mockRejectedValue(new Error('ambiguous resolution')), + }, }); const req = buildSignedRequest({ body: [{ portalId: 111 }] }); const res = makeRes(); @@ -475,9 +490,11 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { const previous = process.env.HUBSPOT_CLIENT_SECRET; process.env.HUBSPOT_CLIENT_SECRET = CLIENT_SECRET; const integration = { - findIntegrationByEntityExternalId: jest - .fn() - .mockResolvedValue('integration-fallback'), + commands: { + findIntegrationByEntityExternalId: jest + .fn() + .mockResolvedValue('integration-fallback'), + }, queueWebhook: jest.fn().mockResolvedValue(undefined), constructor: { Definition: { name: 'hubspot' } }, }; From e68d4638bf7eebeb5f2ca5290ba9a8e4a9647796 Mon Sep 17 00:00:00 2001 From: d-klotz Date: Mon, 25 May 2026 15:05:44 -0300 Subject: [PATCH 3/8] refactor(hubspot-webhooks): drop resolveClientSecret, use env var directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daniel's follow-up: why not just read process.env.HUBSPOT_CLIENT_SECRET directly? There is no good reason. resolveClientSecret was doing a fragile Object.values(Definition.modules) traversal that: - silently picked the first module with env.client_secret if more than one was present — a key-order assumption in a security path - duplicated the env-var read at the bottom of the function anyway - had a JSDoc precedence section that didn't actually match the code Replaced with a direct `process.env.HUBSPOT_CLIENT_SECRET` read at the single call site. Deleted the helper + its export, simplified the test integration mock (no more constructor.Definition.modules scaffolding), and replaced the "falls back to env var" test with one that asserts 401 when the env var is missing. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../hubspot/extensions/webhooks/handlers.js | 45 ++++---------- .../hubspot/tests/extensions/webhooks.test.js | 61 ++++++++----------- 2 files changed, 39 insertions(+), 67 deletions(-) diff --git a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js index ffc82ea..5272a80 100644 --- a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js +++ b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js @@ -1,42 +1,20 @@ const { verifyHubSpotSignature } = require('./signature-verifier'); const { findIntegrationByPortalId } = require('./lookup'); -/** - * Resolve the HubSpot app client secret used to sign webhook payloads. - * - * Precedence: - * 1. Definition.env.client_secret on the integration class (preferred — same - * env block the api-module already reads for OAuth) - * 2. process.env.HUBSPOT_CLIENT_SECRET (escape hatch when extensions are - * used outside a full integration class context, e.g. tests) - * - * @param {Object} integration - The IntegrationBase instance running the handler. - * @returns {string|undefined} The client secret, or undefined if neither source has it. - */ -function resolveClientSecret(integration) { - const fromDefinition = - integration && - integration.constructor && - integration.constructor.Definition && - integration.constructor.Definition.modules && - Object.values(integration.constructor.Definition.modules) - .map((m) => m && m.definition && m.definition.env) - .find((env) => env && env.client_secret); - return fromDefinition?.client_secret || process.env.HUBSPOT_CLIENT_SECRET; -} - /** * Receiver handler for `POST /webhooks`. * - * Verifies HubSpot's v3 signature, iterates the inbound batch, resolves each - * event's `portalId` to a Frigg integration via the platform-neutral reverse - * lookup, and enqueues a per-event `HUBSPOT_WEBHOOK` job for the matched - * integration. Events whose portal does not map to any integration are - * silently skipped (HubSpot sends events for the whole app, not per-account). + * Verifies HubSpot's v3 signature using `process.env.HUBSPOT_CLIENT_SECRET` + * (the same env var the api-module already reads for OAuth), iterates the + * inbound batch, resolves each event's `portalId` to a Frigg integration via + * the platform-neutral reverse lookup, and enqueues a per-event + * `HUBSPOT_WEBHOOK` job for the matched integration. Events whose portal + * does not map to any integration are silently skipped (HubSpot sends events + * for the whole app, not per-account). * * Per the Tier 3 contract, this function is bound as a plain function on the * integration instance — `this` is the IntegrationBase instance and exposes - * `findIntegrationByEntityExternalId` and `queueWebhook`. + * `commands.findIntegrationByEntityExternalId` and `queueWebhook`. * * @this {import('@friggframework/core').IntegrationBase} * @param {Object} args @@ -45,8 +23,10 @@ function resolveClientSecret(integration) { * @returns {Promise} */ async function onHubSpotWebhookReceived({ req, res }) { - const clientSecret = resolveClientSecret(this); - const verification = verifyHubSpotSignature({ req, clientSecret }); + const verification = verifyHubSpotSignature({ + req, + clientSecret: process.env.HUBSPOT_CLIENT_SECRET, + }); if (!verification.valid) { console.warn( `[hubspot-webhooks] rejecting webhook: ${verification.reason}` @@ -116,5 +96,4 @@ async function onHubSpotWebhook({ data }) { module.exports = { onHubSpotWebhookReceived, onHubSpotWebhook, - resolveClientSecret, }; diff --git a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js index d363b3e..6866493 100644 --- a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js +++ b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js @@ -336,6 +336,21 @@ describe('findIntegrationByPortalId wrapper', () => { }); describe('onHubSpotWebhookReceived (default receiver handler)', () => { + let previousClientSecret; + + beforeAll(() => { + previousClientSecret = process.env.HUBSPOT_CLIENT_SECRET; + process.env.HUBSPOT_CLIENT_SECRET = CLIENT_SECRET; + }); + + afterAll(() => { + if (previousClientSecret === undefined) { + delete process.env.HUBSPOT_CLIENT_SECRET; + } else { + process.env.HUBSPOT_CLIENT_SECRET = previousClientSecret; + } + }); + const makeIntegration = (overrides = {}) => { const commandsOverride = overrides.commands; delete overrides.commands; @@ -348,18 +363,6 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { ...commandsOverride, }, queueWebhook: jest.fn().mockResolvedValue(undefined), - constructor: { - Definition: { - name: 'hubspot', - modules: { - hubspot: { - definition: { - env: { client_secret: CLIENT_SECRET }, - }, - }, - }, - }, - }, ...overrides, }; return integration; @@ -486,28 +489,18 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { expect(integration.queueWebhook).not.toHaveBeenCalled(); }); - it('falls back to process.env.HUBSPOT_CLIENT_SECRET when Definition.env is absent', async () => { - const previous = process.env.HUBSPOT_CLIENT_SECRET; - process.env.HUBSPOT_CLIENT_SECRET = CLIENT_SECRET; - const integration = { - commands: { - findIntegrationByEntityExternalId: jest - .fn() - .mockResolvedValue('integration-fallback'), - }, - queueWebhook: jest.fn().mockResolvedValue(undefined), - constructor: { Definition: { name: 'hubspot' } }, - }; - const req = buildSignedRequest({ body: [{ portalId: 111 }] }); - const res = makeRes(); - await onHubSpotWebhookReceived.call(integration, { req, res }); - expect(res.statusCode).toBe(200); - expect(integration.queueWebhook).toHaveBeenCalledTimes(1); - - if (previous === undefined) { - delete process.env.HUBSPOT_CLIENT_SECRET; - } else { - process.env.HUBSPOT_CLIENT_SECRET = previous; + it('rejects with 401 when HUBSPOT_CLIENT_SECRET is not set', async () => { + const saved = process.env.HUBSPOT_CLIENT_SECRET; + delete process.env.HUBSPOT_CLIENT_SECRET; + try { + const integration = makeIntegration(); + const req = buildSignedRequest(); + const res = makeRes(); + await onHubSpotWebhookReceived.call(integration, { req, res }); + expect(res.statusCode).toBe(401); + expect(integration.queueWebhook).not.toHaveBeenCalled(); + } finally { + process.env.HUBSPOT_CLIENT_SECRET = saved; } }); }); From 269f008bdf5219bc3c9c6309ae05699e0a28592d Mon Sep 17 00:00:00 2001 From: d-klotz Date: Mon, 25 May 2026 15:11:31 -0300 Subject: [PATCH 4/8] =?UTF-8?q?perf(hubspot-webhooks):=20parallel=20two-ph?= =?UTF-8?q?ase=20dispatch=20(resolve=20=E2=86=92=20enqueue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the sequential-dispatch finding from the earlier review. Before: a for-loop awaited findIntegrationByPortalId then queueWebhook per event. A 50-event HubSpot batch took 50 × (DB lookup + SQS send) inside the synchronous HTTP receiver — realistic batch sizes could exceed HubSpot's 5s response budget and trigger a full-batch retry, duplicating every event that had already been queued before the timeout. On top of that, if a lookup midway through the batch threw ambiguous, the events ahead of it were already queued; HubSpot retried, those events were queued again. After: two-phase. Phase 1 resolves every portalId in parallel (Promise.all). Phase 2 enqueues every match in parallel. If any Phase 1 lookup throws (ambiguous resolution → cross-tenant routing risk), it bubbles before any queueWebhook fires, so the batch is all-or-nothing on the queue side. Same fail-loud semantics on ambiguity; no partial state for HubSpot's retry to duplicate. Side effect: the manual queued/skipped counters are gone — we just filter + take .length on the resolutions array. Two new tests lock the behavior in: - "queues zero events when any one resolution is ambiguous" - "resolves and queues events in parallel rather than serially" (asserts all three lookups + all three queue writes are in flight concurrently at some point) Co-Authored-By: Claude Opus 4.7 (1M context) --- .../hubspot/extensions/webhooks/handlers.js | 68 ++++++++++------- .../hubspot/tests/extensions/webhooks.test.js | 76 +++++++++++++++++++ 2 files changed, 115 insertions(+), 29 deletions(-) diff --git a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js index 5272a80..b81ba57 100644 --- a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js +++ b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js @@ -41,40 +41,50 @@ async function onHubSpotWebhookReceived({ req, res }) { return; } - let queued = 0; - let skipped = 0; - for (let i = 0; i < events.length; i++) { - const evt = events[i]; - const portalId = evt && evt.portalId; - const subscriptionType = evt && evt.subscriptionType; - if (portalId === undefined || portalId === null) { - console.warn( - `[hubspot-webhooks] event[${i}] missing portalId (subscriptionType=${subscriptionType}); skipping` + // Phase 1 — resolve every portalId in parallel before queueing anything. + // Two reasons for two-phase: (a) HubSpot batches can be large and each + // lookup is an independent DB round-trip; running them serially blows + // the HubSpot response budget; (b) if any lookup is ambiguous, the core + // command throws by design — we want that throw to land BEFORE any + // queueWebhook fires so we never leave the batch in a partial-enqueue + // state that HubSpot's retry would then duplicate. + const resolutions = await Promise.all( + events.map(async (evt, i) => { + const portalId = evt && evt.portalId; + if (portalId === undefined || portalId === null) { + console.warn( + `[hubspot-webhooks] event[${i}] missing portalId ` + + `(subscriptionType=${evt && evt.subscriptionType}); skipping` + ); + return null; + } + const integrationId = await findIntegrationByPortalId( + this, + portalId ); - skipped++; - continue; - } + if (!integrationId) return null; + return { integrationId, evt }; + }) + ); - // Intentionally do not catch — ambiguous resolution is a cross-tenant - // routing risk and the core helper throws by design. - const integrationId = await findIntegrationByPortalId(this, portalId); - if (!integrationId) { - skipped++; - continue; - } - - await this.queueWebhook({ - integrationId, - body: evt, - event: 'HUBSPOT_WEBHOOK', - }); - queued++; - } + // Phase 2 — enqueue matched events in parallel. Every match resolved + // cleanly above, so any failure here is genuinely SQS-side and should + // surface to HubSpot for retry. + const matches = resolutions.filter(Boolean); + await Promise.all( + matches.map(({ integrationId, evt }) => + this.queueWebhook({ + integrationId, + body: evt, + event: 'HUBSPOT_WEBHOOK', + }) + ) + ); res.status(200).json({ received: events.length, - queued, - skipped, + queued: matches.length, + skipped: events.length - matches.length, }); } diff --git a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js index 6866493..f6f550f 100644 --- a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js +++ b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js @@ -489,6 +489,82 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { expect(integration.queueWebhook).not.toHaveBeenCalled(); }); + it('queues zero events when any one resolution is ambiguous (all-or-nothing)', async () => { + // Multi-event batch where event[1] resolves ambiguous. With the + // two-phase implementation, NO queueWebhook calls should fire for + // any event — including event[0] which would have resolved cleanly. + // Prevents partial-enqueue + HubSpot-retry-duplication. + const integration = makeIntegration({ + commands: { + findIntegrationByEntityExternalId: jest.fn(async (portalId) => { + if (portalId === 222) + throw new Error('ambiguous resolution'); + return `integration-for-portal-${portalId}`; + }), + }, + }); + const req = buildSignedRequest({ + body: [ + { portalId: 111, subscriptionType: 'contact.creation' }, + { portalId: 222, subscriptionType: 'contact.creation' }, + { portalId: 333, subscriptionType: 'contact.creation' }, + ], + }); + const res = makeRes(); + await expect( + onHubSpotWebhookReceived.call(integration, { req, res }) + ).rejects.toThrow(/ambiguous/); + expect(integration.queueWebhook).not.toHaveBeenCalled(); + }); + + it('resolves and queues events in parallel rather than serially', async () => { + // Each lookup takes a tick; if dispatch were sequential, the second + // lookup would only start after the first lookup AND the first + // queueWebhook had resolved. We assert all three lookups have begun + // before any of them complete. + const inFlightLookups = jest.fn(); + const inFlightQueueWrites = jest.fn(); + let outstandingLookups = 0; + let outstandingQueueWrites = 0; + const integration = makeIntegration({ + commands: { + findIntegrationByEntityExternalId: jest.fn(async (portalId) => { + outstandingLookups += 1; + inFlightLookups(outstandingLookups); + await new Promise((resolve) => setImmediate(resolve)); + outstandingLookups -= 1; + return `integration-for-portal-${portalId}`; + }), + }, + queueWebhook: jest.fn(async () => { + outstandingQueueWrites += 1; + inFlightQueueWrites(outstandingQueueWrites); + await new Promise((resolve) => setImmediate(resolve)); + outstandingQueueWrites -= 1; + }), + }); + const req = buildSignedRequest({ + body: [ + { portalId: 111, subscriptionType: 'contact.creation' }, + { portalId: 222, subscriptionType: 'contact.creation' }, + { portalId: 333, subscriptionType: 'contact.creation' }, + ], + }); + const res = makeRes(); + await onHubSpotWebhookReceived.call(integration, { req, res }); + + // At some point all three lookups (and all three queue writes) + // should have been in flight simultaneously — that's what + // distinguishes parallel from serial dispatch. + expect(Math.max(...inFlightLookups.mock.calls.map((c) => c[0]))).toBe( + 3 + ); + expect( + Math.max(...inFlightQueueWrites.mock.calls.map((c) => c[0])) + ).toBe(3); + expect(res.body).toEqual({ received: 3, queued: 3, skipped: 0 }); + }); + it('rejects with 401 when HUBSPOT_CLIENT_SECRET is not set', async () => { const saved = process.env.HUBSPOT_CLIENT_SECRET; delete process.env.HUBSPOT_CLIENT_SECRET; From 94adb1881b7a71b88b9bfe765ab09e32be258e86 Mon Sep 17 00:00:00 2001 From: d-klotz Date: Mon, 25 May 2026 15:30:06 -0300 Subject: [PATCH 5/8] fix(hubspot-webhooks): unify empty-batch response shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Graphite review flagged that the empty-array early-return responded { received: 0, queued: 0 } while the normal path returned { received, queued, skipped } — a client referencing .skipped on the empty case would see undefined. Rather than patching the early-return to add skipped: 0, delete the branch entirely. Promise.all([]) → [], filter(Boolean) → [], so the normal path handles the empty case as a degenerate run and emits the same shape ({ received: 0, queued: 0, skipped: 0 }). One less branch, one less response-shape definition, one less inconsistency to remember. Updated the empty-batch test to assert the unified shape. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/v1-ready/hubspot/extensions/webhooks/handlers.js | 4 ---- packages/v1-ready/hubspot/tests/extensions/webhooks.test.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js index b81ba57..eb711a9 100644 --- a/packages/v1-ready/hubspot/extensions/webhooks/handlers.js +++ b/packages/v1-ready/hubspot/extensions/webhooks/handlers.js @@ -36,10 +36,6 @@ async function onHubSpotWebhookReceived({ req, res }) { } const events = Array.isArray(req.body) ? req.body : []; - if (events.length === 0) { - res.status(200).json({ received: 0, queued: 0 }); - return; - } // Phase 1 — resolve every portalId in parallel before queueing anything. // Two reasons for two-phase: (a) HubSpot batches can be large and each diff --git a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js index f6f550f..55f44a7 100644 --- a/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js +++ b/packages/v1-ready/hubspot/tests/extensions/webhooks.test.js @@ -469,7 +469,7 @@ describe('onHubSpotWebhookReceived (default receiver handler)', () => { const res = makeRes(); await onHubSpotWebhookReceived.call(integration, { req, res }); expect(res.statusCode).toBe(200); - expect(res.body).toEqual({ received: 0, queued: 0 }); + expect(res.body).toEqual({ received: 0, queued: 0, skipped: 0 }); expect(integration.queueWebhook).not.toHaveBeenCalled(); }); From b5f6a2b074a60d358b01a637ca538c936e8aaab9 Mon Sep 17 00:00:00 2001 From: d-klotz Date: Wed, 27 May 2026 13:02:51 -0300 Subject: [PATCH 6/8] docs(hubspot): document the webhooks Integration Extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expands the api-module README from a stub to explain the Tier 3 webhooks extension added in this PR: - the app-level vs account-level problem it solves - the concern/identity split (signature → app secret, routing → portalId lookup, business logic → per-portal credentials) - how to bind it on an integration (Definition.extensions + commands) - the HUBSPOT_CLIENT_SECRET requirement - what the bundle contributes (routes + the two events) Also closes the unterminated docs-site markdown link in the intro. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/v1-ready/hubspot/README.md | 91 ++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/packages/v1-ready/hubspot/README.md b/packages/v1-ready/hubspot/README.md index 155d2f0..47a334d 100644 --- a/packages/v1-ready/hubspot/README.md +++ b/packages/v1-ready/hubspot/README.md @@ -2,4 +2,93 @@ This is the API Module for hubspot that allows the [Frigg](https://friggframework.org) code to talk to the hubspot API. -Read more on the [Frigg documentation site](https://docs.friggframework.org/api-modules/list/hubspot \ No newline at end of file +Read more on the [Frigg documentation site](https://docs.friggframework.org/api-modules/list/hubspot). + +## Webhooks (Integration Extension) + +HubSpot webhooks are **app-level**, not account-level: one HubSpot app has a single +webhook target URL, and every connected portal (account) fires events to that same +URL. A Frigg app, by contrast, runs many per-portal integration records behind one +deployment. Bridging the two means every integration that wants HubSpot webhooks has +to write the same plumbing — an HTTP receiver, signature verification, a portal-ID +lookup to find the right integration record, and a hand-off to a queue worker. + +This module ships that plumbing once as a **Tier 3 Integration Extension** — +`hubspot.extensions.webhooks` — so an integration just plugs it in and writes the +business logic. The extension is a reusable bundle of `{ routes, events }` that the +Frigg framework merges into the consuming integration's definition. See the framework +[EXTENSIONS.md](https://github.com/friggframework/frigg/blob/next/packages/core/integrations/EXTENSIONS.md) +for the full Tier 3 contract. + +### How the app-level / account-level split is handled + +| Concern | Where it lives | Identity used | +|---|---|---| +| Signature verification | Extension receiver (`HUBSPOT_WEBHOOK_RECEIVED`) | App client secret (`HUBSPOT_CLIENT_SECRET`) | +| Portal → integration routing | `findIntegrationByEntityExternalId` via friggCommands | Inbound `portalId` from the payload | +| Per-account business logic | Your bound `HUBSPOT_WEBHOOK` handler | Per-portal OAuth credentials (loaded by the worker) | + +At request time HubSpot POSTs the whole app's events to the receiver. The receiver +verifies the v3 signature, then resolves each event's `portalId` to the owning Frigg +integration and enqueues a per-event `HUBSPOT_WEBHOOK` job. The queue worker hydrates +that integration with its own per-portal credentials and runs your handler. + +### Enabling it on an integration + +Bind the extension on your integration's `static Definition.extensions` and map the +`HUBSPOT_WEBHOOK` event to a method on your class: + +```javascript +const { IntegrationBase, createFriggCommands } = require('@friggframework/core'); +const hubspot = require('@friggframework/api-module-hubspot'); + +class HubSpotIntegration extends IntegrationBase { + static Definition = { + name: 'hubspot', + modules: { hubspot: { definition: hubspot.Definition } }, + extensions: { + hubspotWebhooks: { + extension: hubspot.extensions.webhooks, + handlers: { HUBSPOT_WEBHOOK: 'onHubSpotEvent' }, + }, + }, + }; + + constructor(params) { + super(params); + // Required: the receiver resolves portalId → integration through commands. + this.commands = createFriggCommands({ integrationClass: HubSpotIntegration }); + } + + async onHubSpotEvent({ data }) { + // Pure business logic. Signature verification, portalId lookup, and + // queue dispatch are already done — `data.body` is the HubSpot event. + const { subscriptionType, objectId } = data.body; + if (subscriptionType === 'contact.creation') { + await this.syncContact(objectId); + } + } +} +``` + +The framework auto-mounts the receiver route at the integration's base path +(`POST //webhooks`) — register that URL as the app's webhook target +in your HubSpot app settings. + +### Configuration + +| Env var | Purpose | +|---|---| +| `HUBSPOT_CLIENT_SECRET` | App client secret used to verify the `X-HubSpot-Signature-V3` header. The receiver rejects with `401` if it is unset or the signature does not match. | + +### What the bundle contributes + +- **Route:** `POST /webhooks` → `HUBSPOT_WEBHOOK_RECEIVED` +- **`HUBSPOT_WEBHOOK_RECEIVED`** — default receiver: verifies the signature, resolves + each event's `portalId`, and queues matched events. Events whose portal does not map + to any integration are skipped (HubSpot broadcasts every portal's events to the app). + Events are resolved and enqueued in parallel; if a `portalId` resolves ambiguously + (one external ID owned by multiple integrations) the whole batch is rejected rather + than risk cross-tenant routing. +- **`HUBSPOT_WEBHOOK`** — default no-op; override it via `binding.handlers.HUBSPOT_WEBHOOK` + to run your per-event logic. From 19bef018706c3552b2ccb38b7772f10d8ffb5821 Mon Sep 17 00:00:00 2001 From: d-klotz Date: Wed, 27 May 2026 13:11:55 -0300 Subject: [PATCH 7/8] chore(hubspot): pin @friggframework/core to PR #590 canary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The webhooks extension depends on the friggCommands surface added in friggframework/frigg#590 (integration.commands.findIntegrationByEntityExternalId). Pin to the canary published from that PR's commit b2cd5e2 so the api-module installs and tests against the matching framework build: @friggframework/core 2.0.0--canary.590.b2cd5e2.0 Lockfile regenerated — the v2 core pulls in the AWS SDK v3 transitive tree that v1.1.2 did not carry. When #590 lands and a stable @friggframework/core 2.x ships, swap this canary for the released range. Co-Authored-By: Claude Opus 4.7 (1M context) --- package-lock.json | 1920 ++++++++++++++++++++++-- packages/v1-ready/hubspot/package.json | 2 +- 2 files changed, 1795 insertions(+), 127 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4f958d..ed7c6a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -265,12 +265,31 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -286,7 +305,6 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "tslib": "^2.6.2" }, @@ -299,7 +317,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -313,7 +330,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -326,15 +342,13 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -348,15 +362,13 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", - "optional": true, "dependencies": { "tslib": "^2.6.2" } @@ -365,15 +377,13 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/@aws-crypto/util": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -385,7 +395,6 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "tslib": "^2.6.2" }, @@ -398,7 +407,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -412,7 +420,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -425,8 +432,296 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-apigatewaymanagementapi/-/client-apigatewaymanagementapi-3.1054.0.tgz", + "integrity": "sha512-Dm8Si1s8mNnmjXrM9G2Mm9sJEQuOVcN3rYLQ7d7PDYFyBnrfa/4KnpPIGG1nbF6kXM58PdqMA+KqLwp0DNS8KQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-node": "^3.972.45", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/core": { + "version": "3.974.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.14.tgz", + "integrity": "sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@aws-sdk/xml-builder": "^3.972.26", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.3", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.40.tgz", + "integrity": "sha512-jjT0p0Y7KZtcvExYiPCLJnqM9lkXDV1KBEg/13OE2DXv/9batzlyJHVKUEnRNJccY0O2Sul17E1su38CgdBhGQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.42.tgz", + "integrity": "sha512-+3fsKtWybe5BjKEUA3/07oh7Ayfd82IED2+gyyaVfS/4PU78E3TaOQxSGOJ1t7Imefoidw/ne9QA7apX8wEnJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.44.tgz", + "integrity": "sha512-gZFw5wBefCIPg9vpT+gV5FdhfNKhYTVDZa1IsZCcn3SRoYUOJ/E05vwIogkJoonqBL0ttBGi5vhthX7xceekRg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-login": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.45", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.45.tgz", + "integrity": "sha512-3YCv52ExXIRz3LAVNysevd+s7akSpg9dl39v9LJ7dOQH+s5rHi3jMZYQyxwMmglxQGMuzYRfQ0o1VSP2UOlIRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-ini": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.40.tgz", + "integrity": "sha512-cXaozlgJCOwmE6D7x4npcPdyk7kiFZdrGjN3D6tXXtItJJMNGPafDfAJn4YQmciMooG/X+b0Y6RTqdVVMx26jg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.44.tgz", + "integrity": "sha512-YePoj5kQuPmE0MHnyftXCfsO8ZSBd2kDr50XEIUrdejSbGFlayYvUuCohdb8drhGhPm6b65o7H1eC26EZhwUvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/token-providers": "3.1054.0", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.44.tgz", + "integrity": "sha512-Ys/JJe++8Z2Y5meR1taMBaVcrGBA0/XsVTQR+qOKZbdNyg+8Jlv5rYZSwh8SqEHY00goSOZy7PHzZ2rLNQxDLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/token-providers": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1054.0.tgz", + "integrity": "sha512-hG9YKApmZOw+drJ9Nuoaf/OvC8e5W1+3eoLeN5p2uVCZRWsv27teIS0b4kiH6Sfv3WMmamqYJxmE2WMwyp/L/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@smithy/credential-provider-imds": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.4.tgz", + "integrity": "sha512-vKW0MEFRU4Y3MkVZUkpJm+g9qyPGLCXhc0YLggUdSdBB4g7IaSSsCE75P9rBXyWHrXY1UYSQUl8/DwsTR7QciA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@smithy/fetch-http-handler": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.4.tgz", + "integrity": "sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@smithy/node-http-handler": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.4.tgz", + "integrity": "sha512-HIeF+1vrDGzPkkv39Hj2vlHSXHY3p958jd/8ZnePIY6+ZOsQX8coyEUKO5yQu4r0bQIVsbpotVIrXXwyycMStQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-apigatewaymanagementapi/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/@aws-sdk/client-cognito-identity": { "version": "3.631.0", @@ -488,6 +783,874 @@ "license": "0BSD", "optional": true }, + "node_modules/@aws-sdk/client-kms": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.1054.0.tgz", + "integrity": "sha512-Qaf/5dzs0jtR01vlNPI5QfMC3uuxx0TDeXWhXjNlndzhZdfa4IMrjw2FC84HMgEIa4G+6CYETmNPI6sXx/2Slw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-node": "^3.972.45", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/core": { + "version": "3.974.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.14.tgz", + "integrity": "sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@aws-sdk/xml-builder": "^3.972.26", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.3", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.40.tgz", + "integrity": "sha512-jjT0p0Y7KZtcvExYiPCLJnqM9lkXDV1KBEg/13OE2DXv/9batzlyJHVKUEnRNJccY0O2Sul17E1su38CgdBhGQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.42.tgz", + "integrity": "sha512-+3fsKtWybe5BjKEUA3/07oh7Ayfd82IED2+gyyaVfS/4PU78E3TaOQxSGOJ1t7Imefoidw/ne9QA7apX8wEnJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.44.tgz", + "integrity": "sha512-gZFw5wBefCIPg9vpT+gV5FdhfNKhYTVDZa1IsZCcn3SRoYUOJ/E05vwIogkJoonqBL0ttBGi5vhthX7xceekRg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-login": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.45", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.45.tgz", + "integrity": "sha512-3YCv52ExXIRz3LAVNysevd+s7akSpg9dl39v9LJ7dOQH+s5rHi3jMZYQyxwMmglxQGMuzYRfQ0o1VSP2UOlIRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-ini": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.40.tgz", + "integrity": "sha512-cXaozlgJCOwmE6D7x4npcPdyk7kiFZdrGjN3D6tXXtItJJMNGPafDfAJn4YQmciMooG/X+b0Y6RTqdVVMx26jg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.44.tgz", + "integrity": "sha512-YePoj5kQuPmE0MHnyftXCfsO8ZSBd2kDr50XEIUrdejSbGFlayYvUuCohdb8drhGhPm6b65o7H1eC26EZhwUvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/token-providers": "3.1054.0", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.44.tgz", + "integrity": "sha512-Ys/JJe++8Z2Y5meR1taMBaVcrGBA0/XsVTQR+qOKZbdNyg+8Jlv5rYZSwh8SqEHY00goSOZy7PHzZ2rLNQxDLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/token-providers": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1054.0.tgz", + "integrity": "sha512-hG9YKApmZOw+drJ9Nuoaf/OvC8e5W1+3eoLeN5p2uVCZRWsv27teIS0b4kiH6Sfv3WMmamqYJxmE2WMwyp/L/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@smithy/credential-provider-imds": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.4.tgz", + "integrity": "sha512-vKW0MEFRU4Y3MkVZUkpJm+g9qyPGLCXhc0YLggUdSdBB4g7IaSSsCE75P9rBXyWHrXY1UYSQUl8/DwsTR7QciA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@smithy/fetch-http-handler": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.4.tgz", + "integrity": "sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@smithy/node-http-handler": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.4.tgz", + "integrity": "sha512-HIeF+1vrDGzPkkv39Hj2vlHSXHY3p958jd/8ZnePIY6+ZOsQX8coyEUKO5yQu4r0bQIVsbpotVIrXXwyycMStQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-kms/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.1054.0.tgz", + "integrity": "sha512-AzyQfEBmkEcQc454eKLqm7+AtEedqkEW/rO9ZYrMF9SBYbiNaKXkCG+Z1MTgYa1BArrQlQkGVgDcsFzydEfAPw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-node": "^3.972.45", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/core": { + "version": "3.974.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.14.tgz", + "integrity": "sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@aws-sdk/xml-builder": "^3.972.26", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.3", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.40.tgz", + "integrity": "sha512-jjT0p0Y7KZtcvExYiPCLJnqM9lkXDV1KBEg/13OE2DXv/9batzlyJHVKUEnRNJccY0O2Sul17E1su38CgdBhGQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.42.tgz", + "integrity": "sha512-+3fsKtWybe5BjKEUA3/07oh7Ayfd82IED2+gyyaVfS/4PU78E3TaOQxSGOJ1t7Imefoidw/ne9QA7apX8wEnJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.44.tgz", + "integrity": "sha512-gZFw5wBefCIPg9vpT+gV5FdhfNKhYTVDZa1IsZCcn3SRoYUOJ/E05vwIogkJoonqBL0ttBGi5vhthX7xceekRg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-login": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.45", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.45.tgz", + "integrity": "sha512-3YCv52ExXIRz3LAVNysevd+s7akSpg9dl39v9LJ7dOQH+s5rHi3jMZYQyxwMmglxQGMuzYRfQ0o1VSP2UOlIRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-ini": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.40.tgz", + "integrity": "sha512-cXaozlgJCOwmE6D7x4npcPdyk7kiFZdrGjN3D6tXXtItJJMNGPafDfAJn4YQmciMooG/X+b0Y6RTqdVVMx26jg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.44.tgz", + "integrity": "sha512-YePoj5kQuPmE0MHnyftXCfsO8ZSBd2kDr50XEIUrdejSbGFlayYvUuCohdb8drhGhPm6b65o7H1eC26EZhwUvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/token-providers": "3.1054.0", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.44.tgz", + "integrity": "sha512-Ys/JJe++8Z2Y5meR1taMBaVcrGBA0/XsVTQR+qOKZbdNyg+8Jlv5rYZSwh8SqEHY00goSOZy7PHzZ2rLNQxDLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/token-providers": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1054.0.tgz", + "integrity": "sha512-hG9YKApmZOw+drJ9Nuoaf/OvC8e5W1+3eoLeN5p2uVCZRWsv27teIS0b4kiH6Sfv3WMmamqYJxmE2WMwyp/L/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/credential-provider-imds": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.4.tgz", + "integrity": "sha512-vKW0MEFRU4Y3MkVZUkpJm+g9qyPGLCXhc0YLggUdSdBB4g7IaSSsCE75P9rBXyWHrXY1UYSQUl8/DwsTR7QciA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.4.tgz", + "integrity": "sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/node-http-handler": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.4.tgz", + "integrity": "sha512-HIeF+1vrDGzPkkv39Hj2vlHSXHY3p958jd/8ZnePIY6+ZOsQX8coyEUKO5yQu4r0bQIVsbpotVIrXXwyycMStQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-sqs": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1054.0.tgz", + "integrity": "sha512-IWdqcOOpYSNbOcDTsICqaJvCehu2JNP3ZtMzVeb38IH7LcxVrzIUFO8m3OCbUvqVm/Uj78IgiXthvVEPQhdn2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-node": "^3.972.45", + "@aws-sdk/middleware-sdk-sqs": "^3.972.25", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/core": { + "version": "3.974.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.14.tgz", + "integrity": "sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@aws-sdk/xml-builder": "^3.972.26", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.3", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.40.tgz", + "integrity": "sha512-jjT0p0Y7KZtcvExYiPCLJnqM9lkXDV1KBEg/13OE2DXv/9batzlyJHVKUEnRNJccY0O2Sul17E1su38CgdBhGQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.42.tgz", + "integrity": "sha512-+3fsKtWybe5BjKEUA3/07oh7Ayfd82IED2+gyyaVfS/4PU78E3TaOQxSGOJ1t7Imefoidw/ne9QA7apX8wEnJg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.44.tgz", + "integrity": "sha512-gZFw5wBefCIPg9vpT+gV5FdhfNKhYTVDZa1IsZCcn3SRoYUOJ/E05vwIogkJoonqBL0ttBGi5vhthX7xceekRg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-login": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.45", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.45.tgz", + "integrity": "sha512-3YCv52ExXIRz3LAVNysevd+s7akSpg9dl39v9LJ7dOQH+s5rHi3jMZYQyxwMmglxQGMuzYRfQ0o1VSP2UOlIRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.40", + "@aws-sdk/credential-provider-http": "^3.972.42", + "@aws-sdk/credential-provider-ini": "^3.972.44", + "@aws-sdk/credential-provider-process": "^3.972.40", + "@aws-sdk/credential-provider-sso": "^3.972.44", + "@aws-sdk/credential-provider-web-identity": "^3.972.44", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/credential-provider-imds": "^4.3.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.40.tgz", + "integrity": "sha512-cXaozlgJCOwmE6D7x4npcPdyk7kiFZdrGjN3D6tXXtItJJMNGPafDfAJn4YQmciMooG/X+b0Y6RTqdVVMx26jg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.44.tgz", + "integrity": "sha512-YePoj5kQuPmE0MHnyftXCfsO8ZSBd2kDr50XEIUrdejSbGFlayYvUuCohdb8drhGhPm6b65o7H1eC26EZhwUvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/token-providers": "3.1054.0", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.44.tgz", + "integrity": "sha512-Ys/JJe++8Z2Y5meR1taMBaVcrGBA0/XsVTQR+qOKZbdNyg+8Jlv5rYZSwh8SqEHY00goSOZy7PHzZ2rLNQxDLg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/token-providers": { + "version": "3.1054.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1054.0.tgz", + "integrity": "sha512-hG9YKApmZOw+drJ9Nuoaf/OvC8e5W1+3eoLeN5p2uVCZRWsv27teIS0b4kiH6Sfv3WMmamqYJxmE2WMwyp/L/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/credential-provider-imds": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.4.tgz", + "integrity": "sha512-vKW0MEFRU4Y3MkVZUkpJm+g9qyPGLCXhc0YLggUdSdBB4g7IaSSsCE75P9rBXyWHrXY1UYSQUl8/DwsTR7QciA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/fetch-http-handler": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.4.tgz", + "integrity": "sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/node-http-handler": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.4.tgz", + "integrity": "sha512-HIeF+1vrDGzPkkv39Hj2vlHSXHY3p958jd/8ZnePIY6+ZOsQX8coyEUKO5yQu4r0bQIVsbpotVIrXXwyycMStQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-sdk/client-sso": { "version": "3.631.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.631.0.tgz", @@ -606,65 +1769,6 @@ "license": "0BSD", "optional": true }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.631.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.631.0.tgz", - "integrity": "sha512-Zo/2XDrmNpnSRlQLL8XOCJxuN7UIrGKf4itdjHqtEmD2PqstnYe6IMeEVOELpZ8iktjvsIrVr+qxlIX1QlmgCQ==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.631.0", - "@aws-sdk/core": "3.629.0", - "@aws-sdk/credential-provider-node": "3.631.0", - "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-logger": "3.609.0", - "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-user-agent": "3.631.0", - "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/types": "3.609.0", - "@aws-sdk/util-endpoints": "3.631.0", - "@aws-sdk/util-user-agent-browser": "3.609.0", - "@aws-sdk/util-user-agent-node": "3.614.0", - "@smithy/config-resolver": "^3.0.5", - "@smithy/core": "^2.3.2", - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/hash-node": "^3.0.3", - "@smithy/invalid-dependency": "^3.0.3", - "@smithy/middleware-content-length": "^3.0.5", - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.14", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.1.12", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.14", - "@smithy/util-defaults-mode-node": "^3.0.14", - "@smithy/util-endpoints": "^2.0.5", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sts/node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true - }, "node_modules/@aws-sdk/core": { "version": "3.629.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.629.0.tgz", @@ -802,6 +1906,101 @@ "license": "0BSD", "optional": true }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.44.tgz", + "integrity": "sha512-QqEGHfQeZgUDqh7zpqHufrZ8T644ELEWvB+4gUdewLyRw4IRF+6CJqeQuRWqucZdQzoQeMh7fNAD9BWxFAdNig==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/nested-clients": "^3.997.12", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/core": { + "version": "3.974.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.14.tgz", + "integrity": "sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@aws-sdk/xml-builder": "^3.972.26", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.3", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.631.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.631.0.tgz", @@ -1012,6 +2211,66 @@ "license": "0BSD", "optional": true }, + "node_modules/@aws-sdk/middleware-sdk-sqs": { + "version": "3.972.25", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.25.tgz", + "integrity": "sha512-xn/GUO23lRdeyDsOUvUpgzOMTKlHt0U4F2L0vCqUzyunKILoWuxspi36hfP7GinIrnIcudI4zLFD5+7fiIP3SA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.631.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.631.0.tgz", @@ -1036,6 +2295,133 @@ "license": "0BSD", "optional": true }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.12.tgz", + "integrity": "sha512-Js2VYaCM269feB0cs0cGmlIhdOgT9aMqzdBx68lCy6kVCYfzr0T36ovUFDvfUmatkuBeyBJhCwaLBh7P8meH5Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.14", + "@aws-sdk/signature-v4-multi-region": "^3.996.29", + "@aws-sdk/types": "^3.973.9", + "@smithy/core": "^3.24.3", + "@smithy/fetch-http-handler": "^5.4.3", + "@smithy/node-http-handler": "^4.7.3", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/core": { + "version": "3.974.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.14.tgz", + "integrity": "sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@aws-sdk/xml-builder": "^3.972.26", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.3", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/fetch-http-handler": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.4.tgz", + "integrity": "sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.4.tgz", + "integrity": "sha512-HIeF+1vrDGzPkkv39Hj2vlHSXHY3p958jd/8ZnePIY6+ZOsQX8coyEUKO5yQu4r0bQIVsbpotVIrXXwyycMStQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", @@ -1061,6 +2447,80 @@ "license": "0BSD", "optional": true }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.29.tgz", + "integrity": "sha512-Few9FoQqOt/0KSvZYP+qdW0dfOhfQ9N+gl2UUDvCPW6mkPKHli9LMbKxWj+wZ5zKPaOoqxuR3Hhy3OTpndkfSw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.9", + "@smithy/signature-v4": "^5.4.2", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": { + "version": "3.973.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.9.tgz", + "integrity": "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/core": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.4.tgz", + "integrity": "sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/signature-v4": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.4.tgz", + "integrity": "sha512-e5UtkMvsatzBfbeBZjEOt0k0Z3BEsjTFL/n6fdO5vtBLe67tdy0dX7xw2DU7uZ3acwoHyeCqpU2Fzb7pxwHb6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.4", + "@smithy/types": "^4.14.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@aws-sdk/token-providers": { "version": "3.614.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", @@ -1093,7 +2553,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" @@ -1106,8 +2565,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.631.0", @@ -1137,7 +2595,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", "license": "Apache-2.0", - "optional": true, "dependencies": { "tslib": "^2.6.2" }, @@ -1149,8 +2606,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.609.0", @@ -1203,6 +2659,80 @@ "license": "0BSD", "optional": true }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.26.tgz", + "integrity": "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.2", + "fast-xml-parser": "5.7.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder/node_modules/@smithy/types": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", + "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder/node_modules/fast-xml-parser": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/xml-builder/node_modules/strnum": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@aws-sdk/xml-builder/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@azure/abort-controller": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", @@ -1641,6 +3171,7 @@ "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -3850,6 +5381,7 @@ "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^3.0.0", "@octokit/graphql": "^5.0.0", @@ -4599,6 +6131,18 @@ "eslint-scope": "5.1.1" } }, + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5466,6 +7010,7 @@ "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -6333,7 +7878,6 @@ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "tslib": "^2.6.2" }, @@ -6345,8 +7889,7 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/@smithy/url-parser": { "version": "3.0.3", @@ -6758,6 +8301,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@types/aws-lambda": { + "version": "8.10.161", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.161.tgz", + "integrity": "sha512-rUYdp+MQwSFocxIOcSsYSF3YYYC/uUpMbCY/mbO21vGqfrEYvNSoPyKYDj6RhXXpPfS0KstW9RwG3qXh9sL7FQ==", + "license": "MIT", + "optional": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -6914,6 +8464,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.18.2" } @@ -7151,6 +8702,7 @@ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7394,7 +8946,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -8249,8 +9800,7 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -8303,6 +9853,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001646", "electron-to-chromium": "^1.5.4", @@ -8581,7 +10132,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -8871,7 +10421,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -8884,7 +10433,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/color-support": { @@ -9528,12 +11076,30 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -11101,29 +12667,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -11429,6 +12972,7 @@ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -11981,6 +13525,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-builder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, "node_modules/fast-xml-parser": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", @@ -12409,7 +13969,8 @@ "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.9.tgz", "integrity": "sha512-+I2+FnVB+tVaxcYyQkHUq7ZdKScaBlX53A41mxQtpIccsfyv8PzdzP7fzp2AY832T4aoK6UZ5WRX/ebGd8uZuQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fresh": { "version": "0.5.2", @@ -12452,7 +14013,6 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -12998,6 +14558,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.9.0.tgz", "integrity": "sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.x" } @@ -13071,7 +14632,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -14578,6 +16138,7 @@ "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "detect-newline": "^3.0.0" }, @@ -14888,6 +16449,7 @@ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -16171,7 +17733,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -16477,6 +18038,7 @@ "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^3.0.0", "@octokit/graphql": "^5.0.0", @@ -18964,6 +20526,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@nrwl/tao": "18.3.5", "@yarnpkg/lockfile": "^1.1.0", @@ -19233,6 +20796,15 @@ "node": "*" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -19772,6 +21344,21 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -21222,6 +22809,18 @@ "node": ">= 0.8.0" } }, + "node_modules/serverless-http": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/serverless-http/-/serverless-http-2.7.0.tgz", + "integrity": "sha512-iWq0z1X2Xkuvz6wL305uCux/SypbojHlYsB5bzmF5TqoLYsdvMNIoCsgtWjwqWoo3AR2cjw3zAmHN2+U6mF99Q==", + "license": "MIT", + "engines": { + "node": ">=8.0" + }, + "optionalDependencies": { + "@types/aws-lambda": "^8.10.56" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -22003,7 +23602,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -22418,6 +24016,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -22692,6 +24291,7 @@ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -22810,7 +24410,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -23491,6 +25090,21 @@ "node": ">=12" } }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", @@ -32671,7 +34285,7 @@ "version": "1.1.7", "license": "MIT", "dependencies": { - "@friggframework/core": "^1.1.2" + "@friggframework/core": "2.0.0--canary.590.b2cd5e2.0" }, "devDependencies": { "@friggframework/devtools": "^1.1.2", @@ -32683,11 +34297,51 @@ "prettier": "^2.7.1" } }, + "packages/v1-ready/hubspot/node_modules/@friggframework/core": { + "version": "2.0.0--canary.590.b2cd5e2.0", + "resolved": "https://registry.npmjs.org/@friggframework/core/-/core-2.0.0--canary.590.b2cd5e2.0.tgz", + "integrity": "sha512-48Fd890cOul8NveWyEw3daVp/uHZOIlpSEtPJzGKet8HxGx1SGJ7CtKTbjZSOPKQX7wZYv452LNIFJtDpdk05w==", + "license": "MIT", + "dependencies": { + "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0", + "@aws-sdk/client-kms": "^3.588.0", + "@aws-sdk/client-lambda": "^3.714.0", + "@aws-sdk/client-sqs": "^3.588.0", + "@hapi/boom": "^10.0.1", + "bcryptjs": "^2.4.3", + "body-parser": "^1.20.2", + "bson": "^4.7.2", + "chalk": "^4.1.2", + "common-tags": "^1.8.2", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "express": "^4.19.2", + "express-async-handler": "^1.2.0", + "form-data": "^4.0.0", + "fs-extra": "^11.2.0", + "lodash": "4.17.21", + "lodash.get": "^4.4.2", + "node-fetch": "^2.6.7", + "serverless-http": "^2.7.0", + "uuid": "^9.0.1" + }, + "peerDependencies": { + "@prisma/client": "^6.16.3", + "prisma": "^6.16.3" + }, + "peerDependenciesMeta": { + "@prisma/client": { + "optional": true + }, + "prisma": { + "optional": true + } + } + }, "packages/v1-ready/hubspot/node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -32696,6 +34350,20 @@ "url": "https://dotenvx.com" } }, + "packages/v1-ready/hubspot/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/v1-ready/ironclad": { "name": "@friggframework/api-module-ironclad", "version": "1.0.1", diff --git a/packages/v1-ready/hubspot/package.json b/packages/v1-ready/hubspot/package.json index 52869eb..19f0d1a 100644 --- a/packages/v1-ready/hubspot/package.json +++ b/packages/v1-ready/hubspot/package.json @@ -20,6 +20,6 @@ "prettier": "^2.7.1" }, "dependencies": { - "@friggframework/core": "^1.1.2" + "@friggframework/core": "2.0.0--canary.590.b2cd5e2.0" } } From 3686b1878143fafa6b0d2ffebe8c54f21eb6975b Mon Sep 17 00:00:00 2001 From: d-klotz Date: Wed, 27 May 2026 16:52:28 -0300 Subject: [PATCH 8/8] chore(hubspot): move @friggframework/core to 2.0.0-next.88 PR #590 (Tier 3 extensions + friggCommands reverse-lookup) is merged and released as 2.0.0-next.88. Swap the temporary canary pin (2.0.0--canary.590.b2cd5e2.0) for the released range ^2.0.0-next.88. Co-Authored-By: Claude Opus 4.7 (1M context) --- package-lock.json | 8 ++++---- packages/v1-ready/hubspot/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index dab1406..da23932 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52404,7 +52404,7 @@ "version": "1.1.7", "license": "MIT", "dependencies": { - "@friggframework/core": "2.0.0--canary.590.b2cd5e2.0" + "@friggframework/core": "^2.0.0-next.88" }, "devDependencies": { "@friggframework/devtools": "^1.1.2", @@ -52417,9 +52417,9 @@ } }, "packages/v1-ready/hubspot/node_modules/@friggframework/core": { - "version": "2.0.0--canary.590.b2cd5e2.0", - "resolved": "https://registry.npmjs.org/@friggframework/core/-/core-2.0.0--canary.590.b2cd5e2.0.tgz", - "integrity": "sha512-48Fd890cOul8NveWyEw3daVp/uHZOIlpSEtPJzGKet8HxGx1SGJ7CtKTbjZSOPKQX7wZYv452LNIFJtDpdk05w==", + "version": "2.0.0-next.88", + "resolved": "https://registry.npmjs.org/@friggframework/core/-/core-2.0.0-next.88.tgz", + "integrity": "sha512-DmvG035UJQloD3vEwJJPrH4RIPTXQA603yFY1jzSWKxKCnkQl1E1E6mUcGTBcC9r8QjPTLtj9ZDJlgOWESn8aA==", "license": "MIT", "dependencies": { "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0", diff --git a/packages/v1-ready/hubspot/package.json b/packages/v1-ready/hubspot/package.json index 19f0d1a..27c4e8f 100644 --- a/packages/v1-ready/hubspot/package.json +++ b/packages/v1-ready/hubspot/package.json @@ -20,6 +20,6 @@ "prettier": "^2.7.1" }, "dependencies": { - "@friggframework/core": "2.0.0--canary.590.b2cd5e2.0" + "@friggframework/core": "^2.0.0-next.88" } }