diff --git a/.tool-versions b/.tool-versions index 389f264..61d8d75 100644 --- a/.tool-versions +++ b/.tool-versions @@ -3,6 +3,7 @@ gitleaks 8.24.0 jq 1.6 nodejs 22.11.0 pre-commit 3.6.0 +ruby 3.3.6 terraform 1.10.1 terraform-docs 0.19.0 trivy 0.61.0 diff --git a/LICENCE.md b/LICENCE.md index 02174c4..3b8ee5f 100644 --- a/LICENCE.md +++ b/LICENCE.md @@ -1,6 +1,6 @@ # MIT Licence -Copyright (c) 2025 Crown Copyright NHS England. +Copyright (c) 2026 Crown Copyright NHS England. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index ea70e95..666201c 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,10 @@ include scripts/init.mk # ============================================================================== -# Example CI/CD targets are: dependencies, build, publish, deploy, clean, etc. +# Example CI/CD targets are: dependencies, build, clean, etc. dependencies: # Install dependencies needed to build and test the project @Pipeline - # TODO: Implement installation of your project dependencies + npm ci build: # Build the project artefact @Pipeline (cd docs && make build) @@ -20,8 +20,13 @@ deploy: # Deploy the project artefact to the target environment @Pipeline # TODO: Implement the artefact deployment step clean:: # Clean-up project resources (main) @Operations - rm -f .version - # TODO: Implement project resources clean-up step + rm -f .version version.json + rm -rf node_modules + rm -rf lambdas/*/dist + rm -rf lambdas/*/node_modules + rm -rf coverage + rm -rf lambdas/*/coverage + (cd docs && make clean 2>/dev/null || true) config:: _install-dependencies version # Configure development environment (main) @Configuration (cd docs && make install) diff --git a/README.md b/README.md index 2945dea..58e6607 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,79 @@ -# NHS Notify Repository Template +# NHS Notify Client Callbacks -[![CI/CD Pull Request](https://github.com/nhs-england-tools/repository-template/actions/workflows/cicd-1-pull-request.yaml/badge.svg)](https://github.com/nhs-england-tools/repository-template/actions/workflows/cicd-1-pull-request.yaml) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=repository-template&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=repository-template) +Event-driven infrastructure for delivering NHS Notify callback notifications to client webhook endpoints. This repository implements the Callbacks domain as part of the NHS Notify distributed architecture, receiving events from the Shared Event Bus and orchestrating webhook delivery via EventBridge API Destinations. -Start with an overview or a brief description of what the project is about and what it does. For example - +## Overview -Welcome to our repository template designed to streamline your project setup! This robust template provides a reliable starting point for your new projects, covering an essential tech stack and encouraging best practices in documenting. +The Client Callbacks infrastructure processes message and channel status events, applies client-specific subscription filters, and delivers callbacks to configured webhook endpoints. Events flow from the Shared Event Bus through an SQS queue, are transformed and filtered by a Lambda function, then routed to clients via per-client API Destination Target Rules. -This repository template aims to foster a user-friendly development environment by ensuring that every included file is concise and adequately self-documented. By adhering to this standard, we can promote increased clarity and maintainability throughout your project's lifecycle. Bundled within this template are resources that pave the way for seamless repository creation. Currently supported technologies are: +### Key Features -- Terraform -- Docker - -Make use of this repository template to expedite your project setup and enhance your productivity right from the get-go. Enjoy the advantage of having a well-structured, self-documented project that reduces overhead and increases focus on what truly matters - coding! - -Any code files or example documentation that is not used in the new repository should be removed to prevent the proliferation of duplicate copies of redundant code that if not maintained could introduce vulnerabilities (e.g. via un-patched Dependabot issues). +- **Event-Driven Architecture**: Consumes CloudEvents from the Shared Event Bus (`uk.nhs.notify.client-callbacks.*` namespace) +- **Client Subscription Filtering**: Applies per-client rules for message status and channel status event types +- **Webhook Delivery**: EventBridge API Destinations with per-client configuration and retry policies +- **Failure Handling**: Per-client Dead Letter Queues +- **Backward Compatibility**: Maintains callback payload format compatibility with legacy Core domain implementation ## Table of Contents -- [NHS Notify Repository Template](#nhs-notify-repository-template) +- [NHS Notify Client Callbacks](#nhs-notify-client-callbacks) + - [Overview](#overview) + - [Key Features](#key-features) - [Table of Contents](#table-of-contents) - - [Documentation](#documentation) + - [Architecture](#architecture) + - [Components](#components) + - [Event Flow](#event-flow) - [Setup](#setup) - [Prerequisites](#prerequisites) - [Configuration](#configuration) - [Usage](#usage) - [Testing](#testing) - - [Design](#design) - - [Diagrams](#diagrams) - - [Modularity](#modularity) + - [Infrastructure](#infrastructure) - [Contributing](#contributing) - [Contacts](#contacts) - [Licence](#licence) -## Documentation +## Architecture -- [Built](/) -- [Source](/docs/README.md) +### Components -## Setup +- **Shared Event Bus**: Cross-domain EventBridge bus receiving events from Core, Routing, and other NHS Notify domains +- **Callback Event Queue**: SQS queue subscribed to `uk.nhs.notify.client-callbacks.*` events via EventBridge Target Rule +- **Transform & Filter Lambda**: Processes events, loads client configurations, applies subscription filters, and routes to Callbacks Event Bus +- **Callbacks Event Bus**: Domain-specific EventBridge bus for webhook orchestration +- **API Destination Target Rules**: Per-client rules invoking HTTPS endpoints with client-specific authentication +- **Client Config Storage**: S3 bucket storing client subscription configurations (status filters, webhook endpoints) +- **Per-Client DLQs**: SQS Dead Letter Queues for failed webhook deliveries (one per client) + +### Event Flow -By including preferably a one-liner or if necessary a set of clear CLI instructions we improve user experience. This should be a frictionless installation process that works on various operating systems (macOS, Linux, Windows WSL) and handles all the dependencies. +1. Status change events published to Shared Event Bus in `uk.nhs.notify.client-callbacks.*` namespace +2. SQS Target Rule routes events to Callback Event Queue +3. EventBridge Pipe invokes Transform & Filter Lambda with event batches +4. Lambda loads client subscription configs from S3 +5. Lambda applies client-specific filters (message status, channel status) +6. Matching events published to Callbacks Event Bus +7. API Destination Target Rules deliver callbacks to client webhook endpoints +8. Failed deliveries moved to per-client DLQs after retry exhaustion -Clone the repository +## Setup + +Clone the repository: ```shell -git clone https://github.com/nhs-england-tools/repository-template.git -cd nhs-england-tools/repository-template +git clone https://github.com/NHSDigital/nhs-notify-client-callbacks.git +cd nhs-notify-client-callbacks ``` ### Prerequisites The following software packages, or their equivalents, are expected to be installed and configured: -- [Docker](https://www.docker.com/) container runtime or a compatible tool, e.g. [Podman](https://podman.io/), -- [asdf](https://asdf-vm.com/) version manager, -- [GNU make](https://www.gnu.org/software/make/) 3.82 or later, +- [Node.js](https://nodejs.org/) 20.x or later (for Lambda development) +- [Terraform](https://www.terraform.io/) 1.5.x or later +- [AWS CLI](https://aws.amazon.com/cli/) configured with appropriate credentials +- [asdf](https://asdf-vm.com/) version manager +- [GNU make](https://www.gnu.org/software/make/) 3.82 or later > [!NOTE]
> The version of GNU make available by default on macOS is earlier than 3.82. You will need to upgrade it or certain `make` tasks will fail. On macOS, you will need [Homebrew](https://brew.sh/) installed, then to install `make`, like so: @@ -64,20 +82,15 @@ The following software packages, or their equivalents, are expected to be instal > brew install make > ``` > -> You will then see instructions to fix your [`$PATH`](https://github.com/nhs-england-tools/dotfiles/blob/main/dot_path.tmpl) variable to make the newly installed version available. If you are using [dotfiles](https://github.com/nhs-england-tools/dotfiles), this is all done for you. - -- [GNU sed](https://www.gnu.org/software/sed/) and [GNU grep](https://www.gnu.org/software/grep/) are required for the scripted command-line output processing, -- [GNU coreutils](https://www.gnu.org/software/coreutils/) and [GNU binutils](https://www.gnu.org/software/binutils/) may be required to build dependencies like Python, which may need to be compiled during installation, - -> [!NOTE]
-> For macOS users, installation of the GNU toolchain has been scripted and automated as part of the `dotfiles` project. Please see this [script](https://github.com/nhs-england-tools/dotfiles/blob/main/assets/20-install-base-packages.macos.sh) for details. +> You will then see instructions to fix your [`$PATH`](https://github.com/nhs-england-tools/dotfiles/blob/main/dot_path.tmpl) variable to make the newly installed version available. -- [Python](https://www.python.org/) required to run Git hooks, -- [`jq`](https://jqlang.github.io/jq/) a lightweight and flexible command-line JSON processor. +- [GNU sed](https://www.gnu.org/software/sed/) and [GNU grep](https://www.gnu.org/software/grep/) are required for scripted command-line output processing +- [Python](https://www.python.org/) required to run Git hooks +- [`jq`](https://jqlang.github.io/jq/) a lightweight and flexible command-line JSON processor ### Configuration -Installation and configuration of the toolchain dependencies +Install and configure toolchain dependencies: ```shell make config @@ -85,54 +98,62 @@ make config ## Usage -After a successful installation, provide an informative example of how this project can be used. Additional code snippets, screenshots and demos work well in this space. You may also link to the other documentation resources, e.g. the [User Guide](./docs/user-guide.md) to demonstrate more use cases and to show more features. - ### Testing -There are `make` tasks for you to configure to run your tests. Run `make test` to see how they work. You should be able to use the same entry points for local development as in your CI pipeline. +Run unit tests for Lambda functions: -## Design +```shell +npm test +``` -### Diagrams +## Infrastructure -The [C4 model](https://c4model.com/) is a simple and intuitive way to create software architecture diagrams that are clear, consistent, scalable and most importantly collaborative. This should result in documenting all the system interfaces, external dependencies and integration points. +Infrastructure is managed with Terraform under `infrastructure/terraform/`: -![Repository Template](./docs/diagrams/Repository_Template_GitHub_Generic.png) +- `components/`: Terraform components for different environments/accounts +- `modules/`: Reusable Terraform modules for callback infrastructure -The source for diagrams should be in Git for change control and review purposes. Recommendations are [draw.io](https://app.diagrams.net/) (example above in [docs](.docs/diagrams/) folder) and [Mermaids](https://github.com/mermaid-js/mermaid). Here is an example Mermaids sequence diagram: +**Deploy infrastructure**: -```mermaid -sequenceDiagram - User->>+Service: GET /users?params=... - Service->>Service: auth request - Service->>Database: get all users - Database-->>Service: list of users - Service->>Service: filter users - Service-->>-User: list[User] +```shell +cd infrastructure/terraform/components/ +terraform init +terraform plan +terraform apply ``` -### Modularity +Key infrastructure modules: -Most of the projects are built with customisability and extendability in mind. At a minimum, this can be achieved by implementing service level configuration options and settings. The intention of this section is to show how this can be used. If the system processes data, you could mention here for example how the input is prepared for testing - anonymised, synthetic or live data. +- **callback-event-queue**: SQS queue and EventBridge Target Rule for Shared Event Bus subscription +- **transform-filter-lambda**: Lambda function with EventBridge Pipe trigger +- **callbacks-event-bus**: Domain-specific EventBridge bus +- **api-destinations**: Per-client API Destination Target Rules +- **client-config-storage**: S3 bucket for subscription configurations ## Contributing -Describe or link templates on how to raise an issue, feature request or make a contribution to the codebase. Reference the other documentation files, like +Contributions should follow the NHS Notify development standards: + +- See [AGENTS.md](./AGENTS.md) for AI-assisted development guidelines +- Follow existing patterns for Lambda functions and Terraform modules +- Include tests for new functionality +- Update documentation for infrastructure or configuration changes -- Environment setup for contribution, i.e. `CONTRIBUTING.md` -- Coding standards, branching, linting, practices for development and testing -- Release process, versioning, changelog -- Backlog, board, roadmap, ways of working -- High-level requirements, guiding principles, decision records, etc. +Key development practices: + +- **Branching**: Feature branches from `main` with descriptive names (e.g., `feature/CCM-XXXXX-description`) +- **Testing**: Unit tests required for Lambda functions; integration tests for event flow +- **Logging**: Use structured JSON logging with correlation IDs +- **Infrastructure**: All infrastructure changes via Terraform with peer review +- **Event Schema**: Follow NHS Notify CloudEvents specification from `nhs-notify-standards` repository ## Contacts -Provide a way to contact the owners of this project. It can be a team, an individual or information on the means of getting in touch via active communication channels, e.g. opening a GitHub discussion, raising an issue, etc. +- [Tim Marston](https://github.com/cgitim) - Lead Developer +- [Mike Wild](https://github.com/mjewildnhs) - Developer ## Licence -> The [LICENCE.md](./LICENCE.md) file will need to be updated with the correct year and owner - Unless stated otherwise, the codebase is released under the MIT License. This covers both the codebase and any sample code in the documentation. Any HTML or Markdown documentation is [© Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/) and available under the terms of the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/). diff --git a/eslint.config.mjs b/eslint.config.mjs index b086976..957b983 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,28 +1,28 @@ -import jest from 'eslint-plugin-jest'; -import jsxA11y from 'eslint-plugin-jsx-a11y'; -import prettierRecommended from 'eslint-plugin-prettier/recommended'; -import { importX } from 'eslint-plugin-import-x'; -import * as eslintImportResolverTypescript from 'eslint-import-resolver-typescript'; -import noRelativeImportPaths from 'eslint-plugin-no-relative-import-paths'; -import react from 'eslint-plugin-react'; -import security from 'eslint-plugin-security'; -import sonarjs from 'eslint-plugin-sonarjs'; -import json from 'eslint-plugin-json'; -import unicorn from 'eslint-plugin-unicorn'; -import { defineConfig, globalIgnores } from 'eslint/config'; -import js from '@eslint/js'; -import html from 'eslint-plugin-html'; -import tseslint from 'typescript-eslint'; -import sortDestructureKeys from 'eslint-plugin-sort-destructure-keys'; +import jest from "eslint-plugin-jest"; +import jsxA11y from "eslint-plugin-jsx-a11y"; +import prettierRecommended from "eslint-plugin-prettier/recommended"; +import { importX } from "eslint-plugin-import-x"; +import * as eslintImportResolverTypescript from "eslint-import-resolver-typescript"; +import noRelativeImportPaths from "eslint-plugin-no-relative-import-paths"; +import react from "eslint-plugin-react"; +import security from "eslint-plugin-security"; +import sonarjs from "eslint-plugin-sonarjs"; +import json from "eslint-plugin-json"; +import unicorn from "eslint-plugin-unicorn"; +import { defineConfig, globalIgnores } from "eslint/config"; +import js from "@eslint/js"; +import html from "eslint-plugin-html"; +import tseslint from "typescript-eslint"; +import sortDestructureKeys from "eslint-plugin-sort-destructure-keys"; import { configs as airbnbConfigs, plugins as airbnbPlugins, -} from 'eslint-config-airbnb-extended'; -import { rules as prettierConfigRules } from 'eslint-config-prettier'; +} from "eslint-config-airbnb-extended"; +import { rules as prettierConfigRules } from "eslint-config-prettier"; -import { dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { FlatCompat } from '@eslint/eslintrc'; +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; +import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -33,13 +33,13 @@ const compat = new FlatCompat({ export default defineConfig([ globalIgnores([ - '**/*/coverage/*', - '**/.build', - '**/node_modules', - '**/dist', - '**/test-results', - '**/playwright-report*', - 'eslint.config.mjs', + "**/*/coverage/*", + "**/.build", + "**/node_modules", + "**/dist", + "**/test-results", + "**/playwright-report*", + "eslint.config.mjs", ]), //imports @@ -58,7 +58,7 @@ export default defineConfig([ airbnbPlugins.typescriptEslint, { - ignores: ['**/*.json'], + ignores: ["**/*.json"], languageOptions: { parserOptions: { projectService: true, @@ -68,19 +68,19 @@ export default defineConfig([ }, { - files: ['**/*.json'], + files: ["**/*.json"], extends: [tseslint.configs.disableTypeChecked], }, { settings: { - 'import-x/resolver-next': [ + "import-x/resolver-next": [ eslintImportResolverTypescript.createTypeScriptImportResolver({ project: [ - 'frontend/tsconfig.json', - 'lambdas/*/tsconfig.json', - 'tests/test-team/tsconfig.json', - 'utils/*/tsconfig.json', + "frontend/tsconfig.json", + "lambdas/*/tsconfig.json", + "tests/test-team/tsconfig.json", + "utils/*/tsconfig.json", ], }), ], @@ -89,32 +89,32 @@ export default defineConfig([ { rules: { - '@typescript-eslint/no-unused-vars': [ + "@typescript-eslint/no-unused-vars": [ 2, { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", }, ], - '@typescript-eslint/consistent-type-definitions': 0, + "@typescript-eslint/consistent-type-definitions": 0, }, }, // unicorn - unicorn.configs['recommended'], + unicorn.configs["recommended"], { rules: { - 'unicorn/prevent-abbreviations': 0, - 'unicorn/filename-case': [ + "unicorn/prevent-abbreviations": 0, + "unicorn/filename-case": [ 2, { - case: 'kebabCase', - ignore: ['.tsx'], + case: "kebabCase", + ignore: [".tsx"], }, ], - 'unicorn/no-null': 0, - 'unicorn/prefer-module': 0, - 'unicorn/import-style': [ + "unicorn/no-null": 0, + "unicorn/prefer-module": 0, + "unicorn/import-style": [ 2, { styles: { @@ -136,17 +136,17 @@ export default defineConfig([ airbnbPlugins.reactA11y, // jest - jest.configs['flat/recommended'], + jest.configs["flat/recommended"], // prettier prettierRecommended, - { rules: { ...prettierConfigRules, 'prettier/prettier': 2 } }, + { rules: { ...prettierConfigRules, "prettier/prettier": 2 } }, // jsxA11y { - files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], + files: ["**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}"], plugins: { - 'jsx-a11y': jsxA11y, + "jsx-a11y": jsxA11y, }, languageOptions: { parserOptions: { @@ -165,63 +165,49 @@ export default defineConfig([ // html { - files: ['**/*.html'], + files: ["**/*.html"], plugins: { html }, }, - // Next.js - ...compat.config({ - extends: ['next', 'next/core-web-vitals', 'next/typescript'], - settings: { - next: { - rootDir: 'frontend', - }, - }, - rules: { - // needed because next lint rules look for a pages directory - '@next/next/no-html-link-for-pages': 0, - }, - }), - // json { - files: ['**/*.json'], - ...json.configs['recommended'], + files: ["**/*.json"], + ...json.configs["recommended"], }, // destructure sorting { - name: 'eslint-plugin-sort-destructure-keys', + name: "eslint-plugin-sort-destructure-keys", plugins: { - 'sort-destructure-keys': sortDestructureKeys, + "sort-destructure-keys": sortDestructureKeys, }, rules: { - 'sort-destructure-keys/sort-destructure-keys': 2, + "sort-destructure-keys/sort-destructure-keys": 2, }, }, // imports { rules: { - 'sort-imports': [ + "sort-imports": [ 2, { ignoreDeclarationSort: true, }, ], - 'import-x/extensions': 0, + "import-x/extensions": 0, }, }, { - files: ['**/*.ts', '**/*.tsx'], + files: ["**/*.ts", "**/*.tsx"], rules: { - 'import-x/no-unresolved': 0, // trust the typescript compiler to catch unresolved imports + "import-x/no-unresolved": 0, // trust the typescript compiler to catch unresolved imports }, }, { - files: ['tests/test-team/**'], + files: ["tests/test-team/**"], rules: { - 'import-x/no-extraneous-dependencies': [ + "import-x/no-extraneous-dependencies": [ 2, { devDependencies: true, @@ -230,24 +216,24 @@ export default defineConfig([ }, }, { - files: ['**/utils/**', 'tests/test-team/**'], + files: ["**/utils/**", "tests/test-team/**", "lambdas/**/src/**"], rules: { - 'import-x/prefer-default-export': 0, + "import-x/prefer-default-export": 0, }, }, { plugins: { - 'no-relative-import-paths': noRelativeImportPaths, + "no-relative-import-paths": noRelativeImportPaths, }, rules: { - 'no-relative-import-paths/no-relative-import-paths': 2, + "no-relative-import-paths/no-relative-import-paths": 2, }, }, { - files: ['scripts/**'], + files: ["scripts/**"], rules: { - 'import-x/no-extraneous-dependencies': [ - 'error', + "import-x/no-extraneous-dependencies": [ + "error", { devDependencies: true }, ], }, @@ -256,11 +242,11 @@ export default defineConfig([ // misc rule overrides { rules: { - 'no-restricted-syntax': 0, - 'no-underscore-dangle': 0, - 'no-await-in-loop': 0, - 'no-plusplus': [2, { allowForLoopAfterthoughts: true }], - 'unicorn/prefer-top-level-await': 0, // top level await is not available in commonjs + "no-restricted-syntax": 0, + "no-underscore-dangle": 0, + "no-await-in-loop": 0, + "no-plusplus": [2, { allowForLoopAfterthoughts: true }], + "unicorn/prefer-top-level-await": 0, // top level await is not available in commonjs }, }, ]); diff --git a/lambdas/client-transform-filter-lambda/.eslintignore b/lambdas/client-transform-filter-lambda/.eslintignore deleted file mode 100644 index 1521c8b..0000000 --- a/lambdas/client-transform-filter-lambda/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -dist diff --git a/lambdas/client-transform-filter-lambda/jest.config.ts b/lambdas/client-transform-filter-lambda/jest.config.ts index d30f4cd..f88e727 100644 --- a/lambdas/client-transform-filter-lambda/jest.config.ts +++ b/lambdas/client-transform-filter-lambda/jest.config.ts @@ -1,7 +1,7 @@ -import type { Config } from 'jest'; +import type { Config } from "jest"; export const baseJestConfig: Config = { - preset: 'ts-jest', + preset: "ts-jest", // Automatically clear mock calls, instances, contexts and results before every test clearMocks: true, @@ -10,10 +10,10 @@ export const baseJestConfig: Config = { collectCoverage: true, // The directory where Jest should output its coverage files - coverageDirectory: './.reports/unit/coverage', + coverageDirectory: "./.reports/unit/coverage", // Indicates which provider should be used to instrument code for coverage - coverageProvider: 'babel', + coverageProvider: "babel", coverageThreshold: { global: { @@ -24,36 +24,36 @@ export const baseJestConfig: Config = { }, }, - coveragePathIgnorePatterns: ['/__tests__/'], - transform: { '^.+\\.ts$': 'ts-jest' }, - testPathIgnorePatterns: ['.build'], - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + coveragePathIgnorePatterns: ["/__tests__/"], + transform: { "^.+\\.ts$": "ts-jest" }, + testPathIgnorePatterns: [".build"], + testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], // Use this configuration option to add custom reporters to Jest reporters: [ - 'default', + "default", [ - 'jest-html-reporter', + "jest-html-reporter", { - pageTitle: 'Test Report', - outputPath: './.reports/unit/test-report.html', + pageTitle: "Test Report", + outputPath: "./.reports/unit/test-report.html", includeFailureMsg: true, }, ], ], // The test environment that will be used for testing - testEnvironment: 'jsdom', + testEnvironment: "jsdom", }; const utilsJestConfig = { ...baseJestConfig, - testEnvironment: 'node', + testEnvironment: "node", coveragePathIgnorePatterns: [ ...(baseJestConfig.coveragePathIgnorePatterns ?? []), - 'zod-validators.ts', + "zod-validators.ts", ], }; diff --git a/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts b/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts index 2c2996c..b00cc1c 100644 --- a/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts +++ b/lambdas/client-transform-filter-lambda/src/__tests__/index.test.ts @@ -1,21 +1,20 @@ -import { handler } from "../index"; +import { handler } from ".."; describe("Lambda handler", () => { - it("extracts from a stringified event", async () => { const eventStr = JSON.stringify({ body: { dataschemaversion: "1.0", - type: "uk.nhs.notify.client-callbacks.test-sid" - } + type: "uk.nhs.notify.client-callbacks.test-sid", + }, }); const result = await handler(eventStr); expect(result).toEqual({ body: { dataschemaversion: "1.0", - type: "uk.nhs.notify.client-callbacks.test-sid" - } + type: "uk.nhs.notify.client-callbacks.test-sid", + }, }); }); @@ -26,18 +25,18 @@ describe("Lambda handler", () => { body: JSON.stringify({ body: { dataschemaversion: "1.0", - type: "uk.nhs.notify.client-callbacks.test-sid" - } - }) - } + type: "uk.nhs.notify.client-callbacks.test-sid", + }, + }), + }, ]; const result = await handler(eventArray); expect(result).toEqual({ body: { dataschemaversion: "1.0", - type: "uk.nhs.notify.client-callbacks.test-sid" - } + type: "uk.nhs.notify.client-callbacks.test-sid", + }, }); }); @@ -54,19 +53,19 @@ describe("Lambda handler", () => { body: JSON.stringify({ body: { dataschemaversion: "2.0", - type: "nested-type" - } - }) - } - } + type: "nested-type", + }, + }), + }, + }, }; const result = await handler(event); expect(result).toEqual({ body: { dataschemaversion: "2.0", - type: "nested-type" - } + type: "nested-type", + }, }); }); @@ -75,5 +74,4 @@ describe("Lambda handler", () => { const result = await handler(eventStr); expect(result).toEqual({ body: {} }); }); - }); diff --git a/lambdas/client-transform-filter-lambda/src/index.ts b/lambdas/client-transform-filter-lambda/src/index.ts index 119bd36..91bfa94 100644 --- a/lambdas/client-transform-filter-lambda/src/index.ts +++ b/lambdas/client-transform-filter-lambda/src/index.ts @@ -1,11 +1,13 @@ export const handler = async (event: any) => { + // eslint-disable-next-line no-console console.log("RAW EVENT:", JSON.stringify(event, null, 2)); let parsedEvent: any; try { parsedEvent = typeof event === "string" ? JSON.parse(event) : event; - } catch (err) { - console.error("Could not parse event string:", err); + } catch (error) { + // eslint-disable-next-line no-console + console.error("Could not parse event string:", error); return { body: {} }; } @@ -14,16 +16,20 @@ export const handler = async (event: any) => { function findFields(obj: any) { if (!obj || typeof obj !== "object") return; - if (!dataschemaversion && "dataschemaversion" in obj) dataschemaversion = obj.dataschemaversion; + if (!dataschemaversion && "dataschemaversion" in obj) + dataschemaversion = obj.dataschemaversion; if (!type && "type" in obj) type = obj.type; for (const key of Object.keys(obj)) { + // eslint-disable-next-line security/detect-object-injection const val = obj[key]; if (typeof val === "string") { try { const nested = JSON.parse(val); findFields(nested); - } catch {} + } catch { + /* empty */ + } } else if (typeof val === "object") { findFields(val); } @@ -31,12 +37,13 @@ export const handler = async (event: any) => { } if (Array.isArray(parsedEvent)) { - parsedEvent.forEach(item => findFields(item)); + for (const item of parsedEvent) findFields(item); } else { findFields(parsedEvent); } if (!dataschemaversion || !type) { + // eslint-disable-next-line no-console console.error("Failed to extract payload from event!"); return { body: {} }; } @@ -44,7 +51,7 @@ export const handler = async (event: any) => { return { body: { dataschemaversion, - type - } + type, + }, }; }; diff --git a/lambdas/client-transform-filter-lambda/tsconfig.json b/lambdas/client-transform-filter-lambda/tsconfig.json index ea37d69..9e05f63 100644 --- a/lambdas/client-transform-filter-lambda/tsconfig.json +++ b/lambdas/client-transform-filter-lambda/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node22/tsconfig.json", + "extends": "../../tsconfig.base.json", "include": [ "src/**/*", "jest.config.ts" diff --git a/package-lock.json b/package-lock.json index 6cd3e2e..aa3caa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,21 @@ { - "name": "nhs-notify-repository-template", + "name": "nhs-notify-client-callbacks", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "nhs-notify-repository-template", + "name": "nhs-notify-client-callbacks", "workspaces": [ "lambdas/client-transform-filter-lambda" ], "devDependencies": { + "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node22": "^22.0.2", "@types/jest": "^29.5.14", - "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.25.0", "eslint": "^9.27.0", "eslint-config-airbnb-extended": "^1.0.11", - "eslint-config-next": "^15.3.2", "eslint-config-prettier": "^10.1.5", "eslint-import-resolver-typescript": "^4.4.2", "eslint-plugin-html": "^8.1.3", @@ -38,7 +37,8 @@ "lcov-result-merger": "^5.0.1", "ts-jest": "^29.3.0", "ts-node": "^10.9.2", - "tsx": "^4.19.3" + "tsx": "^4.19.3", + "typescript-eslint": "^8.54.0" } }, "lambdas/client-transform-filter-lambda": { @@ -546,6 +546,40 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", @@ -922,7 +956,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1600,12 +1636,27 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@next/eslint-plugin-next": { "version": "15.3.4", "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.4.tgz", "integrity": "sha512-lBxYdj7TI8phbJcLSAqDt57nIcobEign5NYIKCiy0hXQhrUbTqLqOaSDi568U6vFg4hJfBdZYsG4iP/uKhCqgg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "fast-glob": "3.3.1" } @@ -1616,6 +1667,8 @@ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -1633,6 +1686,8 @@ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -1672,16 +1727,6 @@ "node": ">= 8" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, "node_modules/@pkgr/core": { "version": "0.2.7", "dev": true, @@ -1696,14 +1741,9 @@ "node_modules/@rtsao/scc": { "version": "1.1.0", "dev": true, - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", - "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", - "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@sinclair/typebox": { "version": "0.27.8", @@ -1726,76 +1766,50 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@swc/core": { - "version": "1.12.6", + "node_modules/@stylistic/eslint-plugin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz", + "integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==", "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, + "license": "MIT", "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.23" + "@typescript-eslint/utils": "^8.13.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.6", - "@swc/core-darwin-x64": "1.12.6", - "@swc/core-linux-arm-gnueabihf": "1.12.6", - "@swc/core-linux-arm64-gnu": "1.12.6", - "@swc/core-linux-arm64-musl": "1.12.6", - "@swc/core-linux-x64-gnu": "1.12.6", - "@swc/core-linux-x64-musl": "1.12.6", - "@swc/core-win32-arm64-msvc": "1.12.6", - "@swc/core-win32-ia32-msvc": "1.12.6", - "@swc/core-win32-x64-msvc": "1.12.6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } + "eslint": ">=8.40.0" } }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.6", - "cpu": [ - "arm64" - ], + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true - }, - "node_modules/@swc/types": { - "version": "0.1.23", + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@swc/counter": "^0.1.3" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/@tootallnate/once": { @@ -1833,6 +1847,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/aws-lambda": { "version": "8.10.150", "dev": true, @@ -1940,7 +1965,9 @@ "node_modules/@types/json5": { "version": "0.0.29", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/@types/node": { "version": "24.0.3", @@ -1974,19 +2001,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/type-utils": "8.35.0", - "@typescript-eslint/utils": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1996,21 +2024,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.35.0", + "@typescript-eslint/parser": "^8.54.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/typescript-estree": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2021,17 +2061,19 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.35.0", - "@typescript-eslint/types": "^8.35.0", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2041,16 +2083,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2061,7 +2105,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", "dev": true, "license": "MIT", "engines": { @@ -2072,18 +2118,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.35.0", - "@typescript-eslint/utils": "8.35.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2094,11 +2143,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", "dev": true, "license": "MIT", "engines": { @@ -2110,20 +2161,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.35.0", - "@typescript-eslint/tsconfig-utils": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2133,18 +2185,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/typescript-estree": "8.35.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2155,15 +2222,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.35.0", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/types": "8.54.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2176,6 +2245,8 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2185,6 +2256,34 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.2.tgz", + "integrity": "sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.2.tgz", + "integrity": "sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.9.2", "cpu": [ @@ -2197,6 +2296,233 @@ "darwin" ] }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.2.tgz", + "integrity": "sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.2.tgz", + "integrity": "sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.2.tgz", + "integrity": "sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.2.tgz", + "integrity": "sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.2.tgz", + "integrity": "sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.2.tgz", + "integrity": "sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.2.tgz", + "integrity": "sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.2.tgz", + "integrity": "sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.2.tgz", + "integrity": "sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.2.tgz", + "integrity": "sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.2.tgz", + "integrity": "sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.2.tgz", + "integrity": "sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.2.tgz", + "integrity": "sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.2.tgz", + "integrity": "sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.2.tgz", + "integrity": "sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.2.tgz", + "integrity": "sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/abab": { "version": "2.0.6", "dev": true, @@ -2413,6 +2739,8 @@ "version": "1.2.6", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -3142,7 +3470,9 @@ } }, "node_modules/debug": { - "version": "4.4.1", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -3778,69 +4108,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-config-next": { - "version": "15.3.4", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.4.tgz", - "integrity": "sha512-WqeumCq57QcTP2lYlV6BRUySfGiBYEXlQ1L0mQ+u4N4X4ZhUVSSQ52WtjqHv60pJ6dD7jn+YZc0d1/ZSsxccvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@next/eslint-plugin-next": "15.3.4", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, "node_modules/eslint-config-prettier": { "version": "10.1.5", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", @@ -3886,6 +4153,8 @@ "version": "0.3.9", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -3896,6 +4165,8 @@ "version": "3.2.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -3939,6 +4210,8 @@ "version": "2.12.1", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "debug": "^3.2.7" }, @@ -3955,6 +4228,8 @@ "version": "3.2.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -3974,6 +4249,8 @@ "version": "2.32.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -4043,6 +4320,8 @@ "version": "1.1.12", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4052,6 +4331,8 @@ "version": "3.2.7", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -4060,6 +4341,8 @@ "version": "3.1.2", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4071,6 +4354,8 @@ "version": "6.3.1", "dev": true, "license": "ISC", + "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -4249,6 +4534,8 @@ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=10" }, @@ -4824,6 +5111,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "dev": true, @@ -5055,11 +5357,6 @@ "dev": true, "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, "node_modules/has-bigints": { "version": "1.1.0", "dev": true, @@ -6880,6 +7177,8 @@ "version": "1.2.8", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7049,6 +7348,8 @@ "version": "1.0.3", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7958,13 +8259,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" - }, "node_modules/stable-hash-x": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", @@ -8267,12 +8561,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.14", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -8282,9 +8578,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -8295,7 +8596,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -8347,7 +8650,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -8482,6 +8787,8 @@ "version": "3.15.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -8493,6 +8800,8 @@ "version": "1.0.2", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "minimist": "^1.2.0" }, @@ -8504,10 +8813,20 @@ "version": "3.0.0", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/tsx": { "version": "4.20.3", "dev": true, @@ -9065,6 +9384,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.54.0.tgz", + "integrity": "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.54.0", + "@typescript-eslint/parser": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "dev": true, diff --git a/package.json b/package.json index 3c6afdf..2013466 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,12 @@ { - "//": "PLEASE UPDATE THE NAME AND WORKSPACES APPROPRIATELY.", "devDependencies": { + "@stylistic/eslint-plugin": "^3.1.0", "@tsconfig/node22": "^22.0.2", "@types/jest": "^29.5.14", - "@typescript-eslint/eslint-plugin": "^8.27.0", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.25.0", "eslint": "^9.27.0", "eslint-config-airbnb-extended": "^1.0.11", - "eslint-config-next": "^15.3.2", "eslint-config-prettier": "^10.1.5", "eslint-import-resolver-typescript": "^4.4.2", "eslint-plugin-html": "^8.1.3", @@ -30,9 +28,10 @@ "lcov-result-merger": "^5.0.1", "ts-jest": "^29.3.0", "ts-node": "^10.9.2", - "tsx": "^4.19.3" + "tsx": "^4.19.3", + "typescript-eslint": "^8.54.0" }, - "name": "nhs-notify-repository-template", + "name": "nhs-notify-client-callbacks", "overrides": { "pretty-format": { "react-is": "19.0.0" diff --git a/scripts/config/vale/styles/config/vocabularies/words/accept.txt b/scripts/config/vale/styles/config/vocabularies/words/accept.txt index 3aa82c3..e8c5758 100644 --- a/scripts/config/vale/styles/config/vocabularies/words/accept.txt +++ b/scripts/config/vale/styles/config/vocabularies/words/accept.txt @@ -1,9 +1,12 @@ +ajv [A-Z]+s Bitwarden bot +[Cc]onfig Cognito Cyber Dependabot +dev draw.io drawio endcapture @@ -20,6 +23,7 @@ onboarding Podman Python rawContent +[Rr]unbook sed Syft Terraform diff --git a/scripts/docker/examples/python/assets/hello_world/requirements.txt b/scripts/docker/examples/python/assets/hello_world/requirements.txt index 5331aff..6ac36a3 100644 --- a/scripts/docker/examples/python/assets/hello_world/requirements.txt +++ b/scripts/docker/examples/python/assets/hello_world/requirements.txt @@ -8,5 +8,5 @@ MarkupSafe==2.1.3 pip==23.3 setuptools==78.1.1 Werkzeug==3.0.6 -wheel==0.41.1 +wheel==0.46.2 WTForms==3.0.1 diff --git a/scripts/tests/lint.sh b/scripts/tests/lint.sh new file mode 100755 index 0000000..23e4bfd --- /dev/null +++ b/scripts/tests/lint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +# Run linting across all workspaces +npm ci +npm run lint diff --git a/scripts/tests/typecheck.sh b/scripts/tests/typecheck.sh new file mode 100755 index 0000000..a6f096b --- /dev/null +++ b/scripts/tests/typecheck.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +# Run TypeScript type checking across all workspaces +npm ci +npm run typecheck diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..fcbb3d0 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@tsconfig/node22/tsconfig.json" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ccb61df --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "include": [ + "lambdas/*/src/**/*", + "scripts/*/src/**/*", + "src/**/*", + "tests/**/*" + ] +}