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 33b38d7..f29eaf9 100644 --- a/.projen/tasks.json +++ b/.projen/tasks.json @@ -269,13 +269,13 @@ }, "steps": [ { - "exec": "npx npm-check-updates@16 --upgrade --target=minor --peer --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 --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-lock.json b/package-lock.json index da72284..bc73e7c 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.13", "@types/node": "^22.7.6", "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", @@ -8953,6 +9051,8 @@ }, "node_modules/projen/node_modules/glob/node_modules/minimatch": { "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", @@ -9085,17 +9185,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": { @@ -9238,6 +9338,8 @@ }, "node_modules/projen/node_modules/shelljs/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "inBundle": true, "license": "ISC", @@ -9431,6 +9533,8 @@ }, "node_modules/projen/node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "inBundle": true, "license": "ISC", diff --git a/package.json b/package.json index 06ebf37..02c6f0e 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,13 @@ }, "devDependencies": { "@biomejs/biome": "^1.9.4", + "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.13", "@types/node": "^22.7.6", "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/src/bundling.ts b/src/bundling.ts index 006c1f3..b52b084 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']; @@ -69,6 +72,23 @@ export interface BundlingProps extends BundlingOptions { */ export class Bundling { public static bundle(options: BundlingProps): AssetCode { + switch (options.bundlingStrategy) { + case BundlingStrategy.GIT: + return Bundling.gitStrategy(options); + default: + return Bundling.sourceStrategy(options); + } + } + + /** + * 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: HASHABLE_DEPENDENCIES_EXCLUDE, @@ -76,6 +96,54 @@ export class Bundling { }); } + /** + * Uses the AssetHashType.CUSTOM strategy and git output to calculate the asset hash. + * + * This strategy uses the output of git commands to calculate the asset hash. If there are multiple packages in a workspace this method will + * rebuild the asset for a package if the package, local dependency, or a dependency version changes. + * + * @param options + * @private + */ + 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: HASHABLE_DEPENDENCIES_EXCLUDE, + 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; @@ -179,3 +247,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 c5529b8..13eb2a5 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', + GIT = 'git', } /** diff --git a/test/function.test.ts b/test/function.test.ts index 1f4f22a..0b1a922 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -7,7 +7,9 @@ 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 * as fsextra from 'fs-extra'; +import { BundlingStrategy, PythonFunction } from '../src'; + const execAsync = promisify(exec); const resourcesPath = path.resolve(__dirname, 'resources'); @@ -54,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'), ); }); @@ -65,124 +67,831 @@ 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', () => { + 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(); - 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(), + 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); + + 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'); }); - 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, + }, + }); - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'handler.lambda_handler', - Runtime: 'python3.12', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, + 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 = 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(); + + const bundlingSpy = jest + .spyOn(stack, 'bundlingRequired', 'get') + .mockReturnValue(false); + const architecture = await getDockerHostArch(); - new PythonFunction(stack, 'basic_app', { - rootDir: path.join(resourcesPath, 'basic_app'), - index: 'handler', - handler: 'lambda_handler', - runtime: Runtime.PYTHON_3_12, - 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 = 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); + } }); + + 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); }); -test('Create a function from basic_app when skip is true', async () => { - const { stack } = await createStack(); +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 - const bundlingSpy = jest - .spyOn(stack, 'bundlingRequired', 'get') - .mockReturnValue(false); - const architecture = await getDockerHostArch(); + test('Create a function from basic_app', async () => { + const { app, 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', + index: 'handler.py', handler: 'lambda_handler', runtime: Runtime.PYTHON_3_12, - architecture, + architecture: await getDockerHostArch(), + bundling: { + bundlingStrategy: BundlingStrategy.GIT, + }, }); - }).not.toThrow(); - bundlingSpy.mockRestore(); -}); + const template = Template.fromStack(stack); -test('Create a function with workspaces_app', async () => { - const { app, stack } = await createStack('wstest'); + 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'); + }); - 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(), + 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(); }); - const template = Template.fromStack(stack); + 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', + ); - template.hasResourceProperties('AWS::Lambda::Function', { - Handler: 'app_handler.handle_event', - Runtime: 'python3.10', - Code: { - S3Bucket: Match.anyValue(), - S3Key: Match.anyValue(), - }, + ({ 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', + ); }); - 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.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', { + git: true, + }); + 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']); + + // 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)); + + 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', + 'app_handler.py', + ]) { + 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', { + git: true, + }); + 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']); + + // 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)); + + 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', + 'app_handler.py', + ]) { + expect(appContents).toContain(entry); + } + const backendContents = await getAssetContent(run2Functions[1], app); + for (const entry of [ + 'app', + 'backend', + 'common', + 'pydantic', + 'httpx', + '_common.pth', + 'backend_handler.py', + ]) { + 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 + * @param git - if true, a git repository is initialized in the copied workspace + */ +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'), + ); + await fsextra.copy( + 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 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; +} + +/** + * 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}`); - const contents = await fs.readdir(assetPath); - return contents; + return path.join(app.outdir, `asset.${assetHash}`); +} + +// 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), ''); + } } 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/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 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" diff --git a/yarn.lock b/yarn.lock index d4a05a0..cceec03 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== @@ -338,41 +338,6 @@ 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" @@ -565,7 +530,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 +551,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 +587,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 +595,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 +634,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== @@ -783,6 +748,14 @@ dependencies: "@babel/types" "^7.20.7" +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz" + 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 +790,13 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@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== + dependencies: + "@types/node" "*" + "@types/minimist@^1.2.0": version "1.2.5" resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz" @@ -856,14 +836,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" @@ -888,7 +860,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== @@ -1000,7 +982,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== @@ -1090,7 +1072,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== @@ -1138,7 +1120,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== @@ -1148,7 +1135,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== @@ -1162,7 +1154,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== @@ -1241,16 +1249,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" @@ -1323,7 +1331,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== @@ -1345,12 +1353,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== @@ -1449,8 +1462,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" @@ -1472,7 +1485,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== @@ -1534,7 +1552,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== @@ -1749,7 +1767,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== @@ -1808,7 +1826,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== @@ -1872,7 +1898,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: @@ -1966,7 +1992,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== @@ -2109,7 +2146,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== @@ -2482,7 +2519,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== @@ -2626,7 +2663,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== @@ -2641,7 +2678,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== @@ -2737,7 +2782,7 @@ jsii-reflect@^1.103.1, jsii-reflect@^1.104.0: oo-ascii-tree "^1.104.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== @@ -2826,6 +2871,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" @@ -3000,7 +3053,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== @@ -3040,7 +3100,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== @@ -3080,7 +3145,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== @@ -3143,7 +3218,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== @@ -3516,12 +3598,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== @@ -3531,6 +3613,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" @@ -3543,7 +3630,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== @@ -3637,13 +3733,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" @@ -3651,6 +3740,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" @@ -3684,6 +3780,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" @@ -3701,20 +3811,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" @@ -3805,6 +3901,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" @@ -3813,11 +3914,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" @@ -3881,7 +3977,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== @@ -3930,7 +4026,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.6.3, typescript@next: +typescript@^5.6.3, typescript@>=2.7, "typescript@>=4.3 <6", typescript@next: version "5.6.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz" integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== @@ -4093,6 +4189,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" @@ -4103,11 +4204,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" @@ -4133,16 +4229,21 @@ 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.6.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz" + integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== -yaml@^2.2.2, yaml@^2.4.1: +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" + 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"