From 2848c881d981ddcfa1106d3afb30ba086d5ff007 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Thu, 28 Nov 2024 10:10:37 +0000 Subject: [PATCH 01/14] First pass at packageVersion bundling strategy needs tests --- src/bundling.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- src/types.ts | 13 +++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/bundling.ts b/src/bundling.ts index f6e72db..9345776 100644 --- a/src/bundling.ts +++ b/src/bundling.ts @@ -1,3 +1,5 @@ +import { execSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; import * as path from 'node:path'; import { AssetHashType, @@ -13,6 +15,7 @@ import { type Runtime, } from 'aws-cdk-lib/aws-lambda'; import type { BundlingOptions, ICommandHooks } from './types'; +import { BundlingStrategy } from './types'; export const HASHABLE_DEPENDENCIES_EXCLUDE = [ '*.pyc', @@ -86,10 +89,55 @@ export class Bundling { hashableAssetExclude = HASHABLE_DEPENDENCIES_EXCLUDE, ...bundlingOptions } = options; + switch (options.bundlingStrategy) { + case BundlingStrategy.PACKAGE_VERSION: + return Bundling.packageVersionStrategy(bundlingOptions); + default: + return Bundling.sourceStrategy(bundlingOptions); + } + } + + /** + * Uses the AssetHashType.SOURCE strategy to calculate the asset hash. + * + * If there are multiple functions being created from a workspace they will all be rebuilt if the source changes. + * + * @param options + * @private + */ + private static sourceStrategy(options: BundlingProps): AssetCode { return Code.fromAsset(options.rootDir, { assetHashType: AssetHashType.SOURCE, - exclude: hashableAssetExclude, - bundling: new Bundling(bundlingOptions), + exclude: options.hashableAssetExclude, + bundling: new Bundling(options), + }); + } + + /** + * Uses the AssetHashType.CUSTOM strategy and uv tree output to calculate the asset hash. + * + * This strategy uses the output of `uv tree` to calculate the asset hash. If there are multiple packages in a workspace this method will only + * rebuild the asset for a package if the package or a dependency version changes. This will not pick up local changes to the source unless they + * also change the package version in pyproject.toml. + * + * @param options + * @private + */ + private static packageVersionStrategy(options: BundlingProps): AssetCode { + const workspacePackage = options.workspacePackage; + const uvPackageArgs = workspacePackage + ? `--package ${workspacePackage}` + : ''; + const command = `cd ${options.rootDir} && uv tree ${uvPackageArgs}`; + // TODO: find something that works on Windows, maybe automatically changing directory and running the command + const tree = execSync(command).toString().trim(); + const assetHash = createHash('sha256').update(tree).digest('hex'); + + return Code.fromAsset(options.rootDir, { + assetHashType: AssetHashType.CUSTOM, + assetHash, + exclude: options.hashableAssetExclude, + bundling: new Bundling(options), }); } diff --git a/src/types.ts b/src/types.ts index c5529b8..f6ad42a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -93,6 +93,19 @@ export interface BundlingOptions extends DockerRunOptions { * @default - BundlingFileAccess.BIND_MOUNT */ readonly bundlingFileAccess?: BundlingFileAccess; + + /** + * Strategy for bundling + * + * @default - `BundlingStrategy.SOURCE` + * + */ + readonly bundlingStrategy?: BundlingStrategy; +} + +export enum BundlingStrategy { + SOURCE = 'source', + PACKAGE_VERSION = 'package-version', } /** From 43a012b95b2a545b20231e29c4c8c3b037c899d4 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Thu, 28 Nov 2024 12:23:06 +0000 Subject: [PATCH 02/14] Initial tests for PACKAGE_VERSION strategy A copy of the SOURCE tests to start. --- src/bundling.ts | 5 +- test/function.test.ts | 335 +++++++++++++++++++++++++++++++----------- 2 files changed, 252 insertions(+), 88 deletions(-) diff --git a/src/bundling.ts b/src/bundling.ts index 9345776..11c9d23 100644 --- a/src/bundling.ts +++ b/src/bundling.ts @@ -128,7 +128,10 @@ export class Bundling { const uvPackageArgs = workspacePackage ? `--package ${workspacePackage}` : ''; - const command = `cd ${options.rootDir} && uv tree ${uvPackageArgs}`; + const uvTreeOptions = [ + '--frozen', // don't try and update the lock file + ]; + const command = `cd ${options.rootDir} && uv tree ${uvTreeOptions.join(' ')} ${uvPackageArgs}`; // TODO: find something that works on Windows, maybe automatically changing directory and running the command const tree = execSync(command).toString().trim(); const assetHash = createHash('sha256').update(tree).digest('hex'); diff --git a/test/function.test.ts b/test/function.test.ts index 1f4f22a..0bbcc3f 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -7,7 +7,8 @@ import { App, Stack } from 'aws-cdk-lib'; import { Match, Template } from 'aws-cdk-lib/assertions'; import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; import * as cxapi from 'aws-cdk-lib/cx-api'; -import { PythonFunction } from '../src'; +import { BundlingStrategy, PythonFunction } from '../src'; + const execAsync = promisify(exec); const resourcesPath = path.resolve(__dirname, 'resources'); @@ -65,124 +66,284 @@ afterEach(async () => { process.env = OLD_ENV; }); -test('Create a function from basic_app', async () => { - const { app, stack } = await createStack(); +describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { + test('Create a function from basic_app', async () => { + const { app, stack } = await createStack(); + + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler.py', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.SOURCE, + }, + }); + + const template = Template.fromStack(stack); - new PythonFunction(stack, 'basic_app', { - rootDir: path.join(resourcesPath, 'basic_app'), - index: 'handler.py', - handler: 'lambda_handler', - runtime: Runtime.PYTHON_3_12, - architecture: await getDockerHostArch(), + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'handler.lambda_handler', + Runtime: 'python3.12', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); + const functions = Object.values( + template.findResources('AWS::Lambda::Function'), + ); + expect(functions).toHaveLength(1); + const contents = await getFunctionAssetContents(functions[0], app); + expect(contents).toContain('handler.py'); }); - const template = Template.fromStack(stack); + test('Create a function from basic_app with no .py index extension', async () => { + const { stack } = await createStack(); + + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.SOURCE, + }, + }); + + const template = Template.fromStack(stack); - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'handler.lambda_handler', - Runtime: 'python3.12', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'handler.lambda_handler', + Runtime: 'python3.12', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); }); - const functions = Object.values( - template.findResources('AWS::Lambda::Function'), - ); - expect(functions).toHaveLength(1); - const contents = await getFunctionAssetContents(functions[0], app); - expect(contents).toContain('handler.py'); -}); -test('Create a function from basic_app with no .py index extension', async () => { - const { stack } = await createStack(); + test('Create a function from basic_app when skip is true', async () => { + const { stack } = await createStack(); - new PythonFunction(stack, 'basic_app', { - rootDir: path.join(resourcesPath, 'basic_app'), - index: 'handler', - handler: 'lambda_handler', - runtime: Runtime.PYTHON_3_12, - architecture: await getDockerHostArch(), + const bundlingSpy = jest + .spyOn(stack, 'bundlingRequired', 'get') + .mockReturnValue(false); + const architecture = await getDockerHostArch(); + + // To see this fail, comment out the `if (skip) { return; } code in the PythonFunction constructor + expect(() => { + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture, + bundling: { + bundlingStrategy: BundlingStrategy.SOURCE, + }, + }); + }).not.toThrow(); + + bundlingSpy.mockRestore(); }); - const template = Template.fromStack(stack); + test('Create a function with workspaces_app', async () => { + const { app, stack } = await createStack('wstest'); + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(resourcesPath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.SOURCE, + }, + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'app_handler.handle_event', + Runtime: 'python3.10', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'handler.lambda_handler', - Runtime: 'python3.12', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, + const functions = Object.values( + template.findResources('AWS::Lambda::Function'), + ); + expect(functions).toHaveLength(1); + const contents = await getFunctionAssetContents(functions[0], app); + for (const entry of [ + 'app', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(contents).toContain(entry); + } }); }); -test('Create a function from basic_app when skip is true', async () => { - const { stack } = await createStack(); +describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () => { + test('Create a function from basic_app', async () => { + const { app, stack } = await createStack(); + + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler.py', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, + }, + }); + + const template = Template.fromStack(stack); - const bundlingSpy = jest - .spyOn(stack, 'bundlingRequired', 'get') - .mockReturnValue(false); - const architecture = await getDockerHostArch(); + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'handler.lambda_handler', + Runtime: 'python3.12', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); + const functions = Object.values( + template.findResources('AWS::Lambda::Function'), + ); + expect(functions).toHaveLength(1); + const contents = await getFunctionAssetContents(functions[0], app); + expect(contents).toContain('handler.py'); + }); + + test('Create a function from basic_app with no .py index extension', async () => { + const { stack } = await createStack(); - // To see this fail, comment out the `if (skip) { return; } code in the PythonFunction constructor - expect(() => { new PythonFunction(stack, 'basic_app', { rootDir: path.join(resourcesPath, 'basic_app'), index: 'handler', handler: 'lambda_handler', runtime: Runtime.PYTHON_3_12, - architecture, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, + }, }); - }).not.toThrow(); - - bundlingSpy.mockRestore(); -}); -test('Create a function with workspaces_app', async () => { - const { app, stack } = await createStack('wstest'); + const template = Template.fromStack(stack); - new PythonFunction(stack, 'workspaces_app', { - rootDir: path.join(resourcesPath, 'workspaces_app'), - workspacePackage: 'app', - index: 'app_handler.py', - handler: 'handle_event', - runtime: Runtime.PYTHON_3_10, - architecture: await getDockerHostArch(), + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'handler.lambda_handler', + Runtime: 'python3.12', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); }); - const template = Template.fromStack(stack); + test('Create a function from basic_app when skip is true', async () => { + const { stack } = await createStack(); + + const bundlingSpy = jest + .spyOn(stack, 'bundlingRequired', 'get') + .mockReturnValue(false); + const architecture = await getDockerHostArch(); - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'app_handler.handle_event', - Runtime: 'python3.10', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, + // To see this fail, comment out the `if (skip) { return; } code in the PythonFunction constructor + expect(() => { + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture, + bundling: { + bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, + }, + }); + }).not.toThrow(); + + bundlingSpy.mockRestore(); }); - const functions = Object.values( - template.findResources('AWS::Lambda::Function'), - ); - expect(functions).toHaveLength(1); - const contents = await getFunctionAssetContents(functions[0], app); - for (const entry of [ - 'app', - 'common', - 'pydantic', - 'httpx', - '_common.pth', - 'app_handler.py', - ]) { - expect(contents).toContain(entry); - } + test('Create a function with workspaces_app', async () => { + const { app, stack } = await createStack('wstest'); + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(resourcesPath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, + }, + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'app_handler.handle_event', + Runtime: 'python3.10', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); + + const functions = Object.values( + template.findResources('AWS::Lambda::Function'), + ); + expect(functions).toHaveLength(1); + const contents = await getFunctionAssetContents(functions[0], app); + console.log('contents', contents); + for (const entry of [ + 'app', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(contents).toContain(entry); + } + }); }); // biome-ignore lint/suspicious/noExplicitAny: async function getFunctionAssetContents(functionResource: any, app: App) { const [assetHash] = functionResource.Properties.Code.S3Key.split('.'); const assetPath = path.join(app.outdir, `asset.${assetHash}`); - const contents = await fs.readdir(assetPath); - return contents; + return await fs.readdir(assetPath); } + +// async function setFunctionAssetContents( +// functionResource: any, +// app: App, +// contents: string[], +// ) { +// const [assetHash] = functionResource.Properties.Code.S3Key.split('.'); +// const assetPath = path.join(app.outdir, `asset.${assetHash}`); +// console.log({ assetPath }); +// // remove all existing contents and add the new ones as empty files +// const existingContents = await fs.readdir(assetPath); +// for (const entry of existingContents) { +// await fs.rm(entry, { recursive: true }); +// } +// for (const entry of contents) { +// await fs.writeFile(path.join(assetPath, entry), ''); +// } +// } From 9a56b1e07bc12d6969d00c4bbfb9c32a571bca3c Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 11:52:49 +0000 Subject: [PATCH 03/14] Update workspaces_app project to have two lambda functions Make the functions both depend on common and have different external dependencies. --- .../workspaces_app/app/app_handler.py | 9 +- .../workspaces_app/app/pyproject.toml | 2 +- .../workspaces_app/backend/.python-version | 1 + .../workspaces_app/backend/README.md | 0 .../workspaces_app/backend/backend_handler.py | 5 + .../workspaces_app/backend/pyproject.toml | 13 ++ .../workspaces_app/common/pyproject.toml | 1 + .../workspaces_app/common/src/common/car.py | 6 + test/resources/workspaces_app/pyproject.toml | 5 +- test/resources/workspaces_app/uv.lock | 172 +++++++++++++++++- 10 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 test/resources/workspaces_app/backend/.python-version create mode 100644 test/resources/workspaces_app/backend/README.md create mode 100644 test/resources/workspaces_app/backend/backend_handler.py create mode 100644 test/resources/workspaces_app/backend/pyproject.toml create mode 100644 test/resources/workspaces_app/common/src/common/car.py diff --git a/test/resources/workspaces_app/app/app_handler.py b/test/resources/workspaces_app/app/app_handler.py index 9f5af87..d9bdaae 100644 --- a/test/resources/workspaces_app/app/app_handler.py +++ b/test/resources/workspaces_app/app/app_handler.py @@ -1,10 +1,5 @@ -from pydantic import BaseModel - -class Car(BaseModel): - brand: str - model: str - year: int +from common.car import Car def handle_event(event, context): car = Car(brand="Toyota", model="Corolla", year=2020) - return car.model_dump() \ No newline at end of file + return car.model_dump() diff --git a/test/resources/workspaces_app/app/pyproject.toml b/test/resources/workspaces_app/app/pyproject.toml index 75a3019..47d74cb 100644 --- a/test/resources/workspaces_app/app/pyproject.toml +++ b/test/resources/workspaces_app/app/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "common", - "pydantic>=2.9.2", + "flask>=3.1.0", ] [tool.uv.sources] diff --git a/test/resources/workspaces_app/backend/.python-version b/test/resources/workspaces_app/backend/.python-version new file mode 100644 index 0000000..c8cfe39 --- /dev/null +++ b/test/resources/workspaces_app/backend/.python-version @@ -0,0 +1 @@ +3.10 diff --git a/test/resources/workspaces_app/backend/README.md b/test/resources/workspaces_app/backend/README.md new file mode 100644 index 0000000..e69de29 diff --git a/test/resources/workspaces_app/backend/backend_handler.py b/test/resources/workspaces_app/backend/backend_handler.py new file mode 100644 index 0000000..6c3e595 --- /dev/null +++ b/test/resources/workspaces_app/backend/backend_handler.py @@ -0,0 +1,5 @@ +from common.car import Car + +def handle_event(event, context): + car = Car(brand="Nissan", model="Skyline", year=2022) + return car.model_dump() diff --git a/test/resources/workspaces_app/backend/pyproject.toml b/test/resources/workspaces_app/backend/pyproject.toml new file mode 100644 index 0000000..d84c3c1 --- /dev/null +++ b/test/resources/workspaces_app/backend/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "backend" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "common", + "pathlib>=1.0.1", +] + +[tool.uv.sources] +common = { workspace = true } diff --git a/test/resources/workspaces_app/common/pyproject.toml b/test/resources/workspaces_app/common/pyproject.toml index 25a22a3..7f1d0c1 100644 --- a/test/resources/workspaces_app/common/pyproject.toml +++ b/test/resources/workspaces_app/common/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.10" dependencies = [ "httpx>=0.27.2", + "pydantic>=2.9.2", ] [project.scripts] diff --git a/test/resources/workspaces_app/common/src/common/car.py b/test/resources/workspaces_app/common/src/common/car.py new file mode 100644 index 0000000..f0f37a2 --- /dev/null +++ b/test/resources/workspaces_app/common/src/common/car.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + +class Car(BaseModel): + brand: str + model: str + year: int diff --git a/test/resources/workspaces_app/pyproject.toml b/test/resources/workspaces_app/pyproject.toml index 4af2b65..7c347fa 100644 --- a/test/resources/workspaces_app/pyproject.toml +++ b/test/resources/workspaces_app/pyproject.toml @@ -7,15 +7,16 @@ requires-python = ">=3.10" dependencies = [] [tool.uv.workspace] -members = ["common", "app"] +members = ["common", "app", "backend"] [tool.uv.sources] common = { workspace = true } app = { workspace = true } +backend = { workspace = true } [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] -packages = ["app", "common"] +packages = ["app", "backend", "common"] diff --git a/test/resources/workspaces_app/uv.lock b/test/resources/workspaces_app/uv.lock index deb98d5..c34f8ff 100644 --- a/test/resources/workspaces_app/uv.lock +++ b/test/resources/workspaces_app/uv.lock @@ -8,6 +8,7 @@ resolution-markers = [ [manifest] members = [ "app", + "backend", "common", "workspaces-app", ] @@ -42,13 +43,37 @@ version = "0.1.0" source = { editable = "app" } dependencies = [ { name = "common" }, - { name = "pydantic" }, + { name = "flask" }, ] [package.metadata] requires-dist = [ { name = "common", editable = "common" }, - { name = "pydantic", specifier = ">=2.9.2" }, + { name = "flask", specifier = ">=3.1.0" }, +] + +[[package]] +name = "backend" +version = "0.1.0" +source = { editable = "backend" } +dependencies = [ + { name = "common" }, + { name = "pathlib" }, +] + +[package.metadata] +requires-dist = [ + { name = "common", editable = "common" }, + { name = "pathlib", specifier = ">=1.0.1" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, ] [[package]] @@ -60,16 +85,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, ] +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + [[package]] name = "common" version = "0.1.0" source = { editable = "common" } dependencies = [ { name = "httpx" }, + { name = "pydantic" }, ] [package.metadata] -requires-dist = [{ name = "httpx", specifier = ">=0.27.2" }] +requires-dist = [ + { name = "httpx", specifier = ">=0.27.2" }, + { name = "pydantic", specifier = ">=2.9.2" }, +] [[package]] name = "exceptiongroup" @@ -80,6 +130,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, ] +[[package]] +name = "flask" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/50/dff6380f1c7f84135484e176e0cac8690af72fa90e932ad2a0a60e28c69b/flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", size = 680824 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/47/93213ee66ef8fae3b93b3e29206f6b251e65c97bd91d8e1c5596ef15af0a/flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136", size = 102979 }, +] + [[package]] name = "h11" version = "0.14.0" @@ -127,6 +193,94 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "pathlib" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/aa/9b065a76b9af472437a0059f77e8f962fe350438b927cb80184c32f075eb/pathlib-1.0.1.tar.gz", hash = "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f", size = 49298 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/f9/690a8600b93c332de3ab4a344a4ac34f00c8f104917061f779db6a918ed6/pathlib-1.0.1-py3-none-any.whl", hash = "sha256:f35f95ab8b0f59e6d354090350b44a80a80635d22efdedfa84c7ad1cf0a74147", size = 14363 }, +] + [[package]] name = "pydantic" version = "2.9.2" @@ -226,6 +380,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, ] +[[package]] +name = "werkzeug" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498 }, +] + [[package]] name = "workspaces-app" version = "0.1.0" From 34af30408e0f812217c10abc6703829d94aef4aa Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 11:53:52 +0000 Subject: [PATCH 04/14] Add fs-extra for copying the workspace during tests --- .projen/deps.json | 8 ++++++++ .projen/tasks.json | 4 ++-- .projenrc.ts | 6 +++++- package.json | 2 ++ yarn.lock | 15 +++++++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.projen/deps.json b/.projen/deps.json index efa3840..2323239 100644 --- a/.projen/deps.json +++ b/.projen/deps.json @@ -4,6 +4,10 @@ "name": "@biomejs/biome", "type": "build" }, + { + "name": "@types/fs-extra", + "type": "build" + }, { "name": "@types/jest", "type": "build" @@ -17,6 +21,10 @@ "version": "^12", "type": "build" }, + { + "name": "fs-extra", + "type": "build" + }, { "name": "jest", "type": "build" diff --git a/.projen/tasks.json b/.projen/tasks.json index 8eec86f..97db832 100644 --- a/.projen/tasks.json +++ b/.projen/tasks.json @@ -269,13 +269,13 @@ }, "steps": [ { - "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --no-deprecated --dep=dev,peer,prod,optional --filter=@biomejs/biome,@types/jest,@types/node,jest,jsii-diff,jsii-pacmak,projen,ts-jest,ts-node,typescript" + "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --no-deprecated --dep=dev,peer,prod,optional --filter=@biomejs/biome,@types/fs-extra,@types/jest,@types/node,fs-extra,jest,jsii-diff,jsii-pacmak,projen,ts-jest,ts-node,typescript" }, { "exec": "yarn install --check-files" }, { - "exec": "yarn upgrade @biomejs/biome @types/jest @types/node commit-and-tag-version jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii projen ts-jest ts-node typescript aws-cdk-lib constructs" + "exec": "yarn upgrade @biomejs/biome @types/fs-extra @types/jest @types/node commit-and-tag-version fs-extra jest jest-junit jsii-diff jsii-docgen jsii-pacmak jsii-rosetta jsii projen ts-jest ts-node typescript aws-cdk-lib constructs" }, { "exec": "npx projen" diff --git a/.projenrc.ts b/.projenrc.ts index ec85fe2..04dcf14 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -18,7 +18,11 @@ const project = new awscdk.AwsCdkConstructLibrary({ // cdkDependencies: [], /* CDK dependencies of this module. */ // deps: [], /* Runtime dependencies of this module. */ // description: undefined, /* The description is just a string that helps people understand the purpose of the package. */ - devDeps: ['@biomejs/biome'] /* Build dependencies for this module. */, + devDeps: [ + '@biomejs/biome', + 'fs-extra', + '@types/fs-extra', + ] /* Build dependencies for this module. */, // packageName: undefined, /* The "name" in package.json. */ jestOptions: { extraCliOptions: ['--testTimeout=300000'], diff --git a/package.json b/package.json index 2d71de4..5d3b1c3 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,11 @@ "@biomejs/biome": "^1.9.4", "@types/jest": "^29.5.14", "@types/node": "^22.10.3", + "@types/fs-extra": "^11.0.4", "aws-cdk-lib": "2.161.1", "commit-and-tag-version": "^12", "constructs": "10.3.0", + "fs-extra": "^11.2.0", "jest": "^29.7.0", "jest-junit": "^15", "jsii": "~5.5.0", diff --git a/yarn.lock b/yarn.lock index ad31660..c6f3b26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -783,6 +783,14 @@ dependencies: "@babel/types" "^7.20.7" +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz" @@ -817,6 +825,13 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== + dependencies: + "@types/node" "*" + "@types/minimist@^1.2.0": version "1.2.5" resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz" From d261d848ce1d8467bb65881122da5e42eb2cd637 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 12:11:22 +0000 Subject: [PATCH 05/14] Add tests for PACKAGE_VERSION strategy with multiple functions These are not working correctly yet as `uv sync` is changing the lockfile in a way that's breaking things. Committing so can make a PR and get help debugging further. --- src/bundling.ts | 2 + test/function.test.ts | 437 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 400 insertions(+), 39 deletions(-) diff --git a/src/bundling.ts b/src/bundling.ts index 11c9d23..9f526b4 100644 --- a/src/bundling.ts +++ b/src/bundling.ts @@ -129,6 +129,8 @@ export class Bundling { ? `--package ${workspacePackage}` : ''; const uvTreeOptions = [ + '--python-preference=only-system', // will be running the lambda python + '--no-dev', // don't need dev dependencies '--frozen', // don't try and update the lock file ]; const command = `cd ${options.rootDir} && uv tree ${uvTreeOptions.join(' ')} ${uvPackageArgs}`; diff --git a/test/function.test.ts b/test/function.test.ts index 0bbcc3f..bb1a7c4 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -7,6 +7,7 @@ import { App, Stack } from 'aws-cdk-lib'; import { Match, Template } from 'aws-cdk-lib/assertions'; import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; import * as cxapi from 'aws-cdk-lib/cx-api'; +import * as fsextra from 'fs-extra'; import { BundlingStrategy, PythonFunction } from '../src'; const execAsync = promisify(exec); @@ -55,7 +56,7 @@ beforeEach(async () => { jest.resetModules(); process.env = { ...OLD_ENV }; process.env.CDK_OUTDIR = await fs.mkdtemp( - path.join(os.tmpdir(), 'uv-python-lambda-test-'), + path.join(os.tmpdir(), 'uv-python-lambda-function'), ); }); @@ -67,6 +68,8 @@ afterEach(async () => { }); describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { + jest.setTimeout(20000); // we are doing integration tests with the file system so give tests more time + test('Create a function from basic_app', async () => { const { app, stack } = await createStack(); @@ -91,11 +94,9 @@ describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { S3Key: Match.anyValue(), }, }); - const functions = Object.values( - template.findResources('AWS::Lambda::Function'), - ); + const functions = getFunctions(template); expect(functions).toHaveLength(1); - const contents = await getFunctionAssetContents(functions[0], app); + const contents = await getAssetContent(functions[0], app); expect(contents).toContain('handler.py'); }); @@ -176,11 +177,9 @@ describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { }, }); - const functions = Object.values( - template.findResources('AWS::Lambda::Function'), - ); + const functions = getFunctions(template); expect(functions).toHaveLength(1); - const contents = await getFunctionAssetContents(functions[0], app); + const contents = await getAssetContent(functions[0], app); for (const entry of [ 'app', 'common', @@ -192,9 +191,80 @@ describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { expect(contents).toContain(entry); } }); + + test('Create multiple functions with workspaces_app', async () => { + const { app, stack } = await createStack('wstest'); + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(resourcesPath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.SOURCE, + }, + }); + + new PythonFunction(stack, 'workspaces_backend', { + rootDir: path.join(resourcesPath, 'workspaces_app'), + workspacePackage: 'backend', + index: 'backend_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.SOURCE, + }, + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'app_handler.handle_event', + }); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'backend_handler.handle_event', + }); + + const functions = getFunctions(template); + expect(functions).toHaveLength(2); + const appContents = await getAssetContent(functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(appContents).toContain(entry); + } + expect(appContents).not.toContain('backend_handler.py'); + expect(appContents).not.toContain('pathlib'); + const backendContents = await getAssetContent(functions[1], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'backend_handler.py', + ]) { + expect(backendContents).toContain(entry); + } + expect(backendContents).not.toContain('app_handler.py'); + expect(backendContents).not.toContain('flask'); + }, 30000); }); describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () => { + jest.setTimeout(30000); // we are doing integration tests with the file system so give tests more time + test('Create a function from basic_app', async () => { const { app, stack } = await createStack(); @@ -219,11 +289,9 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () S3Key: Match.anyValue(), }, }); - const functions = Object.values( - template.findResources('AWS::Lambda::Function'), - ); + const functions = getFunctions(template); expect(functions).toHaveLength(1); - const contents = await getFunctionAssetContents(functions[0], app); + const contents = await getAssetContent(functions[0], app); expect(contents).toContain('handler.py'); }); @@ -304,12 +372,9 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () }, }); - const functions = Object.values( - template.findResources('AWS::Lambda::Function'), - ); + const functions = getFunctions(template); expect(functions).toHaveLength(1); - const contents = await getFunctionAssetContents(functions[0], app); - console.log('contents', contents); + const contents = await getAssetContent(functions[0], app); for (const entry of [ 'app', 'common', @@ -321,29 +386,323 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () expect(contents).toContain(entry); } }); + + async function createMultipleFunctionStack( + workspacePath?: string, + ): Promise<{ app: App; stack: Stack }> { + const { app, stack } = await createStack('wstest'); + const rootPath = workspacePath ?? resourcesPath; + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(rootPath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, + }, + }); + + new PythonFunction(stack, 'workspaces_backend', { + rootDir: path.join(rootPath, 'workspaces_app'), + workspacePackage: 'backend', + index: 'backend_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, + }, + }); + + return { app, stack }; + } + + test('Create multiple functions with workspaces_app', async () => { + const { app, stack } = await createMultipleFunctionStack(); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'app_handler.handle_event', + }); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'backend_handler.handle_event', + }); + + const functions = getFunctions(template); + expect(functions).toHaveLength(2); + const appContents = await getAssetContent(functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(appContents).toContain(entry); + } + expect(appContents).not.toContain('backend_handler.py'); + expect(appContents).not.toContain('pathlib'); + const backendContents = await getAssetContent(functions[1], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'backend_handler.py', + ]) { + expect(backendContents).toContain(entry); + } + expect(backendContents).not.toContain('app_handler.py'); + expect(backendContents).not.toContain('flask'); + }); + + test("Doesn't rebuild multiple functions if they haven't changed", async () => { + let { app, stack } = await createMultipleFunctionStack(); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const appAssetPath = getAssetPath(run1Functions[0], app); + const backendAssetPath = getAssetPath(run1Functions[1], app); + // change the contents of the folder so we can verify they are not rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + await setAssetContents(run1Functions[1], app, ['backend.text']); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createMultipleFunctionStack()); + + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that the same folder is being used for the functions + expect(getAssetPath(run2Functions[0], app)).toEqual(appAssetPath); + expect(getAssetPath(run2Functions[1], app)).toEqual(backendAssetPath); + + // validate they contain the content we put in above, i.e. haven't been overwritten + expect(await getAssetContent(run2Functions[0], app)).toEqual(['app.text']); + expect(await getAssetContent(run2Functions[1], app)).toEqual([ + 'backend.text', + ]); + }); + + test('Rebuild single function when its version changes', async () => { + // copy the workspace so we can edit it without affecting the original + const workspacePath = await copyWorkspaceToTemp('workspaces_app'); + let { app, stack } = await createMultipleFunctionStack(workspacePath); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const appAssetPath = getAssetPath(run1Functions[0], app); + const backendAssetPath = getAssetPath(run1Functions[1], app); + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + await setAssetContents(run1Functions[1], app, ['backend.text']); + + // Update the version in the workspace + await bumpVersionAndUvSync(workspacePath, 'app'); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createMultipleFunctionStack(workspacePath)); + + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that different folder for updated function but same one for unchanged + expect(getAssetPath(run2Functions[0], app)).not.toEqual(appAssetPath); + expect(getAssetPath(run2Functions[1], app)).toEqual(backendAssetPath); + + // validate app has been rebuild but backend hasn't been touched + const appContents = await getAssetContent(run2Functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + // '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + // 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + ]) { + expect(appContents).toContain(entry); + } + expect(await getAssetContent(run2Functions[1], app)).toEqual([ + 'backend.text', + ]); + }); + + test('Rebuild any function when its dependency version changes', async () => { + // copy the workspace so we can edit it without affecting the original + const workspacePath = await copyWorkspaceToTemp('workspaces_app'); + let { app, stack } = await createMultipleFunctionStack(workspacePath); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const appAssetPath = getAssetPath(run1Functions[0], app); + const backendAssetPath = getAssetPath(run1Functions[1], app); + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + await setAssetContents(run1Functions[1], app, ['backend.text']); + + // Update the version in the workspace + await bumpVersionAndUvSync(workspacePath, 'common'); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createMultipleFunctionStack(workspacePath)); + + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that different folder for both updated functions + expect(getAssetPath(run2Functions[0], app)).not.toEqual(appAssetPath); + expect(getAssetPath(run2Functions[1], app)).not.toEqual(backendAssetPath); + + // validate app and backend have both been rebuilt + const appContents = await getAssetContent(run2Functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + // '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + // 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + ]) { + expect(appContents).toContain(entry); + } + const backendContents = await getAssetContent(run2Functions[1], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + // '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + // 'backend_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + ]) { + expect(backendContents).toContain(entry); + } + }); }); -// biome-ignore lint/suspicious/noExplicitAny: -async function getFunctionAssetContents(functionResource: any, app: App) { +/** + * Copy the workspace to a temporary directory and return the path to the temporary directory. + * The workspace is copied to ensure that the original workspace is not modified when we change it during tests. + * + * @param workspace + */ +async function copyWorkspaceToTemp(workspace: string): Promise { + const tempDir = await fs.mkdtemp( + path.join(os.tmpdir(), 'uv-python-lambda-workspace'), + ); + await fsextra.copy( + path.join(resourcesPath, workspace), + path.join(tempDir, workspace), + ); + return tempDir; +} + +/** + * Bump the version of a workspace package and run `uv sync` to update the workspace. + * + * @param workspacePath + * @param workspacePackage + * @param oldVersion + * @param newVersion + */ +async function bumpVersionAndUvSync( + workspacePath: string, + workspacePackage: string, + oldVersion = '0.1.0', + newVersion = '0.1.1', +): Promise { + // Update the version in the workspace + await replaceStringInFile( + path.join( + workspacePath, + 'workspaces_app', + workspacePackage, + 'pyproject.toml', + ), + `version = "${oldVersion}"`, + `version = "${newVersion}"`, + ); + const uvSyncArgs = [ + '--python-preference=only-system', + '--compile-bytecode', + '--no-dev', + '--no-editable', + '--link-mode=copy', + ]; + await execAsync( + `cd ${workspacePath}/workspaces_app && uv sync --directory ${workspacePath}/workspaces_app --package ${workspacePackage} ${uvSyncArgs.join(' ')}`, + ); +} + +/** + * Replace all occurrences of a string in a file. Used to make modifications to workspace copies for testing purposes. + * + * @param filePath + * @param searchString + * @param replaceString + */ +async function replaceStringInFile( + filePath: string, + searchString: string, + replaceString: string, +): Promise { + try { + // Read the file content + const content = await fs.readFile(filePath, 'utf-8'); + + // Perform the replacement + const updatedContent = content.replace( + new RegExp(searchString, 'g'), + replaceString, + ); + + // Write the modified content back to the file + await fs.writeFile(filePath, updatedContent, 'utf-8'); + } catch (error) { + if (error instanceof Error) { + throw new Error(`Failed to replace string in file: ${error.message}`); + } + throw error; + } +} + +// biome-ignore lint/suspicious/noExplicitAny: any is what is returned from the CDK template.findResources method +function getFunctions(template: Template): any[] { + return Object.values(template.findResources('AWS::Lambda::Function')); +} + +// biome-ignore lint/suspicious/noExplicitAny: any is what is returned from the CDK template.findResources method +function getAssetPath(functionResource: any, app: App): string { const [assetHash] = functionResource.Properties.Code.S3Key.split('.'); - const assetPath = path.join(app.outdir, `asset.${assetHash}`); - return await fs.readdir(assetPath); + return path.join(app.outdir, `asset.${assetHash}`); } -// async function setFunctionAssetContents( -// functionResource: any, -// app: App, -// contents: string[], -// ) { -// const [assetHash] = functionResource.Properties.Code.S3Key.split('.'); -// const assetPath = path.join(app.outdir, `asset.${assetHash}`); -// console.log({ assetPath }); -// // remove all existing contents and add the new ones as empty files -// const existingContents = await fs.readdir(assetPath); -// for (const entry of existingContents) { -// await fs.rm(entry, { recursive: true }); -// } -// for (const entry of contents) { -// await fs.writeFile(path.join(assetPath, entry), ''); -// } -// } +// biome-ignore lint/suspicious/noExplicitAny: any is what is returned from the CDK template.findResources method +async function getAssetContent(functionResource: any, app: App) { + return await fs.readdir(getAssetPath(functionResource, app)); +} + +async function setAssetContents( + // biome-ignore lint/suspicious/noExplicitAny: any is what is returned from the CDK template.findResources method + functionResource: any, + app: App, + contents: string[], +) { + const assetPath = getAssetPath(functionResource, app); + + // remove all existing contents and add the new ones as empty files + const existingContents = await fs.readdir(assetPath); + for (const entry of existingContents) { + await fs.rm(path.join(assetPath, entry), { force: true, recursive: true }); + } + for (const entry of contents) { + await fs.writeFile(path.join(assetPath, entry), ''); + } +} From 13c13a37f2db1537d68e8c90abc902dd2d440c86 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 18:36:23 +0000 Subject: [PATCH 06/14] Add git based bundling strategy If we're not using a package in a workspace app then use the last commit hash for the entire rootDir and anything in git diff to generate the custom hash for CDK bundling. If we are using a package in a workspace app then get the list of dependencies and check and local ones for changes. This uses the hash of the last commit and any git diff in that part of the tree for each dependency and the package itself. --- src/bundling.ts | 62 ++ src/types.ts | 1 + test/function.test.ts | 582 +++++++++++++++++- .../resources/workspaces_app/common/README.md | 1 + 4 files changed, 630 insertions(+), 16 deletions(-) diff --git a/src/bundling.ts b/src/bundling.ts index 9f526b4..2d167bb 100644 --- a/src/bundling.ts +++ b/src/bundling.ts @@ -92,6 +92,8 @@ export class Bundling { switch (options.bundlingStrategy) { case BundlingStrategy.PACKAGE_VERSION: return Bundling.packageVersionStrategy(bundlingOptions); + case BundlingStrategy.GIT: + return Bundling.gitStrategy(bundlingOptions); default: return Bundling.sourceStrategy(bundlingOptions); } @@ -146,6 +148,45 @@ export class Bundling { }); } + private static gitStrategy(options: BundlingProps): AssetCode { + const workspacePackage = options.workspacePackage; + let assetHash: string; + + if (!workspacePackage) { + assetHash = Bundling.gitHash(options.rootDir); + } else { + const uvCommand = `cd ${options.rootDir} && uv export --package ${workspacePackage} --frozen --no-editable --no-dev --no-header`; + const dependencies = execSync(uvCommand).toString(); + // This includes the current workspacePackage + const workspaceDependencies = extractWorkspaceDependencies(dependencies); + const hash = createHash('sha256').update(dependencies); + for (const dependency of workspaceDependencies) { + hash.update(Bundling.gitHash(path.join(options.rootDir, dependency))); + } + assetHash = hash.digest('hex'); + } + + return Code.fromAsset(options.rootDir, { + assetHashType: AssetHashType.CUSTOM, + assetHash, + exclude: options.hashableAssetExclude, + bundling: new Bundling(options), + }); + } + + private static gitHash(path: string): string { + const gitCommands = [ + `cd ${path}`, + 'git log -1 --format=%H -- .', // find the hash of the last commit that changed the files in the directory + 'git diff -- .', // get the diff of the files in the directory and below + ]; + const gitCommand = gitCommands.join(' && '); + const status = execSync(gitCommand).toString().trim(); + const assetHash = createHash('sha256').update(status).digest('hex'); + + return assetHash; + } + public readonly image: DockerImage; public readonly entrypoint?: string[] | undefined; public readonly command: string[] | undefined; @@ -249,3 +290,24 @@ export class Bundling { return commands; } } + +function extractWorkspaceDependencies(content: string): string[] { + // Split content into lines and filter out empty lines + const lines = content.split('\n').filter((line) => line.trim()); + + // Regular expression to match package lines + // Matches lines containing package==version followed by hash(es) + const packageLineRegex = + /^[\w-]+==[0-9]+\.[0-9]+(\.[0-9]+)?([a-z0-9.]+)?\s*(\\|\s|$)/; + + // Filter out package lines and hash lines + return lines.filter((line) => { + // Skip hash lines + if (line.trim().startsWith('--hash=')) { + return false; + } + + // Keep lines that don't match package pattern + return !packageLineRegex.test(line); + }); +} diff --git a/src/types.ts b/src/types.ts index f6ad42a..4eca20d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -106,6 +106,7 @@ export interface BundlingOptions extends DockerRunOptions { export enum BundlingStrategy { SOURCE = 'source', PACKAGE_VERSION = 'package-version', + GIT = 'git', } /** diff --git a/test/function.test.ts b/test/function.test.ts index bb1a7c4..c99a336 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -491,9 +491,538 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () ]); }); - test('Rebuild single function when its version changes', async () => { + test.failing( + 'FAILING: Rebuild single function when its version changes', + async () => { + // copy the workspace so we can edit it without affecting the original + const workspacePath = await copyWorkspaceToTemp('workspaces_app'); + let { app, stack } = await createMultipleFunctionStack(workspacePath); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const appAssetPath = getAssetPath(run1Functions[0], app); + const backendAssetPath = getAssetPath(run1Functions[1], app); + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + await setAssetContents(run1Functions[1], app, ['backend.text']); + + // Update the version in the workspace + await bumpVersionAndUvSync(workspacePath, 'app'); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createMultipleFunctionStack(workspacePath)); + + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that different folder for updated function but same one for unchanged + expect(getAssetPath(run2Functions[0], app)).not.toEqual(appAssetPath); + expect(getAssetPath(run2Functions[1], app)).toEqual(backendAssetPath); + + // validate app has been rebuild but backend hasn't been touched + const appContents = await getAssetContent(run2Functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + ]) { + expect(appContents).toContain(entry); + } + expect(await getAssetContent(run2Functions[1], app)).toEqual([ + 'backend.text', + ]); + }, + ); + + test.failing( + 'FAILING: Rebuild any function when its dependency version changes', + async () => { + // copy the workspace so we can edit it without affecting the original + const workspacePath = await copyWorkspaceToTemp('workspaces_app'); + let { app, stack } = await createMultipleFunctionStack(workspacePath); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const appAssetPath = getAssetPath(run1Functions[0], app); + const backendAssetPath = getAssetPath(run1Functions[1], app); + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + await setAssetContents(run1Functions[1], app, ['backend.text']); + + // Update the version in the workspace + await bumpVersionAndUvSync(workspacePath, 'common'); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createMultipleFunctionStack(workspacePath)); + + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that different folder for both updated functions + expect(getAssetPath(run2Functions[0], app)).not.toEqual(appAssetPath); + expect(getAssetPath(run2Functions[1], app)).not.toEqual(backendAssetPath); + + // validate app and backend have both been rebuilt + const appContents = await getAssetContent(run2Functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + ]) { + expect(appContents).toContain(entry); + } + const backendContents = await getAssetContent(run2Functions[1], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + 'backend_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + ]) { + expect(backendContents).toContain(entry); + } + }, + ); +}); + +describe('When bundlingStrategy is set to BundlingStrategy.GIT', () => { + jest.setTimeout(30000); // we are doing integration tests with the file system so give tests more time + + test('Create a function from basic_app', async () => { + const { app, stack } = await createStack(); + + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler.py', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'handler.lambda_handler', + Runtime: 'python3.12', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); + const functions = getFunctions(template); + expect(functions).toHaveLength(1); + const contents = await getAssetContent(functions[0], app); + expect(contents).toContain('handler.py'); + }); + + test('Create a function from basic_app when skip is true', async () => { + const { stack } = await createStack(); + + const bundlingSpy = jest + .spyOn(stack, 'bundlingRequired', 'get') + .mockReturnValue(false); + const architecture = await getDockerHostArch(); + + // To see this fail, comment out the `if (skip) { return; } code in the PythonFunction constructor + expect(() => { + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(resourcesPath, 'basic_app'), + index: 'handler', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture, + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + }).not.toThrow(); + + bundlingSpy.mockRestore(); + }); + + test('Create a function from basic_app only gets rebuild when source changes', async () => { + const createBasicAppStack = async (workspacePath: string) => { + const { app, stack } = await createStack(); + + new PythonFunction(stack, 'basic_app', { + rootDir: path.join(workspacePath, 'basic_app'), + index: 'handler.py', + handler: 'lambda_handler', + runtime: Runtime.PYTHON_3_12, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + + return { app, stack }; + }; + + const workspacePath = await copyWorkspaceToTemp('basic_app', { git: true }); + + let { app, stack } = await createBasicAppStack(workspacePath); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const basicAppAssetPath = getAssetPath(run1Functions[0], app); + expect(run1Functions).toHaveLength(1); + const contents = await getAssetContent(run1Functions[0], app); + expect(contents).toContain('handler.py'); + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['basic_app.text']); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createBasicAppStack(workspacePath)); + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that same folder is used because the hash is the same + expect(getAssetPath(run2Functions[0], app)).toEqual(basicAppAssetPath); + expect(await getAssetContent(run2Functions[0], app)).toEqual([ + 'basic_app.text', + ]); + + // Now modify the source folder and check that the function is rebuilt + await replaceStringInFile( + path.join(workspacePath, 'basic_app', 'handler.py'), + 'Hello', + 'Hi', + ); + + ({ app, stack } = await createBasicAppStack(workspacePath)); + template = Template.fromStack(stack); + const run3Functions = getFunctions(template); + // validate that different folder is used because the hash is different + expect(getAssetPath(run3Functions[0], app)).not.toEqual(basicAppAssetPath); + expect(await getAssetContent(run3Functions[0], app)).toContain( + 'handler.py', + ); + }); + + test('Create a function with workspaces_app', async () => { + const { app, stack } = await createStack('wstest'); + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(resourcesPath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'app_handler.handle_event', + Runtime: 'python3.10', + Code: { + S3Bucket: Match.anyValue(), + S3Key: Match.anyValue(), + }, + }); + + const functions = getFunctions(template); + expect(functions).toHaveLength(1); + const contents = await getAssetContent(functions[0], app); + for (const entry of [ + 'app', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(contents).toContain(entry); + } + }); + + const createWorkspaceAppStack = async (workspacePath: string) => { + const { app, stack } = await createStack(); + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(workspacePath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + + return { app, stack }; + }; + + test('Create a function with workspaces_app gets rebuilt if app changes', async () => { + const workspacePath = await copyWorkspaceToTemp('workspaces_app', { + git: true, + }); + + let { app, stack } = await createWorkspaceAppStack(workspacePath); + + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + expect(run1Functions).toHaveLength(1); + const appAssetPath = getAssetPath(run1Functions[0], app); + const contents = await getAssetContent(run1Functions[0], app); + for (const entry of [ + 'app', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(contents).toContain(entry); + } + + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createWorkspaceAppStack(workspacePath)); + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that same folder is used because the hash is the same + expect(getAssetPath(run2Functions[0], app)).toEqual(appAssetPath); + expect(await getAssetContent(run2Functions[0], app)).toEqual(['app.text']); + + // Now modify the source folder and check that the function is rebuilt + await replaceStringInFile( + path.join(workspacePath, 'workspaces_app', 'app', 'app_handler.py'), + 'Corolla', + 'Auris', + ); + + ({ app, stack } = await createWorkspaceAppStack(workspacePath)); + template = Template.fromStack(stack); + const run3Functions = getFunctions(template); + const run3AssetPath = getAssetPath(run3Functions[0], app); + // validate that different folder is used because the hash is different + expect(run3AssetPath).not.toEqual(appAssetPath); + expect(await getAssetContent(run3Functions[0], app)).toContain( + 'app_handler.py', + ); + }); + + test('Create a function with workspaces_app gets rebuilt if dependency changes', async () => { + const workspacePath = await copyWorkspaceToTemp('workspaces_app', { + git: true, + }); + + let { app, stack } = await createWorkspaceAppStack(workspacePath); + + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + expect(run1Functions).toHaveLength(1); + const appAssetPath = getAssetPath(run1Functions[0], app); + const contents = await getAssetContent(run1Functions[0], app); + for (const entry of [ + 'app', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(contents).toContain(entry); + } + + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + + // Now modify the dependency source folder and check that the function is rebuilt + await replaceStringInFile( + path.join(workspacePath, 'workspaces_app', 'common', 'README.md'), + 'Common', + 'Shared', + ); + + ({ app, stack } = await createWorkspaceAppStack(workspacePath)); + template = Template.fromStack(stack); + const run3Functions = getFunctions(template); + const run3AssetPath = getAssetPath(run3Functions[0], app); + // validate that different folder is used because the hash is different + expect(run3AssetPath).not.toEqual(appAssetPath); + expect(await getAssetContent(run3Functions[0], app)).toContain( + 'app_handler.py', + ); + }); + + test('Create a function with workspaces_app does NOT get rebuilt if unrelated code changes', async () => { + const workspacePath = await copyWorkspaceToTemp('workspaces_app', { + git: true, + }); + + let { app, stack } = await createWorkspaceAppStack(workspacePath); + + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + expect(run1Functions).toHaveLength(1); + const appAssetPath = getAssetPath(run1Functions[0], app); + const contents = await getAssetContent(run1Functions[0], app); + for (const entry of [ + 'app', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(contents).toContain(entry); + } + + // change the contents of the folder so we can verify what is rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + + // Now modify the unrelated app source and check that the function is not rebuilt + await replaceStringInFile( + path.join( + workspacePath, + 'workspaces_app', + 'backend', + 'backend_handler.py', + ), + 'Skyline', + 'Juke', + ); + + ({ app, stack } = await createWorkspaceAppStack(workspacePath)); + template = Template.fromStack(stack); + const run3Functions = getFunctions(template); + const run3AssetPath = getAssetPath(run3Functions[0], app); + // validate that same folder is used because the hash is the same + expect(run3AssetPath).toEqual(appAssetPath); + expect(await getAssetContent(run3Functions[0], app)).toContain('app.text'); + }); + + async function createMultipleFunctionStack( + workspacePath?: string, + ): Promise<{ app: App; stack: Stack }> { + const { app, stack } = await createStack('wstest'); + const rootPath = workspacePath ?? resourcesPath; + + new PythonFunction(stack, 'workspaces_app', { + rootDir: path.join(rootPath, 'workspaces_app'), + workspacePackage: 'app', + index: 'app_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + + new PythonFunction(stack, 'workspaces_backend', { + rootDir: path.join(rootPath, 'workspaces_app'), + workspacePackage: 'backend', + index: 'backend_handler.py', + handler: 'handle_event', + runtime: Runtime.PYTHON_3_10, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, + }); + + return { app, stack }; + } + + test('Create multiple functions with workspaces_app', async () => { + const { app, stack } = await createMultipleFunctionStack(); + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'app_handler.handle_event', + }); + + template.hasResourceProperties('AWS::Lambda::Function', { + Handler: 'backend_handler.handle_event', + }); + + const functions = getFunctions(template); + expect(functions).toHaveLength(2); + const appContents = await getAssetContent(functions[0], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'app_handler.py', + ]) { + expect(appContents).toContain(entry); + } + expect(appContents).not.toContain('backend_handler.py'); + expect(appContents).not.toContain('pathlib'); + const backendContents = await getAssetContent(functions[1], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'backend_handler.py', + ]) { + expect(backendContents).toContain(entry); + } + expect(backendContents).not.toContain('app_handler.py'); + expect(backendContents).not.toContain('flask'); + }); + + test("Doesn't rebuild multiple functions if they haven't changed", async () => { + let { app, stack } = await createMultipleFunctionStack(); + let template = Template.fromStack(stack); + + const run1Functions = getFunctions(template); + const appAssetPath = getAssetPath(run1Functions[0], app); + const backendAssetPath = getAssetPath(run1Functions[1], app); + // change the contents of the folder so we can verify they are not rebuilt + await setAssetContents(run1Functions[0], app, ['app.text']); + await setAssetContents(run1Functions[1], app, ['backend.text']); + + // Need to create the stack again as synthesize is only called once otherwise + ({ app, stack } = await createMultipleFunctionStack()); + + template = Template.fromStack(stack); + const run2Functions = getFunctions(template); + // validate that the same folder is being used for the functions + expect(getAssetPath(run2Functions[0], app)).toEqual(appAssetPath); + expect(getAssetPath(run2Functions[1], app)).toEqual(backendAssetPath); + + // validate they contain the content we put in above, i.e. haven't been overwritten + expect(await getAssetContent(run2Functions[0], app)).toEqual(['app.text']); + expect(await getAssetContent(run2Functions[1], app)).toEqual([ + 'backend.text', + ]); + }); + + test('Rebuild single function when its content changes', async () => { // copy the workspace so we can edit it without affecting the original - const workspacePath = await copyWorkspaceToTemp('workspaces_app'); + const workspacePath = await copyWorkspaceToTemp('workspaces_app', { + git: true, + }); let { app, stack } = await createMultipleFunctionStack(workspacePath); let template = Template.fromStack(stack); @@ -504,9 +1033,12 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () await setAssetContents(run1Functions[0], app, ['app.text']); await setAssetContents(run1Functions[1], app, ['backend.text']); - // Update the version in the workspace - await bumpVersionAndUvSync(workspacePath, 'app'); - + // Now modify the source folder and check that the function is rebuilt + await replaceStringInFile( + path.join(workspacePath, 'workspaces_app', 'app', 'app_handler.py'), + 'Corolla', + 'Auris', + ); // Need to create the stack again as synthesize is only called once otherwise ({ app, stack } = await createMultipleFunctionStack(workspacePath)); @@ -524,8 +1056,8 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () 'common', 'pydantic', 'httpx', - // '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - // 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + '_common.pth', + 'app_handler.py', ]) { expect(appContents).toContain(entry); } @@ -536,7 +1068,9 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () test('Rebuild any function when its dependency version changes', async () => { // copy the workspace so we can edit it without affecting the original - const workspacePath = await copyWorkspaceToTemp('workspaces_app'); + const workspacePath = await copyWorkspaceToTemp('workspaces_app', { + git: true, + }); let { app, stack } = await createMultipleFunctionStack(workspacePath); let template = Template.fromStack(stack); @@ -547,9 +1081,12 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () await setAssetContents(run1Functions[0], app, ['app.text']); await setAssetContents(run1Functions[1], app, ['backend.text']); - // Update the version in the workspace - await bumpVersionAndUvSync(workspacePath, 'common'); - + // Now modify the dependency source folder and check that both functions are rebuilt + await replaceStringInFile( + path.join(workspacePath, 'workspaces_app', 'common', 'README.md'), + 'Common', + 'Shared', + ); // Need to create the stack again as synthesize is only called once otherwise ({ app, stack } = await createMultipleFunctionStack(workspacePath)); @@ -567,8 +1104,8 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () 'common', 'pydantic', 'httpx', - // '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - // 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + '_common.pth', + 'app_handler.py', ]) { expect(appContents).toContain(entry); } @@ -579,8 +1116,8 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () 'common', 'pydantic', 'httpx', - // '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - // 'backend_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) + '_common.pth', + 'backend_handler.py', ]) { expect(backendContents).toContain(entry); } @@ -592,8 +1129,12 @@ describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () * The workspace is copied to ensure that the original workspace is not modified when we change it during tests. * * @param workspace + * @param git - if true, a git repository is initialized in the copied workspace */ -async function copyWorkspaceToTemp(workspace: string): Promise { +async function copyWorkspaceToTemp( + workspace: string, + { git = false }: { git?: boolean } = { git: false }, +): Promise { const tempDir = await fs.mkdtemp( path.join(os.tmpdir(), 'uv-python-lambda-workspace'), ); @@ -601,6 +1142,15 @@ async function copyWorkspaceToTemp(workspace: string): Promise { path.join(resourcesPath, workspace), path.join(tempDir, workspace), ); + + if (git) { + const gitPath = path.join(tempDir, workspace); + await execAsync('git init', { cwd: gitPath }); + await execAsync('git add .', { cwd: gitPath }); + await execAsync('git commit -m "commit for test purposes"', { + cwd: gitPath, + }); + } return tempDir; } diff --git a/test/resources/workspaces_app/common/README.md b/test/resources/workspaces_app/common/README.md index e69de29..418e2f2 100644 --- a/test/resources/workspaces_app/common/README.md +++ b/test/resources/workspaces_app/common/README.md @@ -0,0 +1 @@ +Common code for all workspaces apps From 87060f9d09728e02bea954dac79348cce77d1253 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 18:55:37 +0000 Subject: [PATCH 07/14] Remove PACKAGE_VERSION bundling strategy Couldn't get the tests to pass so removing for now until we can figure out why and put it back in. --- src/bundling.ts | 35 ---- src/types.ts | 1 - test/function.test.ts | 368 ------------------------------------------ 3 files changed, 404 deletions(-) diff --git a/src/bundling.ts b/src/bundling.ts index 2d167bb..41aecf7 100644 --- a/src/bundling.ts +++ b/src/bundling.ts @@ -90,8 +90,6 @@ export class Bundling { ...bundlingOptions } = options; switch (options.bundlingStrategy) { - case BundlingStrategy.PACKAGE_VERSION: - return Bundling.packageVersionStrategy(bundlingOptions); case BundlingStrategy.GIT: return Bundling.gitStrategy(bundlingOptions); default: @@ -115,39 +113,6 @@ export class Bundling { }); } - /** - * Uses the AssetHashType.CUSTOM strategy and uv tree output to calculate the asset hash. - * - * This strategy uses the output of `uv tree` to calculate the asset hash. If there are multiple packages in a workspace this method will only - * rebuild the asset for a package if the package or a dependency version changes. This will not pick up local changes to the source unless they - * also change the package version in pyproject.toml. - * - * @param options - * @private - */ - private static packageVersionStrategy(options: BundlingProps): AssetCode { - const workspacePackage = options.workspacePackage; - const uvPackageArgs = workspacePackage - ? `--package ${workspacePackage}` - : ''; - const uvTreeOptions = [ - '--python-preference=only-system', // will be running the lambda python - '--no-dev', // don't need dev dependencies - '--frozen', // don't try and update the lock file - ]; - const command = `cd ${options.rootDir} && uv tree ${uvTreeOptions.join(' ')} ${uvPackageArgs}`; - // TODO: find something that works on Windows, maybe automatically changing directory and running the command - const tree = execSync(command).toString().trim(); - const assetHash = createHash('sha256').update(tree).digest('hex'); - - return Code.fromAsset(options.rootDir, { - assetHashType: AssetHashType.CUSTOM, - assetHash, - exclude: options.hashableAssetExclude, - bundling: new Bundling(options), - }); - } - private static gitStrategy(options: BundlingProps): AssetCode { const workspacePackage = options.workspacePackage; let assetHash: string; diff --git a/src/types.ts b/src/types.ts index 4eca20d..13eb2a5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -105,7 +105,6 @@ export interface BundlingOptions extends DockerRunOptions { export enum BundlingStrategy { SOURCE = 'source', - PACKAGE_VERSION = 'package-version', GIT = 'git', } diff --git a/test/function.test.ts b/test/function.test.ts index c99a336..d901ab4 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -262,337 +262,6 @@ describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { }, 30000); }); -describe('When bundlingStrategy is set to BundlingStrategy.PACKAGE_VERSION', () => { - jest.setTimeout(30000); // we are doing integration tests with the file system so give tests more time - - test('Create a function from basic_app', async () => { - const { app, stack } = await createStack(); - - new PythonFunction(stack, 'basic_app', { - rootDir: path.join(resourcesPath, 'basic_app'), - index: 'handler.py', - handler: 'lambda_handler', - runtime: Runtime.PYTHON_3_12, - architecture: await getDockerHostArch(), - bundling: { - bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, - }, - }); - - const template = Template.fromStack(stack); - - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'handler.lambda_handler', - Runtime: 'python3.12', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, - }); - const functions = getFunctions(template); - expect(functions).toHaveLength(1); - const contents = await getAssetContent(functions[0], app); - expect(contents).toContain('handler.py'); - }); - - test('Create a function from basic_app with no .py index extension', async () => { - const { stack } = await createStack(); - - new PythonFunction(stack, 'basic_app', { - rootDir: path.join(resourcesPath, 'basic_app'), - index: 'handler', - handler: 'lambda_handler', - runtime: Runtime.PYTHON_3_12, - architecture: await getDockerHostArch(), - bundling: { - bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, - }, - }); - - const template = Template.fromStack(stack); - - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'handler.lambda_handler', - Runtime: 'python3.12', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, - }); - }); - - test('Create a function from basic_app when skip is true', async () => { - const { stack } = await createStack(); - - const bundlingSpy = jest - .spyOn(stack, 'bundlingRequired', 'get') - .mockReturnValue(false); - const architecture = await getDockerHostArch(); - - // To see this fail, comment out the `if (skip) { return; } code in the PythonFunction constructor - expect(() => { - new PythonFunction(stack, 'basic_app', { - rootDir: path.join(resourcesPath, 'basic_app'), - index: 'handler', - handler: 'lambda_handler', - runtime: Runtime.PYTHON_3_12, - architecture, - bundling: { - bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, - }, - }); - }).not.toThrow(); - - bundlingSpy.mockRestore(); - }); - - test('Create a function with workspaces_app', async () => { - const { app, stack } = await createStack('wstest'); - - new PythonFunction(stack, 'workspaces_app', { - rootDir: path.join(resourcesPath, 'workspaces_app'), - workspacePackage: 'app', - index: 'app_handler.py', - handler: 'handle_event', - runtime: Runtime.PYTHON_3_10, - architecture: await getDockerHostArch(), - bundling: { - bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, - }, - }); - - const template = Template.fromStack(stack); - - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'app_handler.handle_event', - Runtime: 'python3.10', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, - }); - - const functions = getFunctions(template); - expect(functions).toHaveLength(1); - const contents = await getAssetContent(functions[0], app); - for (const entry of [ - 'app', - 'common', - 'pydantic', - 'httpx', - '_common.pth', - 'app_handler.py', - ]) { - expect(contents).toContain(entry); - } - }); - - async function createMultipleFunctionStack( - workspacePath?: string, - ): Promise<{ app: App; stack: Stack }> { - const { app, stack } = await createStack('wstest'); - const rootPath = workspacePath ?? resourcesPath; - - new PythonFunction(stack, 'workspaces_app', { - rootDir: path.join(rootPath, 'workspaces_app'), - workspacePackage: 'app', - index: 'app_handler.py', - handler: 'handle_event', - runtime: Runtime.PYTHON_3_10, - architecture: await getDockerHostArch(), - bundling: { - bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, - }, - }); - - new PythonFunction(stack, 'workspaces_backend', { - rootDir: path.join(rootPath, 'workspaces_app'), - workspacePackage: 'backend', - index: 'backend_handler.py', - handler: 'handle_event', - runtime: Runtime.PYTHON_3_10, - architecture: await getDockerHostArch(), - bundling: { - bundlingStrategy: BundlingStrategy.PACKAGE_VERSION, - }, - }); - - return { app, stack }; - } - - test('Create multiple functions with workspaces_app', async () => { - const { app, stack } = await createMultipleFunctionStack(); - const template = Template.fromStack(stack); - - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'app_handler.handle_event', - }); - - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'backend_handler.handle_event', - }); - - const functions = getFunctions(template); - expect(functions).toHaveLength(2); - const appContents = await getAssetContent(functions[0], app); - for (const entry of [ - 'app', - 'backend', - 'common', - 'pydantic', - 'httpx', - '_common.pth', - 'app_handler.py', - ]) { - expect(appContents).toContain(entry); - } - expect(appContents).not.toContain('backend_handler.py'); - expect(appContents).not.toContain('pathlib'); - const backendContents = await getAssetContent(functions[1], app); - for (const entry of [ - 'app', - 'backend', - 'common', - 'pydantic', - 'httpx', - '_common.pth', - 'backend_handler.py', - ]) { - expect(backendContents).toContain(entry); - } - expect(backendContents).not.toContain('app_handler.py'); - expect(backendContents).not.toContain('flask'); - }); - - test("Doesn't rebuild multiple functions if they haven't changed", async () => { - let { app, stack } = await createMultipleFunctionStack(); - let template = Template.fromStack(stack); - - const run1Functions = getFunctions(template); - const appAssetPath = getAssetPath(run1Functions[0], app); - const backendAssetPath = getAssetPath(run1Functions[1], app); - // change the contents of the folder so we can verify they are not rebuilt - await setAssetContents(run1Functions[0], app, ['app.text']); - await setAssetContents(run1Functions[1], app, ['backend.text']); - - // Need to create the stack again as synthesize is only called once otherwise - ({ app, stack } = await createMultipleFunctionStack()); - - template = Template.fromStack(stack); - const run2Functions = getFunctions(template); - // validate that the same folder is being used for the functions - expect(getAssetPath(run2Functions[0], app)).toEqual(appAssetPath); - expect(getAssetPath(run2Functions[1], app)).toEqual(backendAssetPath); - - // validate they contain the content we put in above, i.e. haven't been overwritten - expect(await getAssetContent(run2Functions[0], app)).toEqual(['app.text']); - expect(await getAssetContent(run2Functions[1], app)).toEqual([ - 'backend.text', - ]); - }); - - test.failing( - 'FAILING: Rebuild single function when its version changes', - async () => { - // copy the workspace so we can edit it without affecting the original - const workspacePath = await copyWorkspaceToTemp('workspaces_app'); - let { app, stack } = await createMultipleFunctionStack(workspacePath); - let template = Template.fromStack(stack); - - const run1Functions = getFunctions(template); - const appAssetPath = getAssetPath(run1Functions[0], app); - const backendAssetPath = getAssetPath(run1Functions[1], app); - // change the contents of the folder so we can verify what is rebuilt - await setAssetContents(run1Functions[0], app, ['app.text']); - await setAssetContents(run1Functions[1], app, ['backend.text']); - - // Update the version in the workspace - await bumpVersionAndUvSync(workspacePath, 'app'); - - // Need to create the stack again as synthesize is only called once otherwise - ({ app, stack } = await createMultipleFunctionStack(workspacePath)); - - template = Template.fromStack(stack); - const run2Functions = getFunctions(template); - // validate that different folder for updated function but same one for unchanged - expect(getAssetPath(run2Functions[0], app)).not.toEqual(appAssetPath); - expect(getAssetPath(run2Functions[1], app)).toEqual(backendAssetPath); - - // validate app has been rebuild but backend hasn't been touched - const appContents = await getAssetContent(run2Functions[0], app); - for (const entry of [ - 'app', - 'backend', - 'common', - 'pydantic', - 'httpx', - '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - ]) { - expect(appContents).toContain(entry); - } - expect(await getAssetContent(run2Functions[1], app)).toEqual([ - 'backend.text', - ]); - }, - ); - - test.failing( - 'FAILING: Rebuild any function when its dependency version changes', - async () => { - // copy the workspace so we can edit it without affecting the original - const workspacePath = await copyWorkspaceToTemp('workspaces_app'); - let { app, stack } = await createMultipleFunctionStack(workspacePath); - let template = Template.fromStack(stack); - - const run1Functions = getFunctions(template); - const appAssetPath = getAssetPath(run1Functions[0], app); - const backendAssetPath = getAssetPath(run1Functions[1], app); - // change the contents of the folder so we can verify what is rebuilt - await setAssetContents(run1Functions[0], app, ['app.text']); - await setAssetContents(run1Functions[1], app, ['backend.text']); - - // Update the version in the workspace - await bumpVersionAndUvSync(workspacePath, 'common'); - - // Need to create the stack again as synthesize is only called once otherwise - ({ app, stack } = await createMultipleFunctionStack(workspacePath)); - - template = Template.fromStack(stack); - const run2Functions = getFunctions(template); - // validate that different folder for both updated functions - expect(getAssetPath(run2Functions[0], app)).not.toEqual(appAssetPath); - expect(getAssetPath(run2Functions[1], app)).not.toEqual(backendAssetPath); - - // validate app and backend have both been rebuilt - const appContents = await getAssetContent(run2Functions[0], app); - for (const entry of [ - 'app', - 'backend', - 'common', - 'pydantic', - 'httpx', - '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - 'app_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - ]) { - expect(appContents).toContain(entry); - } - const backendContents = await getAssetContent(run2Functions[1], app); - for (const entry of [ - 'app', - 'backend', - 'common', - 'pydantic', - 'httpx', - '_common.pth', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - 'backend_handler.py', // TODO: figure out why this is not being included (something to do with editable vs virtual after uv sync) - ]) { - expect(backendContents).toContain(entry); - } - }, - ); -}); - describe('When bundlingStrategy is set to BundlingStrategy.GIT', () => { jest.setTimeout(30000); // we are doing integration tests with the file system so give tests more time @@ -1154,43 +823,6 @@ async function copyWorkspaceToTemp( return tempDir; } -/** - * Bump the version of a workspace package and run `uv sync` to update the workspace. - * - * @param workspacePath - * @param workspacePackage - * @param oldVersion - * @param newVersion - */ -async function bumpVersionAndUvSync( - workspacePath: string, - workspacePackage: string, - oldVersion = '0.1.0', - newVersion = '0.1.1', -): Promise { - // Update the version in the workspace - await replaceStringInFile( - path.join( - workspacePath, - 'workspaces_app', - workspacePackage, - 'pyproject.toml', - ), - `version = "${oldVersion}"`, - `version = "${newVersion}"`, - ); - const uvSyncArgs = [ - '--python-preference=only-system', - '--compile-bytecode', - '--no-dev', - '--no-editable', - '--link-mode=copy', - ]; - await execAsync( - `cd ${workspacePath}/workspaces_app && uv sync --directory ${workspacePath}/workspaces_app --package ${workspacePackage} ${uvSyncArgs.join(' ')}`, - ); -} - /** * Replace all occurrences of a string in a file. Used to make modifications to workspace copies for testing purposes. * From 6bc8dc9600bb3abbd21da7aa795adc4a89a31ff3 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 19:11:22 +0000 Subject: [PATCH 08/14] Fix package related build issues and test git issue --- package-lock.json | 116 ++++++++++++++-- test/function.test.ts | 9 +- yarn.lock | 306 +++++++++++++++++++++++++++++------------- 3 files changed, 329 insertions(+), 102 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81a318b..f47bd9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,13 @@ "license": "Apache-2.0", "devDependencies": { "@biomejs/biome": "^1.9.4", + "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.14", "@types/node": "^22.10.3", "aws-cdk-lib": "2.161.1", "commit-and-tag-version": "^12", "constructs": "10.3.0", + "fs-extra": "^11.2.0", "jest": "^29.7.0", "jest-junit": "^15", "jsii": "~5.5.0", @@ -1907,6 +1909,17 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonfile": "*", + "@types/node": "*" + } + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -1955,6 +1968,16 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", @@ -3097,6 +3120,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/codemaker/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -4225,9 +4263,9 @@ } }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "license": "MIT", "dependencies": { @@ -4236,7 +4274,7 @@ "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -6898,6 +6936,21 @@ "dev": true, "license": "MIT" }, + "node_modules/jsii-diff/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/jsii-diff/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6985,6 +7038,21 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/jsii-docgen/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/jsii-docgen/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -7177,6 +7245,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jsii-pacmak/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/jsii-pacmak/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7319,6 +7402,21 @@ "dev": true, "license": "MIT" }, + "node_modules/jsii-reflect/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/jsii-reflect/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9099,17 +9197,17 @@ } }, "node_modules/projen/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/projen/node_modules/minimatch/node_modules/brace-expansion": { diff --git a/test/function.test.ts b/test/function.test.ts index d901ab4..90aff70 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -816,9 +816,12 @@ async function copyWorkspaceToTemp( const gitPath = path.join(tempDir, workspace); await execAsync('git init', { cwd: gitPath }); await execAsync('git add .', { cwd: gitPath }); - await execAsync('git commit -m "commit for test purposes"', { - cwd: gitPath, - }); + await execAsync( + 'git commit --author "Testy McTestface " -m "commit for test purposes"', + { + cwd: gitPath, + }, + ); } return tempDir; } diff --git a/yarn.lock b/yarn.lock index c6f3b26..f769c31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,7 +46,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz" integrity sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.8.0", "@babel/core@>=7.0.0-beta.0 <8": version "7.25.8" resolved "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz" integrity sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg== @@ -565,7 +565,7 @@ jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^29.7.0": +"@jest/transform@^29.0.0", "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz" integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== @@ -586,7 +586,7 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^29.6.3": +"@jest/types@^29.0.0", "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== @@ -622,14 +622,6 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" @@ -638,6 +630,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jsii/check-node@1.103.1": version "1.103.1" resolved "https://registry.npmjs.org/@jsii/check-node/-/check-node-1.103.1.tgz" @@ -669,7 +669,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -785,7 +785,7 @@ "@types/fs-extra@^11.0.4": version "11.0.4" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz" integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== dependencies: "@types/jsonfile" "*" @@ -827,7 +827,7 @@ "@types/jsonfile@*": version "6.1.4" - resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + resolved "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz" integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== dependencies: "@types/node" "*" @@ -871,14 +871,6 @@ resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.4.tgz" integrity sha512-zglELfWx7g1cEpVMRBZ0srIQO5nEvKvraJ6CVUC/c5Ky1GgX8OIjtUj5qOweTYULYZo5VnXs/LpUUUNiGpX/rA== -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - acorn-walk@^8.1.1: version "8.3.4" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" @@ -903,7 +895,17 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" -ajv@^8.0.1, ajv@^8.17.1: +ajv@^8.0.1: + version "8.17.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ajv@^8.17.1: version "8.17.1" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -1015,7 +1017,7 @@ aws-cdk-lib@2.161.1: table "^6.8.2" yaml "1.10.2" -babel-jest@^29.7.0: +babel-jest@^29.0.0, babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== @@ -1105,7 +1107,7 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0: +browserslist@^4.24.0, "browserslist@>= 4.21.0": version "4.24.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz" integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A== @@ -1153,7 +1155,12 @@ camelcase@^5.3.1: resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0, camelcase@^6.3.0: +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +camelcase@^6.3.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -1163,7 +1170,12 @@ caniuse-lite@^1.0.30001663: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz" integrity sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w== -case@1.6.3, case@^1.6.3: +case@^1.6.3: + version "1.6.3" + resolved "https://registry.npmjs.org/case/-/case-1.6.3.tgz" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== + +case@1.6.3: version "1.6.3" resolved "https://registry.npmjs.org/case/-/case-1.6.3.tgz" integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== @@ -1177,7 +1189,23 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: +chalk@^4, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1256,16 +1284,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -1338,7 +1366,7 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -constructs@10.3.0, constructs@^10.0.0: +constructs@^10.0.0, constructs@10.3.0: version "10.3.0" resolved "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz" integrity sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ== @@ -1360,12 +1388,17 @@ conventional-changelog-codemirror@^3.0.0: resolved "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-3.0.0.tgz" integrity sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw== -conventional-changelog-config-spec@2.1.0, conventional-changelog-config-spec@^2.1.0: +conventional-changelog-config-spec@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz" integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== -conventional-changelog-conventionalcommits@6.1.0, conventional-changelog-conventionalcommits@^6.0.0: +conventional-changelog-config-spec@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz" + integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== + +conventional-changelog-conventionalcommits@^6.0.0, conventional-changelog-conventionalcommits@6.1.0: version "6.1.0" resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz" integrity sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw== @@ -1464,8 +1497,8 @@ conventional-commits-parser@^4.0.0: resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz" integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== dependencies: - JSONStream "^1.3.5" is-text-path "^1.0.1" + JSONStream "^1.3.5" meow "^8.1.2" split2 "^3.2.2" @@ -1487,7 +1520,12 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -core-util-is@^1.0.3, core-util-is@~1.0.0: +core-util-is@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== @@ -1549,7 +1587,7 @@ dateformat@^3.0.3: resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@4: version "4.3.7" resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -1764,7 +1802,7 @@ fast-json-patch@^3.1.1: resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz" integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@^2.1.0, fast-json-stable-stringify@2.x: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -1823,7 +1861,15 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -1887,7 +1933,7 @@ fs.realpath@^1.0.0: fsevents@^2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: @@ -1981,7 +2027,18 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8, glob@^8.1.0: +glob@^8: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +glob@^8.0.3, glob@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -2124,7 +2181,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.3, inherits@~2.0.3, inherits@2: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2497,7 +2554,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@^29.7.0: +jest-resolve@*, jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -2641,7 +2698,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.7.0: +jest@^29.0.0, jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -2656,7 +2713,15 @@ js-tokens@^4.0.0: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@^3.13.1: +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@3.14.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -2752,7 +2817,7 @@ jsii-reflect@^1.103.1, jsii-reflect@^1.106.0: oo-ascii-tree "^1.106.0" yargs "^16.2.0" -jsii-rosetta@~5.5.0: +"jsii-rosetta@^1.104.0 || ~5.2.0 || ~5.3.0 || ~5.4.0 || ~5.5.0", "jsii-rosetta@^1.85.0 || ~5.0.14 || ~5.1.2 || ~5.2.0 || ~5.3.0 || ~5.4.0 || ~5.5.0", jsii-rosetta@~5.5.0: version "5.5.5" resolved "https://registry.npmjs.org/jsii-rosetta/-/jsii-rosetta-5.5.5.tgz" integrity sha512-eXkY5eJck2XPd+xk6f4uRQ1S1d5/on2GO1H1Rr6WkJW7E51FXltpsmPaXzrAtvNd6doBNd6/X1CM4otEt/nnBA== @@ -2841,6 +2906,14 @@ jsonschema@^1.4.1: resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" @@ -3015,7 +3088,14 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.35: +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^2.1.35: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -3055,7 +3135,12 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@~1.2.8: +minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.8: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minimist@^1.2.3: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -3095,7 +3180,17 @@ node-releases@^2.0.18: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -3158,7 +3253,14 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -3531,12 +3633,12 @@ semver-intersect@^1.5.0: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^6.3.0, semver@^6.3.1: +semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -3546,6 +3648,11 @@ semver@^7.0.0, semver@^7.3.2, semver@^7.3.4, semver@^7.5.3, semver@^7.5.4, semve resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -3558,7 +3665,16 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shelljs@^0.8.3, shelljs@^0.8.5: +shelljs@^0.8.3: + version "0.8.5" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shelljs@^0.8.5: version "0.8.5" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -3652,13 +3768,6 @@ spdx-license-list@^6.9.0: resolved "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.9.0.tgz" integrity sha512-L2jl5vc2j6jxWcNCvcVj/BW9A8yGIG02Dw+IUw0ZxDM70f7Ylf5Hq39appV1BI9yxyWQRpq2TQ1qaXvf+yjkqA== -split2@^3.2.2: - version "3.2.2" - resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== - dependencies: - readable-stream "^3.0.0" - split@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" @@ -3666,6 +3775,13 @@ split@^1.0.1: dependencies: through "2" +split2@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" @@ -3699,6 +3815,20 @@ streamroller@^3.1.5: debug "^4.3.4" fs-extra "^8.1.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -3716,20 +3846,6 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -3820,6 +3936,11 @@ text-extensions@^1.0.0: resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== +"through@>=2.2.7 <3", through@2: + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + through2@^2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" @@ -3828,11 +3949,6 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3": - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - tldts-core@^6.1.52: version "6.1.52" resolved "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.52.tgz" @@ -3896,7 +4012,7 @@ ts-jest@^29.2.5: semver "^7.6.3" yargs-parser "^21.1.1" -ts-node@^10.9.2: +ts-node@^10.9.2, ts-node@>=9.0.0: version "10.9.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -4108,6 +4224,11 @@ xml@^1.0.1: resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xmlbuilder2@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz" @@ -4118,11 +4239,6 @@ xmlbuilder2@^3.1.1: "@oozcitak/util" "8.3.8" js-yaml "3.14.1" -xmlbuilder@^15.1.1: - version "15.1.1" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" @@ -4148,6 +4264,16 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^2.2.2: + version "2.6.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz" + integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== + +yaml@^2.4.1: + version "2.6.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz" + integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== + yaml@1.10.2: version "1.10.2" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" From ab30700d428fcd2a2062f973d10b5f4f42c3ffa5 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Fri, 29 Nov 2024 19:17:02 +0000 Subject: [PATCH 09/14] Attempt two at fixing the git related commands --- test/function.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/function.test.ts b/test/function.test.ts index 90aff70..0b1a922 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -816,12 +816,16 @@ async function copyWorkspaceToTemp( const gitPath = path.join(tempDir, workspace); await execAsync('git init', { cwd: gitPath }); await execAsync('git add .', { cwd: gitPath }); - await execAsync( - 'git commit --author "Testy McTestface " -m "commit for test purposes"', - { - cwd: gitPath, - }, - ); + await execAsync('git config user.email "test@example.com"', { + cwd: gitPath, + }); + await execAsync('git config user.name "Testy McTestface"', { + cwd: gitPath, + }); + + await execAsync('git commit -m "commit for test purposes"', { + cwd: gitPath, + }); } return tempDir; } From 1a5fe1df9bcee793c1ac7e411835f506c978f0d9 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Tue, 7 Jan 2025 17:32:14 +0000 Subject: [PATCH 10/14] Fix npm ci issues --- package-lock.json | 10 ++++---- package.json | 2 +- yarn.lock | 62 +++++++++-------------------------------------- 3 files changed, 17 insertions(+), 57 deletions(-) diff --git a/package-lock.json b/package-lock.json index f47bd9d..091ecd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9197,17 +9197,17 @@ } }, "node_modules/projen/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" + "node": "*" } }, "node_modules/projen/node_modules/minimatch/node_modules/brace-expansion": { diff --git a/package.json b/package.json index 5d3b1c3..bcac4bd 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.4", + "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.14", "@types/node": "^22.10.3", - "@types/fs-extra": "^11.0.4", "aws-cdk-lib": "2.161.1", "commit-and-tag-version": "^12", "constructs": "10.3.0", diff --git a/yarn.lock b/yarn.lock index f769c31..947a5cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -335,44 +335,9 @@ "@biomejs/cli-darwin-arm64@1.9.4": version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" + resolved "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz" integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== -"@biomejs/cli-darwin-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" - integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== - -"@biomejs/cli-linux-arm64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" - integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== - -"@biomejs/cli-linux-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" - integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== - -"@biomejs/cli-linux-x64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz" - integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== - -"@biomejs/cli-linux-x64@1.9.4": - version "1.9.4" - resolved "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz" - integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== - -"@biomejs/cli-win32-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" - integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== - -"@biomejs/cli-win32-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" - integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" @@ -2213,6 +2178,11 @@ is-core-module@^2.13.0, is-core-module@^2.5.0: dependencies: hasown "^2.0.2" +is-core-module@^2.16.0: + version "2.16.1" + dependencies: + hasown "^2.0.2" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" @@ -2817,7 +2787,7 @@ jsii-reflect@^1.103.1, jsii-reflect@^1.106.0: oo-ascii-tree "^1.106.0" yargs "^16.2.0" -"jsii-rosetta@^1.104.0 || ~5.2.0 || ~5.3.0 || ~5.4.0 || ~5.5.0", "jsii-rosetta@^1.85.0 || ~5.0.14 || ~5.1.2 || ~5.2.0 || ~5.3.0 || ~5.4.0 || ~5.5.0", jsii-rosetta@~5.5.0: +"jsii-rosetta@^1.85.0 || ~5.0.14 || ~5.1.2 || ~5.2.0 || ~5.3.0 || ~5.4.0 || ~5.5.0", jsii-rosetta@>=5.4.0, jsii-rosetta@~5.5.0: version "5.5.5" resolved "https://registry.npmjs.org/jsii-rosetta/-/jsii-rosetta-5.5.5.tgz" integrity sha512-eXkY5eJck2XPd+xk6f4uRQ1S1d5/on2GO1H1Rr6WkJW7E51FXltpsmPaXzrAtvNd6doBNd6/X1CM4otEt/nnBA== @@ -4061,7 +4031,7 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^5.7.2, typescript@next: +typescript@^5.7.2, typescript@>=2.7, "typescript@>=4.3 <6", typescript@next: version "5.7.2" resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz" integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== @@ -4265,9 +4235,9 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^2.2.2: - version "2.6.0" - resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz" - integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== + version "2.7.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== yaml@^2.4.1: version "2.6.0" @@ -4279,16 +4249,6 @@ yaml@1.10.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.2.2: - version "2.7.0" - resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" - integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== - -yaml@^2.4.1: - version "2.6.0" - resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz" - integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== - yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" From 38ba30864e03fc52923585603b2ed2c131eb9cfa Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Wed, 8 Jan 2025 15:56:59 +0000 Subject: [PATCH 11/14] Install uv before running tests --- .github/workflows/build.yml | 2 ++ .projenrc.ts | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aef6dbd..760aa71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,8 @@ jobs: env: CI: "true" steps: + - name: Install uv + run: pip install uv - name: Checkout uses: actions/checkout@v4 with: diff --git a/.projenrc.ts b/.projenrc.ts index 04dcf14..6cf0aeb 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -1,4 +1,4 @@ -import { awscdk } from 'projen'; +import { JsonPatch, awscdk } from 'projen'; import { JobPermission } from 'projen/lib/github/workflows-model'; const project = new awscdk.AwsCdkConstructLibrary({ author: 'Eoin Shanaghy', @@ -29,6 +29,7 @@ const project = new awscdk.AwsCdkConstructLibrary({ }, eslint: false, }); + const biomeWorkflow = project.github?.addWorkflow('biome'); biomeWorkflow?.on({ pullRequest: { @@ -62,5 +63,15 @@ biomeWorkflow?.addJobs({ }, }); +const buildWorkflow = project.tryFindObjectFile('.github/workflows/build.yml'); +if (buildWorkflow) { + buildWorkflow.patch( + JsonPatch.add('/jobs/build/steps/0', { + name: 'Install uv', + run: 'pip install uv', + }), + ); +} + project.files; project.synth(); From a9ba3f0d42e47e818c5f59b87403bba7122171c4 Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Wed, 8 Jan 2025 16:05:40 +0000 Subject: [PATCH 12/14] Bump test timeout --- .projen/tasks.json | 2 +- .projenrc.ts | 2 +- test/function.test.ts | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.projen/tasks.json b/.projen/tasks.json index 97db832..aad0b76 100644 --- a/.projen/tasks.json +++ b/.projen/tasks.json @@ -230,7 +230,7 @@ "description": "Run tests", "steps": [ { - "exec": "jest --testTimeout=300000 --passWithNoTests --updateSnapshot", + "exec": "jest --testTimeout=400000 --passWithNoTests --updateSnapshot", "receiveArgs": true } ] diff --git a/.projenrc.ts b/.projenrc.ts index 6cf0aeb..5a4f46b 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -25,7 +25,7 @@ const project = new awscdk.AwsCdkConstructLibrary({ ] /* Build dependencies for this module. */, // packageName: undefined, /* The "name" in package.json. */ jestOptions: { - extraCliOptions: ['--testTimeout=300000'], + extraCliOptions: ['--testTimeout=400000'], }, eslint: false, }); diff --git a/test/function.test.ts b/test/function.test.ts index 0b1a922..9970aad 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -68,8 +68,6 @@ afterEach(async () => { }); describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { - jest.setTimeout(20000); // we are doing integration tests with the file system so give tests more time - test('Create a function from basic_app', async () => { const { app, stack } = await createStack(); @@ -98,7 +96,7 @@ describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { expect(functions).toHaveLength(1); const contents = await getAssetContent(functions[0], app); expect(contents).toContain('handler.py'); - }); + }, 40000); // need long timeout as working with file system test('Create a function from basic_app with no .py index extension', async () => { const { stack } = await createStack(); @@ -190,7 +188,7 @@ describe('When bundlingStrategy is set to BundlingStrategy.SOURCE', () => { ]) { expect(contents).toContain(entry); } - }); + }, 40000); // need long timeout as working with file system test('Create multiple functions with workspaces_app', async () => { const { app, stack } = await createStack('wstest'); From 40d0164e4df2639823353be3a3e1bb7c1c57bb6b Mon Sep 17 00:00:00 2001 From: Chris McGrath Date: Wed, 8 Jan 2025 16:17:42 +0000 Subject: [PATCH 13/14] Add updates API.md --- API.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/API.md b/API.md index 029babb..56d6d56 100644 --- a/API.md +++ b/API.md @@ -1155,6 +1155,7 @@ const bundlingOptions: BundlingOptions = { ... } | assetHashType | aws-cdk-lib.AssetHashType | Determines how asset hash is calculated. Assets will get rebuild and uploaded only if their hash has changed. | | buildArgs | {[ key: string ]: string} | Optional build arguments to pass to the default container. | | bundlingFileAccess | aws-cdk-lib.BundlingFileAccess | Which option to use to copy the source files to the docker container and output files back. | +| bundlingStrategy | BundlingStrategy | Strategy for bundling. | | commandHooks | ICommandHooks | Command hooks. | | image | aws-cdk-lib.DockerImage | Docker image to use for bundling. | | outputPathSuffix | string | Output path suffix: the suffix for the directory into which the bundled output is written. | @@ -1388,6 +1389,19 @@ Which option to use to copy the source files to the docker container and output --- +##### `bundlingStrategy`Optional + +```typescript +public readonly bundlingStrategy: BundlingStrategy; +``` + +- *Type:* BundlingStrategy +- *Default:* `BundlingStrategy.SOURCE` + +Strategy for bundling. + +--- + ##### `commandHooks`Optional ```typescript @@ -1460,6 +1474,7 @@ const bundlingProps: BundlingProps = { ... } | assetHashType | aws-cdk-lib.AssetHashType | Determines how asset hash is calculated. Assets will get rebuild and uploaded only if their hash has changed. | | buildArgs | {[ key: string ]: string} | Optional build arguments to pass to the default container. | | bundlingFileAccess | aws-cdk-lib.BundlingFileAccess | Which option to use to copy the source files to the docker container and output files back. | +| bundlingStrategy | BundlingStrategy | Strategy for bundling. | | commandHooks | ICommandHooks | Command hooks. | | image | aws-cdk-lib.DockerImage | Docker image to use for bundling. | | outputPathSuffix | string | Output path suffix: the suffix for the directory into which the bundled output is written. | @@ -1699,6 +1714,19 @@ Which option to use to copy the source files to the docker container and output --- +##### `bundlingStrategy`Optional + +```typescript +public readonly bundlingStrategy: BundlingStrategy; +``` + +- *Type:* BundlingStrategy +- *Default:* `BundlingStrategy.SOURCE` + +Strategy for bundling. + +--- + ##### `commandHooks`Optional ```typescript @@ -2985,3 +3013,25 @@ Commands are chained with `&&`. --- +## Enums + +### BundlingStrategy + +#### Members + +| **Name** | **Description** | +| --- | --- | +| SOURCE | *No description.* | +| GIT | *No description.* | + +--- + +##### `SOURCE` + +--- + + +##### `GIT` + +--- + From 28123a3cc99606ce36a540a0fa352f1c82dba014 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 8 Jan 2025 17:23:36 +0000 Subject: [PATCH 14/14] chore: self mutation Signed-off-by: github-actions --- yarn.lock | 342 ++++++++++++++++++++---------------------------------- 1 file changed, 128 insertions(+), 214 deletions(-) diff --git a/yarn.lock b/yarn.lock index 947a5cd..ec49906 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,7 +46,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz" integrity sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA== -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.8.0", "@babel/core@>=7.0.0-beta.0 <8": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": version "7.25.8" resolved "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz" integrity sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg== @@ -338,6 +338,41 @@ resolved "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz" integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== +"@biomejs/cli-darwin-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" + integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== + +"@biomejs/cli-linux-arm64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" + integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== + +"@biomejs/cli-linux-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" + integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== + +"@biomejs/cli-linux-x64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" + integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== + +"@biomejs/cli-linux-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" + integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== + +"@biomejs/cli-win32-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" + integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== + +"@biomejs/cli-win32-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" + integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" @@ -530,7 +565,7 @@ jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^29.0.0", "@jest/transform@^29.7.0": +"@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz" integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== @@ -551,7 +586,7 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^29.0.0", "@jest/types@^29.6.3": +"@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== @@ -587,14 +622,6 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" @@ -603,6 +630,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jsii/check-node@1.103.1": version "1.103.1" resolved "https://registry.npmjs.org/@jsii/check-node/-/check-node-1.103.1.tgz" @@ -634,7 +669,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -836,6 +871,14 @@ resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.4.tgz" integrity sha512-zglELfWx7g1cEpVMRBZ0srIQO5nEvKvraJ6CVUC/c5Ky1GgX8OIjtUj5qOweTYULYZo5VnXs/LpUUUNiGpX/rA== +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + acorn-walk@^8.1.1: version "8.3.4" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" @@ -860,17 +903,7 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" -ajv@^8.0.1: - version "8.17.1" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - -ajv@^8.17.1: +ajv@^8.0.1, ajv@^8.17.1: version "8.17.1" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -982,7 +1015,7 @@ aws-cdk-lib@2.161.1: table "^6.8.2" yaml "1.10.2" -babel-jest@^29.0.0, babel-jest@^29.7.0: +babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== @@ -1072,7 +1105,7 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0, "browserslist@>= 4.21.0": +browserslist@^4.24.0: version "4.24.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz" integrity sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A== @@ -1120,12 +1153,7 @@ camelcase@^5.3.1: resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -camelcase@^6.3.0: +camelcase@^6.2.0, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -1135,12 +1163,7 @@ caniuse-lite@^1.0.30001663: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz" integrity sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w== -case@^1.6.3: - version "1.6.3" - resolved "https://registry.npmjs.org/case/-/case-1.6.3.tgz" - integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== - -case@1.6.3: +case@1.6.3, case@^1.6.3: version "1.6.3" resolved "https://registry.npmjs.org/case/-/case-1.6.3.tgz" integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== @@ -1154,23 +1177,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.2: +chalk@^4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1249,16 +1256,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -1331,7 +1338,7 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -constructs@^10.0.0, constructs@10.3.0: +constructs@10.3.0, constructs@^10.0.0: version "10.3.0" resolved "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz" integrity sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ== @@ -1353,17 +1360,12 @@ conventional-changelog-codemirror@^3.0.0: resolved "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-3.0.0.tgz" integrity sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw== -conventional-changelog-config-spec@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz" - integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== - -conventional-changelog-config-spec@2.1.0: +conventional-changelog-config-spec@2.1.0, conventional-changelog-config-spec@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz" integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== -conventional-changelog-conventionalcommits@^6.0.0, conventional-changelog-conventionalcommits@6.1.0: +conventional-changelog-conventionalcommits@6.1.0, conventional-changelog-conventionalcommits@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz" integrity sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw== @@ -1462,8 +1464,8 @@ conventional-commits-parser@^4.0.0: resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz" integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== dependencies: - is-text-path "^1.0.1" JSONStream "^1.3.5" + is-text-path "^1.0.1" meow "^8.1.2" split2 "^3.2.2" @@ -1485,12 +1487,7 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -core-util-is@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -core-util-is@~1.0.0: +core-util-is@^1.0.3, core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== @@ -1552,7 +1549,7 @@ dateformat@^3.0.3: resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: version "4.3.7" resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -1767,7 +1764,7 @@ fast-json-patch@^3.1.1: resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz" integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== -fast-json-stable-stringify@^2.1.0, fast-json-stable-stringify@2.x: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -1826,15 +1823,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -1992,18 +1981,7 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8: - version "8.1.0" - resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -glob@^8.0.3, glob@^8.1.0: +glob@^8, glob@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -2146,7 +2124,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.3, inherits@~2.0.3, inherits@2: +inherits@2, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2178,11 +2156,6 @@ is-core-module@^2.13.0, is-core-module@^2.5.0: dependencies: hasown "^2.0.2" -is-core-module@^2.16.0: - version "2.16.1" - dependencies: - hasown "^2.0.2" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" @@ -2524,7 +2497,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@*, jest-resolve@^29.7.0: +jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -2668,7 +2641,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.0.0, jest@^29.7.0: +jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -2683,15 +2656,7 @@ js-tokens@^4.0.0: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@3.14.1: +js-yaml@3.14.1, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -2787,7 +2752,7 @@ jsii-reflect@^1.103.1, jsii-reflect@^1.106.0: oo-ascii-tree "^1.106.0" yargs "^16.2.0" -"jsii-rosetta@^1.85.0 || ~5.0.14 || ~5.1.2 || ~5.2.0 || ~5.3.0 || ~5.4.0 || ~5.5.0", jsii-rosetta@>=5.4.0, jsii-rosetta@~5.5.0: +jsii-rosetta@~5.5.0: version "5.5.5" resolved "https://registry.npmjs.org/jsii-rosetta/-/jsii-rosetta-5.5.5.tgz" integrity sha512-eXkY5eJck2XPd+xk6f4uRQ1S1d5/on2GO1H1Rr6WkJW7E51FXltpsmPaXzrAtvNd6doBNd6/X1CM4otEt/nnBA== @@ -2876,14 +2841,6 @@ jsonschema@^1.4.1: resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - kind-of@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" @@ -3058,14 +3015,7 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime-types@^2.1.35: +mime-types@^2.1.12, mime-types@^2.1.35: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -3105,12 +3055,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.8: - version "1.2.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -minimist@^1.2.3: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@~1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -3150,17 +3095,7 @@ node-releases@^2.0.18: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -3223,14 +3158,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -3603,12 +3531,12 @@ semver-intersect@^1.5.0: dependencies: semver "^6.3.0" -semver@^6.3.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.3.1: +semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -3618,11 +3546,6 @@ semver@^7.0.0, semver@^7.3.2, semver@^7.3.4, semver@^7.5.3, semver@^7.5.4, semve resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -3635,16 +3558,7 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shelljs@^0.8.3: - version "0.8.5" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - -shelljs@^0.8.5: +shelljs@^0.8.3, shelljs@^0.8.5: version "0.8.5" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== @@ -3738,13 +3652,6 @@ spdx-license-list@^6.9.0: resolved "https://registry.npmjs.org/spdx-license-list/-/spdx-license-list-6.9.0.tgz" integrity sha512-L2jl5vc2j6jxWcNCvcVj/BW9A8yGIG02Dw+IUw0ZxDM70f7Ylf5Hq39appV1BI9yxyWQRpq2TQ1qaXvf+yjkqA== -split@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - split2@^3.2.2: version "3.2.2" resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" @@ -3752,6 +3659,13 @@ split2@^3.2.2: dependencies: readable-stream "^3.0.0" +split@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" @@ -3785,20 +3699,6 @@ streamroller@^3.1.5: debug "^4.3.4" fs-extra "^8.1.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -3816,6 +3716,20 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -3906,11 +3820,6 @@ text-extensions@^1.0.0: resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -"through@>=2.2.7 <3", through@2: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - through2@^2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" @@ -3919,6 +3828,11 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" +through@2, "through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + tldts-core@^6.1.52: version "6.1.52" resolved "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.52.tgz" @@ -3982,7 +3896,7 @@ ts-jest@^29.2.5: semver "^7.6.3" yargs-parser "^21.1.1" -ts-node@^10.9.2, ts-node@>=9.0.0: +ts-node@^10.9.2: version "10.9.2" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -4031,7 +3945,7 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^5.7.2, typescript@>=2.7, "typescript@>=4.3 <6", typescript@next: +typescript@^5.7.2, typescript@next: version "5.7.2" resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz" integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== @@ -4194,11 +4108,6 @@ xml@^1.0.1: resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== -xmlbuilder@^15.1.1: - version "15.1.1" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - xmlbuilder2@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz" @@ -4209,6 +4118,11 @@ xmlbuilder2@^3.1.1: "@oozcitak/util" "8.3.8" js-yaml "3.14.1" +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" @@ -4234,6 +4148,11 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@1.10.2: + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yaml@^2.2.2: version "2.7.0" resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" @@ -4244,11 +4163,6 @@ yaml@^2.4.1: resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz" integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== -yaml@1.10.2: - version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"