diff --git a/package.json b/package.json index b229e436..fccb9422 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "packageManager": "pnpm@10.17.1", "devDependencies": { "@changesets/changelog-github": "^0.5.2", - "@changesets/cli": "^2.29.8", + "@changesets/cli": "^2.31.0", "eslint-plugin-prettier": "catalog:", "prettier": "catalog:" } diff --git a/packages/b2c-cli/src/commands/mrt/bundle/deploy.ts b/packages/b2c-cli/src/commands/mrt/bundle/deploy.ts index f768af31..6671112b 100644 --- a/packages/b2c-cli/src/commands/mrt/bundle/deploy.ts +++ b/packages/b2c-cli/src/commands/mrt/bundle/deploy.ts @@ -104,11 +104,9 @@ export default class MrtBundleDeploy extends MrtCommand }), 'ssr-only': Flags.string({ description: 'Glob patterns for server-only files (comma-separated or JSON array, only for local builds)', - default: 'ssr.js,ssr.mjs,server/**/*', }), 'ssr-shared': Flags.string({ description: 'Glob patterns for shared files (comma-separated or JSON array, only for local builds)', - default: 'static/**/*,client/**/*', }), 'node-version': Flags.string({ char: 'n', @@ -237,8 +235,8 @@ export default class MrtBundleDeploy extends MrtCommand } const buildDir = this.flags['build-dir']; - const ssrOnly = parseGlobPatterns(this.flags['ssr-only']); - const ssrShared = parseGlobPatterns(this.flags['ssr-shared']); + const ssrOnly = this.flags['ssr-only'] ? parseGlobPatterns(this.flags['ssr-only']) : undefined; + const ssrShared = this.flags['ssr-shared'] ? parseGlobPatterns(this.flags['ssr-shared']) : undefined; // Build SSR parameters from flags const ssrParameters: Record = parseSsrParams(this.flags['ssr-param']); diff --git a/packages/b2c-cli/src/commands/mrt/bundle/save.ts b/packages/b2c-cli/src/commands/mrt/bundle/save.ts new file mode 100644 index 00000000..4a3ef89a --- /dev/null +++ b/packages/b2c-cli/src/commands/mrt/bundle/save.ts @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {writeFileSync} from 'node:fs'; +import {gzipSync} from 'node:zlib'; +import path from 'node:path'; +import {Flags} from '@oclif/core'; +import {BaseCommand} from '@salesforce/b2c-tooling-sdk/cli'; +import {createBundle, getDefaultMessage, DEFAULT_SSR_PARAMETERS} from '@salesforce/b2c-tooling-sdk/operations/mrt'; +import {t, withDocs} from '../../../i18n/index.js'; + +export interface SaveBundleResult { + filePath: string; + projectSlug: string; + message: string; + ssrOnlyCount: number; + ssrSharedCount: number; +} + +/** + * Save a bundle to a local directory without uploading it to Managed Runtime. + */ +export default class MrtBundleSave extends BaseCommand { + static description = withDocs( + t('commands.mrt.bundle.save.description', 'Save a Managed Runtime bundle to a local directory'), + '/cli/mrt.html#b2c-mrt-bundle-save', + ); + + static enableJsonFlag = true; + + static examples = [ + '<%= config.bin %> <%= command.id %> --project my-storefront --save-dir ./artifacts', + '<%= config.bin %> <%= command.id %> --project my-storefront --save-dir ./artifacts --gzip', + '<%= config.bin %> <%= command.id %> --project my-storefront --save-dir ./artifacts --build-dir ./dist', + '<%= config.bin %> <%= command.id %> --project my-storefront --save-dir ./artifacts --json', + ]; + + static flags = { + ...BaseCommand.baseFlags, + project: Flags.string({ + char: 'p', + description: 'MRT project slug (or set MRT_PROJECT env var)', + env: 'MRT_PROJECT', + default: async () => process.env.SFCC_MRT_PROJECT || undefined, + }), + 'save-dir': Flags.string({ + char: 's', + description: 'Directory to save the bundle to', + required: true, + }), + 'build-dir': Flags.string({ + char: 'b', + description: 'Path to the build directory', + default: 'build', + }), + 'ssr-only': Flags.string({ + description: 'Glob patterns for server-only files (comma-separated or JSON array)', + }), + 'ssr-shared': Flags.string({ + description: 'Glob patterns for shared files (comma-separated or JSON array)', + }), + 'node-version': Flags.string({ + char: 'n', + description: `Node.js version for SSR runtime (default: ${DEFAULT_SSR_PARAMETERS.SSRFunctionNodeVersion})`, + }), + gzip: Flags.boolean({ + char: 'g', + description: 'Gzip the bundle (saves as bundle.tgz instead of bundle.tar)', + default: false, + }), + }; + + async run(): Promise { + const project = this.flags.project; + + if (!project) { + this.error('MRT project is required. Provide --project flag or set MRT_PROJECT.'); + } + + const saveDir = this.flags['save-dir']; + const buildDir = this.flags['build-dir']; + const ssrOnly = this.flags['ssr-only'] ? parseGlobPatterns(this.flags['ssr-only']) : undefined; + const ssrShared = this.flags['ssr-shared'] ? parseGlobPatterns(this.flags['ssr-shared']) : undefined; + const ssrParameters: Record = {}; + + if (this.flags['node-version']) { + ssrParameters.SSRFunctionNodeVersion = this.flags['node-version']; + } + + const message = getDefaultMessage(); + + this.log(t('commands.mrt.bundle.save.creating', 'Creating bundle for {{project}}...', {project})); + + const bundle = await createBundle({ + projectSlug: project, + buildDirectory: buildDir, + message, + ssrOnly, + ssrShared, + ssrParameters, + }); + + const gzip = this.flags.gzip; + const fileName = gzip ? 'bundle.tgz' : 'bundle.tar'; + const filePath = path.resolve(saveDir, fileName); + + let data = Buffer.from(bundle.data, 'base64'); + if (gzip) { + data = gzipSync(data); + } + + writeFileSync(filePath, data); + + if (!this.jsonEnabled()) { + this.log(t('commands.mrt.bundle.save.success', 'Bundle saved to {{filePath}}', {filePath})); + } + + return { + filePath, + projectSlug: project, + message: bundle.message, + ssrOnlyCount: bundle.ssr_only.length, + ssrSharedCount: bundle.ssr_shared.length, + }; + } +} + +function parseGlobPatterns(value: string): string[] { + const trimmed = value.trim(); + if (trimmed.startsWith('[')) { + const parsed: unknown = JSON.parse(trimmed); + if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === 'string')) { + throw new Error(`Invalid glob pattern array: expected an array of strings`); + } + return parsed.map((s: string) => s.trim()).filter(Boolean); + } + return trimmed + .split(',') + .map((s) => s.trim()) + .filter(Boolean); +} diff --git a/packages/b2c-tooling-sdk/src/cli/mrt-command.ts b/packages/b2c-tooling-sdk/src/cli/mrt-command.ts index 12769faf..bc3aa348 100644 --- a/packages/b2c-tooling-sdk/src/cli/mrt-command.ts +++ b/packages/b2c-tooling-sdk/src/cli/mrt-command.ts @@ -55,11 +55,13 @@ export abstract class MrtCommand extends BaseCommand process.env.SFCC_MRT_ENVIRONMENT || process.env.MRT_TARGET || undefined, }), 'cloud-origin': Flags.string({ + char: 'o', description: `MRT cloud origin URL (or set mrtOrigin in dw.json; default: ${DEFAULT_MRT_ORIGIN})`, env: 'MRT_CLOUD_ORIGIN', default: async () => process.env.SFCC_MRT_CLOUD_ORIGIN || undefined, }), 'credentials-file': Flags.string({ + char: 'c', description: 'Path to MRT credentials file (overrides default ~/.mobify)', env: 'MRT_CREDENTIALS_FILE', }), diff --git a/packages/b2c-tooling-sdk/src/operations/mrt/bundle.ts b/packages/b2c-tooling-sdk/src/operations/mrt/bundle.ts index f3533800..46c37e64 100644 --- a/packages/b2c-tooling-sdk/src/operations/mrt/bundle.ts +++ b/packages/b2c-tooling-sdk/src/operations/mrt/bundle.ts @@ -19,6 +19,19 @@ import tar from 'tar-fs'; import {Minimatch} from 'minimatch'; import {getLogger} from '../../logging/logger.js'; +/** + * Shape of config.server.ts exported from an MRT app. + * Used to define ssrOnly, ssrShared, and ssrParameters for bundle creation. + */ +export interface MrtServerConfig { + ssrOnly: string[]; + ssrShared: string[]; + ssrParameters?: Record; +} + +export const DEFAULT_SSR_ONLY = ['ssr.js', 'ssr.mjs', 'server/**/*']; +export const DEFAULT_SSR_SHARED = ['static/**/*', 'client/**/*']; + /** * Default SSR parameters applied to all bundles. * These can be overridden by providing ssrParameters in CreateBundleOptions. @@ -49,15 +62,17 @@ export interface CreateBundleOptions { /** * Glob patterns for files that should only run on the server. + * If omitted, loaded from build/config.server.js if present. * @example ['ssr.js', 'ssr/*.js'] */ - ssrOnly: string[]; + ssrOnly?: string[]; /** * Glob patterns for files shared between client and server. + * If omitted, loaded from build/config.server.js if present. * @example ['static/**\/*', '**\/*.js'] */ - ssrShared: string[]; + ssrShared?: string[]; /** * Path to the build directory containing the application build output. @@ -170,15 +185,39 @@ export function getDefaultMessage(): string { * }); * ``` */ +async function loadServerConfig(buildPath: string): Promise { + const configPath = path.join(buildPath, 'config.server.js'); + try { + await stat(configPath); + } catch { + return null; + } + try { + const mod = await import(configPath); + const config: MrtServerConfig = mod.config ?? mod.default?.config ?? mod.default; + return config ?? null; + } catch { + return null; + } +} + export async function createBundle(options: CreateBundleOptions): Promise { const logger = getLogger(); - const {ssrOnly, ssrShared, projectSlug} = options; + const {projectSlug} = options; const buildDirectory = options.buildDirectory || 'build'; const message = options.message || getDefaultMessage(); + const buildPath = path.isAbsolute(buildDirectory) ? buildDirectory : path.join(process.cwd(), buildDirectory); + + const serverConfig = await loadServerConfig(buildPath); + + const ssrOnly = options.ssrOnly ?? serverConfig?.ssrOnly ?? DEFAULT_SSR_ONLY; + const ssrShared = options.ssrShared ?? serverConfig?.ssrShared ?? DEFAULT_SSR_SHARED; + const ssrParamsFromConfig = serverConfig?.ssrParameters ?? {}; - // Merge default SSR parameters with provided ones (provided values take precedence) + // Merge: defaults < config.server.js < explicit options (explicit values win) const ssrParameters = { ...DEFAULT_SSR_PARAMETERS, + ...ssrParamsFromConfig, ...options.ssrParameters, }; @@ -189,9 +228,6 @@ export async function createBundle(options: CreateBundleOptions): Promise=22.16.0" + } +} diff --git a/packages/mrt-reference-app/build/request-processor.js b/packages/mrt-reference-app/build/request-processor.js new file mode 100644 index 00000000..57cc6640 --- /dev/null +++ b/packages/mrt-reference-app/build/request-processor.js @@ -0,0 +1,2 @@ +"use strict";var n=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var m=(r,e)=>{for(var t in e)n(r,t,{get:e[t],enumerable:!0})},p=(r,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of i(e))!c.call(r,s)&&s!==t&&n(r,s,{get:()=>e[s],enumerable:!(o=a(e,s))||o.enumerable});return r};var g=r=>p(n({},"__esModule",{value:!0}),r);var l={};m(l,{processRequest:()=>P});module.exports=g(l);var f=["removeme"],P=({path:r,querystring:e,parameters:t})=>{console.assert(t,"Missing parameters");let o=new URLSearchParams(e);for(let s of f)o.delete(s);return e=o.toString(),{path:r,querystring:e}};0&&(module.exports={processRequest}); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL3JlcXVlc3QtcHJvY2Vzc29yLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKlxuICogQ29weXJpZ2h0IChjKSAyMDI1LCBTYWxlc2ZvcmNlLCBJbmMuXG4gKiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTJcbiAqIEZvciBmdWxsIGxpY2Vuc2UgdGV4dCwgc2VlIHRoZSBsaWNlbnNlLnR4dCBmaWxlIGluIHRoZSByZXBvIHJvb3Qgb3IgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKi9cbmltcG9ydCB0eXBlIHtQcm94eUNvbmZpZ30gZnJvbSAnQHNhbGVzZm9yY2UvbXJ0LXV0aWxpdGllcyc7XG5cbmNvbnN0IGV4Y2x1c2lvbnMgPSBbJ3JlbW92ZW1lJ107XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJvY2Vzc1JlcXVlc3RQYXJhbWV0ZXJzIHtcbiAgYXBwSG9zdG5hbWU6IHN0cmluZztcbiAgcHJveHlDb25maWdzOiBQcm94eUNvbmZpZ1tdO1xuICBlbnZpcm9ubWVudDogc3RyaW5nO1xuICBkZXBsb3lUYXJnZXQ6IHN0cmluZztcbn1cblxuZXhwb3J0IGNvbnN0IHByb2Nlc3NSZXF1ZXN0ID0gKHtcbiAgcGF0aCxcbiAgcXVlcnlzdHJpbmcsXG4gIHBhcmFtZXRlcnMsXG59OiB7XG4gIHBhdGg6IHN0cmluZztcbiAgcXVlcnlzdHJpbmc6IHN0cmluZztcbiAgcGFyYW1ldGVyczogUHJvY2Vzc1JlcXVlc3RQYXJhbWV0ZXJzO1xufSkgPT4ge1xuICBjb25zb2xlLmFzc2VydChwYXJhbWV0ZXJzLCAnTWlzc2luZyBwYXJhbWV0ZXJzJyk7XG5cbiAgY29uc3QgcXVlcnlQYXJhbWV0ZXJzID0gbmV3IFVSTFNlYXJjaFBhcmFtcyhxdWVyeXN0cmluZyk7XG5cbiAgZm9yIChjb25zdCBleGNsdXNpb24gb2YgZXhjbHVzaW9ucykge1xuICAgIHF1ZXJ5UGFyYW1ldGVycy5kZWxldGUoZXhjbHVzaW9uKTtcbiAgfVxuXG4gIHF1ZXJ5c3RyaW5nID0gcXVlcnlQYXJhbWV0ZXJzLnRvU3RyaW5nKCk7XG5cbiAgcmV0dXJuIHtcbiAgICBwYXRoLFxuICAgIHF1ZXJ5c3RyaW5nLFxuICB9O1xufTtcbiJdLAogICJtYXBwaW5ncyI6ICJ5YUFBQSxJQUFBQSxFQUFBLEdBQUFDLEVBQUFELEVBQUEsb0JBQUFFLElBQUEsZUFBQUMsRUFBQUgsR0FPQSxJQUFNSSxFQUFhLENBQUMsVUFBVSxFQVNqQkYsRUFBaUIsQ0FBQyxDQUM3QixLQUFBRyxFQUNBLFlBQUFDLEVBQ0EsV0FBQUMsQ0FDRixJQUlNLENBQ0osUUFBUSxPQUFPQSxFQUFZLG9CQUFvQixFQUUvQyxJQUFNQyxFQUFrQixJQUFJLGdCQUFnQkYsQ0FBVyxFQUV2RCxRQUFXRyxLQUFhTCxFQUN0QkksRUFBZ0IsT0FBT0MsQ0FBUyxFQUdsQyxPQUFBSCxFQUFjRSxFQUFnQixTQUFTLEVBRWhDLENBQ0wsS0FBQUgsRUFDQSxZQUFBQyxDQUNGLENBQ0YiLAogICJuYW1lcyI6IFsicmVxdWVzdF9wcm9jZXNzb3JfZXhwb3J0cyIsICJfX2V4cG9ydCIsICJwcm9jZXNzUmVxdWVzdCIsICJfX3RvQ29tbW9uSlMiLCAiZXhjbHVzaW9ucyIsICJwYXRoIiwgInF1ZXJ5c3RyaW5nIiwgInBhcmFtZXRlcnMiLCAicXVlcnlQYXJhbWV0ZXJzIiwgImV4Y2x1c2lvbiJdCn0K diff --git a/packages/mrt-reference-app/build/static/example.css b/packages/mrt-reference-app/build/static/example.css new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.css @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.eot b/packages/mrt-reference-app/build/static/example.eot new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.eot @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/build/static/example.gif b/packages/mrt-reference-app/build/static/example.gif new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.gif @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.jpe b/packages/mrt-reference-app/build/static/example.jpe new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.jpe @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.jpeg b/packages/mrt-reference-app/build/static/example.jpeg new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.jpeg @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.jpg b/packages/mrt-reference-app/build/static/example.jpg new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.jpg @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.js b/packages/mrt-reference-app/build/static/example.js new file mode 100644 index 00000000..b2a8d963 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.js @@ -0,0 +1 @@ +/* This file is used in the E2E tests to verify that correct header values are set. */ diff --git a/packages/mrt-reference-app/build/static/example.json b/packages/mrt-reference-app/build/static/example.json new file mode 100644 index 00000000..17edacea --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.json @@ -0,0 +1,3 @@ +{ + "message": "This file is used in the E2E tests to verify that correct header values are set." +} \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.mjs b/packages/mrt-reference-app/build/static/example.mjs new file mode 100644 index 00000000..b2a8d963 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.mjs @@ -0,0 +1 @@ +/* This file is used in the E2E tests to verify that correct header values are set. */ diff --git a/packages/mrt-reference-app/build/static/example.otf b/packages/mrt-reference-app/build/static/example.otf new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.otf @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/build/static/example.png b/packages/mrt-reference-app/build/static/example.png new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.png @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.svg b/packages/mrt-reference-app/build/static/example.svg new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.svg @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.ttf b/packages/mrt-reference-app/build/static/example.ttf new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.ttf @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/build/static/example.txt b/packages/mrt-reference-app/build/static/example.txt new file mode 100644 index 00000000..248bc553 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.txt @@ -0,0 +1,7 @@ +This file is used in the E2E tests to verify that Cloudfront is compressing content properly. + +Cloudfront only compresses files that are 1,000 bytes - 10,000,000 bytes in size +For more details check: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id dolor vitae neque feugiat convallis. Nullam eleifend est at justo commodo facilisis. Vestibulum consectetur ultricies facilisis. In non ligula a ipsum pretium aliquet. Cras vel ligula id lectus tristique viverra sed eu neque. Vivamus sit amet nulla id tortor efficitur finibus. Curabitur ultrices auctor sem, ut consequat nulla interdum ut. +Etiam auctor massa nec mauris ullamcorper, vitae bibendum nisl tempus. Nulla vel facilisis ante, in ultricies mi. Praesent laoreet faucibus mauris. Donec cursus erat ac malesuada mattis. Sed vehicula, erat id consequat fringilla, felis justo fringilla nulla, ac feugiat dui velit in est. Nullam ut lectus et purus lacinia congue a vitae lectus. Fusce rutrum elit at hendrerit malesuada. \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/example.woff b/packages/mrt-reference-app/build/static/example.woff new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.woff @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/build/static/example.woff2 b/packages/mrt-reference-app/build/static/example.woff2 new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/build/static/example.woff2 @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/build/static/favicon.ico b/packages/mrt-reference-app/build/static/favicon.ico new file mode 100644 index 00000000..a15a8f34 Binary files /dev/null and b/packages/mrt-reference-app/build/static/favicon.ico differ diff --git a/packages/mrt-reference-app/build/static/robots.txt b/packages/mrt-reference-app/build/static/robots.txt new file mode 100644 index 00000000..77470cb3 --- /dev/null +++ b/packages/mrt-reference-app/build/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/packages/mrt-reference-app/build/static/saturn.jpg b/packages/mrt-reference-app/build/static/saturn.jpg new file mode 100644 index 00000000..cc09ff94 Binary files /dev/null and b/packages/mrt-reference-app/build/static/saturn.jpg differ diff --git a/packages/mrt-reference-app/config.server.ts b/packages/mrt-reference-app/config.server.ts new file mode 100644 index 00000000..5349d281 --- /dev/null +++ b/packages/mrt-reference-app/config.server.ts @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {MrtServerConfig} from '@salesforce/b2c-tooling-sdk/operations/mrt'; + +const bundleType = process.env.MRT_BUNDLE_TYPE; +const ssrEntryPoint = ['stream', 'streaming', 'streamingHandler'].includes(bundleType ?? '') ? 'streamingHandler' : 'ssr'; + +export const config: MrtServerConfig = { + ssrOnly: [ + `${ssrEntryPoint}.{js,mjs,cjs}`, + `${ssrEntryPoint}.{js,mjs,cjs}.map`, + 'request-processor.js', + 'loader.js', + 'package.json', + '!static/**/*', + ], + ssrShared: [ + 'static/**/*', + '**/*.css', + '**/*.png', + '**/*.jpg', + '**/*.jpeg', + '**/*.gif', + '**/*.svg', + '**/*.ico', + '**/*.woff', + '**/*.woff2', + '**/*.ttf', + '**/*.eot', + ], + ssrParameters: { + ssrFunctionNodeVersion: '24.x', + }, +}; diff --git a/packages/mrt-reference-app/eslint.config.mjs b/packages/mrt-reference-app/eslint.config.mjs new file mode 100644 index 00000000..29152f7c --- /dev/null +++ b/packages/mrt-reference-app/eslint.config.mjs @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {includeIgnoreFile} from '@eslint/compat'; +import headerPlugin from 'eslint-plugin-header'; +import tseslint from 'typescript-eslint'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; + +import {copyrightHeader, sharedRules, chaiTestRules, prettierPlugin} from '../../eslint.config.mjs'; + +const gitignorePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '.gitignore'); +headerPlugin.rules.header.meta.schema = false; + +export default [ + {ignores: ['**/node_modules/**', 'build/**', 'coverage/**']}, + includeIgnoreFile(gitignorePath), + ...tseslint.configs.recommended, + prettierPlugin, + { + files: ['**/*.ts'], + plugins: { + header: headerPlugin, + }, + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + 'header/header': ['error', 'block', copyrightHeader], + ...sharedRules, + }, + }, + { + files: ['src/**/*.test.ts'], + rules: { + ...chaiTestRules, + '@typescript-eslint/no-explicit-any': 'off', + }, + }, +]; diff --git a/packages/mrt-reference-app/package.json b/packages/mrt-reference-app/package.json new file mode 100644 index 00000000..4ee24fdf --- /dev/null +++ b/packages/mrt-reference-app/package.json @@ -0,0 +1,82 @@ +{ + "name": "@salesforce/mrt-reference-app", + "version": "0.0.1", + "description": "Reference application demonstrating MRT features", + "type": "module", + "author": "Salesforce", + "license": "Apache-2.0", + "repository": "SalesforceCommerceCloud/b2c-developer-tooling", + "private": true, + "scripts": { + "dev:tsx": "RP_EXTENSION=1 tsx ./src/dev/tsx-local.ts", + "dev:node": "pnpm build && node ./src/dev/node-local.js", + "build": "node scripts/build.mjs", + "clean": "shx rm -rf build", + "lint": "eslint", + "lint:agent": "eslint --quiet", + "typecheck:agent": "tsc --noEmit --pretty false", + "format": "prettier --write .", + "format:check": "prettier --check .", + "pretest": "tsc --noEmit -p test", + "test": "c8 mocha --forbid-only \"src/**/*.test.ts\"", + "test:ci": "c8 mocha --forbid-only --reporter json --reporter-option output=test-results.json \"src/**/*.test.ts\"", + "test:agent": "mocha --forbid-only --reporter min \"src/**/*.test.ts\"", + "test:watch": "mocha --watch \"src/**/*.test.ts\"", + "coverage": "c8 report", + "push": "pnpm build && b2c mrt bundle deploy", + "deploy": "pnpm build && b2c mrt bundle deploy", + "tail-logs": "b2c mrt tail-logs", + "save-bundle": "pnpm build && b2c mrt bundle save", + "save-credentials": "b2c mrt save-credentials" + }, + "dependencies": { + "@aws-sdk/client-cloudwatch-logs": "3.952.0", + "@aws-sdk/client-dynamodb": "3.980.0", + "@aws-sdk/client-lambda": "3.952.0", + "@aws-sdk/client-s3": "3.952.0", + "@aws-sdk/client-secrets-manager": "3.1004.0", + "@aws-sdk/lib-dynamodb": "3.980.0", + "@h4ad/serverless-adapter": "4.4.0", + "@salesforce/mrt-utilities": "workspace:*", + "compressible": "2.0.18", + "express": "5.1.0", + "express-basic-auth": "1.2.1", + "negotiator": "1.0.0", + "winston": "3.19.0" + }, + "devDependencies": { + "@salesforce/b2c-cli": "workspace:*", + "@salesforce/b2c-tooling-sdk": "workspace:*", + "@salesforce/dev-config": "catalog:", + "@serverless/event-mocks": "1.1.1", + "@smithy/smithy-client": "4.9.1", + "@types/aws-lambda": "8.10.160", + "@types/chai": "catalog:", + "@types/compressible": "2.0.3", + "@types/express": "5.0.4", + "@types/mocha": "catalog:", + "@types/negotiator": "0.6.4", + "@types/node": "catalog:", + "@types/sinon": "catalog:", + "@types/supertest": "6.0.3", + "aws-sdk-client-mock": "4.1.0", + "c8": "catalog:", + "chai": "catalog:", + "esbuild": "0.25.0", + "eslint": "catalog:", + "eslint-config-prettier": "catalog:", + "eslint-plugin-header": "catalog:", + "eslint-plugin-prettier": "catalog:", + "mocha": "catalog:", + "prettier": "catalog:", + "shx": "catalog:", + "sinon": "catalog:", + "supertest": "7.1.4", + "tsx": "catalog:", + "typescript": "catalog:", + "typescript-eslint": "catalog:" + }, + "engines": { + "node": ">=22.16.0" + } +} diff --git a/packages/mrt-reference-app/scripts/build.mjs b/packages/mrt-reference-app/scripts/build.mjs new file mode 100644 index 00000000..3ac65b7a --- /dev/null +++ b/packages/mrt-reference-app/scripts/build.mjs @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import esbuild from 'esbuild'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const pkgRoot = path.resolve(__dirname, '..'); +const buildDir = path.resolve(pkgRoot, 'build'); + +const bundleType = process.env.MRT_BUNDLE_TYPE; +const MRT_STREAMING_BUNDLES = ['stream', 'streaming', 'streamingHandler']; +const isStreaming = MRT_STREAMING_BUNDLES.includes(bundleType ?? ''); +const ssrEntry = isStreaming ? 'streamingHandler' : 'ssr'; +const enableSourceMaps = !process.env.DISABLE_SOURCE_MAPS; +const format = process.env.MRT_EXPORT_TYPE === 'esm' ? 'esm' : 'cjs'; + +/** esbuild plugin that replaces import.meta.url with a CJS-compatible expression */ +const importMetaUrlCjsPlugin = { + name: 'import-meta-url-cjs', + setup(build) { + build.onLoad({filter: /\.[jt]s$/}, async (args) => { + const contents = await fs.readFile(args.path, 'utf8'); + if (!contents.includes('import.meta.url')) return null; + return { + contents: contents.replaceAll('import.meta.url', 'require("url").pathToFileURL(__filename).href'), + loader: args.path.endsWith('.ts') ? 'ts' : 'js', + }; + }); + }, +}; + +const noExternal = [ + /^@aws-sdk\/.*/, + /^@h4ad\/.*/, + 'express', + 'express-basic-auth', + '@salesforce/mrt-utilities', + 'negotiator', + 'compressible', + 'winston', +]; + +const externalFilter = (id) => { + for (const pattern of noExternal) { + if (typeof pattern === 'string' && id === pattern) return false; + if (pattern instanceof RegExp && pattern.test(id)) return false; + } + return true; +}; + +/** esbuild plugin that marks all packages external except noExternal list */ +const bundlePlugin = { + name: 'bundle-deps', + setup(build) { + build.onResolve({filter: /^[^./]/}, (args) => { + // Skip Windows absolute paths (e.g. D:\...) which match the filter + if (/^[A-Za-z]:/.test(args.path)) return null; + if (externalFilter(args.path)) { + return {external: true}; + } + return null; + }); + }, +}; + +async function copyDir(src, dest) { + await fs.mkdir(dest, {recursive: true}); + const entries = await fs.readdir(src, {withFileTypes: true}); + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + if (entry.isDirectory()) { + await copyDir(srcPath, destPath); + } else { + await fs.copyFile(srcPath, destPath); + } + } +} + +function formatSize(bytes) { + if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(2)} MB`; + if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} kB`; + return `${bytes} B`; +} + +async function logOutputFile(filePath) { + const stat = await fs.stat(filePath); + const rel = path.relative(pkgRoot, filePath); + console.log(` build/${path.basename(rel)} ${formatSize(stat.size)}`); +} + +async function build() { + await fs.rm(buildDir, {recursive: true, force: true}); + await fs.mkdir(buildDir, {recursive: true}); + + const commonOptions = { + bundle: true, + platform: 'node', + target: 'node22', + format, + minify: true, + sourcemap: enableSourceMaps ? 'inline' : false, + outdir: buildDir, + define: { + 'process.env.NODE_ENV': '"production"', + ...(bundleType ? {'process.env.MRT_BUNDLE_TYPE': `"${bundleType}"`} : {}), + }, + plugins: [bundlePlugin], + // Inline dynamic imports to avoid chunk resolution issues in Lambda + splitting: false, + }; + + // Build SSR / streaming handler entry + await esbuild.build({ + ...commonOptions, + plugins: [...commonOptions.plugins, ...(format === 'cjs' ? [importMetaUrlCjsPlugin] : [])], + entryPoints: {[ssrEntry]: path.join(pkgRoot, 'src', `${ssrEntry}.ts`)}, + }); + await logOutputFile(path.join(buildDir, `${ssrEntry}.js`)); + + // Build request-processor (always CJS regardless of MRT_EXPORT_TYPE) + await esbuild.build({ + ...commonOptions, + format: 'cjs', + plugins: [...commonOptions.plugins, importMetaUrlCjsPlugin], + entryPoints: {'request-processor': path.join(pkgRoot, 'src', 'request-processor.ts')}, + }); + await logOutputFile(path.join(buildDir, 'request-processor.js')); + + // Copy static assets + const staticSrc = path.join(pkgRoot, 'src', 'static'); + const staticDest = path.join(buildDir, 'static'); + await copyDir(staticSrc, staticDest); + + // Build config.server.ts so the CLI can load ssrOnly/ssrShared/ssrParameters + await esbuild.build({ + bundle: false, + platform: 'node', + target: 'node22', + format: 'cjs', + outdir: buildDir, + entryPoints: {'config.server': path.join(pkgRoot, 'config.server.ts')}, + }); + + // Create empty loader.js required by MRT + await fs.writeFile(path.join(buildDir, 'loader.js'), '// This file is intentionally empty\n'); + + // Write package.json without "type" field (MRT requires CJS bundles without it) + const pkg = JSON.parse(await fs.readFile(path.join(pkgRoot, 'package.json'), 'utf8')); + delete pkg.type; + await fs.writeFile(path.join(buildDir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n'); + + console.log('Build complete'); +} + +build().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/mrt-reference-app/src/app/server.test.ts b/packages/mrt-reference-app/src/app/server.test.ts new file mode 100644 index 00000000..e0e885c2 --- /dev/null +++ b/packages/mrt-reference-app/src/app/server.test.ts @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import request from 'supertest'; +import {expect} from 'chai'; +import sinon from 'sinon'; +import {mockClient} from 'aws-sdk-client-mock'; +import {LambdaClient} from '@aws-sdk/client-lambda'; +import {S3Client} from '@aws-sdk/client-s3'; +import {CloudWatchLogsClient} from '@aws-sdk/client-cloudwatch-logs'; +import {MetricsSender} from '@salesforce/mrt-utilities'; +import {createApp} from './server.js'; + +describe('server', () => { + const lambdaMock = mockClient(LambdaClient); + const s3Mock = mockClient(S3Client); + const logsMock = mockClient(CloudWatchLogsClient); + + beforeEach(() => { + lambdaMock.reset(); + s3Mock.reset(); + logsMock.reset(); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe('createApp', () => { + it('should create an Express app', () => { + const app = createApp(); + expect(app).to.not.be.undefined; + expect(typeof app.get).to.equal('function'); + expect(typeof app.use).to.equal('function'); + expect(typeof app.all).to.equal('function'); + }); + + it('should create app without MRT middleware by default', () => { + const app = createApp(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const routes = (app as any)._router?.stack || []; + const hasMRTMiddleware = routes.some( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (route: any) => route?.handle?.name?.includes('mrt') || route?.handle?.name?.includes('MRT'), + ); + expect(hasMRTMiddleware).to.equal(false); + }); + + it('should disable x-powered-by header', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers['x-powered-by']).to.be.undefined; + }); + + it('should set server header to "mrt-reference-app"', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers.server).to.equal('mrt-reference-app'); + }); + + it('should set server header on all routes', async () => { + const app = createApp(); + for (const route of ['/test', '/headers', '/cache', '/tls']) { + const response = await request(app).get(route); + expect(response.headers.server).to.equal('mrt-reference-app'); + } + }); + + it('should set Cache-Control to no-cache for all responses', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers['cache-control']).to.equal('no-cache'); + }); + + it('should handle /exception route', async () => { + const app = createApp(); + const response = await request(app).get('/exception'); + expect(response.status).to.equal(500); + }); + + it('should handle /tls route', async () => { + const app = createApp(); + const response = await request(app).get('/tls'); + expect(response.status).to.equal(200); + expect(response.headers['content-type']).to.include('application/json'); + }); + + it('should handle /cache route without duration', async () => { + const app = createApp(); + const response = await request(app).get('/cache'); + expect(response.status).to.equal(200); + expect(response.headers['cache-control']).to.equal('s-maxage=60'); + }); + + it('should handle /cache/:duration route', async () => { + const app = createApp(); + const response = await request(app).get('/cache/120'); + expect(response.status).to.equal(200); + expect(response.headers['cache-control']).to.equal('s-maxage=120'); + }); + + it('should handle /memtest route', async () => { + const app = createApp(); + const response = await request(app).get('/memtest'); + expect(response.status).to.equal(200); + expect(response.body).to.have.property('additional_info'); + }); + + it('should handle /cookie route', async () => { + const app = createApp(); + const response = await request(app).get('/cookie'); + expect(response.status).to.equal(200); + expect(response.headers['cache-control']).to.equal('private, max-age=60'); + }); + + it('should handle /headers route', async () => { + const app = createApp(); + const response = await request(app).get('/headers').set('Custom-Header', 'custom-value'); + expect(response.status).to.equal(200); + expect(response.body.headers).to.have.property('custom-header'); + }); + + it('should handle /isolation route', async () => { + const app = createApp(); + const response = await request(app).get('/isolation'); + expect(response.status).to.equal(200); + expect(response.headers['content-type']).to.include('application/json'); + }); + + it('should handle /set-response-headers route', async () => { + const app = createApp(); + const response = await request(app).get('/set-response-headers?header1=value1'); + expect(response.status).to.equal(200); + expect(response.headers.header1).to.equal('value1'); + }); + + it('should handle /streaming route', async () => { + const app = createApp(); + const response = await request(app).get('/streaming').expect(200); + expect(response.headers['content-type']).to.equal('text/plain'); + expect(response.text).to.include('Here is a streaming chunk0'); + expect(response.text).to.include('Here is a streaming chunk99'); + }); + + it('should handle /large-logging route', async () => { + const app = createApp(); + const response = await request(app).get('/large-logging?count=10').expect(200); + expect(response.body).to.have.property('duration'); + expect(response.body).to.have.property('message', 'Large logging'); + expect(response.body).to.have.property('size'); + }); + + it('should stream 100 chunks from /streaming route', async () => { + const app = createApp(); + const response = await request(app).get('/streaming').expect(200); + const chunkMatches = response.text.match(/Here is a streaming chunk\d+/g); + expect(chunkMatches).to.have.lengthOf(100); + }); + + it('should handle /auth/logout route', async () => { + const app = createApp(); + const response = await request(app).get('/auth/logout'); + expect(response.status).to.equal(401); + expect(response.text).to.equal('Logged out'); + }); + + it('should set json spaces to 4', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.text).to.include(' '); + }); + + it('should handle wildcard routes with /{*splat}', async () => { + const app = createApp(); + const response = await request(app).get('/any/random/path'); + expect(response.status).to.equal(200); + expect(response.body).to.have.property('path'); + }); + + it('should disable x-powered-by header in response', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers).to.not.have.property('x-powered-by'); + }); + + it('should handle POST requests to echo route', async () => { + const app = createApp(); + const response = await request(app).post('/test/endpoint'); + expect(response.status).to.equal(200); + expect(response.body.method).to.equal('POST'); + }); + + it('should handle PUT requests to echo route', async () => { + const app = createApp(); + const response = await request(app).put('/test/endpoint'); + expect(response.status).to.equal(200); + expect(response.body.method).to.equal('PUT'); + }); + + it('should handle DELETE requests to echo route', async () => { + const app = createApp(); + const response = await request(app).delete('/test/endpoint'); + expect(response.status).to.equal(200); + expect(response.body.method).to.equal('DELETE'); + }); + }); + + describe('Authentication routes', () => { + it('should return 401 for unauthorized access to /auth/* routes', async () => { + const app = createApp(); + const response = await request(app).get('/auth/protected'); + expect([401, 500]).to.include(response.status); + }); + + it('should handle /auth/logout with POST method', async () => { + const app = createApp(); + const response = await request(app).post('/auth/logout'); + expect(response.status).to.equal(401); + expect(response.text).to.equal('Logged out'); + }); + }); + + describe('Request timing middleware', () => { + let mockSend: sinon.SinonStub; + let originalInstance: MetricsSender | null; + + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + originalInstance = (MetricsSender as any)._instance; + mockSend = sinon.stub(); + const mockMetricsSender = {send: mockSend} as unknown as MetricsSender; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (MetricsSender as any)._instance = mockMetricsSender; + }); + + afterEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (MetricsSender as any)._instance = originalInstance; + }); + + it('should send RequestTime metric when response finishes', async () => { + const app = createApp(); + await request(app).get('/test'); + expect(mockSend.callCount).to.equal(1); + const callArgs = mockSend.args[0]; + expect(callArgs[0]).to.have.lengthOf(2); + expect(callArgs[0][0]).to.include({name: 'RequestTime', unit: 'Milliseconds'}); + expect(callArgs[0][0].value).to.be.at.least(0); + expect(callArgs[0][0].dimensions).to.not.be.undefined; + expect(callArgs[0][1]).to.include({name: 'RequestSuccess', unit: 'Count', value: 1}); + expect(callArgs[1]).to.equal(true); + }); + + it('should calculate response time correctly', async () => { + const app = createApp(); + const startTime = Date.now(); + await request(app).get('/test'); + const actualDuration = Date.now() - startTime; + expect(mockSend.callCount).to.equal(1); + const metrics = mockSend.args[0][0]; + const requestTimeMetric = metrics.find((m: {name: string}) => m.name === 'RequestTime'); + expect(requestTimeMetric).to.not.be.undefined; + expect(requestTimeMetric.value).to.be.at.least(0); + expect(requestTimeMetric.value).to.be.at.most(actualDuration + 100); + }); + + it('should send RequestTime metric for different routes', async () => { + const app = createApp(); + await request(app).get('/headers'); + await request(app).get('/cache'); + await request(app).post('/test'); + expect(mockSend.callCount).to.equal(3); + mockSend.args.forEach((call) => { + expect(call[0]).to.have.lengthOf(2); + const requestTimeMetric = call[0].find((m: {name: string}) => m.name === 'RequestTime'); + expect(requestTimeMetric).to.not.be.undefined; + expect(requestTimeMetric.unit).to.equal('Milliseconds'); + }); + }); + + it('should send RequestTime metric even when response has error', async () => { + const app = createApp(); + await request(app).get('/exception'); + expect(mockSend.callCount).to.equal(1); + const metrics = mockSend.args[0][0]; + expect(metrics).to.have.lengthOf(2); + const requestTimeMetric = metrics.find((m: {name: string}) => m.name === 'RequestTime'); + expect(requestTimeMetric).to.not.be.undefined; + const requestFailedMetric = metrics.find((m: {name: string}) => m.name === 'RequestFailed500'); + expect(requestFailedMetric).to.not.be.undefined; + }); + + it('should include dimensions in the metric', async () => { + const app = createApp(); + await request(app).get('/test'); + expect(mockSend.callCount).to.equal(1); + const dimensions = mockSend.args[0][0][0].dimensions; + expect(dimensions).to.not.be.undefined; + expect(typeof dimensions).to.equal('object'); + }); + + it('should handle multiple concurrent requests', async () => { + const app = createApp(); + await Promise.all([request(app).get('/test'), request(app).get('/headers'), request(app).get('/cache')]); + expect(mockSend.callCount).to.equal(3); + }); + }); +}); diff --git a/packages/mrt-reference-app/src/app/server.ts b/packages/mrt-reference-app/src/app/server.ts new file mode 100644 index 00000000..071123cd --- /dev/null +++ b/packages/mrt-reference-app/src/app/server.ts @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import express, {type Express, type Request, type Response, type NextFunction, type RequestHandler} from 'express'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import basicAuth from 'express-basic-auth'; +import { + createMRTProxyMiddlewares, + createMRTRequestProcessorMiddleware, + createMRTStaticAssetServingMiddleware, + isLocal, + createMRTCommonMiddleware, + createMRTCleanUpMiddleware, + MetricsSender, + getDimensions, +} from '@salesforce/mrt-utilities'; +import { + echo, + exception, + tlsVersionTest, + cacheTest, + memoryTest, + cookieTest, + headerTest, + responseHeadersTest, + loggingMiddleware, + envBasePathMiddleware, + streamingTest, + ssrBundleFileTest, + streamingResponseLarge, + multiValueHeadersTest, + setStatusTest, + winstonLogging, + massLogging, + delayedLogging, + largeLogging, + traceLogging, + dataStoreTest, + secretsManagerTest, + proxyTransformationTest, +} from '../utils/reference-routes.js'; +import {isolationTests} from '../utils/isolation-actions.js'; + +const extraRemappedHeadersMiddleware = (req: Request, res: Response, next: NextFunction) => { + res.set('server', 'mrt-reference-app'); + next(); +}; + +const createRequestTimingMiddleware = (app: AppWithMetrics) => (req: Request, res: Response, next: NextFunction) => { + res.locals.requestStartTime = Date.now(); + const afterResponse = () => { + const responseTime = Date.now() - res.locals.requestStartTime; + if (app.metrics) { + const metrics = [ + {name: 'RequestTime', value: responseTime, unit: 'Milliseconds', dimensions: getDimensions()}, + ]; + const requestResult = {name: 'RequestSuccess', value: 1, unit: 'Count', dimensions: getDimensions()}; + if (res.statusCode === 404) { + requestResult.name = 'RequestFailed404'; + } else if (res.statusCode >= 400 && res.statusCode <= 499) { + requestResult.name = 'RequestFailed400'; + } else if (res.statusCode >= 500 && res.statusCode <= 599) { + requestResult.name = 'RequestFailed500'; + } + metrics.push(requestResult); + app.metrics.send(metrics, true); + } + }; + res.on('finish', afterResponse); + res.on('error', afterResponse); + next(); +}; + +type AppWithMetrics = Express & { + metrics: MetricsSender; +}; + +export const createApp = (): AppWithMetrics => { + const app = express() as AppWithMetrics; + app.disable('x-powered-by'); + app.use(extraRemappedHeadersMiddleware); + app.metrics = MetricsSender.getSender(); + app.use(createRequestTimingMiddleware(app)); + app.use(createMRTCommonMiddleware() as RequestHandler); + + if (isLocal()) { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const rpExtension = process.env.RP_EXTENSION ? '.ts' : '.js'; + const requestProcessorPath = path.resolve(__dirname, `../request-processor${rpExtension}`); + console.log('Using request processor:', requestProcessorPath); + + const proxyConfigs = [ + { + host: 'https://playground-22x-us-west-1.mobify-storefront.com', + path: 'api', + }, + ]; + app.use(createMRTRequestProcessorMiddleware(requestProcessorPath, proxyConfigs) as RequestHandler); + const mrtProxies = createMRTProxyMiddlewares(proxyConfigs); + mrtProxies.forEach((proxy) => { + app.use(proxy.path, proxy.fn); + }); + const staticAssetDir = path.resolve(__dirname, '../static'); + console.log('Using static asset directory:', staticAssetDir); + app.use( + `/mobify/bundle/${process.env.BUNDLE_ID || '1'}/static/`, + createMRTStaticAssetServingMiddleware(staticAssetDir) as RequestHandler, + ); + } + app.use(createMRTCleanUpMiddleware() as RequestHandler); + + app.get('/favicon.ico', express.static('static/favicon.ico')); + app.get('/robots.txt', express.static('static/robots.txt')); + + app.use((req, res, next) => { + res.set('Cache-Control', 'no-cache'); + return next(); + }); + + app.use(loggingMiddleware); + app.use(envBasePathMiddleware); + + app.all('/exception', exception); + app.get('/tls', tlsVersionTest); + app.get('/cache', cacheTest); + app.get('/cache/:duration', cacheTest); + app.get('/memtest', memoryTest); + app.get('/cookie', cookieTest); + app.get('/headers', headerTest); + app.get('/isolation', isolationTests); + app.get('/set-response-headers', responseHeadersTest); + app.get('/multi-value-headers', multiValueHeadersTest); + app.get('/streaming', streamingTest); + app.get('/ssr-shared', ssrBundleFileTest); + app.get('/streaming-large', streamingResponseLarge); + app.get('/set-status', setStatusTest); + app.get('/winston-logging', winstonLogging); + app.get('/delayed-logging', delayedLogging); + app.get('/mass-logging', massLogging); + app.get('/large-logging', largeLogging); + app.get('/trace-logging', traceLogging); + app.get('/data-store/:key', dataStoreTest); + app.get('/secrets-manager', secretsManagerTest); + app.get('/proxy-transformation', proxyTransformationTest); + + app.all('/auth/logout', (req, res) => res.status(401).send('Logged out')); + app.use( + '/auth{*alias}', + basicAuth({ + users: {mobify: 'supersecret'}, + challenge: true, + realm: process.env.EXTERNAL_DOMAIN_NAME || 'echo-test', + }), + ); + app.all('/auth{*alias}', echo); + app.all('/{*splat}', echo); + app.set('json spaces', 4); + return app; +}; diff --git a/packages/mrt-reference-app/src/dev/node-local.js b/packages/mrt-reference-app/src/dev/node-local.js new file mode 100644 index 00000000..8393ead0 --- /dev/null +++ b/packages/mrt-reference-app/src/dev/node-local.js @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import path from 'path'; +import {fileURLToPath} from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const buildDirectory = path.resolve(__dirname, '../..', 'build'); + +const module = await import(path.resolve(buildDirectory, 'ssr.js')); +const app = module.default.app; + +const server = app.listen(2401, 'localhost', () => { + console.log('Server is running on port localhost:2401'); +}); + +['SIGTERM', 'SIGINT'].forEach((signal) => { + process.once(signal, () => server?.close(console.error)); +}); diff --git a/packages/mrt-reference-app/src/dev/tsx-local.ts b/packages/mrt-reference-app/src/dev/tsx-local.ts new file mode 100644 index 00000000..b9200623 --- /dev/null +++ b/packages/mrt-reference-app/src/dev/tsx-local.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {createApp} from '../app/server.js'; + +const app = createApp(); + +const server = app.listen(2401, 'localhost', () => { + console.log('Server is running on port localhost:2401'); +}); + +['SIGTERM', 'SIGINT'].forEach((signal) => { + process.once(signal, () => server?.close(console.error)); +}); diff --git a/packages/mrt-reference-app/src/request-processor.test.ts b/packages/mrt-reference-app/src/request-processor.test.ts new file mode 100644 index 00000000..f3179649 --- /dev/null +++ b/packages/mrt-reference-app/src/request-processor.test.ts @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {expect} from 'chai'; +import sinon from 'sinon'; +import {processRequest, type ProcessRequestParameters} from './request-processor.js'; + +describe('request-processor', () => { + afterEach(() => { + sinon.restore(); + }); + + describe('processRequest', () => { + const defaultParameters: ProcessRequestParameters = { + appHostname: 'localhost:2401', + proxyConfigs: [], + environment: 'development', + deployTarget: 'local-target', + }; + + it('should return path and querystring unchanged when no exclusions', () => { + const result = processRequest({path: '/test', querystring: 'foo=bar&baz=qux', parameters: defaultParameters}); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('foo=bar&baz=qux'); + }); + + it('should remove excluded query parameters', () => { + const result = processRequest({ + path: '/test', + querystring: 'removeme=value&keep=this', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('keep=this'); + }); + + it('should remove multiple excluded parameters', () => { + const result = processRequest({ + path: '/test', + querystring: 'removeme=value1&keep=this&removeme=value2', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('keep=this'); + }); + + it('should handle empty querystring', () => { + const result = processRequest({path: '/test', querystring: '', parameters: defaultParameters}); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal(''); + }); + + it('should handle querystring with only excluded parameters', () => { + const result = processRequest({path: '/test', querystring: 'removeme=value', parameters: defaultParameters}); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal(''); + }); + + it('should handle special characters in querystring', () => { + const result = processRequest({ + path: '/test', + querystring: 'param=value%20with%20spaces&removeme=delete', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('param=value+with+spaces'); + }); + + it('should handle URL encoded excluded parameter', () => { + const result = processRequest({ + path: '/test', + querystring: 'keep=this&removeme=encoded%20value', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('keep=this'); + }); + + it('should preserve path when querystring changes', () => { + const result = processRequest({ + path: '/api/users/123', + querystring: 'removeme=value&page=1', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/api/users/123'); + expect(result.querystring).to.equal('page=1'); + }); + + it('should handle complex querystring with excluded parameter', () => { + const result = processRequest({ + path: '/search', + querystring: 'q=test&removeme=value&sort=name&order=asc', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/search'); + expect(result.querystring).to.equal('q=test&sort=name&order=asc'); + }); + + it('should handle multiple values for same parameter', () => { + const result = processRequest({ + path: '/test', + querystring: 'removeme=value1&keep=this&removeme=value2&keep=that', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('keep=this&keep=that'); + }); + + it('should handle edge case with just key and no value for exclusion', () => { + const result = processRequest({ + path: '/test', + querystring: 'removeme=&keep=value', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('keep=value'); + }); + + it('should handle various parameter types', () => { + const result = processRequest({ + path: '/test', + querystring: 'string=value&number=123&removeme=remove&boolean=true', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('string=value&number=123&boolean=true'); + }); + + it('should accept parameters with different values', () => { + const parameters: ProcessRequestParameters = { + appHostname: 'example.com', + proxyConfigs: [{host: 'https://api.example.com', path: 'api'}], + environment: 'production', + deployTarget: 'production-target', + }; + const result = processRequest({path: '/test', querystring: 'foo=bar', parameters}); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('foo=bar'); + }); + + it('should preserve parameter order after filtering', () => { + const result = processRequest({ + path: '/test', + querystring: 'a=1&removeme=del&b=2&c=3', + parameters: defaultParameters, + }); + expect(result.path).to.equal('/test'); + expect(result.querystring).to.equal('a=1&b=2&c=3'); + }); + }); +}); diff --git a/packages/mrt-reference-app/src/request-processor.ts b/packages/mrt-reference-app/src/request-processor.ts new file mode 100644 index 00000000..db5f73f0 --- /dev/null +++ b/packages/mrt-reference-app/src/request-processor.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {ProxyConfig} from '@salesforce/mrt-utilities'; + +const exclusions = ['removeme']; + +export interface ProcessRequestParameters { + appHostname: string; + proxyConfigs: ProxyConfig[]; + environment: string; + deployTarget: string; +} + +export const processRequest = ({ + path, + querystring, + parameters, +}: { + path: string; + querystring: string; + parameters: ProcessRequestParameters; +}) => { + console.assert(parameters, 'Missing parameters'); + + const queryParameters = new URLSearchParams(querystring); + + for (const exclusion of exclusions) { + queryParameters.delete(exclusion); + } + + querystring = queryParameters.toString(); + + return { + path, + querystring, + }; +}; diff --git a/packages/mrt-reference-app/src/ssr.test.ts b/packages/mrt-reference-app/src/ssr.test.ts new file mode 100644 index 00000000..51b9c424 --- /dev/null +++ b/packages/mrt-reference-app/src/ssr.test.ts @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import request from 'supertest'; +import {LambdaClient, InvokeCommand} from '@aws-sdk/client-lambda'; +import {S3Client, GetObjectCommand} from '@aws-sdk/client-s3'; +import {CloudWatchLogsClient, CreateLogStreamCommand} from '@aws-sdk/client-cloudwatch-logs'; +import {mockClient} from 'aws-sdk-client-mock'; +import {ServiceException} from '@smithy/smithy-client'; +import {expect} from 'chai'; +import sinon from 'sinon'; +import type {Express} from 'express'; +import {processLambdaResponse, CONTENT_TYPE, X_ORIGINAL_CONTENT_TYPE} from './ssr.js'; +import type {APIGatewayProxyResult, APIGatewayProxyEvent} from 'aws-lambda'; +import createEventModule from '@serverless/event-mocks'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const createEvent = ((createEventModule as any).default ?? createEventModule) as typeof createEventModule; + +class AccessDenied extends ServiceException { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(options?: any) { + super({...options, name: 'AccessDenied'}); + } +} + +type PathStatusCodeContentType = [string, number, string]; + +const pathsToCheck: PathStatusCodeContentType[] = [ + ['/', 200, 'application/json; charset=utf-8'], + ['/tls', 200, 'application/json; charset=utf-8'], + ['/exception', 500, 'text/html; charset=utf-8'], + ['/cache', 200, 'application/json; charset=utf-8'], + ['/cookie', 200, 'application/json; charset=utf-8'], + ['/set-response-headers', 200, 'application/json; charset=utf-8'], + ['/isolation', 200, 'application/json; charset=utf-8'], + ['/memtest', 200, 'application/json; charset=utf-8'], + ['/streaming-large', 200, 'application/json; charset=utf-8'], +]; + +const pathsToCheckWithBasePath = (basePath: string): PathStatusCodeContentType[] => + pathsToCheck.map(([p, s, c]) => [`${basePath}${p}`, s, c]); + +describe('server', () => { + let originalEnv: NodeJS.ProcessEnv; + let app: Express; + const lambdaMock = mockClient(LambdaClient); + const s3Mock = mockClient(S3Client); + const logsMock = mockClient(CloudWatchLogsClient); + + beforeEach(async () => { + originalEnv = process.env; + process.env = Object.assign({}, process.env, { + MRT_ALLOW_COOKIES: 'true', + LISTEN_ADDRESS: '', + BUNDLE_ID: '1', + DEPLOY_TARGET: 'test', + EXTERNAL_DOMAIN_NAME: 'test.com', + MOBIFY_PROPERTY_ID: 'test', + AWS_LAMBDA_FUNCTION_NAME: 'pretend-to-be-remote', + AWS_REGION: 'us-east-2', + }); + lambdaMock.reset(); + s3Mock.reset(); + logsMock.reset(); + app = (await import('./ssr.js')).app; + }); + + afterEach(() => { + process.env = originalEnv; + sinon.restore(); + }); + + const checkPath = async (path: string, expectedStatus: number, expectedContentType: string) => { + await request(app).get(path).expect(expectedStatus).expect('Content-Type', expectedContentType); + }; + + pathsToCheck.forEach(([path, status, ct]) => { + it(`Path ${path} should render correctly`, () => checkPath(path, status, ct)); + }); + + pathsToCheckWithBasePath('/test-base-path').forEach(([path, status, ct]) => { + it(`Path ${path} should render correctly with base path set`, async () => { + process.env.MRT_ENV_BASE_PATH = '/test-base-path'; + return checkPath(path, status, ct); + }); + }); + + it('Path /echo should work with base path', async () => { + const basePath = '/test-base-path'; + process.env.MRT_ENV_BASE_PATH = basePath; + const response = await request(app).get(`${basePath}/echo?x=foo&y=bar`); + expect(response.status).to.equal(200); + expect(response.body.query.x).to.equal('foo'); + expect(response.body.query.y).to.equal('bar'); + expect(response.body.path).to.equal('/echo'); + expect(response.body.env.MRT_ENV_BASE_PATH).to.equal(basePath); + }); + + it('Path "/cache" has Cache-Control set', () => { + return request(app).get('/cache').expect('Cache-Control', 's-maxage=60'); + }); + + it('Path "/cache/:duration" has Cache-Control set correctly', () => { + return request(app).get('/cache/123').expect('Cache-Control', 's-maxage=123'); + }); + + it('Path "/headers" echoes request headers', async () => { + const response = await request(app).get('/headers').set('Random-Header', 'random'); + expect(response.body.headers['random-header']).to.equal('random'); + }); + + it('Path "/cookie" sets cookie', async () => { + return request(app) + .get('/cookie?name=test-cookie&value=test-value') + .expect('set-cookie', 'test-cookie=test-value; Path=/'); + }); + + it('Path "/set-response-headers" sets response header', () => { + return request(app) + .get('/set-response-headers?header1=value1&header2=test-value') + .expect('header1', 'value1') + .expect('header2', 'test-value'); + }); + + it('Path "/isolation" succeeds', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const lambdaError = new ServiceException({$fault: 'client', $metadata: {}} as never); + lambdaError.name = 'AccessDeniedException'; + const logsError = new ServiceException({$fault: 'client', $metadata: {}} as never); + logsError.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(lambdaError); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + logsMock.on(CreateLogStreamCommand).rejects(logsError); + const params = `FunctionName=name&Bucket=bucket&Key=key&logGroupName=lgName`; + const response = await request(app).get(`/isolation?${params}`); + expect(response.body.origin).to.equal(true); + expect(response.body.storage).to.equal(true); + expect(response.body.logs).to.equal(true); + consoleSpy.restore(); + }); + + it('Path "/isolation" succeeds with Region', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const lambdaError = new ServiceException({$fault: 'client', $metadata: {}} as never); + lambdaError.name = 'AccessDeniedException'; + const logsError = new ServiceException({$fault: 'client', $metadata: {}} as never); + logsError.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(lambdaError); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + logsMock.on(CreateLogStreamCommand).rejects(logsError); + const params = `FunctionName=name&Bucket=bucket&Key=key&logGroupName=lgName&Region=us-west-1`; + const response = await request(app).get(`/isolation?${params}`); + expect(response.body.origin).to.equal(true); + expect(response.body.storage).to.equal(true); + expect(response.body.logs).to.equal(true); + consoleSpy.restore(); + }); + + it('Path "/isolation" fails', async () => { + const consoleSpy = sinon.stub(console, 'error'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + lambdaMock.on(InvokeCommand).resolves({} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + s3Mock.on(GetObjectCommand).resolves({} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + logsMock.on(CreateLogStreamCommand).resolves({} as any); + const params = `FunctionName=name&Bucket=bucket&Key=key&logGroupName=lgName`; + const response = await request(app).get(`/isolation?${params}`); + expect(response.body.origin).to.equal(false); + expect(response.body.storage).to.equal(false); + expect(response.body.logs).to.equal(false); + const errors = ['Lambda isolation test failed!', 'S3 isolation test failed!', 'Log group isolation test failed!']; + const calls = consoleSpy.args.map((call) => call[0]); + expect(errors.some((error) => calls.includes(error))).to.equal(true); + consoleSpy.restore(); + }); + + it('Check incoming headers are lowercase', async () => { + const response = await request(app) + .get('/headers') + .set('Random-Header', 'random') + .set('Another-Mixed-Case-Header', 'value') + .set('UPPERCASE-HEADER', 'test'); + for (const header in response.body.headers) { + expect(header).to.equal(header.toLowerCase()); + } + }); +}); + +const base64Body = 'SGVsbG8gV29ybGQ='; + +describe('processLambdaResponse', () => { + it('should add date header to response', () => { + const response = {statusCode: 200, headers: {}, body: ''}; + const event = {multiValueHeaders: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers.date).to.not.be.undefined; + expect(result.headers.date).to.match(/^\w+, \d+ \w+ \d+ \d+:\d+:\d+ GMT$/); + }); + + it('should preserve original headers', () => { + const response = { + statusCode: 200, + multiValueHeaders: {'content-type': ['application/json'], 'x-custom': ['value']}, + body: base64Body, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers['content-type']).to.equal('application/json'); + expect(result.headers['x-custom']).to.equal('value'); + }); + + it('should remove multiValueHeaders from response', () => { + const response = { + statusCode: 200, + multiValueHeaders: {'set-cookie': ['cookie1=value1', 'cookie2=value2']}, + body: base64Body, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.multiValueHeaders).to.be.undefined; + }); + + it('should add correlation ID from event headers', () => { + const response = { + statusCode: 200, + multiValueHeaders: {'content-type': ['application/json'], 'x-custom': ['value']}, + body: base64Body, + }; + const event = createEvent('aws:apiGateway', { + path: '/', + httpMethod: 'GET', + headers: {'x-correlation-id': 'test-correlation-123'}, + body: null, + multiValueHeaders: {'x-correlation-id': ['test-correlation-123']}, + isBase64Encoded: false, + pathParameters: null, + queryStringParameters: null, + multiValueQueryStringParameters: null, + stageVariables: null, + // @ts-expect-error - requestContext is not required + requestContext: {}, + }); + const result = processLambdaResponse(response as APIGatewayProxyResult, event); + expect(result.headers['x-correlation-id']).to.equal('test-correlation-123'); + }); + + it('should not add correlation ID when not present in event', () => { + const response = {statusCode: 200, headers: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers['x-correlation-id']).to.be.undefined; + }); + + it('should replace content-type with x-original-content-type', () => { + const response = { + statusCode: 200, + multiValueHeaders: {[X_ORIGINAL_CONTENT_TYPE]: ['application/pdf'], [CONTENT_TYPE]: ['text/plain']}, + body: base64Body, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers[CONTENT_TYPE]).to.equal('application/pdf'); + expect(result.headers[X_ORIGINAL_CONTENT_TYPE]).to.be.undefined; + }); + + it('should preserve content-type when x-original-content-type is not present', () => { + const response = { + statusCode: 200, + body: base64Body, + multiValueHeaders: {[CONTENT_TYPE]: ['application/json']}, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers[CONTENT_TYPE]).to.equal('application/json'); + expect(result.headers[X_ORIGINAL_CONTENT_TYPE]).to.be.undefined; + }); + + it('should handle event with no headers', () => { + const response = {statusCode: 200, headers: {}, body: base64Body}; + const event = {}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.statusCode).to.equal(200); + expect(result.headers.date).to.not.be.undefined; + }); + + it('should flatten multi-value headers', () => { + const response = { + statusCode: 200, + body: base64Body, + multiValueHeaders: { + 'set-cookie': ['cookie1=value1', 'cookie2=value2'], + 'x-custom': ['header1', 'header2'], + }, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers['set-cookie']).to.equal('cookie1=value1,cookie2=value2'); + expect(result.headers['x-custom']).to.equal('header1,header2'); + }); + + it('should handle status code correctly', () => { + const response = {statusCode: 404, headers: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.statusCode).to.equal(404); + }); + + it('should preserve response body when present', () => { + const response = {statusCode: 200, multiValueHeaders: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.body).to.equal(base64Body); + }); + + it('should add date header with correct format', () => { + const response = {statusCode: 200, multiValueHeaders: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers.date).to.not.be.undefined; + expect(result.headers.date).to.match(/^\w+, \d+ \w+ \d+ \d+:\d+:\d+ GMT$/); + }); +}); diff --git a/packages/mrt-reference-app/src/ssr.ts b/packages/mrt-reference-app/src/ssr.ts new file mode 100644 index 00000000..697902fd --- /dev/null +++ b/packages/mrt-reference-app/src/ssr.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {Express} from 'express'; +import {createApp} from './app/server.js'; +import {ServerlessAdapter, getFlattenedHeadersMap} from '@h4ad/serverless-adapter'; +import {DefaultHandler} from '@h4ad/serverless-adapter/lib/handlers/default'; +import {CallbackResolver} from '@h4ad/serverless-adapter/lib/resolvers/callback'; +import {ApiGatewayV1Adapter} from '@h4ad/serverless-adapter/lib/adapters/aws'; +import {ExpressFramework} from '@h4ad/serverless-adapter/lib/frameworks/express'; +import type {APIGatewayProxyEvent, Context, APIGatewayProxyResult, Callback} from 'aws-lambda'; + +export const CONTENT_TYPE = 'content-type'; +export const X_ORIGINAL_CONTENT_TYPE = 'x-original-content-type'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type HandlerFunction = (event: APIGatewayProxyEvent, context: Context, callback: Callback) => any; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const processLambdaResponse = (response: APIGatewayProxyResult, event: APIGatewayProxyEvent): any => { + if (!response) return response; + + const correlationId = event.headers?.['x-correlation-id']; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const joinedHeaders = getFlattenedHeadersMap(response.multiValueHeaders || ({} as any), ',', true); + joinedHeaders.date = new Date().toUTCString(); + delete response.multiValueHeaders; + + if (correlationId) { + joinedHeaders['x-correlation-id'] = correlationId; + } + + const originalContentType = joinedHeaders[X_ORIGINAL_CONTENT_TYPE]; + if (originalContentType) { + joinedHeaders[CONTENT_TYPE] = originalContentType; + delete joinedHeaders[X_ORIGINAL_CONTENT_TYPE]; + } + + return { + ...response, + headers: joinedHeaders, + }; +}; + +const createLambdaHandler = (app: Express): HandlerFunction => { + return (event: APIGatewayProxyEvent, context: Context, callback: Callback) => { + const serverlessAdapterHandler = ServerlessAdapter.new(app) + .setFramework(new ExpressFramework()) + .setHandler(new DefaultHandler()) + .setResolver(new CallbackResolver()) + .addAdapter(new ApiGatewayV1Adapter({lowercaseRequestHeaders: true, throwOnChunkedTransferEncoding: false})) + .build() as HandlerFunction; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (context as any).callbackWaitsForEmptyEventLoop = false; + + const managedCallback = (err: Error | null, response: APIGatewayProxyResult) => { + return callback(err, processLambdaResponse(response, event)); + }; + + return serverlessAdapterHandler(event, context, managedCallback as Callback); + }; +}; + +const mrtApp = createApp(); + +export const app = mrtApp; +export const get = createLambdaHandler(mrtApp); diff --git a/packages/mrt-reference-app/src/static/example.css b/packages/mrt-reference-app/src/static/example.css new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.css @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.eot b/packages/mrt-reference-app/src/static/example.eot new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.eot @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/src/static/example.gif b/packages/mrt-reference-app/src/static/example.gif new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.gif @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.jpe b/packages/mrt-reference-app/src/static/example.jpe new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.jpe @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.jpeg b/packages/mrt-reference-app/src/static/example.jpeg new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.jpeg @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.jpg b/packages/mrt-reference-app/src/static/example.jpg new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.jpg @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.js b/packages/mrt-reference-app/src/static/example.js new file mode 100644 index 00000000..b2a8d963 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.js @@ -0,0 +1 @@ +/* This file is used in the E2E tests to verify that correct header values are set. */ diff --git a/packages/mrt-reference-app/src/static/example.json b/packages/mrt-reference-app/src/static/example.json new file mode 100644 index 00000000..17edacea --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.json @@ -0,0 +1,3 @@ +{ + "message": "This file is used in the E2E tests to verify that correct header values are set." +} \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.mjs b/packages/mrt-reference-app/src/static/example.mjs new file mode 100644 index 00000000..b2a8d963 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.mjs @@ -0,0 +1 @@ +/* This file is used in the E2E tests to verify that correct header values are set. */ diff --git a/packages/mrt-reference-app/src/static/example.otf b/packages/mrt-reference-app/src/static/example.otf new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.otf @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/src/static/example.png b/packages/mrt-reference-app/src/static/example.png new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.png @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.svg b/packages/mrt-reference-app/src/static/example.svg new file mode 100644 index 00000000..3e3e0425 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.svg @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.ttf b/packages/mrt-reference-app/src/static/example.ttf new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.ttf @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/src/static/example.txt b/packages/mrt-reference-app/src/static/example.txt new file mode 100644 index 00000000..248bc553 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.txt @@ -0,0 +1,7 @@ +This file is used in the E2E tests to verify that Cloudfront is compressing content properly. + +Cloudfront only compresses files that are 1,000 bytes - 10,000,000 bytes in size +For more details check: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id dolor vitae neque feugiat convallis. Nullam eleifend est at justo commodo facilisis. Vestibulum consectetur ultricies facilisis. In non ligula a ipsum pretium aliquet. Cras vel ligula id lectus tristique viverra sed eu neque. Vivamus sit amet nulla id tortor efficitur finibus. Curabitur ultrices auctor sem, ut consequat nulla interdum ut. +Etiam auctor massa nec mauris ullamcorper, vitae bibendum nisl tempus. Nulla vel facilisis ante, in ultricies mi. Praesent laoreet faucibus mauris. Donec cursus erat ac malesuada mattis. Sed vehicula, erat id consequat fringilla, felis justo fringilla nulla, ac feugiat dui velit in est. Nullam ut lectus et purus lacinia congue a vitae lectus. Fusce rutrum elit at hendrerit malesuada. \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/example.woff b/packages/mrt-reference-app/src/static/example.woff new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.woff @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/src/static/example.woff2 b/packages/mrt-reference-app/src/static/example.woff2 new file mode 100644 index 00000000..e8001a41 --- /dev/null +++ b/packages/mrt-reference-app/src/static/example.woff2 @@ -0,0 +1 @@ +This file is used in the E2E tests to verify that correct header values are set. diff --git a/packages/mrt-reference-app/src/static/favicon.ico b/packages/mrt-reference-app/src/static/favicon.ico new file mode 100644 index 00000000..a15a8f34 Binary files /dev/null and b/packages/mrt-reference-app/src/static/favicon.ico differ diff --git a/packages/mrt-reference-app/src/static/robots.txt b/packages/mrt-reference-app/src/static/robots.txt new file mode 100644 index 00000000..77470cb3 --- /dev/null +++ b/packages/mrt-reference-app/src/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/packages/mrt-reference-app/src/static/saturn.jpg b/packages/mrt-reference-app/src/static/saturn.jpg new file mode 100644 index 00000000..cc09ff94 Binary files /dev/null and b/packages/mrt-reference-app/src/static/saturn.jpg differ diff --git a/packages/mrt-reference-app/src/streamingHandler.test.ts b/packages/mrt-reference-app/src/streamingHandler.test.ts new file mode 100644 index 00000000..7f9002f4 --- /dev/null +++ b/packages/mrt-reference-app/src/streamingHandler.test.ts @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {expect} from 'chai'; +import sinon from 'sinon'; +import type {APIGatewayProxyEvent, Context} from 'aws-lambda'; +import type {Writable} from 'stream'; +import {EventEmitter} from 'events'; +import {buildHandler} from './streamingHandler.js'; + +const mockHttpResponseStream = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + from: sinon.stub().callsFake((stream: Writable) => stream), +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).awslambda = { + HttpResponseStream: mockHttpResponseStream, +}; + +function createMockWritable(): Writable & EventEmitter { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const stream = new EventEmitter() as any; + let ended = false; + let destroyed = false; + + stream.writable = true; + stream.writableEnded = false; + stream.writableFinished = false; + stream.destroyed = false; + + stream.write = sinon.stub().callsFake((chunk: unknown) => { + if (destroyed || ended) return false; + void chunk; + return true; + }); + + stream.end = sinon.stub().callsFake((chunk?: unknown) => { + if (destroyed) return stream; + if (chunk) void chunk; + ended = true; + stream.writableEnded = true; + stream.writableFinished = true; + stream.emit('finish'); + return stream; + }); + + stream.destroy = sinon.stub().callsFake(() => { + destroyed = true; + stream.destroyed = true; + stream.writable = false; + stream.emit('close'); + return stream; + }); + + stream.flush = sinon.stub(); + + return stream as Writable & EventEmitter; +} + +function createMockEvent(overrides?: Partial): APIGatewayProxyEvent { + return { + httpMethod: 'GET', + path: '/test', + pathParameters: null, + queryStringParameters: null, + headers: {'Content-Type': 'application/json', Host: 'example.com'}, + multiValueHeaders: {}, + body: null, + isBase64Encoded: false, + requestContext: { + requestId: 'test-request-id', + accountId: '123456789012', + apiId: 'test-api-id', + protocol: 'HTTP/1.1', + httpMethod: 'GET', + path: '/test', + stage: 'test', + requestTime: '09/Apr/2015:12:34:56 +0000', + requestTimeEpoch: 1428582896000, + identity: { + sourceIp: '127.0.0.1', + userAgent: 'test-agent', + accessKey: null, + accountId: null, + apiKey: null, + apiKeyId: null, + caller: null, + cognitoAuthenticationProvider: null, + cognitoAuthenticationType: null, + cognitoIdentityId: null, + cognitoIdentityPoolId: null, + principalOrgId: null, + user: null, + userArn: null, + clientCert: null, + }, + resourceId: 'test-resource-id', + resourcePath: '/test', + }, + ...overrides, + } as APIGatewayProxyEvent; +} + +function createMockContext(overrides?: Partial): Context { + return { + callbackWaitsForEmptyEventLoop: false, + functionName: 'test-function', + functionVersion: '$LATEST', + invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:test-function', + memoryLimitInMB: '128', + awsRequestId: 'test-request-id', + logGroupName: '/aws/lambda/test-function', + logStreamName: '2024/01/01/[$LATEST]test', + getRemainingTimeInMillis: () => 30000, + done: sinon.stub() as never, + fail: sinon.stub() as never, + succeed: sinon.stub() as never, + ...overrides, + } as Context; +} + +describe('streamingHandler', () => { + let mockResponseStream: Writable & EventEmitter; + + beforeEach(() => { + mockResponseStream = createMockWritable(); + mockHttpResponseStream.from.resetHistory(); + mockHttpResponseStream.from.callsFake((stream: Writable) => stream); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe('buildHandler', () => { + it('should be a function', () => { + expect(typeof buildHandler).to.equal('function'); + }); + + it('should return a handler function when called with responseStream', () => { + const handler = buildHandler(mockResponseStream); + expect(typeof handler).to.equal('function'); + }); + + it('should return a handler that accepts event and context', async () => { + const handler = buildHandler(mockResponseStream); + const event = createMockEvent(); + const context = createMockContext(); + + const result = handler(event, context); + expect(result).to.be.instanceOf(Promise); + + await result; + + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle requests to existing routes', async () => { + const handler = buildHandler(mockResponseStream); + await handler(createMockEvent({path: '/test'}), createMockContext()); + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle requests to echo route', async () => { + const handler = buildHandler(mockResponseStream); + await handler(createMockEvent({path: '/echo'}), createMockContext()); + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle requests to streaming route', async () => { + const handler = buildHandler(mockResponseStream); + await handler(createMockEvent({path: '/streaming'}), createMockContext()); + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle different HTTP methods', async () => { + const handler = buildHandler(mockResponseStream); + await handler(createMockEvent({httpMethod: 'POST', path: '/test'}), createMockContext()); + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle requests with query parameters', async () => { + const handler = buildHandler(mockResponseStream); + await handler( + createMockEvent({path: '/test', queryStringParameters: {foo: 'bar', baz: 'qux'}}), + createMockContext(), + ); + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle requests with path parameters', async () => { + const handler = buildHandler(mockResponseStream); + await handler(createMockEvent({path: '/test', pathParameters: {id: '123'}}), createMockContext()); + expect((mockResponseStream.write as sinon.SinonStub).called).to.equal(true); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should handle errors gracefully', async () => { + const handler = buildHandler(mockResponseStream); + await handler(createMockEvent({path: '/exception'}), createMockContext()); + expect((mockResponseStream.end as sinon.SinonStub).called).to.equal(true); + }); + + it('should create a new handler for each responseStream', () => { + const stream1 = createMockWritable(); + const stream2 = createMockWritable(); + const handler1 = buildHandler(stream1); + const handler2 = buildHandler(stream2); + expect(handler1).to.not.equal(handler2); + }); + + it('should use the provided responseStream for each handler', async () => { + const stream1 = createMockWritable(); + const stream2 = createMockWritable(); + + const handler1 = buildHandler(stream1); + const handler2 = buildHandler(stream2); + + const event = createMockEvent(); + const context = createMockContext(); + + await handler1(event, context); + expect((stream1.write as sinon.SinonStub).called).to.equal(true); + expect((stream2.write as sinon.SinonStub).called).to.equal(false); + + (stream1.write as sinon.SinonStub).resetHistory(); + (stream2.write as sinon.SinonStub).resetHistory(); + + await handler2(event, context); + expect((stream2.write as sinon.SinonStub).called).to.equal(true); + expect((stream1.write as sinon.SinonStub).called).to.equal(false); + }); + }); +}); diff --git a/packages/mrt-reference-app/src/streamingHandler.ts b/packages/mrt-reference-app/src/streamingHandler.ts new file mode 100644 index 00000000..1a40b678 --- /dev/null +++ b/packages/mrt-reference-app/src/streamingHandler.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {APIGatewayProxyEvent, Context} from 'aws-lambda'; +import type {Writable} from 'stream'; +import type {Express} from 'express'; +import {createApp} from './app/server.js'; +import {createStreamingLambdaAdapter} from '@salesforce/mrt-utilities'; + +type AsyncHandlerFunction = (event: APIGatewayProxyEvent, context: Context) => Promise; + +type BuildHandler = (responseStream: Writable) => AsyncHandlerFunction; + +const createBuildHandler = (app: Express): BuildHandler => { + return (responseStream: Writable) => { + return async (event: APIGatewayProxyEvent, context: Context) => { + const streamingLambdaAdapter = createStreamingLambdaAdapter(app, responseStream); + return streamingLambdaAdapter(event, context); + }; + }; +}; + +const mrtApp = createApp(); + +export const buildHandler = createBuildHandler(mrtApp); diff --git a/packages/mrt-reference-app/src/utils/isolation-actions.test.ts b/packages/mrt-reference-app/src/utils/isolation-actions.test.ts new file mode 100644 index 00000000..aed5b105 --- /dev/null +++ b/packages/mrt-reference-app/src/utils/isolation-actions.test.ts @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {expect} from 'chai'; +import sinon from 'sinon'; +import { + isolationOriginLambdaTest, + isolationS3Test, + isolationLogsTest, + executeIsolationTests, +} from './isolation-actions.js'; +import {LambdaClient, InvokeCommand} from '@aws-sdk/client-lambda'; +import {S3Client, GetObjectCommand} from '@aws-sdk/client-s3'; +import {CloudWatchLogsClient, CreateLogStreamCommand} from '@aws-sdk/client-cloudwatch-logs'; +import {mockClient} from 'aws-sdk-client-mock'; +import {ServiceException} from '@smithy/smithy-client'; + +const lambdaMock = mockClient(LambdaClient); +const s3Mock = mockClient(S3Client); +const logsMock = mockClient(CloudWatchLogsClient); + +class AccessDenied extends ServiceException { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(options?: any) { + super({...options, name: 'AccessDenied'}); + } +} + +describe('isolation-actions', () => { + beforeEach(() => { + lambdaMock.reset(); + s3Mock.reset(); + logsMock.reset(); + }); + + afterEach(() => { + lambdaMock.reset(); + s3Mock.reset(); + logsMock.reset(); + sinon.restore(); + }); + + describe('isolationOriginLambdaTest', () => { + it('should return true when AccessDeniedException is thrown', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const error = new ServiceException({$fault: 'client', $metadata: {}} as never); + error.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(error); + + const result = await isolationOriginLambdaTest({FunctionName: 'test-function'}); + expect(result).to.equal(true); + expect(consoleSpy.called).to.equal(false); + consoleSpy.restore(); + }); + + it('should return false when access is granted', async () => { + const consoleSpy = sinon.stub(console, 'error'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + lambdaMock.on(InvokeCommand).resolves({} as any); + const result = await isolationOriginLambdaTest({FunctionName: 'test-function'}); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + + it('should return false for other errors', async () => { + const consoleSpy = sinon.stub(console, 'error'); + lambdaMock.on(InvokeCommand).rejects(new Error('Network error')); + const result = await isolationOriginLambdaTest({FunctionName: 'test-function'}); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + }); + + describe('isolationS3Test', () => { + it('should return true when AccessDenied is thrown', async () => { + const consoleSpy = sinon.stub(console, 'error'); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const result = await isolationS3Test({Bucket: 'test-bucket', Key: 'test-key', Region: 'us-east-1'}); + expect(result).to.equal(true); + expect(consoleSpy.called).to.equal(false); + consoleSpy.restore(); + }); + + it('should use specified region', async () => { + const consoleSpy = sinon.stub(console, 'error'); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const result = await isolationS3Test({Bucket: 'test-bucket', Key: 'test-key', Region: 'us-west-2'}); + expect(result).to.equal(true); + expect(s3Mock.call(0).args[0].input).to.not.have.property('Region'); + consoleSpy.restore(); + }); + + it('should return false when access is granted', async () => { + const consoleSpy = sinon.stub(console, 'error'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + s3Mock.on(GetObjectCommand).resolves({} as any); + const result = await isolationS3Test({Bucket: 'test-bucket', Key: 'test-key'}); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + + it('should use default region when Region is not specified', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const originalEnv = process.env.AWS_REGION; + process.env.AWS_REGION = 'us-east-1'; + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const result = await isolationS3Test({Bucket: 'test-bucket', Key: 'test-key'}); + expect(result).to.equal(true); + consoleSpy.restore(); + process.env.AWS_REGION = originalEnv; + }); + }); + + describe('isolationLogsTest', () => { + it('should return true when AccessDeniedException is thrown', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const error = new ServiceException({$fault: 'client', $metadata: {}} as never); + error.name = 'AccessDeniedException'; + logsMock.on(CreateLogStreamCommand).rejects(error); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationLogsTest({logGroupName: 'test-log-group', logStreamName: 'test-stream'} as any); + expect(result).to.equal(true); + expect(consoleSpy.called).to.equal(false); + consoleSpy.restore(); + }); + + it('should generate unique log stream names', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const error = new ServiceException({$fault: 'client', $metadata: {}} as never); + error.name = 'AccessDeniedException'; + logsMock.on(CreateLogStreamCommand).rejects(error); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result1 = await isolationLogsTest({logGroupName: 'test-log-group', logStreamName: 'test-stream-1'} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result2 = await isolationLogsTest({logGroupName: 'test-log-group', logStreamName: 'test-stream-2'} as any); + expect(result1).to.equal(true); + expect(result2).to.equal(true); + expect(logsMock.calls().length).to.equal(2); + consoleSpy.restore(); + }); + + it('should return false when access is granted', async () => { + const consoleSpy = sinon.stub(console, 'error'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + logsMock.on(CreateLogStreamCommand).resolves({} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationLogsTest({logGroupName: 'test-log-group', logStreamName: 'test-stream'} as any); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + }); + + describe('executeIsolationTests', () => { + it('should execute all tests with valid parameters', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const lambdaError = new ServiceException({$fault: 'client', $metadata: {}} as never); + lambdaError.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(lambdaError); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const logsError = new ServiceException({$fault: 'client', $metadata: {}} as never); + logsError.name = 'AccessDeniedException'; + logsMock.on(CreateLogStreamCommand).rejects(logsError); + + const results = await executeIsolationTests({ + FunctionName: 'test-function', + Bucket: 'test-bucket', + Key: 'test-key', + logGroupName: 'test-log-group', + }); + expect(results.origin).to.equal(true); + expect(results.storage).to.equal(true); + expect(results.logs).to.equal(true); + consoleSpy.restore(); + }); + + it('should execute tests with region parameter', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const lambdaError = new ServiceException({$fault: 'client', $metadata: {}} as never); + lambdaError.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(lambdaError); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const logsError = new ServiceException({$fault: 'client', $metadata: {}} as never); + logsError.name = 'AccessDeniedException'; + logsMock.on(CreateLogStreamCommand).rejects(logsError); + + const results = await executeIsolationTests({ + FunctionName: 'test-function', + Bucket: 'test-bucket', + Key: 'test-key', + Region: 'us-west-2', + logGroupName: 'test-log-group', + }); + expect(results.origin).to.equal(true); + expect(results.storage).to.equal(true); + expect(results.logs).to.equal(true); + consoleSpy.restore(); + }); + + it('should return false for all tests when access is granted', async () => { + const consoleSpy = sinon.stub(console, 'error'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + lambdaMock.on(InvokeCommand).resolves({} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + s3Mock.on(GetObjectCommand).resolves({} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + logsMock.on(CreateLogStreamCommand).resolves({} as any); + + const results = await executeIsolationTests({ + FunctionName: 'test-function', + Bucket: 'test-bucket', + Key: 'test-key', + logGroupName: 'test-log-group', + }); + expect(results.origin).to.equal(false); + expect(results.storage).to.equal(false); + expect(results.logs).to.equal(false); + consoleSpy.restore(); + }); + + it('should execute tests with partial parameters', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const lambdaError = new ServiceException({$fault: 'client', $metadata: {}} as never); + lambdaError.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(lambdaError); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const logsError = new ServiceException({$fault: 'client', $metadata: {}} as never); + logsError.name = 'AccessDeniedException'; + logsMock.on(CreateLogStreamCommand).rejects(logsError); + + const results = await executeIsolationTests({FunctionName: 'test-function'}); + expect(results.origin).to.equal(true); + expect(results.storage).to.equal(true); + expect(results.logs).to.equal(true); + consoleSpy.restore(); + }); + + it('should handle mixed results', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const lambdaError = new ServiceException({$fault: 'client', $metadata: {}} as never); + lambdaError.name = 'AccessDeniedException'; + lambdaMock.on(InvokeCommand).rejects(lambdaError); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + s3Mock.on(GetObjectCommand).resolves({} as any); + const logsError = new ServiceException({$fault: 'client', $metadata: {}} as never); + logsError.name = 'AccessDeniedException'; + logsMock.on(CreateLogStreamCommand).rejects(logsError); + + const results = await executeIsolationTests({ + FunctionName: 'test-function', + Bucket: 'test-bucket', + Key: 'test-key', + logGroupName: 'test-log-group', + }); + expect(results.origin).to.equal(true); + expect(results.storage).to.equal(false); + expect(results.logs).to.equal(true); + consoleSpy.restore(); + }); + + it('should handle empty parameters object', async () => { + const consoleSpy = sinon.stub(console, 'error'); + lambdaMock.on(InvokeCommand).rejects(new Error('Missing function name')); + lambdaMock.on(InvokeCommand).rejects(new Error('Missing parameters')); + logsMock.on(CreateLogStreamCommand).rejects(new Error('Missing log group name')); + + const results = await executeIsolationTests({}); + expect(results.origin).to.equal(false); + expect(results.storage).to.equal(false); + expect(results.logs).to.equal(false); + consoleSpy.restore(); + }); + }); + + describe('isolationOriginLambdaTest edge cases', () => { + it('should handle different error types correctly', async () => { + const consoleSpy = sinon.stub(console, 'error'); + lambdaMock.on(InvokeCommand).rejects(new Error('Network timeout')); + const result = await isolationOriginLambdaTest({FunctionName: 'test-function'}); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + + it('should handle missing FunctionName', async () => { + const consoleSpy = sinon.stub(console, 'error'); + lambdaMock.on(InvokeCommand).rejects(new Error('Missing parameter')); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationOriginLambdaTest({FunctionName: ''} as any); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + }); + + describe('isolationS3Test edge cases', () => { + it('should handle missing bucket parameter', async () => { + const consoleSpy = sinon.stub(console, 'error'); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationS3Test({Key: 'test-key'} as any); + expect(result).to.equal(true); + expect(consoleSpy.called).to.equal(false); + consoleSpy.restore(); + }); + + it('should handle missing key parameter', async () => { + const consoleSpy = sinon.stub(console, 'error'); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationS3Test({Bucket: 'test-bucket'} as any); + expect(result).to.equal(true); + expect(consoleSpy.called).to.equal(false); + consoleSpy.restore(); + }); + + it('should handle different AWS regions', async () => { + const consoleSpy = sinon.stub(console, 'error'); + s3Mock.on(GetObjectCommand).rejects(new AccessDenied()); + const result = await isolationS3Test({Bucket: 'test-bucket', Key: 'test-key', Region: 'eu-west-1'}); + expect(result).to.equal(true); + expect(consoleSpy.called).to.equal(false); + consoleSpy.restore(); + }); + }); + + describe('isolationLogsTest edge cases', () => { + it('should handle missing logGroupName', async () => { + const consoleSpy = sinon.stub(console, 'error'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + logsMock.on(CreateLogStreamCommand).resolves({} as any); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationLogsTest({} as any); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + + it('should handle different error names', async () => { + const consoleSpy = sinon.stub(console, 'error'); + const error = new ServiceException({$fault: 'server', $metadata: {}} as never); + error.name = 'ServiceException'; + logsMock.on(CreateLogStreamCommand).rejects(error); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await isolationLogsTest({logGroupName: 'test-log-group', logStreamName: 'test-stream'} as any); + expect(result).to.equal(false); + expect(consoleSpy.called).to.equal(true); + consoleSpy.restore(); + }); + }); +}); diff --git a/packages/mrt-reference-app/src/utils/isolation-actions.ts b/packages/mrt-reference-app/src/utils/isolation-actions.ts new file mode 100644 index 00000000..e341871f --- /dev/null +++ b/packages/mrt-reference-app/src/utils/isolation-actions.ts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {LambdaClient, InvokeCommand, type InvokeCommandInput} from '@aws-sdk/client-lambda'; +import {S3Client, GetObjectCommand, type GetObjectCommandInput} from '@aws-sdk/client-s3'; +import {CloudWatchLogsClient, CreateLogStreamCommand, type CreateLogStreamCommandInput} from '@aws-sdk/client-cloudwatch-logs'; +import {DynamoDBClient} from '@aws-sdk/client-dynamodb'; +import {DynamoDBDocumentClient, GetCommand} from '@aws-sdk/lib-dynamodb'; +import type {Request, Response} from 'express'; + +interface IsolationTestParams { + FunctionName?: string; + Bucket?: string; + Key?: string; + Region?: string; + logGroupName?: string; + dalKey?: string; + dalProjectEnvironment?: string; +} + +interface TestResult { + [key: string]: boolean; +} + +const hasErrorName = (error: unknown, name: string): boolean => { + if (typeof error !== 'object' || error === null) return false; + const awsError = error as {name?: string; message?: string}; + return awsError.name === name || awsError.message === name; +}; + +export const isolationOriginLambdaTest = async (input: InvokeCommandInput): Promise => { + const client = new LambdaClient(); + try { + await client.send(new InvokeCommand(input)); + } catch (e) { + if (hasErrorName(e, 'AccessDeniedException')) return true; + console.error(e); + } + console.error('Lambda isolation test failed!'); + return false; +}; + +export const isolationS3Test = async (input: GetObjectCommandInput & {Region?: string}): Promise => { + const region = input.Region || process.env.AWS_REGION || 'us-east-1'; + const client = new S3Client({region}); + delete input.Region; + try { + await client.send(new GetObjectCommand(input)); + } catch (e) { + if (hasErrorName(e, 'AccessDenied')) return true; + console.error(e); + } + console.error('S3 isolation test failed!'); + return false; +}; + +export const isolationLogsTest = async (input: CreateLogStreamCommandInput): Promise => { + const client = new CloudWatchLogsClient(); + try { + const randomString = Math.random().toString(36).slice(2, 7); + await client.send(new CreateLogStreamCommand({...input, logStreamName: `new_log_stream_${randomString}`})); + } catch (e) { + if (hasErrorName(e, 'AccessDeniedException')) return true; + console.error(e); + } + console.error('Log group isolation test failed!'); + return false; +}; + +export const isolationDalTest = async (input: {dalKey?: string; dalProjectEnvironment?: string}): Promise => { + if (!process.env.AWS_REGION || !input.dalKey || !input.dalProjectEnvironment) { + console.error('DAL isolation test failed: missing required parameters'); + return false; + } + const tableName = `DataAccessLayer-${process.env.AWS_REGION}`; + const ddbClient = DynamoDBDocumentClient.from(new DynamoDBClient({region: process.env.AWS_REGION})); + try { + await ddbClient.send( + new GetCommand({ + TableName: tableName, + Key: {projectEnvironment: input.dalProjectEnvironment, key: input.dalKey}, + }), + ); + } catch (e) { + if (hasErrorName(e, 'AccessDeniedException')) return true; + console.error(e); + } + console.error("DAL isolation test failed: Target B accessed Target A's DAL entry"); + return false; +}; + +export const executeIsolationTests = async (params: IsolationTestParams): Promise => { + const tests = [ + {name: 'origin', keys: ['FunctionName'], fn: isolationOriginLambdaTest}, + {name: 'storage', keys: ['Bucket', 'Key', 'Region'], fn: isolationS3Test}, + {name: 'logs', keys: ['logGroupName'], fn: isolationLogsTest}, + {name: 'dal', keys: ['dalKey', 'dalProjectEnvironment'], fn: isolationDalTest}, + ]; + const results: TestResult = {}; + for (const test of tests) { + const {keys, fn, name} = test; + const input = Object.keys(params) + .filter((key) => keys.includes(key)) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .reduce((obj: Record, key) => { + obj[key] = params[key as keyof IsolationTestParams]; + return obj; + }, {}); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + results[name] = await fn(input as any); + } + return results; +}; + +export const isolationTests = async (req: Request, res: Response): Promise => { + const results = await executeIsolationTests(req.query); + res.header('Content-Type', 'application/json'); + res.send(JSON.stringify(results, null, 4)); +}; diff --git a/packages/mrt-reference-app/src/utils/reference-routes.test.ts b/packages/mrt-reference-app/src/utils/reference-routes.test.ts new file mode 100644 index 00000000..603b26c9 --- /dev/null +++ b/packages/mrt-reference-app/src/utils/reference-routes.test.ts @@ -0,0 +1,907 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {expect} from 'chai'; +import sinon from 'sinon'; +import request from 'supertest'; +import express, {type Express} from 'express'; +import fs from 'fs/promises'; +import {mockClient} from 'aws-sdk-client-mock'; +import {SecretsManagerClient, GetSecretValueCommand} from '@aws-sdk/client-secrets-manager'; +import {DataStore, DataStoreNotFoundError, DataStoreServiceError, DataStoreUnavailableError} from '@salesforce/mrt-utilities'; +import { + echo, + exception, + tlsVersionTest, + cacheTest, + cookieTest, + responseHeadersTest, + memoryTest, + headerTest, + loggingMiddleware, + streamingTest, + multiValueHeadersTest, + setStatusTest, + ssrBundleFileTest, + streamingResponseLarge, + winstonLogging, + delayedLogging, + massLogging, + largeLogging, + dataStoreTest, + secretsManagerTest, + proxyTransformationTest, +} from './reference-routes.js'; + +describe('reference-routes', () => { + let app: Express; + + beforeEach(() => { + app = express(); + sinon.stub(fs, 'readFile'); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe('echo', () => { + it('should return JSON with diagnostic values', async () => { + app.get('/test', echo); + const response = await request(app).get('/test').expect(200).expect('Content-Type', /json/); + expect(response.body).to.have.property('args'); + expect(response.body).to.have.property('protocol'); + expect(response.body).to.have.property('method'); + expect(response.body).to.have.property('path'); + expect(response.body).to.have.property('query'); + expect(response.body).to.have.property('headers'); + expect(response.body).to.have.property('ip'); + expect(response.body).to.have.property('env'); + expect(response.body).to.have.property('timestamp'); + expect(response.body).to.have.property('route_path'); + }); + + it('should include query parameters in response', async () => { + app.get('/test', echo); + const response = await request(app).get('/test?foo=bar&baz=qux'); + expect(response.body.query).to.deep.equal({foo: 'bar', baz: 'qux'}); + }); + + it('should include request headers in response', async () => { + app.get('/test', echo); + const response = await request(app).get('/test').set('Custom-Header', 'custom-value'); + expect(response.body.headers).to.have.property('custom-header'); + expect(response.body.headers['custom-header']).to.equal('custom-value'); + }); + }); + + describe('exception', () => { + it('should throw an IntentionalError', () => { + app.get('/test', exception); + return request(app).get('/test').expect(500); + }); + }); + + describe('tlsVersionTest', () => { + it('should make requests to TLS test domains', async () => { + app.get('/test', tlsVersionTest); + const response = await request(app).get('/test').expect(200).expect('Content-Type', /application\/json/); + expect(response.text).to.include('tls1.1'); + expect(response.text).to.include('tls1.2'); + }); + }); + + describe('cacheTest', () => { + it('should set default cache control', async () => { + app.get('/test', cacheTest); + const response = await request(app).get('/test').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=60'); + }); + + it('should set custom cache control duration', async () => { + app.get('/cache/:duration', cacheTest); + const response = await request(app).get('/cache/120').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=120'); + }); + + it('should fallback to default for invalid duration', async () => { + app.get('/cache/:duration', cacheTest); + const response = await request(app).get('/cache/invalid').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=60'); + }); + }); + + describe('cookieTest', () => { + it('should set a cookie when name and value are provided', async () => { + app.get('/test', cookieTest); + const response = await request(app).get('/test?name=test-cookie&value=test-value').expect(200); + expect(response.headers['set-cookie']).to.include('test-cookie=test-value; Path=/'); + expect(response.headers['cache-control']).to.equal('private, max-age=60'); + }); + + it('should not set cookie when name is not provided', async () => { + app.get('/test', cookieTest); + const response = await request(app).get('/test?value=test-value').expect(200); + expect(response.headers['set-cookie']).to.be.undefined; + }); + + it('should return JSON with diagnostic values', async () => { + app.get('/test', cookieTest); + const response = await request(app).get('/test'); + expect(response.body).to.have.property('timestamp'); + expect(response.body).to.have.property('headers'); + }); + }); + + describe('responseHeadersTest', () => { + it('should set response headers from query parameters', async () => { + app.get('/test', responseHeadersTest); + const response = await request(app).get('/test?header1=value1&header2=value2').expect(200); + expect(response.headers.header1).to.equal('value1'); + expect(response.headers.header2).to.equal('value2'); + }); + + it('should handle multi-value headers', async () => { + app.get('/test', responseHeadersTest); + const response = await request(app).get('/test?header3=value3&header3=value4').expect(200); + expect(response.headers.header3).to.not.be.undefined; + }); + }); + + describe('memoryTest', () => { + it('should allocate memory and return diagnostic info', async () => { + app.get('/test', memoryTest); + const response = await request(app).get('/test').expect(200); + expect(response.body.additional_info).to.have.property('memory_end'); + expect(response.body.additional_info).to.have.property('memory_delta'); + expect(response.body.additional_info).to.have.property('malloc_time'); + expect(response.body.additional_info).to.have.property('gc_time'); + }); + + it('should handle custom test parameters', async () => { + app.get('/test', memoryTest); + const response = await request(app).get('/test?count=5&size=2048').expect(200); + expect(response.body.additional_info.test_count).to.equal(5); + expect(response.body.additional_info.test_size).to.equal(2048); + }); + + it('should perform garbage collection when requested', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (global as any).gc = sinon.stub(); + app.get('/test', memoryTest); + const response = await request(app).get('/test?forcegc=true').expect(200); + expect(response.body.additional_info.force_gc).to.equal(true); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(((global as any).gc as sinon.SinonStub).called).to.equal(true); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (global as any).gc; + }); + }); + + describe('headerTest', () => { + it('should return sorted headers', async () => { + app.get('/test', headerTest); + const response = await request(app) + .get('/test') + .set('Random-Header', 'random') + .set('Another-Header', 'another') + .expect(200); + expect(response.body).to.have.property('headers'); + expect(response.body.headers).to.be.instanceOf(Object); + }); + + it('should convert headers to lowercase', async () => { + app.get('/test', headerTest); + const response = await request(app) + .get('/test') + .set('Mixed-Case-Header', 'value') + .set('UPPERCASE', 'value') + .expect(200); + const headerKeys = Object.keys(response.body.headers); + headerKeys.forEach((key) => { + expect(key).to.equal(key.toLowerCase()); + }); + }); + }); + + describe('loggingMiddleware', () => { + it('should log request information', async () => { + const consoleSpy = sinon.stub(console, 'log'); + app.use(loggingMiddleware); + app.get('/test', echo); + await request(app).get('/test?foo=bar').expect(200); + expect(consoleSpy.args.some((args) => String(args[0]).includes('Request:'))).to.equal(true); + expect(consoleSpy.args.some((args) => String(args[0]).includes('Request headers:'))).to.equal(true); + consoleSpy.restore(); + }); + + it('should log response information', async () => { + const consoleSpy = sinon.stub(console, 'log'); + app.use(loggingMiddleware); + app.get('/test', echo); + await request(app).get('/test').expect(200); + expect(consoleSpy.args.some((args) => String(args[0]).includes('Response status:'))).to.equal(true); + expect(consoleSpy.args.some((args) => String(args[0]).includes('Response headers:'))).to.equal(true); + consoleSpy.restore(); + }); + + it('should call next function', () => { + const nextFn = sinon.stub(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const mockReq = {method: 'GET', originalUrl: '/test'} as any; + const mockRes = { + headersSent: true, + statusCode: 200, + on: sinon.stub().callsFake((event: string, cb: () => void) => { + if (event === 'finish') cb(); + }), + getHeaders: sinon.stub().returns({}), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + loggingMiddleware(mockReq, mockRes, nextFn); + expect(nextFn.called).to.equal(true); + }); + + it('should handle responses without headers sent', () => { + const consoleSpy = sinon.stub(console, 'log'); + const nextFn = sinon.stub(); + const mockReq = {method: 'GET', originalUrl: '/test', headers: {}} as never; + const mockRes = { + headersSent: false, + statusCode: 200, + on: sinon.stub().callsFake((event: string, cb: () => void) => { + if (event === 'finish') cb(); + }), + getHeaders: sinon.stub().returns({}), + } as never; + loggingMiddleware(mockReq, mockRes, nextFn); + expect(nextFn.called).to.equal(true); + consoleSpy.restore(); + }); + }); + + describe('echo edge cases', () => { + it('should handle POST requests with body', async () => { + app.use(express.json()); + app.post('/test', echo); + const response = await request(app).post('/test').send({foo: 'bar'}).expect(200); + expect(response.body.body).to.deep.equal({foo: 'bar'}); + }); + + it('should handle requests with complex headers', async () => { + app.get('/test', echo); + const response = await request(app) + .get('/test') + .set('X-Forwarded-For', '192.168.1.1') + .set('User-Agent', 'Test-Agent') + .set('Accept-Language', 'en-US,en;q=0.9'); + expect(response.body.headers).to.have.property('x-forwarded-for'); + expect(response.body.headers).to.have.property('user-agent'); + expect(response.body.headers).to.have.property('accept-language'); + }); + + it('should handle IPv6 addresses', async () => { + app.get('/test', echo); + const response = await request(app).get('/test'); + expect(response.body.ip).to.not.be.undefined; + }); + }); + + describe('cacheTest edge cases', () => { + it('should handle very large cache durations', async () => { + app.get('/cache/:duration', cacheTest); + const response = await request(app).get('/cache/999999').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=999999'); + }); + + it('should handle zero duration', async () => { + app.get('/cache/:duration', cacheTest); + const response = await request(app).get('/cache/0').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=0'); + }); + + it('should handle negative duration', async () => { + app.get('/cache/:duration', cacheTest); + const response = await request(app).get('/cache/-1').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=-1'); + }); + + it('should handle float duration', async () => { + app.get('/cache/:duration', cacheTest); + const response = await request(app).get('/cache/60.5').expect(200); + expect(response.headers['cache-control']).to.equal('s-maxage=60.5'); + }); + }); + + describe('cookieTest edge cases', () => { + it('should handle cookie without value', async () => { + app.get('/test', cookieTest); + const response = await request(app).get('/test?name=test-cookie').expect(200); + expect(response.headers['set-cookie']).to.include('test-cookie=undefined; Path=/'); + }); + + it('should handle empty cookie value', async () => { + app.get('/test', cookieTest); + const response = await request(app).get('/test?name=test-cookie&value=').expect(200); + expect(response.headers['set-cookie']).to.satisfy( + (v: string | string[]) => (Array.isArray(v) ? v.join(',') : v).includes('test-cookie=; Path=/'), + ); + }); + + it('should handle special characters in cookie values', async () => { + app.get('/test', cookieTest); + const response = await request(app).get('/test?name=test-cookie&value=test%20value!').expect(200); + expect(response.headers['set-cookie']).to.not.be.undefined; + const cookieVal = Array.isArray(response.headers['set-cookie']) + ? response.headers['set-cookie'][0] + : response.headers['set-cookie']; + expect(cookieVal).to.include('test-cookie='); + }); + }); + + describe('responseHeadersTest edge cases', () => { + it('should handle single header value', async () => { + app.get('/test', responseHeadersTest); + const response = await request(app).get('/test?header1=value1').expect(200); + expect(response.headers.header1).to.equal('value1'); + }); + + it('should handle multiple values for the same header', async () => { + app.get('/test', responseHeadersTest); + const response = await request(app).get('/test?header1=value1&header1=value2').expect(200); + expect(response.headers.header1).to.not.be.undefined; + }); + }); + + describe('multiValueHeadersTest', () => { + it('should set multi-value header with array of values', async () => { + app.get('/test', multiValueHeadersTest); + const response = await request(app).get('/test').expect(200); + expect(response.headers['x-multi-value-header']).to.not.be.undefined; + const headerValue = response.headers['x-multi-value-header']; + expect(headerValue).to.include('value1'); + expect(headerValue).to.include('value2'); + expect(headerValue).to.include('value3'); + }); + + it('should return JSON response with request info', async () => { + app.get('/test', multiValueHeadersTest); + const response = await request(app).get('/test?param1=value1').expect(200); + expect(response.body).to.not.be.undefined; + expect(typeof response.body).to.equal('object'); + }); + }); + + describe('memoryTest edge cases', () => { + it('should handle zero test count', async () => { + app.get('/test', memoryTest); + const response = await request(app).get('/test?count=0').expect(200); + expect(response.body.additional_info.test_count).to.equal(0); + }); + + it('should handle very small test size', async () => { + app.get('/test', memoryTest); + const response = await request(app).get('/test?size=1').expect(200); + expect(response.body.additional_info.test_size).to.equal(1); + }); + + it('should handle very large test size', async () => { + app.get('/test', memoryTest); + const response = await request(app).get('/test?size=1048576').expect(200); + expect(response.body.additional_info.test_size).to.equal(1048576); + }); + + it('should handle fractional test count', async () => { + app.get('/test', memoryTest); + const response = await request(app).get('/test?count=5.5').expect(200); + expect(response.body.additional_info.test_count).to.equal(5); + }); + + it('should handle multiple gc-related parameters', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (global as any).gc = sinon.stub(); + app.get('/test', memoryTest); + const response = await request(app).get('/test?gc=true&forcegc=true').expect(200); + expect(response.body.additional_info.force_gc).to.equal(true); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (global as any).gc; + }); + }); + + describe('streamingTest', () => { + it('should set Content-Type header to text/plain', async () => { + app.get('/test', streamingTest); + const response = await request(app).get('/test').expect(200); + expect(response.headers['content-type']).to.equal('text/plain'); + }); + + it('should write multiple chunks to the response', async () => { + app.get('/test', streamingTest); + const response = await request(app).get('/test').expect(200); + expect(response.text).to.include('Here is a streaming chunk0'); + expect(response.text).to.include('Here is a streaming chunk50'); + expect(response.text).to.include('Here is a streaming chunk99'); + }); + + it('should write exactly 100 chunks', async () => { + app.get('/test', streamingTest); + const response = await request(app).get('/test').expect(200); + const chunkMatches = response.text.match(/Here is a streaming chunk\d+/g); + expect(chunkMatches).to.have.lengthOf(100); + }); + + it('should write chunks in sequential order', async () => { + app.get('/test', streamingTest); + const response = await request(app).get('/test').expect(200); + for (let i = 0; i < 100; i++) { + expect(response.text).to.include(`Here is a streaming chunk${i}`); + } + }); + + it('should end the response after all chunks', async () => { + app.get('/test', streamingTest); + const response = await request(app).get('/test').expect(200); + expect(response.text).to.be.ok; + }); + }); + + describe('setStatusTest', () => { + it('should return 200 by default', async () => { + app.get('/test', setStatusTest); + const response = await request(app).get('/test').expect(200); + expect(response.body).to.have.property('method'); + expect(response.headers['x-status-code']).to.equal('200'); + }); + + it('should return custom status from query parameter', async () => { + app.get('/test', setStatusTest); + const response = await request(app).get('/test?status=201').expect(201); + expect(response.headers['x-status-code']).to.equal('201'); + }); + + it('should return 404 when status=404', async () => { + app.get('/test', setStatusTest); + await request(app).get('/test?status=404').expect(404); + }); + + it('should return 500 when status=500', async () => { + app.get('/test', setStatusTest); + await request(app).get('/test?status=500').expect(500); + }); + }); + + describe('ssrBundleFileTest', () => { + it('should return JSON from file when read succeeds', async () => { + (fs.readFile as sinon.SinonStub).resolves('{"message":"test content"}'); + app.get('/test', ssrBundleFileTest); + const response = await request(app).get('/test').expect(200); + expect(response.body).to.deep.equal({message: 'test content'}); + }); + + it('should return 500 when file read fails', async () => { + (fs.readFile as sinon.SinonStub).rejects(new Error('ENOENT')); + app.get('/test', ssrBundleFileTest); + const response = await request(app).get('/test').expect(500); + expect(response.body).to.have.property('error'); + }); + }); + + describe('streamingResponseLarge', () => { + it('should return streaming: false when MRT_BUNDLE_TYPE is not stream', async () => { + const originalEnv = process.env.MRT_BUNDLE_TYPE; + delete process.env.MRT_BUNDLE_TYPE; + app.get('/test', streamingResponseLarge); + const response = await request(app).get('/test').expect(200); + expect(response.body).to.deep.equal({streaming: false}); + if (originalEnv !== undefined) process.env.MRT_BUNDLE_TYPE = originalEnv; + }); + + it('should set status and content-type and write first chunk when MRT_BUNDLE_TYPE is stream', () => { + const originalEnv = process.env.MRT_BUNDLE_TYPE; + process.env.MRT_BUNDLE_TYPE = 'stream'; + const write = sinon.stub(); + const end = sinon.stub(); + const mockRes = { + status: sinon.stub().returnsThis(), + setHeader: sinon.stub(), + write, + end, + flush: sinon.stub(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + streamingResponseLarge({} as never, mockRes); + expect((mockRes.status as sinon.SinonStub).calledWith(200)).to.equal(true); + expect((mockRes.setHeader as sinon.SinonStub).calledWith('Content-Type', 'text/plain')).to.equal(true); + expect(write.calledWith('This is the first piece of streamed data.\n')).to.equal(true); + if (originalEnv !== undefined) process.env.MRT_BUNDLE_TYPE = originalEnv; + else delete process.env.MRT_BUNDLE_TYPE; + }); + }); + + describe('winstonLogging', () => { + it('should run without throwing', () => { + const mockReq = {} as never; + const mockRes = {json: sinon.stub()} as never; + expect(() => winstonLogging(mockReq, mockRes)).to.not.throw(); + }); + + it('should log via console and return JSON response', () => { + const consoleSpy = sinon.stub(console, 'log'); + const mockReq = {} as never; + const mockRes = {json: sinon.stub()} as never; + winstonLogging(mockReq, mockRes); + expect(consoleSpy.called).to.equal(true); + expect((mockRes as {json: sinon.SinonStub}).json.calledWith({message: 'Winston logging completed'})).to.equal( + true, + ); + consoleSpy.restore(); + }); + }); + + describe('delayedLogging', () => { + it('should return JSON with duration after delay', async () => { + const clock = sinon.useFakeTimers(); + const mockReq = {query: {}} as never; + const mockRes = {json: sinon.stub()} as never; + const promise = delayedLogging(mockReq, mockRes); + await clock.tickAsync(10000); + await promise; + expect( + (mockRes as {json: sinon.SinonStub}).json.calledWith( + sinon.match({message: 'Delayed logging 10000ms'}), + ), + ).to.equal(true); + expect((mockRes as {json: sinon.SinonStub}).json.args[0][0]).to.have.property('duration'); + clock.restore(); + }); + }); + + describe('massLogging', () => { + it('should return 200 with duration and message using default count', async () => { + app.get('/test', massLogging); + const response = await request(app).get('/test').expect(200); + expect(response.body).to.have.property('duration'); + expect(response.body).to.have.property('message', 'Mass logging 10000 logs'); + }); + + it('should use custom count from query parameter', async () => { + app.get('/test', massLogging); + const response = await request(app).get('/test?count=100').expect(200); + expect(response.body).to.have.property('duration'); + expect(response.body).to.have.property('message', 'Mass logging 100 logs'); + }); + + it('should include size in response', async () => { + app.get('/test', massLogging); + const response = await request(app).get('/test?count=10').expect(200); + expect(response.body).to.have.property('size'); + expect(response.body.size).to.match(/^\d+(\.\d+)?KB$/); + }); + }); + + describe('largeLogging', () => { + it('should return 200 with duration, message and size', async () => { + app.get('/test', largeLogging); + const response = await request(app).get('/test?count=10').expect(200); + expect(response.body).to.have.property('duration'); + expect(response.body).to.have.property('message', 'Large logging'); + expect(response.body).to.have.property('size'); + expect(response.body.size).to.match(/^\d+(\.\d+)?KB$/); + }); + + it('should use custom count from query parameter', async () => { + app.get('/test', largeLogging); + const response = await request(app).get('/test?count=50').expect(200); + expect(response.body).to.have.property('duration'); + expect(response.body).to.have.property('message', 'Large logging'); + expect(response.body).to.have.property('size'); + }); + }); + + describe('dataStoreTest', () => { + let mockGetEntry: sinon.SinonStub; + let mockIsDataStoreAvailable: sinon.SinonStub; + let originalInstance: DataStore | null; + + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + originalInstance = (DataStore as any)._instance; + mockGetEntry = sinon.stub(); + mockIsDataStoreAvailable = sinon.stub().returns(true); + const mockDataStore = {getEntry: mockGetEntry, isDataStoreAvailable: mockIsDataStoreAvailable} as never; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (DataStore as any)._instance = mockDataStore; + }); + + afterEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (DataStore as any)._instance = originalInstance; + }); + + it('should return 200 with { dataStore: false } when the data store is unavailable', async () => { + mockIsDataStoreAvailable.returns(false); + app.get('/data-store/:key', dataStoreTest); + const response = await request(app).get('/data-store/my-key').expect(200); + expect(response.body).to.deep.equal({dataStore: false}); + }); + + it('should return 200 with the entry when getEntry returns data', async () => { + const entry = {key: 'my-key', value: {foo: 'bar'}}; + mockGetEntry.resolves(entry); + app.get('/data-store/:key', dataStoreTest); + const response = await request(app).get('/data-store/my-key').expect(200); + expect(response.body).to.deep.equal(entry); + }); + + it('should return 400 when getEntry throws DataStoreUnavailableError', async () => { + mockGetEntry.rejects(new DataStoreUnavailableError('The data store is unavailable.')); + app.get('/data-store/:key', dataStoreTest); + const response = await request(app).get('/data-store/my-key').expect(400); + expect(response.body).to.have.property('error', 'The data store is unavailable.'); + }); + + it('should return 404 when getEntry throws DataStoreNotFoundError', async () => { + mockGetEntry.rejects(new DataStoreNotFoundError("Data store entry 'my-key' not found.")); + app.get('/data-store/:key', dataStoreTest); + const response = await request(app).get('/data-store/my-key').expect(404); + expect(response.body).to.have.property('error', "Data store entry 'my-key' not found."); + }); + + it('should return 500 when getEntry throws DataStoreServiceError', async () => { + mockGetEntry.rejects(new DataStoreServiceError('Data store request failed.')); + app.get('/data-store/:key', dataStoreTest); + const response = await request(app).get('/data-store/my-key').expect(500); + expect(response.body).to.have.property('error', 'Data store request failed.'); + }); + }); + + describe('secretsManagerTest', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const secretsManagerMock: any = mockClient(SecretsManagerClient as any); + + beforeEach(() => { + secretsManagerMock.reset(); + }); + + it('should return 400 when secretId is missing', async () => { + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test').expect(400); + expect(response.body).to.deep.equal({error: 'Missing required query parameter: secretId'}); + }); + + it('should return 200 with secret metadata on success', async () => { + secretsManagerMock.on(GetSecretValueCommand).resolves({ + ARN: 'arn:aws:secretsmanager:us-west-2:123456789:secret:test-secret', + VersionId: 'version-123', + SecretString: 'secret-value', + }); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=test-secret').expect(200); + expect(response.body).to.deep.equal({ + success: true, + secretId: 'test-secret', + arn: 'arn:aws:secretsmanager:us-west-2:123456789:secret:test-secret', + versionId: 'version-123', + hasSecretString: true, + hasSecretBinary: false, + }); + }); + + it('should return 200 with binary secret metadata', async () => { + secretsManagerMock.on(GetSecretValueCommand).resolves({ + ARN: 'arn:aws:secretsmanager:us-west-2:123456789:secret:binary-secret', + VersionId: 'version-456', + SecretBinary: Buffer.from('binary-data'), + }); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=binary-secret').expect(200); + expect(response.body).to.deep.equal({ + success: true, + secretId: 'binary-secret', + arn: 'arn:aws:secretsmanager:us-west-2:123456789:secret:binary-secret', + versionId: 'version-456', + hasSecretString: false, + hasSecretBinary: true, + }); + }); + + it('should return 404 when secret is not found', async () => { + secretsManagerMock.on(GetSecretValueCommand).rejects({name: 'ResourceNotFoundException', message: 'Secret not found'}); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=nonexistent').expect(404); + expect(response.body).to.deep.equal({ + success: false, + error: 'Secret not found', + errorCode: 'ResourceNotFoundException', + secretId: 'nonexistent', + }); + }); + + it('should return 403 when access is denied', async () => { + secretsManagerMock.on(GetSecretValueCommand).rejects({name: 'AccessDeniedException', message: 'User is not authorized to perform action'}); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=forbidden-secret').expect(403); + expect(response.body).to.deep.equal({ + success: false, + error: 'User is not authorized to perform action', + errorCode: 'AccessDeniedException', + secretId: 'forbidden-secret', + }); + }); + + it('should return 400 for invalid request', async () => { + secretsManagerMock.on(GetSecretValueCommand).rejects({name: 'InvalidRequestException', message: 'Invalid request'}); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=invalid').expect(400); + expect(response.body).to.deep.equal({ + success: false, + error: 'Invalid request', + errorCode: 'InvalidRequestException', + secretId: 'invalid', + }); + }); + + it('should return 500 for decryption failure', async () => { + secretsManagerMock.on(GetSecretValueCommand).rejects({name: 'DecryptionFailure', message: 'Failed to decrypt secret'}); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=encrypted-secret').expect(500); + expect(response.body).to.deep.equal({ + success: false, + error: 'Failed to decrypt secret', + errorCode: 'DecryptionFailure', + secretId: 'encrypted-secret', + }); + }); + + it('should return 500 for unknown errors', async () => { + secretsManagerMock.on(GetSecretValueCommand).rejects({name: 'UnknownError', message: 'Something went wrong'}); + app.get('/test', secretsManagerTest); + const response = await request(app).get('/test?secretId=test-secret').expect(500); + expect(response.body).to.deep.equal({ + success: false, + error: 'Something went wrong', + errorCode: 'UnknownError', + secretId: 'test-secret', + }); + }); + }); + + describe('proxyTransformationTest', () => { + it('should return success when all validations pass', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer token123') + .set('sfdc_dwsid', 'session456') + .set('cookie', 'cc-at_test-site=token123; dwsid=session456') + .expect(200); + expect(response.body).to.deep.equal({ + success: true, + validations: { + siteIdPresent: true, + siteId: 'test-site', + authHeaderPresent: true, + authHeader: 'Bearer token123', + expectedCookieName: 'cc-at_test-site', + expectedCookieValue: 'token123', + authHeaderMatchesCookie: true, + sfdc_dwsidPresent: true, + sfdc_dwsidValue: 'session456', + dwsidPresent: true, + dwsidValue: 'session456', + sfdc_dwsidMatchesDwsid: true, + }, + allCookies: {'cc-at_test-site': 'token123', dwsid: 'session456'}, + headers: { + 'x-site-id': 'test-site', + authorization: 'Bearer token123', + cookie: 'cc-at_test-site=token123; dwsid=session456', + sfdc_dwsid: 'session456', + }, + }); + }); + + it('should return failure when site ID is missing', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('authorization', 'Bearer token123') + .set('sfdc_dwsid', 'session456') + .set('cookie', 'dwsid=session456') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.validations.siteIdPresent).to.equal(false); + }); + + it('should return failure when authorization header is missing', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('sfdc_dwsid', 'session456') + .set('cookie', 'cc-at_test-site=token123; dwsid=session456') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.validations.authHeaderPresent).to.equal(false); + }); + + it('should return failure when authorization header does not match cookie', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer wrong-token') + .set('sfdc_dwsid', 'session456') + .set('cookie', 'cc-at_test-site=token123; dwsid=session456') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.validations.authHeaderMatchesCookie).to.equal(false); + }); + + it('should return failure when sfdc_dwsid header is missing', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer token123') + .set('cookie', 'cc-at_test-site=token123; dwsid=session456') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.validations.sfdc_dwsidPresent).to.equal(false); + }); + + it('should return failure when dwsid cookie is missing', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer token123') + .set('sfdc_dwsid', 'session456') + .set('cookie', 'cc-at_test-site=token123') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.validations.dwsidPresent).to.equal(false); + }); + + it('should return failure when sfdc_dwsid and dwsid do not match', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer token123') + .set('sfdc_dwsid', 'different-session') + .set('cookie', 'cc-at_test-site=token123; dwsid=session456') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.validations.sfdc_dwsidMatchesDwsid).to.equal(false); + }); + + it('should handle requests with no cookies', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer token123') + .set('sfdc_dwsid', 'session456') + .expect(200); + expect(response.body.success).to.equal(false); + expect(response.body.allCookies).to.deep.equal({}); + }); + + it('should parse cookies with spaces correctly', async () => { + app.get('/test', proxyTransformationTest); + const response = await request(app) + .get('/test') + .set('x-site-id', 'test-site') + .set('authorization', 'Bearer token123') + .set('sfdc_dwsid', 'session456') + .set('cookie', 'cc-at_test-site=token123; dwsid=session456') + .expect(200); + expect(response.body.success).to.equal(true); + expect(response.body.allCookies).to.deep.equal({'cc-at_test-site': 'token123', dwsid: 'session456'}); + }); + }); +}); diff --git a/packages/mrt-reference-app/src/utils/reference-routes.ts b/packages/mrt-reference-app/src/utils/reference-routes.ts new file mode 100644 index 00000000..43ccdc65 --- /dev/null +++ b/packages/mrt-reference-app/src/utils/reference-routes.ts @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {Request, Response, NextFunction} from 'express'; +import fs from 'fs/promises'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import winston from 'winston'; +import crypto from 'crypto'; +import {SecretsManagerClient, GetSecretValueCommand} from '@aws-sdk/client-secrets-manager'; +import {DataStore, DataStoreNotFoundError, DataStoreServiceError, DataStoreUnavailableError} from '@salesforce/mrt-utilities'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +class IntentionalError extends Error { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(diagnostics: any, ...params: any[]) { + super(...params); + this.message = JSON.stringify(diagnostics, null, 2); + this.name = 'IntentionalError'; + } +} + +const ENVS_TO_EXPOSE = [ + 'aws_execution_env', + 'aws_lambda_function_memory_size', + 'aws_lambda_function_name', + 'aws_lambda_function_version', + 'aws_lambda_log_group_name', + 'aws_lambda_log_stream_name', + 'aws_region', + 'bundle_id', + 'customer_*', + 'deploy_id', + 'deploy_target', + 'external_domain_name', + 'mobify_property_id', + 'mrt_allow_cookies', + 'mrt_env_base_path', + 'node_env', + 'node_options', + 'tz', +]; + +const BADSSL_TLS1_1_URL = 'https://tls-v1-1.badssl.com:1011/'; +const BADSSL_TLS1_2_URL = 'https://tls-v1-2.badssl.com:1012/'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const sortObjectKeys = (o: Record): Record => { + return Object.assign( + {}, + ...Object.keys(o) + .sort() + .map((k) => ({[k]: o[k]})), + ); +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const filterAndSortObjectKeys = (o: Record, whitelist: string[]): Record => + o && + Object.keys(o) + .filter((key) => { + const keylc = key.toLowerCase().trim(); + return whitelist.some( + (pattern) => (pattern.endsWith('*') && keylc.startsWith(pattern.slice(0, -1))) || pattern === keylc, + ); + }) + .sort() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .reduce((acc: Record, key) => { + acc[key] = o[key]; + return acc; + }, {}); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const jsonFromRequest = (req: Request, additional_info?: any): any => { + return { + args: req.query, + protocol: req.protocol, + method: req.method, + path: req.path, + query: req.query, + route_path: req.route.path, + body: req.body, + headers: sortObjectKeys(req.headers), + ip: req.ip, + env: filterAndSortObjectKeys(process.env, ENVS_TO_EXPOSE), + timestamp: new Date().toISOString(), + ...(typeof additional_info === 'object' ? {additional_info} : {}), + }; +}; + +export const echo = (req: Request, res: Response) => res.json(jsonFromRequest(req)); + +export const exception = (req: Request) => { + throw new IntentionalError(jsonFromRequest(req)); +}; + +export const tlsVersionTest = async (_: Request, res: Response) => { + const response11 = await fetch(BADSSL_TLS1_1_URL) + .then((response) => response.ok) + .catch(() => false); + const response12 = await fetch(BADSSL_TLS1_2_URL) + .then((response) => response.ok) + .catch(() => false); + res.header('Content-Type', 'application/json'); + res.send(JSON.stringify({'tls1.1': response11, 'tls1.2': response12}, null, 4)); +}; + +export const cacheTest = async (req: Request, res: Response) => { + let duration = String(req.params.duration || '60'); + if (isNaN(parseInt(duration))) { + duration = '60'; + } + res.set('Cache-Control', `s-maxage=${duration}`); + res.json(jsonFromRequest(req)); +}; + +export const cookieTest = async (req: Request, res: Response) => { + if (Object.hasOwn(req.query, 'name')) { + const name = req.query.name as string; + const value = req.query.value as string; + res.cookie(name, value); + } + res.set('Cache-Control', 'private, max-age=60'); + res.json(jsonFromRequest(req)); +}; + +export const responseHeadersTest = async (req: Request, res: Response) => { + for (const [key, value] of Object.entries(req.query)) { + res.set(key, value as string | string[] | undefined); + } + res.json(jsonFromRequest(req)); +}; + +export const multiValueHeadersTest = async (req: Request, res: Response) => { + res.set('X-Multi-Value-Header', ['value1', 'value2', 'value3']); + res.json(jsonFromRequest(req)); +}; + +export const setStatusTest = async (req: Request, res: Response) => { + const status = req.query.status ? parseInt(req.query.status as string) : 200; + res.status(status); + res.set('X-Status-Code', status.toString()); + res.json(jsonFromRequest(req)); +}; + +export const memoryTest = async (req: Request, res: Response) => { + const memory_before = process.memoryUsage(); + const test_count = req?.query?.count ? parseInt(req.query.count as string) : 1; + const test_size = req?.query?.size ? parseInt(req.query.size as string) : 1024; + const force_gc = + global?.gc && + req?.query && + (parseBoolean(req.query.forcegc as string) || parseBoolean(req.query.gc as string)); + + const malloc_time_start = Date.now(); + allocateMemory(test_count, test_size); + const malloc_time_ms = Date.now() - malloc_time_start; + + const gc_time_start = Date.now(); + if (force_gc && global.gc) { + global.gc(); + } + const gc_time_ms = Date.now() - gc_time_start; + + const memory_after = process.memoryUsage(); + const memory_delta = calculateNumericDeltas(memory_after, memory_before); + const factor = 10; + const test_total_alloc_mb = Math.round(((test_count * test_size) / 1024 / 1024) * factor) / factor; + const additional_info = { + memory_end: memory_after, + memory_delta, + malloc_time: malloc_time_ms, + gc_time: gc_time_ms, + force_gc, + test_count, + test_size, + test_total_alloc_mb, + }; + res.json(jsonFromRequest(req, additional_info)); +}; + +const allocateMemory = (test_count: number, test_size: number): number => { + const buffer: Buffer[] = []; + for (let index = 0; index < test_count; index++) { + buffer.push(Buffer.alloc(test_size, 0, 'ascii')); + } + return buffer.length; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const calculateNumericDeltas = (obj1: Record, obj2: Record): Record => { + const delta: Record = {}; + for (const key in obj1) { + if (Object.prototype.hasOwnProperty.call(obj1, key) && Object.prototype.hasOwnProperty.call(obj2, key)) { + if (typeof obj1[key] === 'number' && typeof obj2[key] === 'number') { + delta[key] = obj1[key] - obj2[key]; + } + } + } + return delta; +}; + +const parseBoolean = (string_value: string) => { + if ( + string_value === null || + string_value === undefined || + typeof string_value !== 'string' || + string_value.trim() === '' + ) { + return false; + } + const lowerCaseValue = string_value.toLowerCase(); + return ( + lowerCaseValue === 'true' || + lowerCaseValue === '1' || + lowerCaseValue === 'on' || + lowerCaseValue === 'enable' || + lowerCaseValue === 'enabled' || + lowerCaseValue === 'yes' + ); +}; + +export const headerTest = async (req: Request, res: Response) => { + res.json({headers: sortObjectKeys(req.headers)}); +}; + +export const loggingMiddleware = (req: Request, res: Response, next: NextFunction) => { + console.log(`Request: ${req.method} ${req.originalUrl}`); + console.log(`Request headers: ${JSON.stringify(req.headers, null, 2)}`); + const values = { + string: 'string value', + number: 1234567890, + boolean: true, + null: null, + undefined, + NaN, + object: {foo: 'bar'}, + array: [1, 2, 3], + date: new Date(), + }; + for (const value of Object.values(values)) { + console.log(value); + } + res.on('finish', () => { + const statusCode = res.headersSent ? String(res.statusCode) : String(-1); + console.log(`Response status: ${statusCode}`); + if (res.headersSent) { + const headers = JSON.stringify(res.getHeaders(), null, 2); + console.log(`Response headers: ${headers}`); + } + }); + return next(); +}; + +export const envBasePathMiddleware = (req: Request, res: Response, next: NextFunction) => { + const basePath = process.env.MRT_ENV_BASE_PATH; + console.debug(`Base path: Base path: ${basePath}`); + console.debug(`Request path: Request path: ${req.url}`); + if (basePath && (req.path.startsWith(`${basePath}/`) || req.path === basePath)) { + req.url = req.url.slice(basePath.length) || '/'; + console.debug(`Base path: Rewrote ${basePath} -> Original url: ${req.originalUrl} -> New url: ${req.url}`); + } + return next(); +}; + +export const streamingTest = async (req: Request, res: Response) => { + res.setHeader('Content-Type', 'text/plain'); + const delayMs = 20; + for (let i = 0; i < 100; i++) { + res.write(`Here is a streaming chunk${i}\n`); + if (i < 99) { + await new Promise((resolve) => setTimeout(resolve, delayMs)); + } + } + res.end(); +}; + +export const ssrBundleFileTest = async (req: Request, res: Response) => { + try { + const fileName = `${__dirname}/static/example.json`; + const data = await fs.readFile(fileName, {encoding: 'utf8'}); + const jsonData = JSON.parse(data); + res.json(jsonData); + } catch (error) { + res.status(500).json({error}); + } +}; + +const generateRandomText = (numChars: number): string => { + const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + let result = ''; + for (let i = 0; i < numChars; i++) { + result += alphabet[Math.floor(Math.random() * alphabet.length)]; + } + return result; +}; + +export const streamingResponseLarge = (req: Request, res: Response) => { + if (process.env.MRT_BUNDLE_TYPE !== 'stream') { + res.status(200); + res.json({streaming: false}); + return; + } + res.status(200); + res.setHeader('Content-Type', 'text/plain'); + res.write('This is the first piece of streamed data.\n'); + const startTime = Date.now(); + for (let i = 0; i < 20; i++) { + setTimeout(() => { + const item = `${Date.now() - startTime}ms This is the ${i + 1} piece of streamed data after a delay ${generateRandomText(1024)}.\n`; + res.write(item); + // @ts-expect-error - flush added by streaming adapter + res.flush(); + if (i === 19) { + res.end(); + } + }, i * 500); + } +}; + +export const winstonLogging = (req: Request, res: Response) => { + const logger = winston.createLogger({ + level: 'info', + format: winston.format.json(), + defaultMeta: {service: 'user-service'}, + }); + logger.add(new winston.transports.Console({format: winston.format.json()})); + logger.log('info', 'json log using winston that triggers KeyError'); + logger.log({ + metadata: 'defined by cust', + level: 'info', + log: 'json log using winston', + telemetryEvent: 'telemetryEvent field defined by customer', + } as unknown as winston.LogEntry); + console.log({'winston-test': 'this is a json log using console.log'}); + console.warn({'winston-test': 'this is a json log using console.warn'}); + console.debug({'winston-test': 'this is a json log using console.debug'}); + console.info({'winston-test': 'this is a json log using console.info'}); + console.error({'winston-test': 'this is a json log using console.error'}); + res.json({message: 'Winston logging completed'}); +}; + +export const delayedLogging = async (req: Request, res: Response) => { + const delay = req.query.delay ? parseInt(req.query.delay as string) : 10000; + const startTime = Date.now(); + console.log({duration: Date.now() - startTime, message: `Pre-delayed logging ${delay}ms`}); + await new Promise((resolve) => setTimeout(resolve, delay)); + console.log({duration: Date.now() - startTime, message: `Delayed logging ${delay}ms`}); + res.json({duration: Date.now() - startTime, message: `Delayed logging ${delay}ms`}); +}; + +export const massLogging = (req: Request, res: Response) => { + const count = req.query.count ? parseInt(req.query.count as string) : 10000; + const textEncoder = new TextEncoder(); + const startTime = Date.now(); + let size = 0; + for (let i = 0; i < count; i++) { + const message = `Mass logging ${i} of ${count}`; + size += textEncoder.encode(message).length; + console.log(message); + } + const sizeInKB = (size / 1024).toFixed(2); + res.json({duration: Date.now() - startTime, message: `Mass logging ${count} logs`, size: `${sizeInKB}KB`}); +}; + +export const largeLogging = (req: Request, res: Response) => { + const count = req.query.count ? parseInt(req.query.count as string) : 10000; + const textEncoder = new TextEncoder(); + const startTime = Date.now(); + let size = 0; + let message = ''; + for (let i = 0; i < count; i++) { + const msg = `Mass logging ${i} of ${count}`; + size += textEncoder.encode(msg).length; + message += msg; + } + console.log(message); + const sizeInKB = (size / 1024).toFixed(2); + res.json({duration: Date.now() - startTime, message: 'Large logging', size: `${sizeInKB}KB`}); +}; + +export const traceLogging = (req: Request, res: Response) => { + const start = [new Date().getTime(), Number(process.hrtime.bigint())]; + const trace = { + name: 'mrt.ref.trace', + traceId: crypto.randomBytes(16).toString('hex'), + id: crypto.randomBytes(8).toString('hex'), + forwardTrace: true, + start_time: start, + end_time: [new Date().getTime(), Number(process.hrtime.bigint())], + }; + console.log(JSON.stringify(trace)); + res.json(trace); +}; + +export const dataStoreTest = async (req: Request, res: Response) => { + const store = DataStore.getDataStore(); + if (!store.isDataStoreAvailable()) { + return res.json({dataStore: false}); + } + let result; + try { + result = await store.getEntry(String(req.params.key)); + } catch (err) { + if (err instanceof DataStoreUnavailableError) { + return res.status(400).json({error: err.message}); + } else if (err instanceof DataStoreNotFoundError) { + return res.status(404).json({error: err.message}); + } else if (err instanceof DataStoreServiceError) { + return res.status(500).json({error: err.message}); + } + throw err; + } + return res.json(result); +}; + +export const secretsManagerTest = async (req: Request, res: Response) => { + const secretId = req.query.secretId as string; + if (!secretId) { + return res.status(400).json({error: 'Missing required query parameter: secretId'}); + } + const client = new SecretsManagerClient({region: process.env.AWS_REGION}); + const command = new GetSecretValueCommand({SecretId: secretId}); + try { + const response = await client.send(command); + return res.status(200).json({ + success: true, + secretId, + arn: response.ARN, + versionId: response.VersionId, + hasSecretString: !!response.SecretString, + hasSecretBinary: !!response.SecretBinary, + }); + } catch (error: unknown) { + const err = error as {name?: string; code?: string; message?: string}; + const errorCode = err.name || err.code; + let statusCode = 500; + const errorMessage = err.message || 'Unknown error'; + switch (errorCode) { + case 'ResourceNotFoundException': + statusCode = 404; + break; + case 'InvalidRequestException': + case 'InvalidParameterException': + statusCode = 400; + break; + case 'DecryptionFailure': + case 'InternalServiceError': + statusCode = 500; + break; + case 'AccessDeniedException': + statusCode = 403; + break; + default: + statusCode = 500; + } + return res.status(statusCode).json({success: false, error: errorMessage, errorCode, secretId}); + } +}; + +export const proxyTransformationTest = (req: Request, res: Response) => { + const siteId = req.headers['x-site-id'] as string; + const authHeader = req.headers.authorization as string; + const cookies = req.headers.cookie as string; + + const cookieMap: Record = {}; + if (cookies) { + cookies.split(';').forEach((cookie) => { + const [key, value] = cookie.trim().split('='); + if (key && value) { + cookieMap[key] = value; + } + }); + } + + const expectedCookieName = siteId ? `cc-at_${siteId}` : null; + const expectedCookieValue = expectedCookieName ? cookieMap[expectedCookieName] : null; + const sfdcDwsidFromHeader = req.headers.sfdc_dwsid as string; + + const validations = { + siteIdPresent: !!siteId, + siteId, + authHeaderPresent: !!authHeader, + authHeader, + expectedCookieName, + expectedCookieValue, + authHeaderMatchesCookie: false, + sfdc_dwsidPresent: !!sfdcDwsidFromHeader, + sfdc_dwsidValue: sfdcDwsidFromHeader, + dwsidPresent: !!cookieMap.dwsid, + dwsidValue: cookieMap.dwsid, + sfdc_dwsidMatchesDwsid: sfdcDwsidFromHeader === cookieMap.dwsid, + }; + + if (authHeader && expectedCookieValue) { + const expectedAuthHeader = `Bearer ${expectedCookieValue}`; + validations.authHeaderMatchesCookie = authHeader === expectedAuthHeader; + } + + const allValidationsPassed = + validations.siteIdPresent && + validations.authHeaderPresent && + validations.authHeaderMatchesCookie && + validations.sfdc_dwsidPresent && + validations.sfdc_dwsidMatchesDwsid; + + return res.json({ + success: allValidationsPassed, + validations, + allCookies: cookieMap, + headers: { + 'x-site-id': req.headers['x-site-id'], + authorization: req.headers.authorization, + cookie: req.headers.cookie, + sfdc_dwsid: req.headers.sfdc_dwsid, + }, + }); +}; diff --git a/packages/mrt-reference-app/test/tsconfig.json b/packages/mrt-reference-app/test/tsconfig.json new file mode 100644 index 00000000..f548933f --- /dev/null +++ b/packages/mrt-reference-app/test/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "types": ["node", "mocha", "chai"], + "paths": { + "@salesforce/mrt-utilities": ["../../mrt-utilities/src/index.ts"], + "@salesforce/mrt-utilities/streaming": ["../../mrt-utilities/src/streaming/index.ts"], + "@salesforce/mrt-utilities/data-store": ["../../mrt-utilities/src/data-store/index.ts"] + } + }, + "include": ["./**/*", "../src/**/*"] +} diff --git a/packages/mrt-reference-app/tsconfig.json b/packages/mrt-reference-app/tsconfig.json new file mode 100644 index 00000000..7240ee46 --- /dev/null +++ b/packages/mrt-reference-app/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@salesforce/dev-config/tsconfig-strict-esm", + "compilerOptions": { + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "verbatimModuleSyntax": false, + "moduleResolution": "bundler", + "allowImportingTsExtensions": false, + "resolveJsonModule": true, + "allowJs": true, + "noEmit": true + }, + "include": ["src/**/*"] +} diff --git a/packages/mrt-welcome-app/.c8rc.json b/packages/mrt-welcome-app/.c8rc.json new file mode 100644 index 00000000..a6caf355 --- /dev/null +++ b/packages/mrt-welcome-app/.c8rc.json @@ -0,0 +1,11 @@ +{ + "all": true, + "src": ["src"], + "exclude": [ + "src/**/*.test.ts", + "src/dev/**", + "**/*.d.ts" + ], + "reporter": ["text", "text-summary", "html", "lcov"], + "report-dir": "coverage" +} diff --git a/packages/mrt-welcome-app/.gitignore b/packages/mrt-welcome-app/.gitignore new file mode 100644 index 00000000..6cea704c --- /dev/null +++ b/packages/mrt-welcome-app/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +coverage/ +*.tsbuildinfo +build/ \ No newline at end of file diff --git a/packages/mrt-welcome-app/.mocharc.json b/packages/mrt-welcome-app/.mocharc.json new file mode 100644 index 00000000..39a98944 --- /dev/null +++ b/packages/mrt-welcome-app/.mocharc.json @@ -0,0 +1,6 @@ +{ + "node-option": ["import=tsx", "conditions=development"], + "timeout": 30000, + "recursive": true, + "extension": ["ts"] +} diff --git a/packages/mrt-welcome-app/config.server.ts b/packages/mrt-welcome-app/config.server.ts new file mode 100644 index 00000000..cae3717a --- /dev/null +++ b/packages/mrt-welcome-app/config.server.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {MrtServerConfig} from '@salesforce/b2c-tooling-sdk/operations/mrt'; + +export const config: MrtServerConfig = { + ssrOnly: [ + 'ssr.js', + 'loader.js', + 'package.json', + 'views/layout.html', + ], + ssrShared: [], + ssrParameters: { + ssrFunctionNodeVersion: '24.x', + }, +}; diff --git a/packages/mrt-welcome-app/eslint.config.mjs b/packages/mrt-welcome-app/eslint.config.mjs new file mode 100644 index 00000000..29152f7c --- /dev/null +++ b/packages/mrt-welcome-app/eslint.config.mjs @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {includeIgnoreFile} from '@eslint/compat'; +import headerPlugin from 'eslint-plugin-header'; +import tseslint from 'typescript-eslint'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; + +import {copyrightHeader, sharedRules, chaiTestRules, prettierPlugin} from '../../eslint.config.mjs'; + +const gitignorePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '.gitignore'); +headerPlugin.rules.header.meta.schema = false; + +export default [ + {ignores: ['**/node_modules/**', 'build/**', 'coverage/**']}, + includeIgnoreFile(gitignorePath), + ...tseslint.configs.recommended, + prettierPlugin, + { + files: ['**/*.ts'], + plugins: { + header: headerPlugin, + }, + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + 'header/header': ['error', 'block', copyrightHeader], + ...sharedRules, + }, + }, + { + files: ['src/**/*.test.ts'], + rules: { + ...chaiTestRules, + '@typescript-eslint/no-explicit-any': 'off', + }, + }, +]; diff --git a/packages/mrt-welcome-app/package.json b/packages/mrt-welcome-app/package.json new file mode 100644 index 00000000..6ca9fd1f --- /dev/null +++ b/packages/mrt-welcome-app/package.json @@ -0,0 +1,70 @@ +{ + "name": "@salesforce/mrt-welcome-app", + "version": "0.0.1", + "description": "Welcome application deployed to new MRT environments", + "type": "module", + "author": "Salesforce", + "license": "Apache-2.0", + "repository": "SalesforceCommerceCloud/b2c-developer-tooling", + "private": true, + "scripts": { + "dev:tsx": "tsx ./src/dev/tsx-local.ts", + "dev:node": "pnpm build && node ./src/dev/node-local.js", + "build": "node scripts/build.mjs", + "clean": "shx rm -rf build", + "lint": "eslint", + "lint:agent": "eslint --quiet", + "typecheck:agent": "tsc --noEmit --pretty false", + "format": "prettier --write .", + "format:check": "prettier --check .", + "pretest": "tsc --noEmit -p test", + "test": "c8 mocha --forbid-only \"src/**/*.test.ts\"", + "test:ci": "c8 mocha --forbid-only --reporter json --reporter-option output=test-results.json \"src/**/*.test.ts\"", + "test:agent": "mocha --forbid-only --reporter min \"src/**/*.test.ts\"", + "test:watch": "mocha --watch \"src/**/*.test.ts\"", + "coverage": "c8 report", + "push": "pnpm build && b2c mrt bundle deploy", + "deploy": "pnpm build && b2c mrt bundle deploy", + "tail-logs": "b2c mrt tail-logs", + "save-bundle": "pnpm build && b2c mrt bundle save", + "save-credentials": "b2c mrt save-credentials" + }, + "dependencies": { + "@h4ad/serverless-adapter": "4.4.0", + "@salesforce/mrt-utilities": "workspace:*", + "ejs": "3.1.10", + "express": "5.1.0" + }, + "devDependencies": { + "@salesforce/b2c-cli": "workspace:*", + "@salesforce/b2c-tooling-sdk": "workspace:*", + "@salesforce/dev-config": "catalog:", + "@serverless/event-mocks": "1.1.1", + "@types/aws-lambda": "8.10.160", + "@types/chai": "catalog:", + "@types/ejs": "3.1.5", + "@types/express": "5.0.4", + "@types/mocha": "catalog:", + "@types/node": "catalog:", + "@types/sinon": "catalog:", + "@types/supertest": "6.0.3", + "c8": "catalog:", + "chai": "catalog:", + "esbuild": "0.25.0", + "eslint": "catalog:", + "eslint-config-prettier": "catalog:", + "eslint-plugin-header": "catalog:", + "eslint-plugin-prettier": "catalog:", + "mocha": "catalog:", + "prettier": "catalog:", + "shx": "catalog:", + "sinon": "catalog:", + "supertest": "7.1.4", + "tsx": "catalog:", + "typescript": "catalog:", + "typescript-eslint": "catalog:" + }, + "engines": { + "node": ">=22.16.0" + } +} diff --git a/packages/mrt-welcome-app/scripts/build.mjs b/packages/mrt-welcome-app/scripts/build.mjs new file mode 100644 index 00000000..a2045490 --- /dev/null +++ b/packages/mrt-welcome-app/scripts/build.mjs @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import esbuild from 'esbuild'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const pkgRoot = path.resolve(__dirname, '..'); +const buildDir = path.resolve(pkgRoot, 'build'); + +const enableSourceMaps = !process.env.DISABLE_SOURCE_MAPS; +const format = process.env.MRT_EXPORT_TYPE === 'esm' ? 'esm' : 'cjs'; + +const noExternal = [/^@h4ad\/.*/, 'express', '@salesforce/mrt-utilities', 'ejs']; + +const externalFilter = (id) => { + for (const pattern of noExternal) { + if (typeof pattern === 'string' && id === pattern) return false; + if (pattern instanceof RegExp && pattern.test(id)) return false; + } + return true; +}; + +const bundlePlugin = { + name: 'bundle-deps', + setup(build) { + build.onResolve({filter: /^[^./]/}, (args) => { + // Skip Windows absolute paths (e.g. D:\...) which match the filter + if (/^[A-Za-z]:/.test(args.path)) return null; + if (externalFilter(args.path)) { + return {external: true}; + } + return null; + }); + }, +}; + +async function copyDir(src, dest) { + await fs.mkdir(dest, {recursive: true}); + const entries = await fs.readdir(src, {withFileTypes: true}); + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + if (entry.isDirectory()) { + await copyDir(srcPath, destPath); + } else { + await fs.copyFile(srcPath, destPath); + } + } +} + +function formatSize(bytes) { + if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(2)} MB`; + if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} kB`; + return `${bytes} B`; +} + +async function build() { + await fs.rm(buildDir, {recursive: true, force: true}); + await fs.mkdir(buildDir, {recursive: true}); + + await esbuild.build({ + bundle: true, + platform: 'node', + target: 'node22', + format, + minify: true, + sourcemap: enableSourceMaps ? 'inline' : false, + outdir: buildDir, + define: { + 'process.env.NODE_ENV': '"production"', + }, + plugins: [bundlePlugin], + splitting: false, + entryPoints: {ssr: path.join(pkgRoot, 'src', 'ssr.ts')}, + }); + + const ssrStat = await fs.stat(path.join(buildDir, 'ssr.js')); + console.log(` build/ssr.js ${formatSize(ssrStat.size)}`); + + // Copy views + const viewsSrc = path.join(pkgRoot, 'src', 'views'); + const viewsDest = path.join(buildDir, 'views'); + await copyDir(viewsSrc, viewsDest); + + // Build config.server.ts so the CLI can load ssrOnly/ssrShared/ssrParameters + await esbuild.build({ + bundle: false, + platform: 'node', + target: 'node22', + format: 'cjs', + outdir: buildDir, + entryPoints: {'config.server': path.join(pkgRoot, 'config.server.ts')}, + }); + + // Create empty loader.js required by MRT + await fs.writeFile(path.join(buildDir, 'loader.js'), '// This file is intentionally empty\n'); + + // Write package.json without "type" field + const pkg = JSON.parse(await fs.readFile(path.join(pkgRoot, 'package.json'), 'utf8')); + delete pkg.type; + await fs.writeFile(path.join(buildDir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n'); + + console.log('Build complete'); +} + +build().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/mrt-welcome-app/src/app/server.test.ts b/packages/mrt-welcome-app/src/app/server.test.ts new file mode 100644 index 00000000..0998fbb6 --- /dev/null +++ b/packages/mrt-welcome-app/src/app/server.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {expect} from 'chai'; +import request from 'supertest'; +import {createApp} from './server.js'; + +describe('server', () => { + describe('createApp', () => { + it('should create an Express app', () => { + const app = createApp(); + expect(app).to.not.be.undefined; + expect(typeof app.get).to.equal('function'); + expect(typeof app.use).to.equal('function'); + expect(typeof app.all).to.equal('function'); + }); + + it('should create app without MRT middleware by default', () => { + const app = createApp(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const routes = (app as any)._router?.stack || []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const hasMRTMiddleware = routes.some( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (route: any) => route?.handle?.name?.includes('mrt') || route?.handle?.name?.includes('MRT'), + ); + expect(hasMRTMiddleware).to.equal(false); + }); + + it('should disable x-powered-by header', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers['x-powered-by']).to.be.undefined; + }); + + it('should set server header to "mrt welcome app"', async () => { + const app = createApp(); + const response = await request(app).get('/'); + expect(response.headers.server).to.equal('mrt welcome app'); + }); + + it('should set Cache-Control to no-cache for all responses', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers['cache-control']).to.equal('no-cache'); + }); + + it('should handle wildcard routes with /{*splat}', async () => { + const app = createApp(); + const response = await request(app).get('/any/random/path'); + expect(response.status).to.equal(200); + expect(response.headers['content-type']).to.include('text/html'); + expect(response.text).to.include('Welcome to your Environment'); + }); + + it('should disable x-powered-by header in response', async () => { + const app = createApp(); + const response = await request(app).get('/test'); + expect(response.headers).to.not.have.property('x-powered-by'); + }); + + it('should use build views path when not in local environment', () => { + const originalEnv = process.env.AWS_LAMBDA_FUNCTION_NAME; + process.env.AWS_LAMBDA_FUNCTION_NAME = 'test-function'; + + const app = createApp(); + const viewsPath = app.get('views'); + + expect(viewsPath).to.include('build'); + expect(viewsPath).to.include('views'); + + if (originalEnv !== undefined) { + process.env.AWS_LAMBDA_FUNCTION_NAME = originalEnv; + } else { + delete process.env.AWS_LAMBDA_FUNCTION_NAME; + } + }); + }); +}); diff --git a/packages/mrt-welcome-app/src/app/server.ts b/packages/mrt-welcome-app/src/app/server.ts new file mode 100644 index 00000000..b7495ce8 --- /dev/null +++ b/packages/mrt-welcome-app/src/app/server.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import express, {type Express, type Request, type Response, type NextFunction, type RequestHandler} from 'express'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import ejs from 'ejs'; +import {isLocal, createMRTCommonMiddleware, createMRTCleanUpMiddleware} from '@salesforce/mrt-utilities'; +import {echo} from '../utils/welcome-routes.js'; + +const extraRemappedHeadersMiddleware = (req: Request, res: Response, next: NextFunction) => { + res.set('server', 'mrt welcome app'); + next(); +}; + +export const createApp = (): Express => { + const app = express(); + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + app.disable('x-powered-by'); + app.use(extraRemappedHeadersMiddleware); + app.use(createMRTCommonMiddleware() as RequestHandler); + app.use(createMRTCleanUpMiddleware() as RequestHandler); + + app.engine('html', ejs.renderFile); + const viewsPath = isLocal() + ? path.resolve(__dirname, __dirname.includes(`${path.sep}src${path.sep}`) ? '../views' : 'views') + : path.resolve(process.cwd(), 'build', 'views'); + app.set('views', viewsPath); + app.set('view engine', 'html'); + + app.use((req, res, next) => { + res.set('Cache-Control', 'no-cache'); + return next(); + }); + + app.all('/{*splat}', echo); + return app; +}; diff --git a/packages/mrt-welcome-app/src/dev/tsx-local.ts b/packages/mrt-welcome-app/src/dev/tsx-local.ts new file mode 100644 index 00000000..b9200623 --- /dev/null +++ b/packages/mrt-welcome-app/src/dev/tsx-local.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {createApp} from '../app/server.js'; + +const app = createApp(); + +const server = app.listen(2401, 'localhost', () => { + console.log('Server is running on port localhost:2401'); +}); + +['SIGTERM', 'SIGINT'].forEach((signal) => { + process.once(signal, () => server?.close(console.error)); +}); diff --git a/packages/mrt-welcome-app/src/ssr.test.ts b/packages/mrt-welcome-app/src/ssr.test.ts new file mode 100644 index 00000000..0606a2b3 --- /dev/null +++ b/packages/mrt-welcome-app/src/ssr.test.ts @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import request from 'supertest'; +import {expect} from 'chai'; +import sinon from 'sinon'; +import type {Express} from 'express'; +import {processLambdaResponse, get} from './ssr.js'; +import type {APIGatewayProxyResult, APIGatewayProxyEvent, Context} from 'aws-lambda'; +import createEventModule from '@serverless/event-mocks'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const createEvent = ((createEventModule as any).default ?? createEventModule) as typeof createEventModule; + +describe('server', () => { + let originalEnv: NodeJS.ProcessEnv; + let app: Express; + + beforeEach(async () => { + originalEnv = process.env; + process.env = Object.assign({}, process.env, { + MRT_ALLOW_COOKIES: 'true', + LISTEN_ADDRESS: '', + BUNDLE_ID: '1', + DEPLOY_TARGET: 'test', + EXTERNAL_DOMAIN_NAME: 'test.com', + MOBIFY_PROPERTY_ID: 'test', + AWS_LAMBDA_FUNCTION_NAME: 'pretend-to-be-remote', + AWS_REGION: 'us-east-2', + }); + app = (await import('./ssr.js')).app; + }); + + afterEach(() => { + process.env = originalEnv; + sinon.restore(); + }); + + it('Path / should render correctly', async () => { + const response = await request(app).get('/').expect(200).expect('Content-Type', /text\/html/); + expect(response.text).to.include('Welcome to your Environment'); + }); + + it('Check incoming headers are lowercase', async () => { + const response = await request(app) + .get('/headers') + .set('Random-Header', 'random') + .set('Another-Mixed-Case-Header', 'value') + .set('UPPERCASE-HEADER', 'test'); + for (const header in response.body.headers) { + expect(header).to.equal(header.toLowerCase()); + } + }); +}); + +const base64Body = 'SGVsbG8gV29ybGQ='; + +describe('processLambdaResponse', () => { + it('should add date header to response', () => { + const response = {statusCode: 200, headers: {}, body: ''}; + const event = {multiValueHeaders: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers.date).to.not.be.undefined; + expect(result.headers.date).to.match(/^\w+, \d+ \w+ \d+ \d+:\d+:\d+ GMT$/); + }); + + it('should preserve original headers', () => { + const response = { + statusCode: 200, + multiValueHeaders: {'content-type': ['application/json'], 'x-custom': ['value']}, + body: base64Body, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers['content-type']).to.equal('application/json'); + expect(result.headers['x-custom']).to.equal('value'); + }); + + it('should remove multiValueHeaders from response', () => { + const response = { + statusCode: 200, + multiValueHeaders: {'set-cookie': ['cookie1=value1', 'cookie2=value2']}, + body: base64Body, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.multiValueHeaders).to.be.undefined; + }); + + it('should not add correlation ID when not present in event', () => { + const response = {statusCode: 200, headers: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers['x-correlation-id']).to.be.undefined; + }); + + it('should handle event with no headers', () => { + const response = {statusCode: 200, headers: {}, body: base64Body}; + const event = {}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.statusCode).to.equal(200); + expect(result.headers.date).to.not.be.undefined; + }); + + it('should flatten multi-value headers', () => { + const response = { + statusCode: 200, + body: base64Body, + multiValueHeaders: { + 'set-cookie': ['cookie1=value1', 'cookie2=value2'], + 'x-custom': ['header1', 'header2'], + }, + }; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers['set-cookie']).to.equal('cookie1=value1,cookie2=value2'); + expect(result.headers['x-custom']).to.equal('header1,header2'); + }); + + it('should handle status code correctly', () => { + const response = {statusCode: 404, headers: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.statusCode).to.equal(404); + }); + + it('should preserve response body when present', () => { + const response = {statusCode: 200, multiValueHeaders: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.body).to.equal(base64Body); + }); + + it('should add date header with correct format', () => { + const response = {statusCode: 200, multiValueHeaders: {}, body: base64Body}; + const event = {headers: {}}; + const result = processLambdaResponse(response as APIGatewayProxyResult, event as APIGatewayProxyEvent); + expect(result.headers.date).to.not.be.undefined; + expect(result.headers.date).to.match(/^\w+, \d+ \w+ \d+ \d+:\d+:\d+ GMT$/); + }); +}); + +describe('createLambdaHandler', () => { + it('should handle Lambda event and return processed response', async () => { + const event = createEvent('aws:apiGateway', { + path: '/', + httpMethod: 'GET', + headers: {}, + body: null, + isBase64Encoded: false, + multiValueHeaders: {}, + pathParameters: null, + queryStringParameters: null, + multiValueQueryStringParameters: null, + stageVariables: null, + // @ts-expect-error - requestContext is not required + requestContext: {}, + }); + + const context = { + callbackWaitsForEmptyEventLoop: true, + functionName: 'test-function', + functionVersion: '$LATEST', + invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:test-function', + memoryLimitInMB: '128', + awsRequestId: 'test-request-id', + logGroupName: '/aws/lambda/test-function', + logStreamName: '2024/01/01/[$LATEST]test', + getRemainingTimeInMillis: () => 30000, + done: () => {}, + fail: () => {}, + succeed: () => {}, + } as Context; + + const response = await new Promise((resolve, reject) => { + get(event, context, (err, res) => { + if (err) reject(err); + else resolve(res as APIGatewayProxyResult); + }); + }); + + expect(response).to.not.be.undefined; + expect(response?.statusCode).to.equal(200); + expect(response?.headers).to.not.be.undefined; + expect(response?.headers?.date).to.not.be.undefined; + }); +}); diff --git a/packages/mrt-welcome-app/src/ssr.ts b/packages/mrt-welcome-app/src/ssr.ts new file mode 100644 index 00000000..4ddf5c10 --- /dev/null +++ b/packages/mrt-welcome-app/src/ssr.ts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import type {Express} from 'express'; +import {createApp} from './app/server.js'; +import {ServerlessAdapter, getFlattenedHeadersMap} from '@h4ad/serverless-adapter'; +import {DefaultHandler} from '@h4ad/serverless-adapter/lib/handlers/default'; +import {CallbackResolver} from '@h4ad/serverless-adapter/lib/resolvers/callback'; +import {ApiGatewayV1Adapter} from '@h4ad/serverless-adapter/lib/adapters/aws'; +import {ExpressFramework} from '@h4ad/serverless-adapter/lib/frameworks/express'; +import type {APIGatewayProxyEvent, Context, APIGatewayProxyResult, Callback} from 'aws-lambda'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type HandlerFunction = (event: APIGatewayProxyEvent, context: Context, callback: Callback) => any; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const processLambdaResponse = (response: APIGatewayProxyResult, _event: APIGatewayProxyEvent): any => { + if (!response) return response; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const joinedHeaders = getFlattenedHeadersMap(response.multiValueHeaders || ({} as any), ',', true); + joinedHeaders.date = new Date().toUTCString(); + delete response.multiValueHeaders; + + const result = { + ...response, + headers: joinedHeaders, + }; + return result; +}; + +const createLambdaHandler = (app: Express): HandlerFunction => { + const handler = (event: APIGatewayProxyEvent, context: Context, callback: Callback) => { + const serverlessAdapterHandler = ServerlessAdapter.new(app) + .setFramework(new ExpressFramework()) + .setHandler(new DefaultHandler()) + .setResolver(new CallbackResolver()) + .addAdapter( + new ApiGatewayV1Adapter({lowercaseRequestHeaders: true, throwOnChunkedTransferEncoding: false}), + ) + .build() as HandlerFunction; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (context as any).callbackWaitsForEmptyEventLoop = false; + + const managedCallback = (err: Error | null, response: APIGatewayProxyResult) => { + return callback(err, processLambdaResponse(response, event)); + }; + + return serverlessAdapterHandler(event, context, managedCallback as Callback); + }; + return handler; +}; + +const mrtApp = createApp(); + +export const app = mrtApp; +export const get = createLambdaHandler(mrtApp); diff --git a/packages/mrt-welcome-app/src/utils/welcome-routes.test.ts b/packages/mrt-welcome-app/src/utils/welcome-routes.test.ts new file mode 100644 index 00000000..92545c47 --- /dev/null +++ b/packages/mrt-welcome-app/src/utils/welcome-routes.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025, Salesforce, Inc. + * SPDX-License-Identifier: Apache-2 + * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0 + */ +import {expect} from 'chai'; +import sinon from 'sinon'; +import request from 'supertest'; +import express, {type Express} from 'express'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import ejs from 'ejs'; +import {echo} from './welcome-routes.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const parseJsonFromHtml = (html: string): any => { + const match = html.match(/
([\s\S]*?)<\/pre>/);
+  if (!match || !match[1]) {
+    throw new Error('Could not find JSON in HTML response');
+  }
+  return JSON.parse(match[1].trim());
+};
+
+describe('welcome-routes', () => {
+  let app: Express;
+
+  beforeEach(() => {
+    app = express();
+    app.engine('html', ejs.renderFile);
+    app.set('views', path.resolve(__dirname, '../views'));
+    app.set('view engine', 'html');
+  });
+
+  afterEach(() => {
+    sinon.restore();
+  });
+
+  describe('echo', () => {
+    it('should render HTML template', async () => {
+      app.get('/test', echo);
+
+      const response = await request(app).get('/test').expect(200).expect('Content-Type', /text\/html/);
+
+      expect(response.text).to.include('Welcome to your Environment');
+
+      const json = parseJsonFromHtml(response.text);
+      expect(json).to.have.property('method', 'GET');
+      expect(json).to.have.property('path', '/test');
+      expect(json).to.have.property('protocol');
+      expect(json).to.have.property('query');
+      expect(json).to.have.property('headers');
+      expect(json).to.have.property('ip');
+      expect(json).to.have.property('env');
+    });
+
+    it('should include query parameters in response', async () => {
+      app.get('/test', echo);
+
+      const response = await request(app).get('/test?foo=bar&baz=qux');
+
+      const json = parseJsonFromHtml(response.text);
+      expect(json.query).to.deep.equal({
+        foo: 'bar',
+        baz: 'qux',
+      });
+    });
+
+    it('should include request headers in response', async () => {
+      app.get('/test', echo);
+
+      const response = await request(app).get('/test').set('Custom-Header', 'custom-value');
+
+      const json = parseJsonFromHtml(response.text);
+      expect(json.headers).to.have.property('custom-header');
+      expect(json.headers['custom-header']).to.equal('custom-value');
+    });
+  });
+
+  describe('echo edge cases', () => {
+    it('should handle IPv6 addresses', async () => {
+      app.get('/test', echo);
+
+      const response = await request(app).get('/test');
+
+      const json = parseJsonFromHtml(response.text);
+      expect(json.ip).to.not.be.undefined;
+    });
+  });
+});
diff --git a/packages/mrt-welcome-app/src/utils/welcome-routes.ts b/packages/mrt-welcome-app/src/utils/welcome-routes.ts
new file mode 100644
index 00000000..396e3838
--- /dev/null
+++ b/packages/mrt-welcome-app/src/utils/welcome-routes.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2025, Salesforce, Inc.
+ * SPDX-License-Identifier: Apache-2
+ * For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
+ */
+import type {Request, Response} from 'express';
+
+const ENVS_TO_EXPOSE = [
+  'aws_execution_env',
+  'aws_lambda_function_memory_size',
+  'aws_lambda_function_name',
+  'aws_lambda_function_version',
+  'aws_lambda_log_group_name',
+  'aws_lambda_log_stream_name',
+  'aws_region',
+  'bundle_id',
+  // These "customer" defined environment variables are set by the Manager
+  // and expected by the MRT smoke test suite
+  'customer_*',
+  'deploy_id',
+  'deploy_target',
+  'external_domain_name',
+  'mobify_property_id',
+  'mrt_allow_cookies',
+  'node_env',
+  'node_options',
+  'tz',
+];
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const sortObjectKeys = (o: Record): Record => {
+  return Object.assign(
+    {},
+    ...Object.keys(o)
+      .sort()
+      .map((k) => ({[k]: o[k]})),
+  );
+};
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const filterAndSortObjectKeys = (o: Record, whitelist: string[]): Record =>
+  o &&
+  Object.keys(o)
+    .filter((key) => {
+      const keylc = key.toLowerCase().trim();
+      return whitelist.some(
+        (pattern) =>
+          (pattern.endsWith('*') && keylc.startsWith(pattern.slice(0, -1))) || pattern === keylc,
+      );
+    })
+    .sort()
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    .reduce((acc: Record, key) => {
+      acc[key] = o[key];
+      return acc;
+    }, {});
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const jsonFromRequest = (req: Request, additional_info?: any): any => {
+  return {
+    protocol: req.protocol,
+    method: req.method,
+    path: req.path,
+    query: req.query,
+    body: req.body,
+    headers: sortObjectKeys(req.headers),
+    ip: req.ip,
+    env: filterAndSortObjectKeys(process.env, ENVS_TO_EXPOSE),
+    ...(typeof additional_info === 'object' ? {additional_info} : {}),
+  };
+};
+
+export const echo = (req: Request, res: Response) => {
+  const content = jsonFromRequest(req);
+  res.render('layout', {json_content: content});
+};
diff --git a/packages/mrt-welcome-app/src/views/layout.html b/packages/mrt-welcome-app/src/views/layout.html
new file mode 100644
index 00000000..7ff6803d
--- /dev/null
+++ b/packages/mrt-welcome-app/src/views/layout.html
@@ -0,0 +1,121 @@
+
+
+  
+    
+    
+    
+  
+  
+    
+    
+

👋 Welcome to your Environment

+
+

Push your first bundle

+

+ Before publishing to your site, you will first need to push a bundle + to the environment +

+ Read our how-to guide +
+
+

Update your environment's settings

+

Use Runtime Admin to update your environment's settings

+ Read our API docs +
+

HTTP request info

+
<%- JSON.stringify(json_content, null, 2) %>
+
+ + diff --git a/packages/mrt-welcome-app/test/tsconfig.json b/packages/mrt-welcome-app/test/tsconfig.json new file mode 100644 index 00000000..4a377fa2 --- /dev/null +++ b/packages/mrt-welcome-app/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "types": ["node", "mocha", "chai"], + "paths": { + "@salesforce/mrt-utilities": ["../../mrt-utilities/src/index.ts"], + "@salesforce/mrt-utilities/streaming": ["../../mrt-utilities/src/streaming/index.ts"] + } + }, + "include": ["./**/*", "../src/**/*"] +} diff --git a/packages/mrt-welcome-app/tsconfig.json b/packages/mrt-welcome-app/tsconfig.json new file mode 100644 index 00000000..5c734139 --- /dev/null +++ b/packages/mrt-welcome-app/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@salesforce/dev-config/tsconfig-strict-esm", + "compilerOptions": { + "skipLibCheck": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "verbatimModuleSyntax": false, + "moduleResolution": "bundler", + "allowImportingTsExtensions": false, + "resolveJsonModule": true, + "allowJs": true + }, + "include": ["src/**/*"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 623cc1da..d71893de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ catalogs: specifier: ^3.1.1 version: 3.1.1 eslint-plugin-prettier: - specifier: ^5.5.4 + specifier: ^5.5.5 version: 5.5.4 glob: specifier: 13.0.0 @@ -76,7 +76,7 @@ catalogs: specifier: 11.0.0 version: 11.0.0 prettier: - specifier: ^3.6.2 + specifier: ^3.8.3 version: 3.6.2 shx: specifier: ^0.3.3 @@ -117,8 +117,8 @@ importers: specifier: ^0.5.2 version: 0.5.2 '@changesets/cli': - specifier: ^2.29.8 - version: 2.29.8(@types/node@22.19.0) + specifier: ^2.31.0 + version: 2.31.0(@types/node@25.6.0) eslint-plugin-prettier: specifier: 'catalog:' version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.6.2) @@ -145,10 +145,10 @@ importers: version: 1.1.2(typedoc-plugin-markdown@4.9.0(typedoc@0.28.14(typescript@5.9.3))) vitepress: specifier: 2.0.0-alpha.17 - version: 2.0.0-alpha.17(@types/node@22.19.0)(change-case@5.4.4)(fuse.js@7.1.0)(postcss@8.5.10)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + version: 2.0.0-alpha.17(@types/node@25.6.0)(change-case@5.4.4)(fuse.js@7.1.0)(postcss@8.5.10)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) vitepress-plugin-group-icons: specifier: ^1.7.5 - version: 1.7.5(vite@7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1)) + version: 1.7.5(vite@7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1)) packages/b2c-cli: dependencies: @@ -611,6 +611,139 @@ importers: specifier: 'catalog:' version: 8.54.0(eslint@9.39.1)(typescript@5.9.3) + packages/mrt-reference-app: + dependencies: + '@aws-sdk/client-cloudwatch-logs': + specifier: 3.952.0 + version: 3.952.0 + '@aws-sdk/client-dynamodb': + specifier: 3.980.0 + version: 3.980.0 + '@aws-sdk/client-lambda': + specifier: 3.952.0 + version: 3.952.0 + '@aws-sdk/client-s3': + specifier: 3.952.0 + version: 3.952.0 + '@aws-sdk/client-secrets-manager': + specifier: 3.1004.0 + version: 3.1004.0 + '@aws-sdk/lib-dynamodb': + specifier: 3.980.0 + version: 3.980.0(@aws-sdk/client-dynamodb@3.980.0) + '@h4ad/serverless-adapter': + specifier: 4.4.0 + version: 4.4.0(@types/aws-lambda@8.10.160)(@types/body-parser@1.19.6)(@types/cors@2.8.19)(@types/express@5.0.4)(body-parser@2.2.1)(cors@2.8.5)(express@5.1.0)(http-errors@2.0.1) + '@salesforce/mrt-utilities': + specifier: workspace:* + version: link:../mrt-utilities + compressible: + specifier: 2.0.18 + version: 2.0.18 + express: + specifier: 5.1.0 + version: 5.1.0 + express-basic-auth: + specifier: 1.2.1 + version: 1.2.1 + negotiator: + specifier: 1.0.0 + version: 1.0.0 + winston: + specifier: 3.19.0 + version: 3.19.0 + devDependencies: + '@salesforce/b2c-cli': + specifier: workspace:* + version: link:../b2c-cli + '@salesforce/b2c-tooling-sdk': + specifier: workspace:* + version: link:../b2c-tooling-sdk + '@salesforce/dev-config': + specifier: 'catalog:' + version: 4.3.2 + '@serverless/event-mocks': + specifier: 1.1.1 + version: 1.1.1 + '@smithy/smithy-client': + specifier: 4.9.1 + version: 4.9.1 + '@types/aws-lambda': + specifier: 8.10.160 + version: 8.10.160 + '@types/chai': + specifier: 'catalog:' + version: 4.3.20 + '@types/compressible': + specifier: 2.0.3 + version: 2.0.3 + '@types/express': + specifier: 5.0.4 + version: 5.0.4 + '@types/mocha': + specifier: 'catalog:' + version: 10.0.10 + '@types/negotiator': + specifier: 0.6.4 + version: 0.6.4 + '@types/node': + specifier: 'catalog:' + version: 22.19.0 + '@types/sinon': + specifier: 'catalog:' + version: 21.0.0 + '@types/supertest': + specifier: 6.0.3 + version: 6.0.3 + aws-sdk-client-mock: + specifier: 4.1.0 + version: 4.1.0 + c8: + specifier: 'catalog:' + version: 11.0.0 + chai: + specifier: 'catalog:' + version: 4.5.0 + esbuild: + specifier: 0.25.0 + version: 0.25.0 + eslint: + specifier: 'catalog:' + version: 9.39.1 + eslint-config-prettier: + specifier: 'catalog:' + version: 10.1.8(eslint@9.39.1) + eslint-plugin-header: + specifier: 'catalog:' + version: 3.1.1(eslint@9.39.1) + eslint-plugin-prettier: + specifier: 'catalog:' + version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.6.2) + mocha: + specifier: 'catalog:' + version: 10.8.2 + prettier: + specifier: 'catalog:' + version: 3.6.2 + shx: + specifier: 'catalog:' + version: 0.3.4 + sinon: + specifier: 'catalog:' + version: 21.0.1 + supertest: + specifier: 7.1.4 + version: 7.1.4 + tsx: + specifier: 'catalog:' + version: 4.20.6 + typescript: + specifier: 'catalog:' + version: 5.9.3 + typescript-eslint: + specifier: 'catalog:' + version: 8.54.0(eslint@9.39.1)(typescript@5.9.3) + packages/mrt-utilities: dependencies: '@aws-sdk/client-cloudwatch': @@ -732,6 +865,103 @@ importers: specifier: 'catalog:' version: 8.54.0(eslint@9.39.1)(typescript@5.9.3) + packages/mrt-welcome-app: + dependencies: + '@h4ad/serverless-adapter': + specifier: 4.4.0 + version: 4.4.0(@types/aws-lambda@8.10.160)(@types/body-parser@1.19.6)(@types/cors@2.8.19)(@types/express@5.0.4)(body-parser@2.2.1)(cors@2.8.5)(express@5.1.0)(http-errors@2.0.1) + '@salesforce/mrt-utilities': + specifier: workspace:* + version: link:../mrt-utilities + ejs: + specifier: 3.1.10 + version: 3.1.10 + express: + specifier: 5.1.0 + version: 5.1.0 + devDependencies: + '@salesforce/b2c-cli': + specifier: workspace:* + version: link:../b2c-cli + '@salesforce/b2c-tooling-sdk': + specifier: workspace:* + version: link:../b2c-tooling-sdk + '@salesforce/dev-config': + specifier: 'catalog:' + version: 4.3.2 + '@serverless/event-mocks': + specifier: 1.1.1 + version: 1.1.1 + '@types/aws-lambda': + specifier: 8.10.160 + version: 8.10.160 + '@types/chai': + specifier: 'catalog:' + version: 4.3.20 + '@types/ejs': + specifier: 3.1.5 + version: 3.1.5 + '@types/express': + specifier: 5.0.4 + version: 5.0.4 + '@types/mocha': + specifier: 'catalog:' + version: 10.0.10 + '@types/node': + specifier: 'catalog:' + version: 22.19.0 + '@types/sinon': + specifier: 'catalog:' + version: 21.0.0 + '@types/supertest': + specifier: 6.0.3 + version: 6.0.3 + c8: + specifier: 'catalog:' + version: 11.0.0 + chai: + specifier: 'catalog:' + version: 4.5.0 + esbuild: + specifier: 0.25.0 + version: 0.25.0 + eslint: + specifier: 'catalog:' + version: 9.39.1 + eslint-config-prettier: + specifier: 'catalog:' + version: 10.1.8(eslint@9.39.1) + eslint-plugin-header: + specifier: 'catalog:' + version: 3.1.1(eslint@9.39.1) + eslint-plugin-prettier: + specifier: 'catalog:' + version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.6.2) + mocha: + specifier: 'catalog:' + version: 10.8.2 + prettier: + specifier: 'catalog:' + version: 3.6.2 + shx: + specifier: 'catalog:' + version: 0.3.4 + sinon: + specifier: 'catalog:' + version: 21.0.1 + supertest: + specifier: 7.1.4 + version: 7.1.4 + tsx: + specifier: 'catalog:' + version: 4.20.6 + typescript: + specifier: 'catalog:' + version: 5.9.3 + typescript-eslint: + specifier: 'catalog:' + version: 8.54.0(eslint@9.39.1)(typescript@5.9.3) + skills: {} packages: @@ -766,6 +996,10 @@ packages: resolution: {integrity: sha512-JcgMsxlAfl+Hh/z4+2uDqwg6xYuLneHblhHaD1WTfOiNpWLstv4t6JJLw01xiIgC510wj0OeY84mhZor7E2QZA==} engines: {node: '>=18.0.0'} + '@aws-sdk/client-cloudwatch-logs@3.952.0': + resolution: {integrity: sha512-oXqHaHfP31puNMzPwrstKjGwL3nArBq3aoneFLMYbS7fbVe8XQCmxzWg4KglkEw6DBgeUOYNFuXwpuKo4ogTDA==} + engines: {node: '>=18.0.0'} + '@aws-sdk/client-cloudwatch@3.952.0': resolution: {integrity: sha512-vKR+AzSus8SZBR1wvpzqMZwoLlyA/UVZXPtSQLBP/CcTRKuJSMvIvMXN88U4bPseROrN83UoSQdkmVo93+qQJg==} engines: {node: '>=18.0.0'} @@ -774,10 +1008,22 @@ packages: resolution: {integrity: sha512-1rGhAx4cHZy3pMB3R3r84qMT5WEvQ6ajr2UksnD48fjQxwaUcpI6NsPvU5j/5BI5LqGiUO6ThOrMwSMm95twQA==} engines: {node: '>=20.0.0'} + '@aws-sdk/client-lambda@3.952.0': + resolution: {integrity: sha512-F0cK/RX1qs9NLZctGokrKe6AlK4YgiHqYaYBo32d+xT+EZ4EpNuOIWNZ4Rgq+78tG+Y9DT98Nwwl9lejYQtrog==} + engines: {node: '>=18.0.0'} + '@aws-sdk/client-s3@3.929.0': resolution: {integrity: sha512-M6G+1CBTowN+m0Jrww5/AXMqlk4nIJqwaa/vOw+EbvLD7ROpBs6bStSai9esP9PkIVW6KMu4zCIgHzKhGa3R2A==} engines: {node: '>=18.0.0'} + '@aws-sdk/client-s3@3.952.0': + resolution: {integrity: sha512-5EFrOpLL1f29eepx6lHYRlwk5Eeo0PVEMROZQmGAGo/7avjWGPwfgkDdfgP2JCIusiKUG6sHN4FOjd86Q7htLw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-secrets-manager@3.1004.0': + resolution: {integrity: sha512-a9rlezmPNeE08ijD5p0o5bzIxeQCSVWxHfF5Me9ywhjF8fc+/1HP7EQpeITMUE7X2SU+Ac9QibKfiGnPDY58Rw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/client-sso@3.929.0': resolution: {integrity: sha512-CE1T7PvN2MDRCw96BTUz2Zcnb6Lae3Dl4w3TPB5auBv2sAiIPbQegFUwT2C8teMDGCNXyndzoTvAd4wmO9AcpA==} engines: {node: '>=18.0.0'} @@ -802,6 +1048,10 @@ packages: resolution: {integrity: sha512-hFiezao0lCEddPhSQEF6vCu+TepUN3edKxWYbswMoH87XpUvHJmFVX5+zttj4qi33saGiuOaJciswWcN6YSA9g==} engines: {node: '>=20.0.0'} + '@aws-sdk/core@3.974.8': + resolution: {integrity: sha512-njR2qoG6ZuB0kvAS2FyICsFZJ6gmCcf2X/7JcD14sUvGDm26wiZ5BrA6LOiUxKFEF+IVe7kdroxyE00YlkiYsw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.928.0': resolution: {integrity: sha512-tB8F9Ti0/NFyFVQX8UQtgRik88evtHpyT6WfXOB4bAY6lEnEHA0ubJZmk9y+aUeoE+OsGLx70dC3JUsiiCPJkQ==} engines: {node: '>=18.0.0'} @@ -814,6 +1064,10 @@ packages: resolution: {integrity: sha512-YTWjM78Wiqix0Jv/anbq7+COFOFIBBMLZ+JsLKGwbTZNJ2DG4JNBnLVJAWylPOHwurMws9157pqzU8ODrpBOow==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.972.34': + resolution: {integrity: sha512-XT0jtf8Fw9JE6ppsQeoNnZRiG+jqRixMT1v1ZR17G60UvVdsQmTG8nbEyHuEPfMxDXEhfdARaM/XiEhca4lGHQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.928.0': resolution: {integrity: sha512-67ynC/8UW9Y8Gn1ZZtC3OgcQDGWrJelHmkbgpmmxYUrzVhp+NINtz3wiTzrrBFhPH/8Uy6BxvhMfXhn0ptcMEQ==} engines: {node: '>=18.0.0'} @@ -826,6 +1080,10 @@ packages: resolution: {integrity: sha512-adDRE3iFrgJJ7XhRHkb6RdFDMrA5x64WAWxygI3F6wND+3v5qQ4Uks12vsnEZgduU/+JQBgFB6L4vfwUS+rpBQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.972.36': + resolution: {integrity: sha512-DPoGWfy7J7RKxvbf5kOKIGQkD2ek3dbKgzKIGrnLuvZBz5myU+Im/H6pmc14QcnFbqHMqxvtWSgRDSJW3qXLQg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.929.0': resolution: {integrity: sha512-XIzWsJUYeS/DjggHFB53sGGjXdlN/BA6x+Y/JvLbpdkGD2yLISU34/cDPbK/O8BAQCRTCQ69VPa/1AdNgZZRQw==} engines: {node: '>=18.0.0'} @@ -838,6 +1096,10 @@ packages: resolution: {integrity: sha512-uAXUMfnQJxJ25qeiX4e3Z36NTm1XT7woajV8BXx2yAUDD4jF6kubqnLEcqtiPzHANxmhta2SXm5PbDwSdhThBw==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.972.38': + resolution: {integrity: sha512-oDzUBu2MGJFgoar05sPMCwSrhw44ASyccrHzj66vO69OZqi7I6hZZxXfuPLC8OCzW7C+sU+bI73XHij41yekgQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.952.0': resolution: {integrity: sha512-jL9zc+e+7sZeJrHzYKK9GOjl1Ktinh0ORU3cM2uRBi7fuH/0zV9pdMN8PQnGXz0i4tJaKcZ1lrE4V0V6LB9NQg==} engines: {node: '>=18.0.0'} @@ -846,6 +1108,10 @@ packages: resolution: {integrity: sha512-7Me+/EkY3kQC1nehBjb9ryc558N+a8R4Dg3rSV3zpiB7iQtvXh4gU3rV14h/dIbn2/VkK9sh55YdXamSjfdb/Q==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.38': + resolution: {integrity: sha512-g1NosS8qe4OF++G2UFCM5ovSkgipC7YYor5KCWatG0UoMSO5YFj9C8muePlyVmOBV/WTI16Jo3/s1NUo/o1Bww==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.929.0': resolution: {integrity: sha512-GhNZEacpa7fh8GNggshm5S93UK25bCV5aDK8c2vfe7Y3OxBiL89Ox5GUKCu0xIOqiBdfYkI9wvWCFsQRRn7Bjw==} engines: {node: '>=18.0.0'} @@ -858,6 +1124,10 @@ packages: resolution: {integrity: sha512-maPmjL7nOT93a1QdSDzdF/qLbI+jit3oslKp7g+pTbASewkSYax7FwboETdKRxufPfCdrsRzMW2pIJ+QA8e+Bg==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.972.39': + resolution: {integrity: sha512-HEswDQyxUtadoZ/bJsPPENHg7R0Lzym5LuMksJeHvqhCOpP+rtkDLKI4/ZChH4w3cf5kG8n6bZuI8PzajoiqMg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.928.0': resolution: {integrity: sha512-XL0juran8yhqwn0mreV+NJeHJOkcRBaExsvVn9fXWW37A4gLh4esSJxM2KbSNh0t+/Bk3ehBI5sL9xad+yRDuw==} engines: {node: '>=18.0.0'} @@ -870,6 +1140,10 @@ packages: resolution: {integrity: sha512-tk/XxFhk37rKviArOIYbJ8crXiN3Mzn7Tb147jH51JTweNgUOwmqN+s027uqc3d8UeAyUcPUH8Bmfj86SzOhBQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.972.34': + resolution: {integrity: sha512-T3IFs4EVmVi1dVN5RciFnklCANSzvrQd/VuHY9ThHSQmYkTogjcGkoJEr+oNUPQZnso52183088NqysMPji1/Q==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.929.0': resolution: {integrity: sha512-aADe6cLo4+9MUOe0GnC5kUn8IduEKnTxqBlsciZOplU0/0+Rdp9rRh/e9ZBskeIXZ33eO2HG+KDAf1lvtPT7dA==} engines: {node: '>=18.0.0'} @@ -882,6 +1156,10 @@ packages: resolution: {integrity: sha512-tIz/O0yV1s77/FjMTWvvzU2vsztap2POlbetheOyRXq+E3PQtLOzCYopasXP+aeO1oerw3PFd9eycLbiwpgZZA==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.972.38': + resolution: {integrity: sha512-5ZxG+t0+3Q3QPh8KEjX6syskhgNf7I0MN7oGioTf6Lm1NTjfP7sIcYGNsthXC2qR8vcD3edNZwCr2ovfSSWuRA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.929.0': resolution: {integrity: sha512-L18JtW28xUZVTRHblgqZ8QTVGQfxpMLIuVYgQXrVWiY9Iz9EF4XrfZo3ywCAgqfgLi5pgg3fCxx/pe7uiMOs2w==} engines: {node: '>=18.0.0'} @@ -894,6 +1172,10 @@ packages: resolution: {integrity: sha512-HFlIVx8mm+Au7hkO7Hq/ZkPomjTt26iRj8uWZqEE1cJWMZ2NKvieNiT1ngzWt60Bc2uD51LqQUqiwr5JDgS4iQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.38': + resolution: {integrity: sha512-lYHFF30DGI20jZcYX8cm6Ns0V7f1dDN6g/MBDLTyD/5iw+bXs3yBr2iAiHDkx4RFU5JgsnZvCHYKiRVPRdmOgw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/dynamodb-codec@3.972.13': resolution: {integrity: sha512-dPKkCMX5BNnM4SRvX89s0SVxzlnujlLXZmZ9wJei2O6HT+5OQC7QvdfYv/odaTVc/s6MWv4FuoP4K9TTdTB0eg==} engines: {node: '>=20.0.0'} @@ -912,6 +1194,10 @@ packages: resolution: {integrity: sha512-Dpr2YeOaLFqt3q1hocwBesynE3x8/dXZqXZRuzSX/9/VQcwYBFChHAm4mTAl4zuvArtDbLrwzWSxmOWYZGtq5w==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.936.0': + resolution: {integrity: sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-endpoint-discovery@3.972.3': resolution: {integrity: sha512-xAxA8/TOygQmMrzcw9CrlpTHCGWSG/lvzrHCySfSZpDN4/yVSfXO+gUwW9WxeskBmuv9IIFATOVpzc9EzfTZ0Q==} engines: {node: '>=20.0.0'} @@ -920,10 +1206,18 @@ packages: resolution: {integrity: sha512-xmnLWMtmHJHJBupSWMUEW1gyxuRIeQ1Ov2xa8Tqq77fPr4Ft2AluEwiDMaZIMHoAvpxWKEEt9Si59Li7GIA+bQ==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-expect-continue@3.936.0': + resolution: {integrity: sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.928.0': resolution: {integrity: sha512-9+aCRt7teItSIMbnGvOY+FhtJnW2ZBUbfD+ug29a/ZbobDfTwmtrmtgEIWdXryFaRbT03HHfaJ3a++lTw4osuw==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.947.0': + resolution: {integrity: sha512-kXXxS2raNESNO+zR0L4YInVjhcGGNI2Mx0AE1ThRhDkAt2se3a+rGf9equ9YvOqA1m8Jl/GSI8cXYvSxXmS9Ag==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.922.0': resolution: {integrity: sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA==} engines: {node: '>=18.0.0'} @@ -932,6 +1226,10 @@ packages: resolution: {integrity: sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.972.10': + resolution: {integrity: sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-host-header@3.972.3': resolution: {integrity: sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==} engines: {node: '>=20.0.0'} @@ -940,6 +1238,10 @@ packages: resolution: {integrity: sha512-T4iqd7WQ2DDjCH/0s50mnhdoX+IJns83ZE+3zj9IDlpU0N2aq8R91IG890qTfYkUEdP9yRm0xir/CNed+v6Dew==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-location-constraint@3.936.0': + resolution: {integrity: sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.922.0': resolution: {integrity: sha512-AkvYO6b80FBm5/kk2E636zNNcNgjztNNUxpqVx+huyGn9ZqGTzS4kLqW2hO6CBe5APzVtPCtiQsXL24nzuOlAg==} engines: {node: '>=18.0.0'} @@ -948,6 +1250,10 @@ packages: resolution: {integrity: sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.972.10': + resolution: {integrity: sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-logger@3.972.3': resolution: {integrity: sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==} engines: {node: '>=20.0.0'} @@ -960,6 +1266,10 @@ packages: resolution: {integrity: sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.11': + resolution: {integrity: sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.3': resolution: {integrity: sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==} engines: {node: '>=20.0.0'} @@ -968,10 +1278,22 @@ packages: resolution: {integrity: sha512-LTkjS6cpJ2PEtsottTKq7JxZV0oH+QJ12P/dGNPZL4URayjEMBVR/dp4zh835X/FPXzijga3sdotlIKzuFy9FA==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-sdk-s3@3.947.0': + resolution: {integrity: sha512-DS2tm5YBKhPW2PthrRBDr6eufChbwXe0NjtTZcYDfUCXf0OR+W6cIqyKguwHMJ+IyYdey30AfVw9/Lb5KB8U8A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.37': + resolution: {integrity: sha512-Km7M+i8DrLArVzrid1gfxeGhYHBd3uxvE77g0s5a52zPSVosxzQBnJ0gwWb6NIp/DOk8gsBMhi7V+cpJG0ndTA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-ssec@3.922.0': resolution: {integrity: sha512-eHvSJZTSRJO+/tjjGD6ocnPc8q9o3m26+qbwQTu/4V6yOJQ1q+xkDZNqwJQphL+CodYaQ7uljp8g1Ji/AN3D9w==} engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-ssec@3.936.0': + resolution: {integrity: sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==} + engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-user-agent@3.928.0': resolution: {integrity: sha512-ESvcfLx5PtpdUM3ptCwb80toBTd3y5I4w5jaeOPHihiZr7jkRLE/nsaCKzlqscPs6UQ8xI0maav04JUiTskcHw==} engines: {node: '>=18.0.0'} @@ -984,6 +1306,10 @@ packages: resolution: {integrity: sha512-iv9toQZloEJp+dIuOr+1XWGmBMLU9c2qqNtgscfnEBZnUq3qKdBJHmLTKoq3mkLlV+41GrCWn8LrOunc6OlP6g==} engines: {node: '>=20.0.0'} + '@aws-sdk/middleware-user-agent@3.972.38': + resolution: {integrity: sha512-iz+B29TXcAZsJpwB+AwG/TTGA5l/VnmMZ2UxtiySOZjI6gCdmviXPwdgzcmuazMy16rXoPY4mYCGe7zdNKfx5A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/nested-clients@3.929.0': resolution: {integrity: sha512-emR4LTSupxPed1ni0zVxz5msezz/gA1YYXooiW567+NyhvLgSzDvNjK7GPU1waLCj1LrRFe7NkXX1pwa5sPrpw==} engines: {node: '>=18.0.0'} @@ -996,6 +1322,10 @@ packages: resolution: {integrity: sha512-edZwYLgRI0rZlH9Hru9+JvTsR1OAxuCRGEtJohkZneIJ5JIYzvFoMR1gaASjl1aPKRhjkCv8SSAb7hes5a1GGA==} engines: {node: '>=20.0.0'} + '@aws-sdk/nested-clients@3.997.6': + resolution: {integrity: sha512-WBDnqatJl+kGObpfmfSxqnXeYTu3Me8wx8WCtvoxX3pfWrrTv8I4WTMSSs7PZqcRcVh8WeUKMgGFjMG+52SR1w==} + engines: {node: '>=20.0.0'} + '@aws-sdk/region-config-resolver@3.925.0': resolution: {integrity: sha512-FOthcdF9oDb1pfQBRCfWPZhJZT5wqpvdAS5aJzB1WDZ+6EuaAhLzLH/fW1slDunIqq1PSQGG3uSnVglVVOvPHQ==} engines: {node: '>=18.0.0'} @@ -1004,6 +1334,10 @@ packages: resolution: {integrity: sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==} engines: {node: '>=18.0.0'} + '@aws-sdk/region-config-resolver@3.972.13': + resolution: {integrity: sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==} + engines: {node: '>=20.0.0'} + '@aws-sdk/region-config-resolver@3.972.3': resolution: {integrity: sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==} engines: {node: '>=20.0.0'} @@ -1012,6 +1346,18 @@ packages: resolution: {integrity: sha512-1+Ic8+MyqQy+OE6QDoQKVCIcSZO+ETmLLLpVS5yu0fihBU85B5HHU7iaKX1qX7lEaGPMpSN/mbHW0VpyQ0Xqaw==} engines: {node: '>=18.0.0'} + '@aws-sdk/signature-v4-multi-region@3.947.0': + resolution: {integrity: sha512-UaYmzoxf9q3mabIA2hc4T6x5YSFUG2BpNjAZ207EA1bnQMiK+d6vZvb83t7dIWL/U1de1sGV19c1C81Jf14rrA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.25': + resolution: {integrity: sha512-+CMIt3e1VzlklAECmG+DtP1sV8iKq25FuA0OKpnJ4KA0kxUtd7CgClY7/RU6VzJBQwbN4EJ9Ue6plvqx1qGadw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1041.0': + resolution: {integrity: sha512-Th7kPI6YPtvJUcdznooXJMy+9rQWjmEF81LxaJssngBzuysK4a/x+l8kjm1zb7nYsUPbndnBdUnwng/3PLvtGw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/token-providers@3.929.0': resolution: {integrity: sha512-78kph1R6TVJ53VXDKUmt64HMqWjTECLymJ7kLguz2QJiWh2ZdLvpyYGvaueEwwhisHYBh2qef1tGIf/PpEb8SQ==} engines: {node: '>=18.0.0'} @@ -1036,10 +1382,18 @@ packages: resolution: {integrity: sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==} engines: {node: '>=20.0.0'} + '@aws-sdk/types@3.973.8': + resolution: {integrity: sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/util-arn-parser@3.893.0': resolution: {integrity: sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==} engines: {node: '>=18.0.0'} + '@aws-sdk/util-arn-parser@3.972.3': + resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/util-dynamodb@3.980.0': resolution: {integrity: sha512-jG/yzr/JLFl7II9TTDWRKJRHThTXYNDYy694bRTj7JCXCU/Gb11ir5fJ7sV6FhlR9LrIaDb7Fft3RifvEnZcSQ==} engines: {node: '>=20.0.0'} @@ -1062,6 +1416,10 @@ packages: resolution: {integrity: sha512-EhSBGWSGQ6Jcbt6jRyX1/0EV7rf+6RGbIIskN0MTtHk0k8uj5FAa1FZhLf+1ETfnDTy/BT39t5IUOQiZL5X1jQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/util-endpoints@3.996.8': + resolution: {integrity: sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==} + engines: {node: '>=20.0.0'} + '@aws-sdk/util-locate-window@3.893.0': resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==} engines: {node: '>=18.0.0'} @@ -1072,6 +1430,9 @@ packages: '@aws-sdk/util-user-agent-browser@3.936.0': resolution: {integrity: sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==} + '@aws-sdk/util-user-agent-browser@3.972.10': + resolution: {integrity: sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==} + '@aws-sdk/util-user-agent-browser@3.972.3': resolution: {integrity: sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==} @@ -1102,6 +1463,15 @@ packages: aws-crt: optional: true + '@aws-sdk/util-user-agent-node@3.973.24': + resolution: {integrity: sha512-ZWwlkjcIp7cEL8ZfTpTAPNkwx25p7xol0xlKoWVVf22+nsjwmLcHYtTPjIV1cSpmB/b6DaK4cb1fSkvCXHgRdw==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + '@aws-sdk/xml-builder@3.921.0': resolution: {integrity: sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q==} engines: {node: '>=18.0.0'} @@ -1110,6 +1480,10 @@ packages: resolution: {integrity: sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==} engines: {node: '>=18.0.0'} + '@aws-sdk/xml-builder@3.972.22': + resolution: {integrity: sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/xml-builder@3.972.5': resolution: {integrity: sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA==} engines: {node: '>=20.0.0'} @@ -1205,6 +1579,10 @@ packages: resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} @@ -1213,11 +1591,11 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@changesets/apply-release-plan@7.0.14': - resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} - '@changesets/assemble-release-plan@6.0.9': - resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} @@ -1225,24 +1603,24 @@ packages: '@changesets/changelog-github@0.5.2': resolution: {integrity: sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==} - '@changesets/cli@2.29.8': - resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} + '@changesets/cli@2.31.0': + resolution: {integrity: sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==} hasBin: true - '@changesets/config@3.1.2': - resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - '@changesets/get-dependents-graph@2.1.3': - resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} '@changesets/get-github-info@0.7.0': resolution: {integrity: sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==} - '@changesets/get-release-plan@4.0.14': - resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==} + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -1253,14 +1631,14 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.2': - resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==} + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} '@changesets/pre@2.0.2': resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.6': - resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==} + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} '@changesets/should-skip-package@0.1.2': resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} @@ -1278,10 +1656,17 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@cspotcode/source-map-support@0.8.1': + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@dabh/diagnostics@2.0.8': + resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} + '@docsearch/css@4.6.2': resolution: {integrity: sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==} @@ -1304,6 +1689,12 @@ packages: resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} engines: {node: '>=18'} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -1316,6 +1707,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} @@ -1328,6 +1725,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -1340,6 +1743,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -1352,6 +1761,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -1364,6 +1779,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -1376,6 +1797,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -1388,6 +1815,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -1400,6 +1833,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -1412,6 +1851,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -1424,6 +1869,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -1436,6 +1887,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -1448,6 +1905,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -1460,6 +1923,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -1472,6 +1941,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -1484,6 +1959,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -1496,6 +1977,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.25.0': + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -1508,6 +1995,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} @@ -1520,6 +2013,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.0': + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -1532,6 +2031,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} @@ -1544,6 +2049,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.0': + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -1568,6 +2079,12 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -1580,6 +2097,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -1592,6 +2115,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -1604,6 +2133,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -2179,6 +2714,13 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodable/entities@2.1.0': + resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2305,6 +2847,9 @@ packages: resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} engines: {node: '>=14'} + '@paralleldrive/cuid2@2.3.1': + resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -3016,9 +3561,15 @@ packages: '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@sinonjs/fake-timers@11.2.2': + resolution: {integrity: sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==} + '@sinonjs/fake-timers@15.1.0': resolution: {integrity: sha512-cqfapCxwTGsrR80FEgOoPsTonoefMBY7dnUEbQ+GRcved0jvkJLzvX6F4WtN+HBqbPX/SiFsIRUp+IrCW/2I2w==} + '@sinonjs/fake-timers@15.3.2': + resolution: {integrity: sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==} + '@sinonjs/samsam@8.0.3': resolution: {integrity: sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==} @@ -3038,6 +3589,10 @@ packages: resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} engines: {node: '>=18.0.0'} + '@smithy/config-resolver@4.4.17': + resolution: {integrity: sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==} + engines: {node: '>=18.0.0'} + '@smithy/config-resolver@4.4.3': resolution: {integrity: sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==} engines: {node: '>=18.0.0'} @@ -3051,10 +3606,18 @@ packages: engines: {node: '>=18.0.0'} deprecated: Please upgrade your lockfile to use the latest 3.x version of @smithy/core for various fixes, see https://github.com/smithy-lang/smithy-typescript/blob/main/packages/core/CHANGELOG.md + '@smithy/core@3.23.17': + resolution: {integrity: sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==} + engines: {node: '>=18.0.0'} + '@smithy/core@3.23.4': resolution: {integrity: sha512-IH7G3hWxUhd2Z6HtvjZ1EiyDBCRYRr2sngOB9KUWf96XQ8JP2O5ascUH6TouW5YCIMFaVnKADEscM/vUfI3TvA==} engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.2.14': + resolution: {integrity: sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==} + engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.2.5': resolution: {integrity: sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==} engines: {node: '>=18.0.0'} @@ -3087,6 +3650,10 @@ packages: resolution: {integrity: sha512-qF4EcrEtEf2P6f2kGGuSVe1lan26cn7PsWJBC3vZJ6D16Fm5FSN06udOMVoW6hjzQM3W7VDFwtyUG2szQY50dA==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.3.17': + resolution: {integrity: sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==} + engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.3.6': resolution: {integrity: sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==} engines: {node: '>=18.0.0'} @@ -3095,6 +3662,10 @@ packages: resolution: {integrity: sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==} engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.2.14': + resolution: {integrity: sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==} + engines: {node: '>=18.0.0'} + '@smithy/hash-node@4.2.5': resolution: {integrity: sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==} engines: {node: '>=18.0.0'} @@ -3107,6 +3678,10 @@ packages: resolution: {integrity: sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q==} engines: {node: '>=18.0.0'} + '@smithy/invalid-dependency@4.2.14': + resolution: {integrity: sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==} + engines: {node: '>=18.0.0'} + '@smithy/invalid-dependency@4.2.5': resolution: {integrity: sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==} engines: {node: '>=18.0.0'} @@ -3127,6 +3702,10 @@ packages: resolution: {integrity: sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==} engines: {node: '>=18.0.0'} + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + '@smithy/md5-js@4.2.5': resolution: {integrity: sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==} engines: {node: '>=18.0.0'} @@ -3135,6 +3714,10 @@ packages: resolution: {integrity: sha512-GRDCjRd5//dK1gYHJKXdiChqW46HwRr+/YMctKL3PtlYOxD0+I0PqgTTKfSvMV7Q4SsEkBYoaF4+kwWDFs/lbg==} engines: {node: '>=18.0.0'} + '@smithy/middleware-content-length@4.2.14': + resolution: {integrity: sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-content-length@4.2.5': resolution: {integrity: sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==} engines: {node: '>=18.0.0'} @@ -3151,6 +3734,10 @@ packages: resolution: {integrity: sha512-4OS3TP3IWZysT8KlSG/UwfKdelJmuQ2CqVNfrkjm2Rsm146/DuSTfXiD1ulgWpp9L6lJmPYfWTp7/m4b4dQSdQ==} engines: {node: '>=18.0.0'} + '@smithy/middleware-endpoint@4.4.32': + resolution: {integrity: sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.4.35': resolution: {integrity: sha512-sz+Th9ofKypOtaboPTcyZtIfCs2LNb84bzxEhPffCElyMorVYDBdeGzxYqSLC6gWaZUqpPSbj5F6TIxYUlSCfQ==} engines: {node: '>=18.0.0'} @@ -3159,14 +3746,26 @@ packages: resolution: {integrity: sha512-E7Vc6WHCHlzDRTx1W0jZ6J1L6ziEV0PIWcUdmfL4y+c8r7WYr6I+LkQudaD8Nfb7C5c4P3SQ972OmXHtv6m/OA==} engines: {node: '>=18.0.0'} + '@smithy/middleware-retry@4.5.7': + resolution: {integrity: sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.2.10': resolution: {integrity: sha512-BQsdoi7ma4siJAzD0S6MedNPhiMcTdTLUqEUjrHeT1TJppBKWnwqySg34Oh/uGRhJeBd1sAH2t5tghBvcyD6tw==} engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.2.20': + resolution: {integrity: sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-serde@4.2.5': resolution: {integrity: sha512-La1ldWTJTZ5NqQyPqnCNeH9B+zjFhrNoQIL1jTh4zuqXRlmXhxYHhMtI1/92OlnoAtp6JoN7kzuwhWoXrBwPqg==} engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.2.14': + resolution: {integrity: sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==} + engines: {node: '>=18.0.0'} + '@smithy/middleware-stack@4.2.5': resolution: {integrity: sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==} engines: {node: '>=18.0.0'} @@ -3175,6 +3774,10 @@ packages: resolution: {integrity: sha512-pid7ksBr7nm0X/3paIlGo9Fh3UK1pQ5yH0007tBmdkVvv+AsBZAOzC2dmLhlzDWKkSB+ZCiiyDArjAW3klkbMg==} engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.3.14': + resolution: {integrity: sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==} + engines: {node: '>=18.0.0'} + '@smithy/node-config-provider@4.3.5': resolution: {integrity: sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==} engines: {node: '>=18.0.0'} @@ -3191,6 +3794,14 @@ packages: resolution: {integrity: sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==} engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.6.1': + resolution: {integrity: sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.14': + resolution: {integrity: sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==} + engines: {node: '>=18.0.0'} + '@smithy/property-provider@4.2.5': resolution: {integrity: sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==} engines: {node: '>=18.0.0'} @@ -3199,6 +3810,10 @@ packages: resolution: {integrity: sha512-ibHwLxq4KlbfueoNxMNrZkG+O7V/5XKrewhDGYn0p9DYKCsdsofuWHKdX3QW4zHlAUfLStqdCUSDi/q/9WSjwA==} engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.3.14': + resolution: {integrity: sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==} + engines: {node: '>=18.0.0'} + '@smithy/protocol-http@5.3.5': resolution: {integrity: sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==} engines: {node: '>=18.0.0'} @@ -3207,6 +3822,10 @@ packages: resolution: {integrity: sha512-PRy4yZqsKI3Eab8TLc16Dj2NzC4dnw/8E95+++Jc+wwlkjBpAq3tNLqkLHMmSvDfxKQ+X5PmmCYt+rM/GcMKPA==} engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.2.14': + resolution: {integrity: sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-builder@4.2.5': resolution: {integrity: sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==} engines: {node: '>=18.0.0'} @@ -3215,6 +3834,10 @@ packages: resolution: {integrity: sha512-/AIDaq0+ehv+QfeyAjCUFShwHIt+FA1IodsV/2AZE5h4PUZcQYv5sjmy9V67UWfsBoTjOPKUFYSRfGoNW9T2UQ==} engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.2.14': + resolution: {integrity: sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==} + engines: {node: '>=18.0.0'} + '@smithy/querystring-parser@4.2.5': resolution: {integrity: sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==} engines: {node: '>=18.0.0'} @@ -3231,6 +3854,10 @@ packages: resolution: {integrity: sha512-DYYd4xrm9Ozik+ZT4f5ZqSXdzscVHF/tFCzqieIFcLrjRDxWSgRtvtXOohJGoniLfPcBcy5ltR3tp2Lw4/d9ag==} engines: {node: '>=18.0.0'} + '@smithy/service-error-classification@4.3.1': + resolution: {integrity: sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==} + engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.4.0': resolution: {integrity: sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==} engines: {node: '>=18.0.0'} @@ -3239,6 +3866,14 @@ packages: resolution: {integrity: sha512-tA5Cm11BHQCk/67y6VPIWydLh/pMY90jqOEWIr/2VAzTOoDwGpwp0C/AuHBc3/xWSOA5m5PXLN+lIOrsnTm/PQ==} engines: {node: '>=18.0.0'} + '@smithy/shared-ini-file-loader@4.4.9': + resolution: {integrity: sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.14': + resolution: {integrity: sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==} + engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.3.5': resolution: {integrity: sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==} engines: {node: '>=18.0.0'} @@ -3251,18 +3886,30 @@ packages: resolution: {integrity: sha512-gQP2J3qB/Wmc26gdmB8gA6zq2o2spG5sEU3o7TaTATBJEk29sYGWdEFoGEy91BczSpifTo0DQhVYjZXBEVcrpA==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.9.3': - resolution: {integrity: sha512-8tlueuTgV5n7inQCkhyptrB3jo2AO80uGrps/XTYZivv5MFQKKBj3CIWIGMI2fRY5LEduIiazOhAWdFknY1O9w==} + '@smithy/smithy-client@4.12.13': + resolution: {integrity: sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.9.1': + resolution: {integrity: sha512-Ngb95ryR5A9xqvQFT5mAmYkCwbXvoLavLFwmi7zVg/IowFPCfiqRfkOKnbc/ZRL8ZKJ4f+Tp6kSu6wjDQb8L/g==} engines: {node: '>=18.0.0'} '@smithy/types@4.12.1': resolution: {integrity: sha512-ow30Ze/DD02KH2p0eMyIF2+qJzGyNb0kFrnTRtPpuOkQ4hrgvLdaU4YC6r/K8aOrCML4FH0Cmm0aI4503L1Hwg==} engines: {node: '>=18.0.0'} + '@smithy/types@4.14.1': + resolution: {integrity: sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==} + engines: {node: '>=18.0.0'} + '@smithy/types@4.9.0': resolution: {integrity: sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==} engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.2.14': + resolution: {integrity: sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==} + engines: {node: '>=18.0.0'} + '@smithy/url-parser@4.2.5': resolution: {integrity: sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==} engines: {node: '>=18.0.0'} @@ -3279,6 +3926,10 @@ packages: resolution: {integrity: sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==} engines: {node: '>=18.0.0'} + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.2.0': resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} engines: {node: '>=18.0.0'} @@ -3287,10 +3938,18 @@ packages: resolution: {integrity: sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==} engines: {node: '>=18.0.0'} + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-body-length-node@4.2.1': resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} engines: {node: '>=18.0.0'} + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} @@ -3303,6 +3962,10 @@ packages: resolution: {integrity: sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==} engines: {node: '>=18.0.0'} + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + '@smithy/util-config-provider@4.2.0': resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} engines: {node: '>=18.0.0'} @@ -3311,10 +3974,18 @@ packages: resolution: {integrity: sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==} engines: {node: '>=18.0.0'} + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.34': resolution: {integrity: sha512-m75CH7xaVG8ErlnfXsIBLrgVrApejrvUpohr41CMdeWNcEu/Ouvj9fbNA7oW9Qpr0Awf+BmDRrYx72hEKgY+FQ==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.49': + resolution: {integrity: sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-browser@4.3.6': resolution: {integrity: sha512-kbpuXbEf2YQ9zEE6eeVnUCQWO0e1BjMnKrXL8rfXgiWA0m8/E0leU4oSNzxP04WfCmW8vjEqaDeXWxwE4tpOjQ==} engines: {node: '>=18.0.0'} @@ -3323,6 +3994,10 @@ packages: resolution: {integrity: sha512-1LcAt0PV1dletxiGwcw2IJ8vLNhfkir02NTi1i/CFCY2ObtM5wDDjn/8V2dbPrbyoh6OTFH+uayI1rSVRBMT3A==} engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.2.54': + resolution: {integrity: sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==} + engines: {node: '>=18.0.0'} + '@smithy/util-defaults-mode-node@4.2.9': resolution: {integrity: sha512-dgyribrVWN5qE5usYJ0m5M93mVM3L3TyBPZWe1Xl6uZlH2gzfQx3dz+ZCdW93lWqdedJRkOecnvbnoEEXRZ5VQ==} engines: {node: '>=18.0.0'} @@ -3335,6 +4010,10 @@ packages: resolution: {integrity: sha512-9FTqTzKxCFelCKdtHb22BTbrLgw7tTI+D6r/Ci/njI0tzqWLQctS0uEDTzraCR5K6IJItfFp1QmESlBytSpRhQ==} engines: {node: '>=18.0.0'} + '@smithy/util-endpoints@3.4.2': + resolution: {integrity: sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==} + engines: {node: '>=18.0.0'} + '@smithy/util-hex-encoding@4.2.0': resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} engines: {node: '>=18.0.0'} @@ -3343,6 +4022,14 @@ packages: resolution: {integrity: sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==} engines: {node: '>=18.0.0'} + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.14': + resolution: {integrity: sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==} + engines: {node: '>=18.0.0'} + '@smithy/util-middleware@4.2.5': resolution: {integrity: sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==} engines: {node: '>=18.0.0'} @@ -3359,10 +4046,18 @@ packages: resolution: {integrity: sha512-79hfhL/oxP40SCXJGfjfE9pjbUVfHhXZFpCWXTHqXSluzaVy7jwWs9Ui7lLbfDBSp+7i+BIwgeVIRerbIRWN6g==} engines: {node: '>=18.0.0'} + '@smithy/util-retry@4.3.8': + resolution: {integrity: sha512-LUIxbTBi+OpvXpg91poGA6BdyoleMDLnfXjVDqyi2RvZmTveY5loE/FgYUBCR5LU2BThW2SoZRh8dTIIy38IPw==} + engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.14': resolution: {integrity: sha512-IOBEiJTOltSx6MAfwkx/GSVM8/UCJxdtw13haP5OEL543lb1DN6TAypsxv+qcj4l/rKcpapbS6zK9MQGBOhoaA==} engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.25': + resolution: {integrity: sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==} + engines: {node: '>=18.0.0'} + '@smithy/util-stream@4.5.6': resolution: {integrity: sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==} engines: {node: '>=18.0.0'} @@ -3375,6 +4070,10 @@ packages: resolution: {integrity: sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==} engines: {node: '>=18.0.0'} + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} @@ -3387,6 +4086,10 @@ packages: resolution: {integrity: sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==} engines: {node: '>=18.0.0'} + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + '@smithy/util-waiter@4.2.5': resolution: {integrity: sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==} engines: {node: '>=18.0.0'} @@ -3403,6 +4106,13 @@ packages: resolution: {integrity: sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==} engines: {node: '>=18.0.0'} + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + + '@so-ric/colorspace@1.1.6': + resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@stylistic/eslint-plugin@3.1.0': resolution: {integrity: sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3467,6 +4177,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/cors@2.8.19': resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} @@ -3482,6 +4195,9 @@ packages: '@types/express@5.0.3': resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + '@types/express@5.0.4': + resolution: {integrity: sha512-g64dbryHk7loCIrsa0R3shBnEu5p6LPJ09bu9NG58+jz+cRUjFrc3Bz0kNQ7j9bXeCsrRDvNET1G54P/GJkAyA==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -3521,6 +4237,9 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + '@types/mime-types@3.0.1': resolution: {integrity: sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==} @@ -3539,6 +4258,9 @@ packages: '@types/node@22.19.0': resolution: {integrity: sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==} + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3563,6 +4285,9 @@ packages: '@types/shimmer@1.2.0': resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + '@types/sinon@17.0.4': + resolution: {integrity: sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==} + '@types/sinon@21.0.0': resolution: {integrity: sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==} @@ -3572,12 +4297,21 @@ packages: '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@types/tar-fs@2.0.4': resolution: {integrity: sha512-ipPec0CjTmVDWE+QKr9cTmIIoTl7dFG/yARCM5MqK8i6CNLIG1P8x4kwDsOQY1ChZOZjH0wO9nvfgBvWl4R3kA==} '@types/tar-stream@3.1.4': resolution: {integrity: sha512-921gW0+g29mCJX0fRvqeHzBlE/XclDaAG0Ousy1LCghsOhvaKacDeRGEVzQP9IPfKn8Vysy7FEXAIxycpc/CMg==} + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -4077,6 +4811,9 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -4113,6 +4850,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + aws-sdk-client-mock@4.1.0: + resolution: {integrity: sha512-h/tOYTkXEsAcV3//6C1/7U4ifSpKyJvb6auveAepqqNJl6TdZaPFEtKjBQNf8UxQdDP850knB2i/whq4zlsxJw==} + azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} @@ -4176,6 +4916,10 @@ packages: resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} hasBin: true + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -4456,9 +5200,25 @@ packages: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-convert@3.1.3: + resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} + engines: {node: '>=14.6'} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@2.1.0: + resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} + engines: {node: '>=12.20'} + + color-string@2.1.4: + resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} + engines: {node: '>=18'} + + color@5.0.3: + resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} + engines: {node: '>=18'} + colorette@1.4.0: resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} @@ -4484,6 +5244,9 @@ packages: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -4549,6 +5312,9 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + core-js-compat@3.46.0: resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} @@ -4704,6 +5470,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + diagnostic-channel-publishers@1.0.8: resolution: {integrity: sha512-HmSm9hXxSPxA9BaLGY98QU1zsdjeCk113KjAYGPCen1ZP6mhVaTPzHd6UYv5r21DnWANi+f+NyPOHruGT9jpqQ==} peerDependencies: @@ -4788,6 +5557,9 @@ packages: emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -4857,6 +5629,11 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -5134,6 +5911,9 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + express-basic-auth@1.2.1: + resolution: {integrity: sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==} + express-rate-limit@8.2.1: resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} engines: {node: '>= 16'} @@ -5186,6 +5966,9 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-xml-builder@1.1.5: + resolution: {integrity: sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==} + fast-xml-parser@5.3.5: resolution: {integrity: sha512-JeaA2Vm9ffQKp9VjvfzObuMCjUYAp5WDYhRYL5LrBPY/jUDlUtOvDfot0vKSkB9tuX885BDHjtw4fZadD95wnA==} hasBin: true @@ -5194,12 +5977,16 @@ packages: resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} hasBin: true + fast-xml-parser@5.7.2: + resolution: {integrity: sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==} + hasBin: true + fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -5210,6 +5997,9 @@ packages: picomatch: optional: true + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -5262,6 +6052,9 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + focus-trap@8.0.1: resolution: {integrity: sha512-9ptSG6z51YQOstI/oN4XuVGP/03u2nh0g//qz7L6zX0i6PZiPnkcf3GenXq7N2hZnASXaMxTPpbKwdI+PFvxlw==} @@ -5294,6 +6087,10 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -5309,8 +6106,8 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.3: - resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} engines: {node: '>=14.14'} fs-extra@7.0.1: @@ -5622,10 +6419,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} - engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} @@ -6017,6 +6810,9 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + just-extend@6.2.0: + resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} + jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} @@ -6029,6 +6825,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -6099,6 +6898,10 @@ packages: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -6251,6 +7054,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} @@ -6384,6 +7192,9 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + nise@6.1.5: + resolution: {integrity: sha512-SnRDPDBjxZZoU2n0+gzzLtSvo1OZo7j6jnbXsoh3AFxEGhaFU7ZF0TmefuKERq79wxR2U+MPn7ArW+Tl+clC3A==} + no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -6578,6 +7389,9 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -6741,6 +7555,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.5.0: + resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -6876,8 +7694,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} prettier@2.8.8: @@ -7228,6 +8046,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -7328,6 +8151,9 @@ packages: simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + sinon@18.0.1: + resolution: {integrity: sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==} + sinon@21.0.1: resolution: {integrity: sha512-Z0NVCW45W8Mg5oC/27/+fCqIHFnW8kpkFOq0j9XJIev4Ld0mKmERaZv5DMLAb9fGCevjKwaEeIQz5+MBXfZcDw==} @@ -7401,6 +8227,9 @@ packages: stack-chain@1.3.7: resolution: {integrity: sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug==} + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -7491,9 +8320,20 @@ packages: strnum@2.1.2: resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + strnum@2.2.3: + resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==} + structured-source@4.0.0: resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} + superagent@10.3.0: + resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} + engines: {node: '>=14.18.0'} + + supertest@7.1.4: + resolution: {integrity: sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==} + engines: {node: '>=14.18.0'} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -7517,8 +8357,8 @@ packages: swagger-ui-dist@5.32.0: resolution: {integrity: sha512-nKZB0OuDvacB0s/lC2gbge+RigYvGRGpLLMWMFxaTUwfM+CfndVk9Th2IaTinqXiz6Mn26GK2zriCpv6/+5m3Q==} - synckit@0.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} tabbable@6.4.0: @@ -7574,6 +8414,9 @@ packages: text-decoder@1.2.3: resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -7635,6 +8478,10 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -7795,6 +8642,9 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici@7.19.2: resolution: {integrity: sha512-4VQSpGEGsWzk0VYxyB/wVX/Q7qf9t5znLRgs0dzszr9w9Fej/8RVNQ+S20vdXSAyra/bJ7ZQfGv6ZMj7UEbzSg==} engines: {node: '>=20.18.1'} @@ -8042,6 +8892,14 @@ packages: resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} engines: {node: '>=8'} + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.19.0: + resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} + engines: {node: '>= 12.0.0'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -8190,20 +9048,20 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.922.0 + '@aws-sdk/types': 3.973.1 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.922.0 + '@aws-sdk/types': 3.973.1 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.922.0 + '@aws-sdk/types': 3.973.1 '@aws-sdk/util-locate-window': 3.893.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -8213,7 +9071,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.936.0 + '@aws-sdk/types': 3.973.1 '@aws-sdk/util-locate-window': 3.893.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -8221,7 +9079,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.936.0 + '@aws-sdk/types': 3.973.1 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -8230,7 +9088,7 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.936.0 + '@aws-sdk/types': 3.973.1 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -8263,7 +9121,7 @@ snapshots: '@smithy/node-config-provider': 4.3.5 '@smithy/node-http-handler': 4.4.5 '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.3 + '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.9.0 '@smithy/url-parser': 4.2.5 '@smithy/util-base64': 4.3.0 @@ -8281,6 +9139,53 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/client-cloudwatch-logs@3.952.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.947.0 + '@aws-sdk/credential-provider-node': 3.952.0 + '@aws-sdk/middleware-host-header': 3.936.0 + '@aws-sdk/middleware-logger': 3.936.0 + '@aws-sdk/middleware-recursion-detection': 3.948.0 + '@aws-sdk/middleware-user-agent': 3.947.0 + '@aws-sdk/region-config-resolver': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 + '@aws-sdk/util-user-agent-browser': 3.936.0 + '@aws-sdk/util-user-agent-node': 3.947.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/core': 3.23.4 + '@smithy/eventstream-serde-browser': 4.2.5 + '@smithy/eventstream-serde-config-resolver': 4.3.5 + '@smithy/eventstream-serde-node': 4.2.5 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-node': 4.2.9 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/middleware-content-length': 4.2.9 + '@smithy/middleware-endpoint': 4.4.18 + '@smithy/middleware-retry': 4.4.35 + '@smithy/middleware-serde': 4.2.10 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.34 + '@smithy/util-defaults-mode-node': 4.2.37 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/client-cloudwatch@3.952.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -8360,15 +9265,64 @@ snapshots: '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.12.1 '@smithy/url-parser': 4.2.9 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.34 '@smithy/util-defaults-mode-node': 4.2.37 '@smithy/util-endpoints': 3.2.9 '@smithy/util-middleware': 4.2.9 '@smithy/util-retry': 4.2.9 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 + '@smithy/util-waiter': 4.2.9 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-lambda@3.952.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.947.0 + '@aws-sdk/credential-provider-node': 3.952.0 + '@aws-sdk/middleware-host-header': 3.936.0 + '@aws-sdk/middleware-logger': 3.936.0 + '@aws-sdk/middleware-recursion-detection': 3.948.0 + '@aws-sdk/middleware-user-agent': 3.947.0 + '@aws-sdk/region-config-resolver': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 + '@aws-sdk/util-user-agent-browser': 3.936.0 + '@aws-sdk/util-user-agent-node': 3.947.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/core': 3.23.4 + '@smithy/eventstream-serde-browser': 4.2.5 + '@smithy/eventstream-serde-config-resolver': 4.3.5 + '@smithy/eventstream-serde-node': 4.2.5 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-node': 4.2.9 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/middleware-content-length': 4.2.9 + '@smithy/middleware-endpoint': 4.4.18 + '@smithy/middleware-retry': 4.4.35 + '@smithy/middleware-serde': 4.2.10 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.34 + '@smithy/util-defaults-mode-node': 4.2.37 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 '@smithy/util-waiter': 4.2.9 tslib: 2.8.1 transitivePeerDependencies: @@ -8417,7 +9371,7 @@ snapshots: '@smithy/node-config-provider': 4.3.5 '@smithy/node-http-handler': 4.4.5 '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.3 + '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.9.0 '@smithy/url-parser': 4.2.5 '@smithy/util-base64': 4.3.0 @@ -8436,6 +9390,110 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/client-s3@3.952.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.947.0 + '@aws-sdk/credential-provider-node': 3.952.0 + '@aws-sdk/middleware-bucket-endpoint': 3.936.0 + '@aws-sdk/middleware-expect-continue': 3.936.0 + '@aws-sdk/middleware-flexible-checksums': 3.947.0 + '@aws-sdk/middleware-host-header': 3.936.0 + '@aws-sdk/middleware-location-constraint': 3.936.0 + '@aws-sdk/middleware-logger': 3.936.0 + '@aws-sdk/middleware-recursion-detection': 3.948.0 + '@aws-sdk/middleware-sdk-s3': 3.947.0 + '@aws-sdk/middleware-ssec': 3.936.0 + '@aws-sdk/middleware-user-agent': 3.947.0 + '@aws-sdk/region-config-resolver': 3.936.0 + '@aws-sdk/signature-v4-multi-region': 3.947.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 + '@aws-sdk/util-user-agent-browser': 3.936.0 + '@aws-sdk/util-user-agent-node': 3.947.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/core': 3.23.4 + '@smithy/eventstream-serde-browser': 4.2.5 + '@smithy/eventstream-serde-config-resolver': 4.3.5 + '@smithy/eventstream-serde-node': 4.2.5 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-blob-browser': 4.2.6 + '@smithy/hash-node': 4.2.9 + '@smithy/hash-stream-node': 4.2.5 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/md5-js': 4.2.5 + '@smithy/middleware-content-length': 4.2.9 + '@smithy/middleware-endpoint': 4.4.18 + '@smithy/middleware-retry': 4.4.35 + '@smithy/middleware-serde': 4.2.10 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.1 + '@smithy/util-defaults-mode-browser': 4.3.34 + '@smithy/util-defaults-mode-node': 4.2.37 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 + '@smithy/util-waiter': 4.2.9 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-secrets-manager@3.1004.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.8 + '@aws-sdk/credential-provider-node': 3.972.39 + '@aws-sdk/middleware-host-header': 3.972.10 + '@aws-sdk/middleware-logger': 3.972.10 + '@aws-sdk/middleware-recursion-detection': 3.972.11 + '@aws-sdk/middleware-user-agent': 3.972.38 + '@aws-sdk/region-config-resolver': 3.972.13 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-endpoints': 3.996.8 + '@aws-sdk/util-user-agent-browser': 3.972.10 + '@aws-sdk/util-user-agent-node': 3.973.24 + '@smithy/config-resolver': 4.4.17 + '@smithy/core': 3.23.17 + '@smithy/fetch-http-handler': 5.3.17 + '@smithy/hash-node': 4.2.14 + '@smithy/invalid-dependency': 4.2.14 + '@smithy/middleware-content-length': 4.2.14 + '@smithy/middleware-endpoint': 4.4.32 + '@smithy/middleware-retry': 4.5.7 + '@smithy/middleware-serde': 4.2.20 + '@smithy/middleware-stack': 4.2.14 + '@smithy/node-config-provider': 4.3.14 + '@smithy/node-http-handler': 4.6.1 + '@smithy/protocol-http': 5.3.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + '@smithy/url-parser': 4.2.14 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.49 + '@smithy/util-defaults-mode-node': 4.2.54 + '@smithy/util-endpoints': 3.4.2 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-retry': 4.3.8 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/client-sso@3.929.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -8450,31 +9508,31 @@ snapshots: '@aws-sdk/util-endpoints': 3.922.0 '@aws-sdk/util-user-agent-browser': 3.922.0 '@aws-sdk/util-user-agent-node': 3.928.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.0 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.7 - '@smithy/middleware-retry': 4.4.7 - '@smithy/middleware-serde': 4.2.5 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/core': 3.23.4 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-node': 4.2.9 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/middleware-content-length': 4.2.9 + '@smithy/middleware-endpoint': 4.4.18 + '@smithy/middleware-retry': 4.4.35 + '@smithy/middleware-serde': 4.2.10 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.6 - '@smithy/util-defaults-mode-node': 4.2.9 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-defaults-mode-browser': 4.3.34 + '@smithy/util-defaults-mode-node': 4.2.37 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8493,31 +9551,31 @@ snapshots: '@aws-sdk/util-endpoints': 3.936.0 '@aws-sdk/util-user-agent-browser': 3.936.0 '@aws-sdk/util-user-agent-node': 3.947.0 - '@smithy/config-resolver': 4.4.3 + '@smithy/config-resolver': 4.4.7 '@smithy/core': 3.23.4 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-node': 4.2.9 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/middleware-content-length': 4.2.9 '@smithy/middleware-endpoint': 4.4.18 '@smithy/middleware-retry': 4.4.35 '@smithy/middleware-serde': 4.2.10 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 '@smithy/smithy-client': 4.11.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.34 '@smithy/util-defaults-mode-node': 4.2.37 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8552,15 +9610,15 @@ snapshots: '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.12.1 '@smithy/url-parser': 4.2.9 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.34 '@smithy/util-defaults-mode-node': 4.2.37 '@smithy/util-endpoints': 3.2.9 '@smithy/util-middleware': 4.2.9 '@smithy/util-retry': 4.2.9 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8569,16 +9627,16 @@ snapshots: dependencies: '@aws-sdk/types': 3.922.0 '@aws-sdk/xml-builder': 3.921.0 - '@smithy/core': 3.18.0 - '@smithy/node-config-provider': 4.3.5 + '@smithy/core': 3.23.4 + '@smithy/node-config-provider': 4.3.9 '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/protocol-http': 5.3.9 '@smithy/signature-v4': 5.3.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-utf8': 4.2.0 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/util-base64': 4.3.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@aws-sdk/core@3.947.0': @@ -8586,15 +9644,15 @@ snapshots: '@aws-sdk/types': 3.936.0 '@aws-sdk/xml-builder': 3.930.0 '@smithy/core': 3.23.4 - '@smithy/node-config-provider': 4.3.5 - '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/signature-v4': 5.3.5 + '@smithy/node-config-provider': 4.3.9 + '@smithy/property-provider': 4.2.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/signature-v4': 5.3.9 '@smithy/smithy-client': 4.11.7 - '@smithy/types': 4.9.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.12.1 + '@smithy/util-base64': 4.3.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@aws-sdk/core@3.973.12': @@ -8608,25 +9666,42 @@ snapshots: '@smithy/signature-v4': 5.3.9 '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.12.1 - '@smithy/util-base64': 4.3.0 + '@smithy/util-base64': 4.3.1 '@smithy/util-middleware': 4.2.9 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/core@3.974.8': + dependencies: + '@aws-sdk/types': 3.973.8 + '@aws-sdk/xml-builder': 3.972.22 + '@smithy/core': 3.23.17 + '@smithy/node-config-provider': 4.3.14 + '@smithy/property-provider': 4.2.14 + '@smithy/protocol-http': 5.3.14 + '@smithy/signature-v4': 5.3.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-retry': 4.3.8 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 '@aws-sdk/credential-provider-env@3.928.0': dependencies: '@aws-sdk/core': 3.928.0 '@aws-sdk/types': 3.922.0 - '@smithy/property-provider': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/credential-provider-env@3.947.0': dependencies: '@aws-sdk/core': 3.947.0 '@aws-sdk/types': 3.936.0 - '@smithy/property-provider': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/credential-provider-env@3.972.10': @@ -8637,30 +9712,38 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.972.34': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/types': 3.973.8 + '@smithy/property-provider': 4.2.14 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.928.0': dependencies: '@aws-sdk/core': 3.928.0 '@aws-sdk/types': 3.922.0 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/node-http-handler': 4.4.5 - '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 - '@smithy/util-stream': 4.5.6 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/node-http-handler': 4.4.11 + '@smithy/property-provider': 4.2.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/util-stream': 4.5.14 tslib: 2.8.1 '@aws-sdk/credential-provider-http@3.947.0': dependencies: '@aws-sdk/core': 3.947.0 '@aws-sdk/types': 3.936.0 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/node-http-handler': 4.4.5 - '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/node-http-handler': 4.4.11 + '@smithy/property-provider': 4.2.9 + '@smithy/protocol-http': 5.3.9 '@smithy/smithy-client': 4.11.7 - '@smithy/types': 4.9.0 - '@smithy/util-stream': 4.5.6 + '@smithy/types': 4.12.1 + '@smithy/util-stream': 4.5.14 tslib: 2.8.1 '@aws-sdk/credential-provider-http@3.972.12': @@ -8676,6 +9759,19 @@ snapshots: '@smithy/util-stream': 4.5.14 tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.36': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/types': 3.973.8 + '@smithy/fetch-http-handler': 5.3.17 + '@smithy/node-http-handler': 4.6.1 + '@smithy/property-provider': 4.2.14 + '@smithy/protocol-http': 5.3.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + '@smithy/util-stream': 4.5.25 + tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.929.0': dependencies: '@aws-sdk/core': 3.928.0 @@ -8686,10 +9782,10 @@ snapshots: '@aws-sdk/credential-provider-web-identity': 3.929.0 '@aws-sdk/nested-clients': 3.929.0 '@aws-sdk/types': 3.922.0 - '@smithy/credential-provider-imds': 4.2.5 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/credential-provider-imds': 4.2.9 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8705,10 +9801,10 @@ snapshots: '@aws-sdk/credential-provider-web-identity': 3.952.0 '@aws-sdk/nested-clients': 3.952.0 '@aws-sdk/types': 3.936.0 - '@smithy/credential-provider-imds': 4.2.5 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/credential-provider-imds': 4.2.9 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8732,15 +9828,34 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-ini@3.972.38': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/credential-provider-env': 3.972.34 + '@aws-sdk/credential-provider-http': 3.972.36 + '@aws-sdk/credential-provider-login': 3.972.38 + '@aws-sdk/credential-provider-process': 3.972.34 + '@aws-sdk/credential-provider-sso': 3.972.38 + '@aws-sdk/credential-provider-web-identity': 3.972.38 + '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/types': 3.973.8 + '@smithy/credential-provider-imds': 4.2.14 + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-login@3.952.0': dependencies: '@aws-sdk/core': 3.947.0 '@aws-sdk/nested-clients': 3.952.0 '@aws-sdk/types': 3.936.0 - '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8758,6 +9873,19 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-login@3.972.38': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/types': 3.973.8 + '@smithy/property-provider': 4.2.14 + '@smithy/protocol-http': 5.3.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-node@3.929.0': dependencies: '@aws-sdk/credential-provider-env': 3.928.0 @@ -8770,7 +9898,7 @@ snapshots: '@smithy/credential-provider-imds': 4.2.5 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8784,10 +9912,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.952.0 '@aws-sdk/credential-provider-web-identity': 3.952.0 '@aws-sdk/types': 3.936.0 - '@smithy/credential-provider-imds': 4.2.5 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/credential-provider-imds': 4.2.9 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8809,22 +9937,39 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-node@3.972.39': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.34 + '@aws-sdk/credential-provider-http': 3.972.36 + '@aws-sdk/credential-provider-ini': 3.972.38 + '@aws-sdk/credential-provider-process': 3.972.34 + '@aws-sdk/credential-provider-sso': 3.972.38 + '@aws-sdk/credential-provider-web-identity': 3.972.38 + '@aws-sdk/types': 3.973.8 + '@smithy/credential-provider-imds': 4.2.14 + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-process@3.928.0': dependencies: '@aws-sdk/core': 3.928.0 '@aws-sdk/types': 3.922.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/credential-provider-process@3.947.0': dependencies: '@aws-sdk/core': 3.947.0 '@aws-sdk/types': 3.936.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/credential-provider-process@3.972.10': @@ -8836,15 +9981,24 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.972.34': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/types': 3.973.8 + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-sso@3.929.0': dependencies: '@aws-sdk/client-sso': 3.929.0 '@aws-sdk/core': 3.928.0 '@aws-sdk/token-providers': 3.929.0 '@aws-sdk/types': 3.922.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8855,9 +10009,9 @@ snapshots: '@aws-sdk/core': 3.947.0 '@aws-sdk/token-providers': 3.952.0 '@aws-sdk/types': 3.936.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8875,14 +10029,27 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-sso@3.972.38': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/token-providers': 3.1041.0 + '@aws-sdk/types': 3.973.8 + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/credential-provider-web-identity@3.929.0': dependencies: '@aws-sdk/core': 3.928.0 '@aws-sdk/nested-clients': 3.929.0 '@aws-sdk/types': 3.922.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8892,9 +10059,9 @@ snapshots: '@aws-sdk/core': 3.947.0 '@aws-sdk/nested-clients': 3.952.0 '@aws-sdk/types': 3.936.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -8911,13 +10078,25 @@ snapshots: transitivePeerDependencies: - aws-crt + '@aws-sdk/credential-provider-web-identity@3.972.38': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/types': 3.973.8 + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/dynamodb-codec@3.972.13': dependencies: '@aws-sdk/core': 3.973.12 '@smithy/core': 3.23.4 '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.12.1 - '@smithy/util-base64': 4.3.0 + '@smithy/util-base64': 4.3.1 tslib: 2.8.1 '@aws-sdk/endpoint-cache@3.972.2': @@ -8939,12 +10118,22 @@ snapshots: dependencies: '@aws-sdk/types': 3.922.0 '@aws-sdk/util-arn-parser': 3.893.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 '@smithy/util-config-provider': 4.2.0 tslib: 2.8.1 + '@aws-sdk/middleware-bucket-endpoint@3.936.0': + dependencies: + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-arn-parser': 3.893.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + '@smithy/util-config-provider': 4.2.1 + tslib: 2.8.1 + '@aws-sdk/middleware-endpoint-discovery@3.972.3': dependencies: '@aws-sdk/endpoint-cache': 3.972.2 @@ -8957,8 +10146,15 @@ snapshots: '@aws-sdk/middleware-expect-continue@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.936.0': + dependencies: + '@aws-sdk/types': 3.936.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-flexible-checksums@3.928.0': @@ -8969,26 +10165,49 @@ snapshots: '@aws-sdk/core': 3.928.0 '@aws-sdk/types': 3.922.0 '@smithy/is-array-buffer': 4.2.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-stream': 4.5.6 - '@smithy/util-utf8': 4.2.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-flexible-checksums@3.947.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.947.0 + '@aws-sdk/types': 3.936.0 + '@smithy/is-array-buffer': 4.2.1 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@aws-sdk/middleware-host-header@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-host-header@3.936.0': dependencies: '@aws-sdk/types': 3.936.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 tslib: 2.8.1 '@aws-sdk/middleware-host-header@3.972.3': @@ -9001,19 +10220,31 @@ snapshots: '@aws-sdk/middleware-location-constraint@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-location-constraint@3.936.0': + dependencies: + '@aws-sdk/types': 3.936.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-logger@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-logger@3.936.0': dependencies: '@aws-sdk/types': 3.936.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/types': 4.14.1 tslib: 2.8.1 '@aws-sdk/middleware-logger@3.972.3': @@ -9026,16 +10257,24 @@ snapshots: dependencies: '@aws-sdk/types': 3.922.0 '@aws/lambda-invoke-store': 0.1.1 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-recursion-detection@3.948.0': dependencies: '@aws-sdk/types': 3.936.0 '@aws/lambda-invoke-store': 0.2.3 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.11': + dependencies: + '@aws-sdk/types': 3.973.8 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 tslib: 2.8.1 '@aws-sdk/middleware-recursion-detection@3.972.3': @@ -9051,22 +10290,62 @@ snapshots: '@aws-sdk/core': 3.928.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-arn-parser': 3.893.0 - '@smithy/core': 3.18.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/core': 3.23.4 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 '@smithy/signature-v4': 5.3.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 '@smithy/util-config-provider': 4.2.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-stream': 4.5.6 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.947.0': + dependencies: + '@aws-sdk/core': 3.947.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-arn-parser': 3.893.0 + '@smithy/core': 3.23.4 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/signature-v4': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/util-config-provider': 4.2.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.37': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/core': 3.23.17 + '@smithy/node-config-provider': 4.3.14 + '@smithy/protocol-http': 5.3.14 + '@smithy/signature-v4': 5.3.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-stream': 4.5.25 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 '@aws-sdk/middleware-ssec@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-ssec@3.936.0': + dependencies: + '@aws-sdk/types': 3.936.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-user-agent@3.928.0': @@ -9074,9 +10353,9 @@ snapshots: '@aws-sdk/core': 3.928.0 '@aws-sdk/types': 3.922.0 '@aws-sdk/util-endpoints': 3.922.0 - '@smithy/core': 3.18.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/core': 3.23.4 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-user-agent@3.947.0': @@ -9085,8 +10364,8 @@ snapshots: '@aws-sdk/types': 3.936.0 '@aws-sdk/util-endpoints': 3.936.0 '@smithy/core': 3.23.4 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/middleware-user-agent@3.972.12': @@ -9099,6 +10378,17 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@aws-sdk/middleware-user-agent@3.972.38': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-endpoints': 3.996.8 + '@smithy/core': 3.23.17 + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 + '@smithy/util-retry': 4.3.8 + tslib: 2.8.1 + '@aws-sdk/nested-clients@3.929.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -9113,31 +10403,31 @@ snapshots: '@aws-sdk/util-endpoints': 3.922.0 '@aws-sdk/util-user-agent-browser': 3.922.0 '@aws-sdk/util-user-agent-node': 3.928.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.0 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.7 - '@smithy/middleware-retry': 4.4.7 - '@smithy/middleware-serde': 4.2.5 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/core': 3.23.4 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-node': 4.2.9 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/middleware-content-length': 4.2.9 + '@smithy/middleware-endpoint': 4.4.18 + '@smithy/middleware-retry': 4.4.35 + '@smithy/middleware-serde': 4.2.10 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.6 - '@smithy/util-defaults-mode-node': 4.2.9 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-defaults-mode-browser': 4.3.34 + '@smithy/util-defaults-mode-node': 4.2.37 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -9156,31 +10446,31 @@ snapshots: '@aws-sdk/util-endpoints': 3.936.0 '@aws-sdk/util-user-agent-browser': 3.936.0 '@aws-sdk/util-user-agent-node': 3.947.0 - '@smithy/config-resolver': 4.4.3 + '@smithy/config-resolver': 4.4.7 '@smithy/core': 3.23.4 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/hash-node': 4.2.9 + '@smithy/invalid-dependency': 4.2.9 + '@smithy/middleware-content-length': 4.2.9 '@smithy/middleware-endpoint': 4.4.18 '@smithy/middleware-retry': 4.4.35 '@smithy/middleware-serde': 4.2.10 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/middleware-stack': 4.2.9 + '@smithy/node-config-provider': 4.3.9 + '@smithy/node-http-handler': 4.4.11 + '@smithy/protocol-http': 5.3.9 '@smithy/smithy-client': 4.11.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.34 '@smithy/util-defaults-mode-node': 4.2.37 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -9215,15 +10505,59 @@ snapshots: '@smithy/smithy-client': 4.11.7 '@smithy/types': 4.12.1 '@smithy/url-parser': 4.2.9 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 '@smithy/util-body-length-node': 4.2.1 '@smithy/util-defaults-mode-browser': 4.3.34 '@smithy/util-defaults-mode-node': 4.2.37 '@smithy/util-endpoints': 3.2.9 '@smithy/util-middleware': 4.2.9 '@smithy/util-retry': 4.2.9 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/nested-clients@3.997.6': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.8 + '@aws-sdk/middleware-host-header': 3.972.10 + '@aws-sdk/middleware-logger': 3.972.10 + '@aws-sdk/middleware-recursion-detection': 3.972.11 + '@aws-sdk/middleware-user-agent': 3.972.38 + '@aws-sdk/region-config-resolver': 3.972.13 + '@aws-sdk/signature-v4-multi-region': 3.996.25 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-endpoints': 3.996.8 + '@aws-sdk/util-user-agent-browser': 3.972.10 + '@aws-sdk/util-user-agent-node': 3.973.24 + '@smithy/config-resolver': 4.4.17 + '@smithy/core': 3.23.17 + '@smithy/fetch-http-handler': 5.3.17 + '@smithy/hash-node': 4.2.14 + '@smithy/invalid-dependency': 4.2.14 + '@smithy/middleware-content-length': 4.2.14 + '@smithy/middleware-endpoint': 4.4.32 + '@smithy/middleware-retry': 4.5.7 + '@smithy/middleware-serde': 4.2.20 + '@smithy/middleware-stack': 4.2.14 + '@smithy/node-config-provider': 4.3.14 + '@smithy/node-http-handler': 4.6.1 + '@smithy/protocol-http': 5.3.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + '@smithy/url-parser': 4.2.14 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.49 + '@smithy/util-defaults-mode-node': 4.2.54 + '@smithy/util-endpoints': 3.4.2 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-retry': 4.3.8 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -9231,17 +10565,25 @@ snapshots: '@aws-sdk/region-config-resolver@3.925.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/node-config-provider': 4.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/region-config-resolver@3.936.0': dependencies: '@aws-sdk/types': 3.936.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/config-resolver': 4.4.7 + '@smithy/node-config-provider': 4.3.9 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/region-config-resolver@3.972.13': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/config-resolver': 4.4.17 + '@smithy/node-config-provider': 4.3.14 + '@smithy/types': 4.14.1 tslib: 2.8.1 '@aws-sdk/region-config-resolver@3.972.3': @@ -9256,19 +10598,49 @@ snapshots: dependencies: '@aws-sdk/middleware-sdk-s3': 3.928.0 '@aws-sdk/types': 3.922.0 - '@smithy/protocol-http': 5.3.5 + '@smithy/protocol-http': 5.3.9 '@smithy/signature-v4': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.947.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.947.0 + '@aws-sdk/types': 3.936.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/signature-v4': 5.3.9 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.25': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.972.37 + '@aws-sdk/types': 3.973.8 + '@smithy/protocol-http': 5.3.14 + '@smithy/signature-v4': 5.3.14 + '@smithy/types': 4.14.1 tslib: 2.8.1 + '@aws-sdk/token-providers@3.1041.0': + dependencies: + '@aws-sdk/core': 3.974.8 + '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/types': 3.973.8 + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/token-providers@3.929.0': dependencies: '@aws-sdk/core': 3.928.0 '@aws-sdk/nested-clients': 3.929.0 '@aws-sdk/types': 3.922.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -9278,9 +10650,9 @@ snapshots: '@aws-sdk/core': 3.947.0 '@aws-sdk/nested-clients': 3.952.0 '@aws-sdk/types': 3.936.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.9 + '@smithy/shared-ini-file-loader': 4.4.4 + '@smithy/types': 4.12.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -9299,12 +10671,12 @@ snapshots: '@aws-sdk/types@3.922.0': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/types@3.936.0': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/types@3.973.1': @@ -9312,10 +10684,19 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@aws-sdk/types@3.973.8': + dependencies: + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@aws-sdk/util-arn-parser@3.893.0': dependencies: tslib: 2.8.1 + '@aws-sdk/util-arn-parser@3.972.3': + dependencies: + tslib: 2.8.1 + '@aws-sdk/util-dynamodb@3.980.0(@aws-sdk/client-dynamodb@3.980.0)': dependencies: '@aws-sdk/client-dynamodb': 3.980.0 @@ -9324,17 +10705,17 @@ snapshots: '@aws-sdk/util-endpoints@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-endpoints': 3.2.5 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-endpoints': 3.2.9 tslib: 2.8.1 '@aws-sdk/util-endpoints@3.936.0': dependencies: '@aws-sdk/types': 3.936.0 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-endpoints': 3.2.5 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-endpoints': 3.2.9 tslib: 2.8.1 '@aws-sdk/util-endpoints@3.980.0': @@ -9353,6 +10734,14 @@ snapshots: '@smithy/util-endpoints': 3.2.9 tslib: 2.8.1 + '@aws-sdk/util-endpoints@3.996.8': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/types': 4.14.1 + '@smithy/url-parser': 4.2.14 + '@smithy/util-endpoints': 3.4.2 + tslib: 2.8.1 + '@aws-sdk/util-locate-window@3.893.0': dependencies: tslib: 2.8.1 @@ -9360,14 +10749,21 @@ snapshots: '@aws-sdk/util-user-agent-browser@3.922.0': dependencies: '@aws-sdk/types': 3.922.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 bowser: 2.12.1 tslib: 2.8.1 '@aws-sdk/util-user-agent-browser@3.936.0': dependencies: '@aws-sdk/types': 3.936.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + bowser: 2.12.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/types': 4.14.1 bowser: 2.12.1 tslib: 2.8.1 @@ -9382,16 +10778,16 @@ snapshots: dependencies: '@aws-sdk/middleware-user-agent': 3.928.0 '@aws-sdk/types': 3.922.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/util-user-agent-node@3.947.0': dependencies: '@aws-sdk/middleware-user-agent': 3.947.0 '@aws-sdk/types': 3.936.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@aws-sdk/util-user-agent-node@3.972.11': @@ -9402,16 +10798,32 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@aws-sdk/util-user-agent-node@3.973.24': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.38 + '@aws-sdk/types': 3.973.8 + '@smithy/node-config-provider': 4.3.14 + '@smithy/types': 4.14.1 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + '@aws-sdk/xml-builder@3.921.0': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 fast-xml-parser: 5.3.5 tslib: 2.8.1 '@aws-sdk/xml-builder@3.930.0': dependencies: - '@smithy/types': 4.9.0 - fast-xml-parser: 5.3.5 + '@smithy/types': 4.12.1 + fast-xml-parser: 5.3.6 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.22': + dependencies: + '@nodable/entities': 2.1.0 + '@smithy/types': 4.14.1 + fast-xml-parser: 5.7.2 tslib: 2.8.1 '@aws-sdk/xml-builder@3.972.5': @@ -9562,6 +10974,8 @@ snapshots: '@babel/runtime@7.28.4': {} + '@babel/runtime@7.29.2': {} + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -9569,9 +10983,9 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@changesets/apply-release-plan@7.0.14': + '@changesets/apply-release-plan@7.1.1': dependencies: - '@changesets/config': 3.1.2 + '@changesets/config': 3.1.4 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.4 '@changesets/should-skip-package': 0.1.2 @@ -9583,16 +10997,16 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 - '@changesets/assemble-release-plan@6.0.9': + '@changesets/assemble-release-plan@6.0.10': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-dependents-graph': 2.1.4 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - semver: 7.7.3 + semver: 7.7.4 '@changesets/changelog-git@0.2.1': dependencies: @@ -9606,44 +11020,43 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/cli@2.29.8(@types/node@22.19.0)': + '@changesets/cli@2.31.0(@types/node@25.6.0)': dependencies: - '@changesets/apply-release-plan': 7.0.14 - '@changesets/assemble-release-plan': 6.0.9 + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.2 + '@changesets/config': 3.1.4 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.14 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.6 + '@changesets/read': 0.6.7 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@22.19.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.6.0) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 - ci-info: 3.9.0 enquirer: 2.4.1 fs-extra: 7.0.1 mri: 1.2.0 - p-limit: 2.3.0 package-manager-detector: 0.2.11 picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.7.3 + semver: 7.7.4 spawndamnit: 3.0.1 term-size: 2.2.1 transitivePeerDependencies: - '@types/node' - '@changesets/config@3.1.2': + '@changesets/config@3.1.4': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-dependents-graph': 2.1.4 '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 @@ -9653,12 +11066,12 @@ snapshots: dependencies: extendable-error: 0.1.7 - '@changesets/get-dependents-graph@2.1.3': + '@changesets/get-dependents-graph@2.1.4': dependencies: '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 - semver: 7.7.3 + semver: 7.7.4 '@changesets/get-github-info@0.7.0': dependencies: @@ -9667,12 +11080,12 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/get-release-plan@4.0.14': + '@changesets/get-release-plan@4.0.16': dependencies: - '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.2 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.6 + '@changesets/read': 0.6.7 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 @@ -9690,7 +11103,7 @@ snapshots: dependencies: picocolors: 1.1.1 - '@changesets/parse@0.4.2': + '@changesets/parse@0.4.3': dependencies: '@changesets/types': 6.1.0 js-yaml: 4.1.1 @@ -9702,11 +11115,11 @@ snapshots: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.6': + '@changesets/read@0.6.7': dependencies: '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.2 + '@changesets/parse': 0.4.3 '@changesets/types': 6.1.0 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -9731,10 +11144,18 @@ snapshots: '@colors/colors@1.5.0': optional: true + '@colors/colors@1.6.0': {} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@dabh/diagnostics@2.0.8': + dependencies: + '@so-ric/colorspace': 1.1.6 + enabled: 2.0.0 + kuler: 2.0.0 + '@docsearch/css@4.6.2': {} '@docsearch/js@4.6.2': {} @@ -9765,126 +11186,189 @@ snapshots: esquery: 1.6.0 jsdoc-type-pratt-parser: 4.1.0 + '@esbuild/aix-ppc64@0.25.0': + optional: true + '@esbuild/aix-ppc64@0.25.12': optional: true '@esbuild/aix-ppc64@0.27.7': optional: true + '@esbuild/android-arm64@0.25.0': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true '@esbuild/android-arm64@0.27.7': optional: true + '@esbuild/android-arm@0.25.0': + optional: true + '@esbuild/android-arm@0.25.12': optional: true '@esbuild/android-arm@0.27.7': optional: true + '@esbuild/android-x64@0.25.0': + optional: true + '@esbuild/android-x64@0.25.12': optional: true '@esbuild/android-x64@0.27.7': optional: true + '@esbuild/darwin-arm64@0.25.0': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true '@esbuild/darwin-arm64@0.27.7': optional: true + '@esbuild/darwin-x64@0.25.0': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true '@esbuild/darwin-x64@0.27.7': optional: true + '@esbuild/freebsd-arm64@0.25.0': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true '@esbuild/freebsd-arm64@0.27.7': optional: true + '@esbuild/freebsd-x64@0.25.0': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true '@esbuild/freebsd-x64@0.27.7': optional: true + '@esbuild/linux-arm64@0.25.0': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true '@esbuild/linux-arm64@0.27.7': optional: true + '@esbuild/linux-arm@0.25.0': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-arm@0.27.7': optional: true + '@esbuild/linux-ia32@0.25.0': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-ia32@0.27.7': optional: true + '@esbuild/linux-loong64@0.25.0': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true '@esbuild/linux-loong64@0.27.7': optional: true + '@esbuild/linux-mips64el@0.25.0': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true '@esbuild/linux-mips64el@0.27.7': optional: true + '@esbuild/linux-ppc64@0.25.0': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true '@esbuild/linux-ppc64@0.27.7': optional: true + '@esbuild/linux-riscv64@0.25.0': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true '@esbuild/linux-riscv64@0.27.7': optional: true + '@esbuild/linux-s390x@0.25.0': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true '@esbuild/linux-s390x@0.27.7': optional: true + '@esbuild/linux-x64@0.25.0': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true '@esbuild/linux-x64@0.27.7': optional: true + '@esbuild/netbsd-arm64@0.25.0': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true '@esbuild/netbsd-arm64@0.27.7': optional: true + '@esbuild/netbsd-x64@0.25.0': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true '@esbuild/netbsd-x64@0.27.7': optional: true + '@esbuild/openbsd-arm64@0.25.0': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true '@esbuild/openbsd-arm64@0.27.7': optional: true + '@esbuild/openbsd-x64@0.25.0': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true @@ -9897,24 +11381,36 @@ snapshots: '@esbuild/openharmony-arm64@0.27.7': optional: true + '@esbuild/sunos-x64@0.25.0': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true '@esbuild/sunos-x64@0.27.7': optional: true + '@esbuild/win32-arm64@0.25.0': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true '@esbuild/win32-arm64@0.27.7': optional: true + '@esbuild/win32-ia32@0.25.0': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true '@esbuild/win32-ia32@0.27.7': optional: true + '@esbuild/win32-x64@0.25.0': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true @@ -10041,6 +11537,18 @@ snapshots: express: 5.1.0 http-errors: 2.0.1 + '@h4ad/serverless-adapter@4.4.0(@types/aws-lambda@8.10.160)(@types/body-parser@1.19.6)(@types/cors@2.8.19)(@types/express@5.0.4)(body-parser@2.2.1)(cors@2.8.5)(express@5.1.0)(http-errors@2.0.1)': + dependencies: + '@types/body-parser': 1.19.6 + '@types/cors': 2.8.19 + optionalDependencies: + '@types/aws-lambda': 8.10.160 + '@types/express': 5.0.4 + body-parser: 2.2.1 + cors: 2.8.5 + express: 5.1.0 + http-errors: 2.0.1 + '@hono/node-server@1.19.9(hono@4.11.9)': dependencies: hono: 4.11.9 @@ -10194,10 +11702,17 @@ snapshots: '@inquirer/external-editor@1.0.3(@types/node@22.19.0)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.0 + iconv-lite: 0.7.2 optionalDependencies: '@types/node': 22.19.0 + '@inquirer/external-editor@1.0.3(@types/node@25.6.0)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 25.6.0 + '@inquirer/external-editor@2.0.3(@types/node@22.19.0)': dependencies: chardet: 2.1.1 @@ -10398,14 +11913,14 @@ snapshots: '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.29.2 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.29.2 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -10535,6 +12050,10 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@noble/hashes@1.8.0': {} + + '@nodable/entities@2.1.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -10545,7 +12064,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@nolyfill/is-core-module@1.0.39': {} @@ -10706,6 +12225,10 @@ snapshots: '@opentelemetry/semantic-conventions@1.39.0': {} + '@paralleldrive/cuid2@2.3.1': + dependencies: + '@noble/hashes': 1.8.0 + '@pinojs/redact@0.4.0': {} '@pkgjs/parseargs@0.11.0': @@ -11310,10 +12833,18 @@ snapshots: dependencies: type-detect: 4.0.8 + '@sinonjs/fake-timers@11.2.2': + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers@15.1.0': dependencies: '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers@15.3.2': + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/samsam@8.0.3': dependencies: '@sinonjs/commons': 3.0.1 @@ -11321,7 +12852,7 @@ snapshots: '@smithy/abort-controller@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/abort-controller@4.2.9': @@ -11331,20 +12862,29 @@ snapshots: '@smithy/chunked-blob-reader-native@4.2.1': dependencies: - '@smithy/util-base64': 4.3.0 + '@smithy/util-base64': 4.3.1 tslib: 2.8.1 '@smithy/chunked-blob-reader@5.2.0': dependencies: tslib: 2.8.1 + '@smithy/config-resolver@4.4.17': + dependencies: + '@smithy/node-config-provider': 4.3.14 + '@smithy/types': 4.14.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.4.2 + '@smithy/util-middleware': 4.2.14 + tslib: 2.8.1 + '@smithy/config-resolver@4.4.3': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/types': 4.12.1 '@smithy/util-config-provider': 4.2.0 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 + '@smithy/util-endpoints': 3.2.9 + '@smithy/util-middleware': 4.2.9 tslib: 2.8.1 '@smithy/config-resolver@4.4.7': @@ -11358,17 +12898,30 @@ snapshots: '@smithy/core@3.18.0': dependencies: - '@smithy/middleware-serde': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-stream': 4.5.6 - '@smithy/util-utf8': 4.2.0 + '@smithy/middleware-serde': 4.2.10 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-stream': 4.5.14 + '@smithy/util-utf8': 4.2.1 '@smithy/uuid': 1.1.0 tslib: 2.8.1 + '@smithy/core@3.23.17': + dependencies: + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 + '@smithy/url-parser': 4.2.14 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-stream': 4.5.25 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + '@smithy/core@3.23.4': dependencies: '@smithy/middleware-serde': 4.2.10 @@ -11382,12 +12935,20 @@ snapshots: '@smithy/uuid': 1.1.1 tslib: 2.8.1 + '@smithy/credential-provider-imds@4.2.14': + dependencies: + '@smithy/node-config-provider': 4.3.14 + '@smithy/property-provider': 4.2.14 + '@smithy/types': 4.14.1 + '@smithy/url-parser': 4.2.14 + tslib: 2.8.1 + '@smithy/credential-provider-imds@4.2.5': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/property-provider': 4.2.5 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@smithy/node-config-provider': 4.3.9 + '@smithy/property-provider': 4.2.9 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 tslib: 2.8.1 '@smithy/credential-provider-imds@4.2.9': @@ -11401,31 +12962,31 @@ snapshots: '@smithy/eventstream-codec@4.2.5': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.9.0 - '@smithy/util-hex-encoding': 4.2.0 + '@smithy/types': 4.12.1 + '@smithy/util-hex-encoding': 4.2.1 tslib: 2.8.1 '@smithy/eventstream-serde-browser@4.2.5': dependencies: '@smithy/eventstream-serde-universal': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/eventstream-serde-config-resolver@4.3.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/eventstream-serde-node@4.2.5': dependencies: '@smithy/eventstream-serde-universal': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/eventstream-serde-universal@4.2.5': dependencies: '@smithy/eventstream-codec': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/fetch-http-handler@5.3.10': @@ -11436,26 +12997,41 @@ snapshots: '@smithy/util-base64': 4.3.1 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.3.17': + dependencies: + '@smithy/protocol-http': 5.3.14 + '@smithy/querystring-builder': 4.2.14 + '@smithy/types': 4.14.1 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + '@smithy/fetch-http-handler@5.3.6': dependencies: - '@smithy/protocol-http': 5.3.5 + '@smithy/protocol-http': 5.3.9 '@smithy/querystring-builder': 4.2.5 - '@smithy/types': 4.9.0 - '@smithy/util-base64': 4.3.0 + '@smithy/types': 4.12.1 + '@smithy/util-base64': 4.3.1 tslib: 2.8.1 '@smithy/hash-blob-browser@4.2.6': dependencies: '@smithy/chunked-blob-reader': 5.2.0 '@smithy/chunked-blob-reader-native': 4.2.1 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.14': + dependencies: + '@smithy/types': 4.14.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 '@smithy/hash-node@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@smithy/hash-node@4.2.9': @@ -11467,13 +13043,18 @@ snapshots: '@smithy/hash-stream-node@4.2.5': dependencies: - '@smithy/types': 4.9.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.12.1 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.14': + dependencies: + '@smithy/types': 4.14.1 tslib: 2.8.1 '@smithy/invalid-dependency@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/invalid-dependency@4.2.9': @@ -11493,10 +13074,14 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/md5-js@4.2.5': dependencies: - '@smithy/types': 4.9.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.12.1 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@smithy/middleware-compression@4.3.33': @@ -11512,10 +13097,16 @@ snapshots: fflate: 0.8.1 tslib: 2.8.1 + '@smithy/middleware-content-length@4.2.14': + dependencies: + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/middleware-content-length@4.2.5': dependencies: - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/middleware-content-length@4.2.9': @@ -11526,13 +13117,13 @@ snapshots: '@smithy/middleware-endpoint@4.3.7': dependencies: - '@smithy/core': 3.18.0 - '@smithy/middleware-serde': 4.2.5 - '@smithy/node-config-provider': 4.3.5 + '@smithy/core': 3.23.4 + '@smithy/middleware-serde': 4.2.10 + '@smithy/node-config-provider': 4.3.9 '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-middleware': 4.2.5 + '@smithy/types': 4.12.1 + '@smithy/url-parser': 4.2.9 + '@smithy/util-middleware': 4.2.9 tslib: 2.8.1 '@smithy/middleware-endpoint@4.4.18': @@ -11546,6 +13137,17 @@ snapshots: '@smithy/util-middleware': 4.2.9 tslib: 2.8.1 + '@smithy/middleware-endpoint@4.4.32': + dependencies: + '@smithy/core': 3.23.17 + '@smithy/middleware-serde': 4.2.20 + '@smithy/node-config-provider': 4.3.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + '@smithy/url-parser': 4.2.14 + '@smithy/util-middleware': 4.2.14 + tslib: 2.8.1 + '@smithy/middleware-retry@4.4.35': dependencies: '@smithy/node-config-provider': 4.3.9 @@ -11560,31 +13162,56 @@ snapshots: '@smithy/middleware-retry@4.4.7': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/node-config-provider': 4.3.9 + '@smithy/protocol-http': 5.3.9 '@smithy/service-error-classification': 4.2.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 + '@smithy/util-middleware': 4.2.9 + '@smithy/util-retry': 4.2.9 '@smithy/uuid': 1.1.0 tslib: 2.8.1 + '@smithy/middleware-retry@4.5.7': + dependencies: + '@smithy/core': 3.23.17 + '@smithy/node-config-provider': 4.3.14 + '@smithy/protocol-http': 5.3.14 + '@smithy/service-error-classification': 4.3.1 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-retry': 4.3.8 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + '@smithy/middleware-serde@4.2.10': dependencies: '@smithy/protocol-http': 5.3.9 '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/middleware-serde@4.2.20': + dependencies: + '@smithy/core': 3.23.17 + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/middleware-serde@4.2.5': dependencies: - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.14': + dependencies: + '@smithy/types': 4.14.1 tslib: 2.8.1 '@smithy/middleware-stack@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/middleware-stack@4.2.9': @@ -11592,11 +13219,18 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/node-config-provider@4.3.14': + dependencies: + '@smithy/property-provider': 4.2.14 + '@smithy/shared-ini-file-loader': 4.4.9 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/node-config-provider@4.3.5': dependencies: '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/node-config-provider@4.3.9': @@ -11617,14 +13251,26 @@ snapshots: '@smithy/node-http-handler@4.4.5': dependencies: '@smithy/abort-controller': 4.2.5 - '@smithy/protocol-http': 5.3.5 + '@smithy/protocol-http': 5.3.9 '@smithy/querystring-builder': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.6.1': + dependencies: + '@smithy/protocol-http': 5.3.14 + '@smithy/querystring-builder': 4.2.14 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.14': + dependencies: + '@smithy/types': 4.14.1 tslib: 2.8.1 '@smithy/property-provider@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/property-provider@4.2.9': @@ -11632,9 +13278,14 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/protocol-http@5.3.14': + dependencies: + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/protocol-http@5.3.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/protocol-http@5.3.9': @@ -11642,9 +13293,15 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/querystring-builder@4.2.14': + dependencies: + '@smithy/types': 4.14.1 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + '@smithy/querystring-builder@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 '@smithy/util-uri-escape': 4.2.0 tslib: 2.8.1 @@ -11654,9 +13311,14 @@ snapshots: '@smithy/util-uri-escape': 4.2.1 tslib: 2.8.1 + '@smithy/querystring-parser@4.2.14': + dependencies: + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/querystring-parser@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/querystring-parser@4.2.9': @@ -11666,15 +13328,19 @@ snapshots: '@smithy/service-error-classification@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 '@smithy/service-error-classification@4.2.9': dependencies: '@smithy/types': 4.12.1 + '@smithy/service-error-classification@4.3.1': + dependencies: + '@smithy/types': 4.14.1 + '@smithy/shared-ini-file-loader@4.4.0': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/shared-ini-file-loader@4.4.4': @@ -11682,15 +13348,31 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/shared-ini-file-loader@4.4.9': + dependencies: + '@smithy/types': 4.14.1 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.14': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.14 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + '@smithy/signature-v4@5.3.5': dependencies: - '@smithy/is-array-buffer': 4.2.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-middleware': 4.2.5 + '@smithy/is-array-buffer': 4.2.1 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-middleware': 4.2.9 '@smithy/util-uri-escape': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@smithy/signature-v4@5.3.9': @@ -11714,28 +13396,48 @@ snapshots: '@smithy/util-stream': 4.5.14 tslib: 2.8.1 - '@smithy/smithy-client@4.9.3': + '@smithy/smithy-client@4.12.13': dependencies: - '@smithy/core': 3.18.0 - '@smithy/middleware-endpoint': 4.3.7 - '@smithy/middleware-stack': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 - '@smithy/util-stream': 4.5.6 + '@smithy/core': 3.23.17 + '@smithy/middleware-endpoint': 4.4.32 + '@smithy/middleware-stack': 4.2.14 + '@smithy/protocol-http': 5.3.14 + '@smithy/types': 4.14.1 + '@smithy/util-stream': 4.5.25 + tslib: 2.8.1 + + '@smithy/smithy-client@4.9.1': + dependencies: + '@smithy/core': 3.23.4 + '@smithy/middleware-endpoint': 4.4.18 + '@smithy/middleware-stack': 4.2.9 + '@smithy/protocol-http': 5.3.9 + '@smithy/types': 4.12.1 + '@smithy/util-stream': 4.5.14 tslib: 2.8.1 '@smithy/types@4.12.1': dependencies: tslib: 2.8.1 + '@smithy/types@4.14.1': + dependencies: + tslib: 2.8.1 + '@smithy/types@4.9.0': dependencies: tslib: 2.8.1 + '@smithy/url-parser@4.2.14': + dependencies: + '@smithy/querystring-parser': 4.2.14 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/url-parser@4.2.5': dependencies: '@smithy/querystring-parser': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/url-parser@4.2.9': @@ -11747,7 +13449,7 @@ snapshots: '@smithy/util-base64@4.3.0': dependencies: '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@smithy/util-base64@4.3.1': @@ -11756,6 +13458,12 @@ snapshots: '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + '@smithy/util-body-length-browser@4.2.0': dependencies: tslib: 2.8.1 @@ -11764,10 +13472,18 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-body-length-node@4.2.1': dependencies: tslib: 2.8.1 + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 @@ -11775,7 +13491,7 @@ snapshots: '@smithy/util-buffer-from@4.2.0': dependencies: - '@smithy/is-array-buffer': 4.2.0 + '@smithy/is-array-buffer': 4.2.1 tslib: 2.8.1 '@smithy/util-buffer-from@4.2.1': @@ -11783,6 +13499,11 @@ snapshots: '@smithy/is-array-buffer': 4.2.1 tslib: 2.8.1 + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + '@smithy/util-config-provider@4.2.0': dependencies: tslib: 2.8.1 @@ -11791,6 +13512,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.3.34': dependencies: '@smithy/property-provider': 4.2.9 @@ -11798,11 +13523,18 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.3.49': + dependencies: + '@smithy/property-provider': 4.2.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/util-defaults-mode-browser@4.3.6': dependencies: '@smithy/property-provider': 4.2.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/util-defaults-mode-node@4.2.37': @@ -11815,20 +13547,30 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.2.54': + dependencies: + '@smithy/config-resolver': 4.4.17 + '@smithy/credential-provider-imds': 4.2.14 + '@smithy/node-config-provider': 4.3.14 + '@smithy/property-provider': 4.2.14 + '@smithy/smithy-client': 4.12.13 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/util-defaults-mode-node@4.2.9': dependencies: - '@smithy/config-resolver': 4.4.3 + '@smithy/config-resolver': 4.4.7 '@smithy/credential-provider-imds': 4.2.5 - '@smithy/node-config-provider': 4.3.5 + '@smithy/node-config-provider': 4.3.9 '@smithy/property-provider': 4.2.5 - '@smithy/smithy-client': 4.9.3 - '@smithy/types': 4.9.0 + '@smithy/smithy-client': 4.11.7 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/util-endpoints@3.2.5': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.9 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/util-endpoints@3.2.9': @@ -11837,6 +13579,12 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/util-endpoints@3.4.2': + dependencies: + '@smithy/node-config-provider': 4.3.14 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/util-hex-encoding@4.2.0': dependencies: tslib: 2.8.1 @@ -11845,9 +13593,18 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.14': + dependencies: + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/util-middleware@4.2.5': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/util-middleware@4.2.9': @@ -11858,7 +13615,7 @@ snapshots: '@smithy/util-retry@4.2.5': dependencies: '@smithy/service-error-classification': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/util-retry@4.2.9': @@ -11867,6 +13624,12 @@ snapshots: '@smithy/types': 4.12.1 tslib: 2.8.1 + '@smithy/util-retry@4.3.8': + dependencies: + '@smithy/service-error-classification': 4.3.1 + '@smithy/types': 4.14.1 + tslib: 2.8.1 + '@smithy/util-stream@4.5.14': dependencies: '@smithy/fetch-http-handler': 5.3.10 @@ -11878,15 +13641,26 @@ snapshots: '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 + '@smithy/util-stream@4.5.25': + dependencies: + '@smithy/fetch-http-handler': 5.3.17 + '@smithy/node-http-handler': 4.6.1 + '@smithy/types': 4.14.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + '@smithy/util-stream@4.5.6': dependencies: - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/node-http-handler': 4.4.5 - '@smithy/types': 4.9.0 - '@smithy/util-base64': 4.3.0 + '@smithy/fetch-http-handler': 5.3.10 + '@smithy/node-http-handler': 4.4.11 + '@smithy/types': 4.12.1 + '@smithy/util-base64': 4.3.1 '@smithy/util-buffer-from': 4.2.0 '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 '@smithy/util-uri-escape@4.2.0': @@ -11897,6 +13671,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 @@ -11912,10 +13690,15 @@ snapshots: '@smithy/util-buffer-from': 4.2.1 tslib: 2.8.1 + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + '@smithy/util-waiter@4.2.5': dependencies: '@smithy/abort-controller': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/types': 4.12.1 tslib: 2.8.1 '@smithy/util-waiter@4.2.9': @@ -11932,6 +13715,15 @@ snapshots: dependencies: tslib: 2.8.1 + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + + '@so-ric/colorspace@1.1.6': + dependencies: + color: 5.0.3 + text-hex: 1.0.0 + '@stylistic/eslint-plugin@3.1.0(eslint@9.39.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/utils': 8.54.0(eslint@9.39.1)(typescript@5.9.3) @@ -12021,9 +13813,11 @@ snapshots: dependencies: '@types/node': 22.19.0 + '@types/cookiejar@2.1.5': {} + '@types/cors@2.8.19': dependencies: - '@types/node': 22.19.0 + '@types/node': 25.6.0 '@types/ejs@3.1.5': {} @@ -12042,6 +13836,12 @@ snapshots: '@types/express-serve-static-core': 5.1.1 '@types/serve-static': 2.2.0 + '@types/express@5.0.4': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-static': 2.2.0 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -12077,6 +13877,8 @@ snapshots: '@types/mdurl@2.0.0': {} + '@types/methods@1.1.4': {} + '@types/mime-types@3.0.1': {} '@types/mocha@10.0.10': {} @@ -12093,6 +13895,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + '@types/normalize-package-data@2.4.4': {} '@types/qs@6.14.0': {} @@ -12116,6 +13922,10 @@ snapshots: '@types/shimmer@1.2.0': {} + '@types/sinon@17.0.4': + dependencies: + '@types/sinonjs__fake-timers': 15.0.1 + '@types/sinon@21.0.0': dependencies: '@types/sinonjs__fake-timers': 15.0.1 @@ -12124,6 +13934,18 @@ snapshots: '@types/statuses@2.0.6': {} + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 22.19.0 + form-data: 4.0.5 + + '@types/supertest@6.0.3': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.9 + '@types/tar-fs@2.0.4': dependencies: '@types/node': 22.19.0 @@ -12133,6 +13955,8 @@ snapshots: dependencies: '@types/node': 22.19.0 + '@types/triple-beam@1.3.5': {} + '@types/unist@3.0.3': {} '@types/vscode@1.109.0': {} @@ -12305,10 +14129,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-vue@6.0.6(vite@7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.32(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.6(vite@7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.32(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.13 - vite: 7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.32(typescript@5.9.3) '@vscode/debugadapter@1.68.0': @@ -12666,6 +14490,8 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + asap@2.0.6: {} + assertion-error@1.1.0: {} astral-regex@2.0.0: {} @@ -12695,6 +14521,12 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + aws-sdk-client-mock@4.1.0: + dependencies: + '@types/sinon': 17.0.4 + sinon: 18.0.1 + tslib: 2.8.1 + azure-devops-node-api@12.5.0: dependencies: tunnel: 0.0.6 @@ -12749,6 +14581,10 @@ snapshots: baseline-browser-mapping@2.9.19: {} + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 @@ -12846,7 +14682,7 @@ snapshots: builtins@5.1.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 bundle-name@4.1.0: dependencies: @@ -13111,8 +14947,23 @@ snapshots: dependencies: color-name: 1.1.4 + color-convert@3.1.3: + dependencies: + color-name: 2.1.0 + color-name@1.1.4: {} + color-name@2.1.0: {} + + color-string@2.1.4: + dependencies: + color-name: 2.1.0 + + color@5.0.3: + dependencies: + color-convert: 3.1.3 + color-string: 2.1.4 + colorette@1.4.0: {} colorette@2.0.20: {} @@ -13129,6 +14980,8 @@ snapshots: comment-parser@1.4.1: {} + component-emitter@1.3.1: {} + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -13186,6 +15039,8 @@ snapshots: cookie@1.1.1: {} + cookiejar@2.1.4: {} + core-js-compat@3.46.0: dependencies: browserslist: 4.28.0 @@ -13322,6 +15177,11 @@ snapshots: dependencies: dequal: 2.0.3 + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + diagnostic-channel-publishers@1.0.8(diagnostic-channel@1.1.1): dependencies: diagnostic-channel: 1.1.1 @@ -13403,6 +15263,8 @@ snapshots: emojilib@2.4.0: {} + enabled@2.0.0: {} + encodeurl@1.0.2: {} encodeurl@2.0.0: {} @@ -13520,6 +15382,34 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild@0.25.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -13589,7 +15479,7 @@ snapshots: eslint-compat-utils@0.5.1(eslint@9.39.1): dependencies: eslint: 9.39.1 - semver: 7.7.3 + semver: 7.7.4 eslint-config-oclif@5.2.2(eslint@9.39.1): dependencies: @@ -13740,7 +15630,7 @@ snapshots: espree: 10.4.0 esquery: 1.6.0 parse-imports-exports: 0.2.4 - semver: 7.7.3 + semver: 7.7.4 spdx-expression-parse: 4.0.0 transitivePeerDependencies: - supports-color @@ -13762,7 +15652,7 @@ snapshots: is-core-module: 2.16.1 minimatch: 3.1.2 resolve: 1.22.11 - semver: 7.7.3 + semver: 7.7.4 eslint-plugin-n@17.23.1(eslint@9.39.1)(typescript@5.9.3): dependencies: @@ -13774,7 +15664,7 @@ snapshots: globals: 15.15.0 globrex: 0.1.2 ignore: 5.3.2 - semver: 7.7.3 + semver: 7.7.4 ts-declaration-location: 1.0.7(typescript@5.9.3) transitivePeerDependencies: - typescript @@ -13793,8 +15683,8 @@ snapshots: dependencies: eslint: 9.39.1 prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.12 optionalDependencies: eslint-config-prettier: 10.1.8(eslint@9.39.1) @@ -13814,7 +15704,7 @@ snapshots: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - semver: 7.7.3 + semver: 7.7.4 strip-indent: 3.0.0 eslint-plugin-unicorn@56.0.1(eslint@9.39.1): @@ -13834,7 +15724,7 @@ snapshots: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - semver: 7.7.3 + semver: 7.7.4 strip-indent: 3.0.0 eslint-scope@8.4.0: @@ -13954,6 +15844,10 @@ snapshots: expand-template@2.0.3: optional: true + express-basic-auth@1.2.1: + dependencies: + basic-auth: 2.0.1 + express-rate-limit@8.2.1(express@5.2.1): dependencies: express: 5.2.1 @@ -14090,6 +15984,10 @@ snapshots: fast-uri@3.1.0: {} + fast-xml-builder@1.1.5: + dependencies: + path-expression-matcher: 1.5.0 + fast-xml-parser@5.3.5: dependencies: strnum: 2.1.2 @@ -14098,9 +15996,16 @@ snapshots: dependencies: strnum: 2.1.2 + fast-xml-parser@5.7.2: + dependencies: + '@nodable/entities': 2.1.0 + fast-xml-builder: 1.1.5 + path-expression-matcher: 1.5.0 + strnum: 2.2.3 + fastest-levenshtein@1.0.16: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -14108,6 +16013,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fecha@4.2.3: {} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -14177,6 +16084,8 @@ snapshots: flatted@3.3.3: {} + fn.name@1.1.0: {} + focus-trap@8.0.1: dependencies: tabbable: 6.4.0 @@ -14208,6 +16117,12 @@ snapshots: dependencies: fetch-blob: 3.2.0 + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.3.1 + dezalgo: 1.0.4 + once: 1.4.0 + forwarded@0.2.0: {} fresh@0.5.2: {} @@ -14217,7 +16132,7 @@ snapshots: fs-constants@1.0.0: optional: true - fs-extra@11.3.3: + fs-extra@11.3.4: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -14586,10 +16501,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.0: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -14677,7 +16588,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 is-callable@1.2.7: {} @@ -14936,6 +16847,8 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 + just-extend@6.2.0: {} + jwa@2.0.1: dependencies: buffer-equal-constant-time: 1.0.1 @@ -14957,6 +16870,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kuler@2.0.0: {} + leven@3.1.0: {} levn@0.4.1: @@ -15016,6 +16931,15 @@ snapshots: chalk: 5.6.2 is-unicode-supported: 1.3.0 + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + long@5.3.2: {} loose-envify@1.4.0: @@ -15052,7 +16976,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 make-error@1.3.6: {} @@ -15156,6 +17080,8 @@ snapshots: mime@1.6.0: {} + mime@2.6.0: {} + mimic-function@5.0.1: {} mimic-response@3.1.0: {} @@ -15313,6 +17239,13 @@ snapshots: negotiator@1.0.0: {} + nise@6.1.5: + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers': 15.3.2 + just-extend: 6.2.0 + path-to-regexp: 8.3.0 + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -15350,7 +17283,7 @@ snapshots: node-sarif-builder@3.4.0: dependencies: '@types/sarif': 2.1.7 - fs-extra: 11.3.3 + fs-extra: 11.3.4 normalize-package-data@2.5.0: dependencies: @@ -15471,6 +17404,10 @@ snapshots: dependencies: wrappy: 1.0.2 + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -15660,6 +17597,8 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.5.0: {} + path-is-absolute@1.0.1: {} path-is-inside@1.0.2: {} @@ -15797,7 +17736,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.1: dependencies: fast-diff: 1.3.0 @@ -15986,7 +17925,6 @@ snapshots: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - optional: true readdirp@3.6.0: dependencies: @@ -16188,6 +18126,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + send@0.19.0: dependencies: debug: 2.6.9 @@ -16360,6 +18300,15 @@ snapshots: simple-concat: 1.0.1 optional: true + sinon@18.0.1: + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers': 11.2.2 + '@sinonjs/samsam': 8.0.3 + diff: 8.0.3 + nise: 6.1.5 + supports-color: 7.2.0 + sinon@21.0.1: dependencies: '@sinonjs/commons': 3.0.1 @@ -16447,6 +18396,8 @@ snapshots: stack-chain@1.3.7: {} + stack-trace@0.0.10: {} + statuses@2.0.1: {} statuses@2.0.2: {} @@ -16517,7 +18468,6 @@ snapshots: string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - optional: true stringify-entities@4.0.4: dependencies: @@ -16549,10 +18499,33 @@ snapshots: strnum@2.1.2: {} + strnum@2.2.3: {} + structured-source@4.0.0: dependencies: boundary: 2.0.0 + superagent@10.3.0: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.3(supports-color@10.2.2) + fast-safe-stringify: 2.1.1 + form-data: 4.0.5 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.1 + transitivePeerDependencies: + - supports-color + + supertest@7.1.4: + dependencies: + methods: 1.1.2 + superagent: 10.3.0 + transitivePeerDependencies: + - supports-color + supports-color@10.2.2: {} supports-color@7.2.0: @@ -16574,7 +18547,7 @@ snapshots: dependencies: '@scarf/scarf': 1.4.0 - synckit@0.11.11: + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -16665,6 +18638,8 @@ snapshots: transitivePeerDependencies: - react-native-b4a + text-hex@1.0.0: {} + text-table@0.2.0: {} textextensions@6.11.0: @@ -16716,6 +18691,8 @@ snapshots: trim-lines@3.0.1: {} + triple-beam@1.4.1: {} + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -16892,6 +18869,8 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.19.2: {} + undici@7.19.2: {} unicode-emoji-modifier-base@1.0.0: {} @@ -17021,7 +19000,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1): + vite@7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.3) @@ -17030,20 +19009,20 @@ snapshots: rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.19.0 + '@types/node': 25.6.0 fsevents: 2.3.3 tsx: 4.20.6 yaml: 2.8.1 - vitepress-plugin-group-icons@1.7.5(vite@7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1)): + vitepress-plugin-group-icons@1.7.5(vite@7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@iconify-json/logos': 1.2.11 '@iconify-json/vscode-icons': 1.2.45 '@iconify/utils': 3.1.0 optionalDependencies: - vite: 7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1) - vitepress@2.0.0-alpha.17(@types/node@22.19.0)(change-case@5.4.4)(fuse.js@7.1.0)(postcss@8.5.10)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): + vitepress@2.0.0-alpha.17(@types/node@25.6.0)(change-case@5.4.4)(fuse.js@7.1.0)(postcss@8.5.10)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): dependencies: '@docsearch/css': 4.6.2 '@docsearch/js': 4.6.2 @@ -17053,7 +19032,7 @@ snapshots: '@shikijs/transformers': 3.23.0 '@shikijs/types': 3.23.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 6.0.6(vite@7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.32(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.6(vite@7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.32(typescript@5.9.3)) '@vue/devtools-api': 8.1.1 '@vue/shared': 3.5.32 '@vueuse/core': 14.2.1(vue@3.5.32(typescript@5.9.3)) @@ -17062,7 +19041,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 3.23.0 - vite: 7.3.2(@types/node@22.19.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.3.2(@types/node@25.6.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.32(typescript@5.9.3) optionalDependencies: postcss: 8.5.10 @@ -17169,6 +19148,26 @@ snapshots: dependencies: string-width: 4.2.3 + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.19.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.8 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + word-wrap@1.2.5: {} wordwrap@1.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b620c676..325892e6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,15 +4,8 @@ packages: - skills catalog: - # Production dependencies (exact versions for published packages) - '@oclif/core': 4.8.0 - cliui: 9.0.1 - glob: 13.0.0 - jsonschema: 1.5.0 - open: 11.0.0 - - # Dev dependencies (shared across packages) '@eslint/compat': ^1 + '@oclif/core': 4.8.0 '@oclif/prettier-config': ^0.2.1 '@oclif/test': ^4 '@salesforce/dev-config': ^4.3.2 @@ -22,15 +15,19 @@ catalog: '@types/sinon': ^21.0.0 c8: ^11.0.0 chai: ^4 + cliui: 9.0.1 eslint: ^9 eslint-config-oclif: ^6 eslint-config-prettier: ^10 eslint-plugin-header: ^3.1.1 - eslint-plugin-prettier: ^5.5.4 + eslint-plugin-prettier: ^5.5.5 + glob: 13.0.0 + jsonschema: 1.5.0 mocha: ^10 msw: ^2.12.4 oclif: ^4 - prettier: ^3.6.2 + open: 11.0.0 + prettier: ^3.8.3 shx: ^0.3.3 sinon: ^21.0.1 tsx: ^4 @@ -43,16 +40,13 @@ minimumReleaseAgeExclude: [] nodeLinker: hoisted -# Pin exact versions by default when adding dependencies (no ^ or ~ prefix) -savePrefix: '' - onlyBuiltDependencies: - unrs-resolver - yarn overrides: - baseline-browser-mapping: '>=2.9.19' '@isaacs/brace-expansion@<=5.0.0': '>=5.0.1' + baseline-browser-mapping: '>=2.9.19' diff@>=4.0.0 <4.0.4: '>=4.0.4' diff@>=5.0.0 <5.2.2: '>=5.2.2' esbuild@<=0.24.2: '>=0.25.0' @@ -65,6 +59,8 @@ overrides: preact@>=10.27.0 <10.27.3: '>=10.27.3' qs@<6.14.1: '>=6.14.1' +savePrefix: '' + trustPolicy: no-downgrade trustPolicyExclude: []