Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cd67dce01 | ||
|
|
b7970a68c9 | ||
|
|
35b31b3188 | ||
|
|
f0daa354bf |
No files matched your search
@@ -0,0 +1,34 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# Enable version updates for npm
|
||||||
|
- package-ecosystem: 'npm'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
|
day: 'monday'
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
groups:
|
||||||
|
angular:
|
||||||
|
patterns:
|
||||||
|
- '@angular/*'
|
||||||
|
development-dependencies:
|
||||||
|
dependency-type: 'development'
|
||||||
|
update-types:
|
||||||
|
- 'minor'
|
||||||
|
- 'patch'
|
||||||
|
|
||||||
|
# Enable version updates for GitHub Actions
|
||||||
|
- package-ecosystem: 'github-actions'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
|
day: 'monday'
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
|
||||||
|
# Enable version updates for Docker
|
||||||
|
- package-ecosystem: 'docker'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
|
day: 'monday'
|
||||||
|
open-pull-requests-limit: 5
|
||||||
@@ -1,101 +1,41 @@
|
|||||||
# For most projects, this workflow file will not need changing; you simply need
|
name: CodeQL Security Analysis
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL Advanced"
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '27 7 * * 2'
|
- cron: '0 0 * * 1' # Run every Monday at midnight UTC
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze (${{ matrix.language }})
|
name: Analyze Code
|
||||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
runs-on: ubuntu-latest
|
||||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
permissions:
|
||||||
# - https://gh.io/supported-runners-and-hardware-resources
|
security-events: write
|
||||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
actions: read
|
||||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
contents: read
|
||||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
||||||
permissions:
|
|
||||||
# required for all workflows
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
# required to fetch internal or private CodeQL packs
|
strategy:
|
||||||
packages: read
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: ['javascript-typescript']
|
||||||
|
|
||||||
# only required for workflows in private repositories
|
steps:
|
||||||
actions: read
|
- name: Checkout repository
|
||||||
contents: read
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
strategy:
|
- name: Initialize CodeQL
|
||||||
fail-fast: false
|
uses: github/codeql-action/init@v3
|
||||||
matrix:
|
with:
|
||||||
include:
|
languages: ${{ matrix.language }}
|
||||||
- language: actions
|
queries: security-extended,security-and-quality
|
||||||
build-mode: none
|
|
||||||
- language: javascript-typescript
|
|
||||||
build-mode: none
|
|
||||||
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
|
||||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
|
||||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
|
||||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
|
||||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
|
||||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
|
||||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
|
||||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
- name: Autobuild
|
||||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
uses: github/codeql-action/autobuild@v3
|
||||||
# or others). This is typically only required for manual builds.
|
|
||||||
# - name: Setup runtime (example)
|
|
||||||
# uses: actions/setup-example@v1
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
- name: Perform CodeQL Analysis
|
||||||
- name: Initialize CodeQL
|
uses: github/codeql-action/analyze@v3
|
||||||
uses: github/codeql-action/init@v4
|
with:
|
||||||
with:
|
category: '/language:${{matrix.language}}'
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
build-mode: ${{ matrix.build-mode }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
|
|
||||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
||||||
# queries: security-extended,security-and-quality
|
|
||||||
|
|
||||||
# If the analyze step fails for one of the languages you are analyzing with
|
|
||||||
# "We were unable to automatically build your code", modify the matrix above
|
|
||||||
# to set the build mode to "manual" for that language. Then modify this step
|
|
||||||
# to build your code.
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
||||||
- name: Run manual build steps
|
|
||||||
if: matrix.build-mode == 'manual'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
|
||||||
'languages you are analyzing, replace this with the commands to build' \
|
|
||||||
'your code, for example:'
|
|
||||||
echo ' make bootstrap'
|
|
||||||
echo ' make release'
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
@@ -5,6 +5,7 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
@@ -12,10 +13,14 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
security-events: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -33,11 +38,27 @@ jobs:
|
|||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
type=sha
|
type=sha
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Run Trivy vulnerability scanner
|
||||||
|
uses: aquasecurity/trivy-action@0.27.0
|
||||||
|
with:
|
||||||
|
image-ref: ghcr.io/${{ github.repository }}:sha-${{ github.sha }}
|
||||||
|
format: 'sarif'
|
||||||
|
output: 'trivy-results.sarif'
|
||||||
|
|
||||||
|
- name: Upload Trivy results to GitHub Security tab
|
||||||
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
sarif_file: 'trivy-results.sarif'
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
name: PR Validation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Lint Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Check code formatting with Prettier
|
||||||
|
run: npx prettier --check "src/**/*.{ts,html,scss,css,json}"
|
||||||
|
|
||||||
|
- name: Run ESLint
|
||||||
|
run: npx eslint "src/**/*.ts"
|
||||||
|
|
||||||
|
type-check:
|
||||||
|
name: TypeScript Type Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run TypeScript compiler
|
||||||
|
run: npx tsc --noEmit
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: npm test -- --watch=false --browsers=ChromeHeadless --code-coverage
|
||||||
|
|
||||||
|
- name: Upload coverage reports
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
files: ./coverage/vac-optimizer/lcov.info
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build Application
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build production bundle
|
||||||
|
run: npm run build -- --configuration production
|
||||||
|
env:
|
||||||
|
NG_BUILD_CACHE: '.angular/cache'
|
||||||
|
|
||||||
|
- name: Check bundle size
|
||||||
|
run: |
|
||||||
|
du -sh dist/vac-optimizer/browser
|
||||||
|
echo "Build completed successfully"
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build-artifacts
|
||||||
|
path: dist/
|
||||||
|
retention-days: 7
|
||||||
@@ -12,6 +12,38 @@ ng serve
|
|||||||
|
|
||||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
This project uses ESLint and Prettier to maintain code quality and consistency.
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
|
||||||
|
Check for linting errors:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Auto-fix linting issues:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run lint:fix
|
||||||
|
```
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
Check code formatting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run format:check
|
||||||
|
```
|
||||||
|
|
||||||
|
Format code:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run format
|
||||||
|
```
|
||||||
|
|
||||||
## Code scaffolding
|
## Code scaffolding
|
||||||
|
|
||||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||||
@@ -54,6 +86,15 @@ ng e2e
|
|||||||
|
|
||||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||||
|
|
||||||
|
## CI/CD
|
||||||
|
|
||||||
|
This project uses GitHub Actions for continuous integration and deployment:
|
||||||
|
|
||||||
|
- **PR Validation**: Automatically runs on pull requests to validate code quality, run tests, and build the application
|
||||||
|
- **Docker Build & Push**: Builds and publishes Docker images to GitHub Container Registry on main branch pushes and releases
|
||||||
|
- **CodeQL Analysis**: Runs security analysis weekly and on pull requests
|
||||||
|
- **Dependabot**: Automatically updates dependencies weekly
|
||||||
|
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||||
+4
-1
@@ -52,7 +52,10 @@
|
|||||||
"maximumError": "16kB"
|
"maximumError": "16kB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputHashing": "all"
|
"outputHashing": "all",
|
||||||
|
"optimization": {
|
||||||
|
"fonts": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import eslintJs from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import prettierPlugin from 'eslint-plugin-prettier';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
eslintJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
prettier,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
plugins: {
|
||||||
|
prettier: prettierPlugin,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'prettier/prettier': 'error',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['dist/**', 'node_modules/**', '.angular/**', 'coverage/**'],
|
||||||
|
},
|
||||||
|
];
|
||||||
Generated
+449
-1
@@ -28,6 +28,7 @@
|
|||||||
"@angular/build": "^20.3.7",
|
"@angular/build": "^20.3.7",
|
||||||
"@angular/cli": "^20.3.7",
|
"@angular/cli": "^20.3.7",
|
||||||
"@angular/compiler-cli": "^20.3.0",
|
"@angular/compiler-cli": "^20.3.0",
|
||||||
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/express": "^5.0.1",
|
"@types/express": "^5.0.1",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "~5.9.2"
|
"typescript": "~5.9.2",
|
||||||
|
"typescript-eslint": "^8.19.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@acemir/cssom": {
|
"node_modules/@acemir/cssom": {
|
||||||
@@ -3718,6 +3720,44 @@
|
|||||||
"@tybys/wasm-util": "^0.10.0"
|
"@tybys/wasm-util": "^0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
|
"run-parallel": "^1.1.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.stat": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.walk": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
|
"fastq": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@npmcli/agent": {
|
"node_modules/@npmcli/agent": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz",
|
||||||
@@ -5123,6 +5163,264 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
|
"@typescript-eslint/scope-manager": "8.46.3",
|
||||||
|
"@typescript-eslint/type-utils": "8.46.3",
|
||||||
|
"@typescript-eslint/utils": "8.46.3",
|
||||||
|
"@typescript-eslint/visitor-keys": "8.46.3",
|
||||||
|
"graphemer": "^1.4.0",
|
||||||
|
"ignore": "^7.0.0",
|
||||||
|
"natural-compare": "^1.4.0",
|
||||||
|
"ts-api-utils": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@typescript-eslint/parser": "^8.46.3",
|
||||||
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
|
"typescript": ">=4.8.4 <6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/parser": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/scope-manager": "8.46.3",
|
||||||
|
"@typescript-eslint/types": "8.46.3",
|
||||||
|
"@typescript-eslint/typescript-estree": "8.46.3",
|
||||||
|
"@typescript-eslint/visitor-keys": "8.46.3",
|
||||||
|
"debug": "^4.3.4"
|
||||||
|
},
|
||||||
|
"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/@typescript-eslint/project-service": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/tsconfig-utils": "^8.46.3",
|
||||||
|
"@typescript-eslint/types": "^8.46.3",
|
||||||
|
"debug": "^4.3.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.8.4 <6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/types": "8.46.3",
|
||||||
|
"@typescript-eslint/visitor-keys": "8.46.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.8.4 <6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/types": "8.46.3",
|
||||||
|
"@typescript-eslint/typescript-estree": "8.46.3",
|
||||||
|
"@typescript-eslint/utils": "8.46.3",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"ts-api-utils": "^2.1.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/@typescript-eslint/types": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/project-service": "8.46.3",
|
||||||
|
"@typescript-eslint/tsconfig-utils": "8.46.3",
|
||||||
|
"@typescript-eslint/types": "8.46.3",
|
||||||
|
"@typescript-eslint/visitor-keys": "8.46.3",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.8.4 <6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@typescript-eslint/utils": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@eslint-community/eslint-utils": "^4.7.0",
|
||||||
|
"@typescript-eslint/scope-manager": "8.46.3",
|
||||||
|
"@typescript-eslint/types": "8.46.3",
|
||||||
|
"@typescript-eslint/typescript-estree": "8.46.3"
|
||||||
|
},
|
||||||
|
"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/@typescript-eslint/visitor-keys": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/types": "8.46.3",
|
||||||
|
"eslint-visitor-keys": "^4.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ungap/structured-clone": {
|
"node_modules/@ungap/structured-clone": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||||
@@ -7680,6 +7978,36 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-glob": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
|
"@nodelib/fs.walk": "^1.2.3",
|
||||||
|
"glob-parent": "^5.1.2",
|
||||||
|
"merge2": "^1.3.0",
|
||||||
|
"micromatch": "^4.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-glob/node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fast-json-stable-stringify": {
|
"node_modules/fast-json-stable-stringify": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
@@ -7711,6 +8039,16 @@
|
|||||||
],
|
],
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/fastq": {
|
||||||
|
"version": "1.19.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||||
|
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"reusify": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fb-watchman": {
|
"node_modules/fb-watchman": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
|
||||||
@@ -8127,6 +8465,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/graphemer": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/handlebars": {
|
"node_modules/handlebars": {
|
||||||
"version": "4.7.8",
|
"version": "4.7.8",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
|
||||||
@@ -10696,6 +11041,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/merge2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
@@ -12251,6 +12606,27 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/queue-microtask": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/range-parser": {
|
"node_modules/range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
@@ -12427,6 +12803,17 @@
|
|||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reusify": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"iojs": ">=1.0.0",
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rfdc": {
|
"node_modules/rfdc": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||||
@@ -12531,6 +12918,30 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/run-parallel": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"queue-microtask": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rxjs": {
|
"node_modules/rxjs": {
|
||||||
"version": "7.8.2",
|
"version": "7.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||||
@@ -13695,6 +14106,19 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-api-utils": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.8.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-jest": {
|
"node_modules/ts-jest": {
|
||||||
"version": "29.4.5",
|
"version": "29.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz",
|
||||||
@@ -13859,6 +14283,30 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typescript-eslint": {
|
||||||
|
"version": "8.46.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz",
|
||||||
|
"integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "8.46.3",
|
||||||
|
"@typescript-eslint/parser": "8.46.3",
|
||||||
|
"@typescript-eslint/typescript-estree": "8.46.3",
|
||||||
|
"@typescript-eslint/utils": "8.46.3"
|
||||||
|
},
|
||||||
|
"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/ua-parser-js": {
|
"node_modules/ua-parser-js": {
|
||||||
"version": "0.7.41",
|
"version": "0.7.41",
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz",
|
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz",
|
||||||
|
|||||||
+8
-2
@@ -7,7 +7,11 @@
|
|||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"watch": "ng build --watch --configuration development",
|
"watch": "ng build --watch --configuration development",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"serve:ssr:vac-optimizer": "node dist/vac-optimizer/server/server.mjs"
|
"serve:ssr:vac-optimizer": "node dist/vac-optimizer/server/server.mjs",
|
||||||
|
"lint": "eslint \"src/**/*.ts\"",
|
||||||
|
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
||||||
|
"format": "prettier --write \"src/**/*.{ts,html,scss,css,json}\"",
|
||||||
|
"format:check": "prettier --check \"src/**/*.{ts,html,scss,css,json}\""
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
@@ -43,6 +47,7 @@
|
|||||||
"@angular/build": "^20.3.7",
|
"@angular/build": "^20.3.7",
|
||||||
"@angular/cli": "^20.3.7",
|
"@angular/cli": "^20.3.7",
|
||||||
"@angular/compiler-cli": "^20.3.0",
|
"@angular/compiler-cli": "^20.3.0",
|
||||||
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/express": "^5.0.1",
|
"@types/express": "^5.0.1",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
@@ -59,6 +64,7 @@
|
|||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "~5.9.2"
|
"typescript": "~5.9.2",
|
||||||
|
"typescript-eslint": "^8.19.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,7 @@ import { appConfig } from './app.config';
|
|||||||
import { serverRoutes } from './app.routes.server';
|
import { serverRoutes } from './app.routes.server';
|
||||||
|
|
||||||
const serverConfig: ApplicationConfig = {
|
const serverConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [provideServerRendering(withRoutes(serverRoutes))],
|
||||||
provideServerRendering(withRoutes(serverRoutes))
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||||
@@ -3,6 +3,6 @@ import { RenderMode, ServerRoute } from '@angular/ssr';
|
|||||||
export const serverRoutes: ServerRoute[] = [
|
export const serverRoutes: ServerRoute[] = [
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
renderMode: RenderMode.Client
|
renderMode: RenderMode.Client,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
+1
-1
@@ -6,7 +6,7 @@ describe('App', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [App],
|
imports: [App],
|
||||||
providers: [provideZonelessChangeDetection()]
|
providers: [provideZonelessChangeDetection()],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, Component, model, signal } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
||||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
import { provideNativeDateAdapter } from '@angular/material/core';
|
import { provideNativeDateAdapter } from '@angular/material/core';
|
||||||
import { MatDivider } from '@angular/material/divider';
|
import { MatDivider } from '@angular/material/divider';
|
||||||
|
|||||||
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
|||||||
import { CalendarMonth } from './calendar-month';
|
import { CalendarMonth } from './calendar-month';
|
||||||
|
|
||||||
describe('CalendarMonth', () => {
|
describe('CalendarMonth', () => {
|
||||||
let component: CalendarMonth;
|
let component: CalendarMonth;
|
||||||
let fixture: ComponentFixture<CalendarMonth>;
|
let fixture: ComponentFixture<CalendarMonth>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CalendarMonth],
|
imports: [CalendarMonth],
|
||||||
providers: [provideZonelessChangeDetection()]
|
providers: [provideZonelessChangeDetection()],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CalendarMonth);
|
fixture = TestBed.createComponent(CalendarMonth);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
|||||||
import { CalendarSettings } from './calendar-settings';
|
import { CalendarSettings } from './calendar-settings';
|
||||||
|
|
||||||
describe('CalendarSettings', () => {
|
describe('CalendarSettings', () => {
|
||||||
let component: CalendarSettings;
|
let component: CalendarSettings;
|
||||||
let fixture: ComponentFixture<CalendarSettings>;
|
let fixture: ComponentFixture<CalendarSettings>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CalendarSettings],
|
imports: [CalendarSettings],
|
||||||
providers: [provideZonelessChangeDetection()]
|
providers: [provideZonelessChangeDetection()],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CalendarSettings);
|
fixture = TestBed.createComponent(CalendarSettings);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { MatButton, MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatIcon, MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { CalendarSettingsDialog } from '../dialog/calendar-settings-dialog/calendar-settings-dialog';
|
import { CalendarSettingsDialog } from '../dialog/calendar-settings-dialog/calendar-settings-dialog';
|
||||||
import { MatDividerModule } from '@angular/material/divider';
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
<div class="calendar-year">
|
<div class="calendar-year">
|
||||||
@if (isLoading()) {
|
@if (isLoading()) {
|
||||||
<div class="loading-overlay">
|
<div class="loading-overlay">
|
||||||
<div class="loading-spinner">
|
<div class="loading-spinner">
|
||||||
<mat-spinner></mat-spinner>
|
<mat-spinner></mat-spinner>
|
||||||
<p>Calcul en cours...</p>
|
<p>Calcul en cours...</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
@for (month of months; track $index) {
|
@for (month of months; track $index) {
|
||||||
<mat-card class="card">
|
<mat-card class="card">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<app-calendar-month [monthIndex]="month" [year]="getYear(month)" [selectedDates]="selectedDates"></app-calendar-month>
|
<app-calendar-month
|
||||||
</mat-card-content>
|
[monthIndex]="month"
|
||||||
</mat-card>
|
[year]="getYear(month)"
|
||||||
|
[selectedDates]="selectedDates"
|
||||||
|
></app-calendar-month>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -66,5 +66,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@include mat.progress-spinner-overrides((size: 0.5rem, active-indicator-width: 0.25rem));
|
@include mat.progress-spinner-overrides(
|
||||||
|
(
|
||||||
|
size: 0.5rem,
|
||||||
|
active-indicator-width: 0.25rem,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
|||||||
import { CalendarYear } from './calendar-year';
|
import { CalendarYear } from './calendar-year';
|
||||||
|
|
||||||
describe('CalendarYear', () => {
|
describe('CalendarYear', () => {
|
||||||
let component: CalendarYear;
|
let component: CalendarYear;
|
||||||
let fixture: ComponentFixture<CalendarYear>;
|
let fixture: ComponentFixture<CalendarYear>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CalendarYear],
|
imports: [CalendarYear],
|
||||||
providers: [provideZonelessChangeDetection()]
|
providers: [provideZonelessChangeDetection()],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CalendarYear);
|
fixture = TestBed.createComponent(CalendarYear);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
+29
-30
@@ -4,39 +4,38 @@ import { MatDialogRef } from '@angular/material/dialog';
|
|||||||
import { CalendarSettingsDialog } from './calendar-settings-dialog';
|
import { CalendarSettingsDialog } from './calendar-settings-dialog';
|
||||||
|
|
||||||
describe('CalendarSettingsDialog', () => {
|
describe('CalendarSettingsDialog', () => {
|
||||||
let component: CalendarSettingsDialog;
|
let component: CalendarSettingsDialog;
|
||||||
let fixture: ComponentFixture<CalendarSettingsDialog>;
|
let fixture: ComponentFixture<CalendarSettingsDialog>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CalendarSettingsDialog],
|
imports: [CalendarSettingsDialog],
|
||||||
providers: [
|
providers: [
|
||||||
provideZonelessChangeDetection(),
|
provideZonelessChangeDetection(),
|
||||||
{
|
{
|
||||||
provide: MatDialogRef,
|
provide: MatDialogRef,
|
||||||
useValue: {
|
useValue: {
|
||||||
close: jasmine.createSpy('close')
|
close: jasmine.createSpy('close'),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CalendarSettingsDialog);
|
fixture = TestBed.createComponent(CalendarSettingsDialog);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have calendar settings service', () => {
|
it('should have calendar settings service', () => {
|
||||||
expect(component['calendarSettingsService']).toBeDefined();
|
expect(component['calendarSettingsService']).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should close dialog when closeDialog is called', () => {
|
it('should close dialog when closeDialog is called', () => {
|
||||||
component.closeDialog();
|
component.closeDialog();
|
||||||
expect(component.dialogRef.close).toHaveBeenCalled();
|
expect(component.dialogRef.close).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { CalendarSettingsService } from '../../../services/calendar-settings-service';
|
import { CalendarSettingsService } from '../../../services/calendar-settings-service';
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
import { CdkFixedSizeVirtualScroll } from '@angular/cdk/scrolling';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-calendar-settings-dialog',
|
selector: 'app-calendar-settings-dialog',
|
||||||
|
|||||||
@@ -3,39 +3,38 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
|||||||
import { UserInput } from './user-input';
|
import { UserInput } from './user-input';
|
||||||
|
|
||||||
describe('UserInput', () => {
|
describe('UserInput', () => {
|
||||||
let component: UserInput;
|
let component: UserInput;
|
||||||
let fixture: ComponentFixture<UserInput>;
|
let fixture: ComponentFixture<UserInput>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [UserInput],
|
imports: [UserInput],
|
||||||
providers: [provideZonelessChangeDetection()]
|
providers: [provideZonelessChangeDetection()],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(UserInput);
|
fixture = TestBed.createComponent(UserInput);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize form with values from service', () => {
|
it('should initialize form with values from service', () => {
|
||||||
expect(component.userInputForm).toBeDefined();
|
expect(component.userInputForm).toBeDefined();
|
||||||
expect(component.userInputForm.get('CP')).toBeDefined();
|
expect(component.userInputForm.get('CP')).toBeDefined();
|
||||||
expect(component.userInputForm.get('RTT')).toBeDefined();
|
expect(component.userInputForm.get('RTT')).toBeDefined();
|
||||||
expect(component.userInputForm.get('Others')).toBeDefined();
|
expect(component.userInputForm.get('Others')).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have valid form controls', () => {
|
it('should have valid form controls', () => {
|
||||||
const cpControl = component.userInputForm.get('CP');
|
const cpControl = component.userInputForm.get('CP');
|
||||||
const rttControl = component.userInputForm.get('RTT');
|
const rttControl = component.userInputForm.get('RTT');
|
||||||
const othersControl = component.userInputForm.get('Others');
|
const othersControl = component.userInputForm.get('Others');
|
||||||
|
|
||||||
expect(cpControl).toBeTruthy();
|
expect(cpControl).toBeTruthy();
|
||||||
expect(rttControl).toBeTruthy();
|
expect(rttControl).toBeTruthy();
|
||||||
expect(othersControl).toBeTruthy();
|
expect(othersControl).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit, OnDestroy, effect } from '@angular/core';
|
import { Component, OnInit, OnDestroy, effect } from '@angular/core';
|
||||||
import { MatFormField, MatLabel } from '@angular/material/form-field';
|
import { MatFormField, MatLabel } from '@angular/material/form-field';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { UserInputService } from '../../services/user-input-service';
|
import { UserInputService } from '../../services/user-input-service';
|
||||||
import { VacationsNumber } from '../../services/calendar-service';
|
import { VacationsNumber } from '../../services/calendar-service';
|
||||||
import { debounceTime, takeUntil, Subject } from 'rxjs';
|
import { debounceTime, takeUntil, Subject } from 'rxjs';
|
||||||
|
|||||||
@@ -3,22 +3,21 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
|||||||
import { UserSettings } from './user-settings';
|
import { UserSettings } from './user-settings';
|
||||||
|
|
||||||
describe('UserSettings', () => {
|
describe('UserSettings', () => {
|
||||||
let component: UserSettings;
|
let component: UserSettings;
|
||||||
let fixture: ComponentFixture<UserSettings>;
|
let fixture: ComponentFixture<UserSettings>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [UserSettings],
|
imports: [UserSettings],
|
||||||
providers: [provideZonelessChangeDetection()]
|
providers: [provideZonelessChangeDetection()],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(UserSettings);
|
fixture = TestBed.createComponent(UserSettings);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { provideZonelessChangeDetection } from '@angular/core';
|
import { provideZonelessChangeDetection } from '@angular/core';
|
||||||
import {
|
import { CalendarService, DayType, SelectedDate, SelectedDates } from './calendar-service';
|
||||||
CalendarService,
|
|
||||||
DayType,
|
|
||||||
SelectedDate,
|
|
||||||
SelectedDates,
|
|
||||||
} from './calendar-service';
|
|
||||||
import { DateRange } from '@angular/material/datepicker';
|
import { DateRange } from '@angular/material/datepicker';
|
||||||
|
|
||||||
describe('CalendarService', () => {
|
describe('CalendarService', () => {
|
||||||
@@ -111,47 +106,32 @@ describe('CalendarService', () => {
|
|||||||
describe('SelectedDate', () => {
|
describe('SelectedDate', () => {
|
||||||
it('should check if date is selected', () => {
|
it('should check if date is selected', () => {
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||||
DayType.CP,
|
|
||||||
new DateRange<Date>(date, date),
|
|
||||||
);
|
|
||||||
expect(selectedDate.isSelected(date)).toBe(true);
|
expect(selectedDate.isSelected(date)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if date is start', () => {
|
it('should check if date is start', () => {
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||||
DayType.CP,
|
|
||||||
new DateRange<Date>(date, date),
|
|
||||||
);
|
|
||||||
expect(selectedDate.isStart(date)).toBe(true);
|
expect(selectedDate.isStart(date)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if date is end', () => {
|
it('should check if date is end', () => {
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||||
DayType.CP,
|
|
||||||
new DateRange<Date>(date, date),
|
|
||||||
);
|
|
||||||
expect(selectedDate.isEnd(date)).toBe(true);
|
expect(selectedDate.isEnd(date)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return correct classes for date', () => {
|
it('should return correct classes for date', () => {
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||||
DayType.CP,
|
|
||||||
new DateRange<Date>(date, date),
|
|
||||||
);
|
|
||||||
const classes = selectedDate.getClasses(date);
|
const classes = selectedDate.getClasses(date);
|
||||||
expect(classes).toContain(DayType.CP);
|
expect(classes).toContain(DayType.CP);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return tooltip for selected date', () => {
|
it('should return tooltip for selected date', () => {
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(date, date));
|
||||||
DayType.CP,
|
|
||||||
new DateRange<Date>(date, date),
|
|
||||||
);
|
|
||||||
const tooltip = selectedDate.getTooltip(date);
|
const tooltip = selectedDate.getTooltip(date);
|
||||||
expect(tooltip).toBeTruthy();
|
expect(tooltip).toBeTruthy();
|
||||||
});
|
});
|
||||||
@@ -159,19 +139,13 @@ describe('SelectedDate', () => {
|
|||||||
it('should calculate length in days', () => {
|
it('should calculate length in days', () => {
|
||||||
const startDate = new Date(2024, 0, 1);
|
const startDate = new Date(2024, 0, 1);
|
||||||
const endDate = new Date(2024, 0, 3);
|
const endDate = new Date(2024, 0, 3);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.CP, new DateRange<Date>(startDate, endDate));
|
||||||
DayType.CP,
|
|
||||||
new DateRange<Date>(startDate, endDate),
|
|
||||||
);
|
|
||||||
expect(selectedDate.getLengthInDays()).toBe(3);
|
expect(selectedDate.getLengthInDays()).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not mark TODAY as selected', () => {
|
it('should not mark TODAY as selected', () => {
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
const selectedDate = new SelectedDate(
|
const selectedDate = new SelectedDate(DayType.TODAY, new DateRange<Date>(date, date));
|
||||||
DayType.TODAY,
|
|
||||||
new DateRange<Date>(date, date),
|
|
||||||
);
|
|
||||||
expect(selectedDate.isSelected(date)).toBe(false);
|
expect(selectedDate.isSelected(date)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -180,18 +154,14 @@ describe('SelectedDates', () => {
|
|||||||
it('should push selected dates', () => {
|
it('should push selected dates', () => {
|
||||||
const selectedDates = new SelectedDates();
|
const selectedDates = new SelectedDates();
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
selectedDates.push(
|
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
|
||||||
);
|
|
||||||
expect(selectedDates._datesSelected.length).toBe(1);
|
expect(selectedDates._datesSelected.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if date is selected', () => {
|
it('should check if date is selected', () => {
|
||||||
const selectedDates = new SelectedDates();
|
const selectedDates = new SelectedDates();
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
selectedDates.push(
|
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
|
||||||
);
|
|
||||||
expect(selectedDates.isSelected(date)).toBe(true);
|
expect(selectedDates.isSelected(date)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -212,9 +182,7 @@ describe('SelectedDates', () => {
|
|||||||
it('should compute heuristics', () => {
|
it('should compute heuristics', () => {
|
||||||
const selectedDates = new SelectedDates();
|
const selectedDates = new SelectedDates();
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
selectedDates.push(
|
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
|
||||||
);
|
|
||||||
const heuristic = selectedDates.computeHeuristics();
|
const heuristic = selectedDates.computeHeuristics();
|
||||||
expect(typeof heuristic).toBe('number');
|
expect(typeof heuristic).toBe('number');
|
||||||
});
|
});
|
||||||
@@ -222,9 +190,7 @@ describe('SelectedDates', () => {
|
|||||||
it('should return correct classes', () => {
|
it('should return correct classes', () => {
|
||||||
const selectedDates = new SelectedDates();
|
const selectedDates = new SelectedDates();
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
selectedDates.push(
|
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
|
||||||
);
|
|
||||||
const classes = selectedDates.getClasses(date);
|
const classes = selectedDates.getClasses(date);
|
||||||
expect(classes).toContain(DayType.CP);
|
expect(classes).toContain(DayType.CP);
|
||||||
});
|
});
|
||||||
@@ -232,9 +198,7 @@ describe('SelectedDates', () => {
|
|||||||
it('should return tooltip', () => {
|
it('should return tooltip', () => {
|
||||||
const selectedDates = new SelectedDates();
|
const selectedDates = new SelectedDates();
|
||||||
const date = new Date(2024, 0, 1);
|
const date = new Date(2024, 0, 1);
|
||||||
selectedDates.push(
|
selectedDates.push(new SelectedDate(DayType.CP, new DateRange<Date>(date, date)));
|
||||||
new SelectedDate(DayType.CP, new DateRange<Date>(date, date)),
|
|
||||||
);
|
|
||||||
const tooltip = selectedDates.getTooltip(date);
|
const tooltip = selectedDates.getTooltip(date);
|
||||||
expect(tooltip).toBeTruthy();
|
expect(tooltip).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ export class CalendarService {
|
|||||||
|
|
||||||
// Get selected dates from current date to the next year
|
// Get selected dates from current date to the next year
|
||||||
getSelectedDatesFromNow(): SelectedDates {
|
getSelectedDatesFromNow(): SelectedDates {
|
||||||
const holidays = this.getHolidays();
|
const _holidays = this.getHolidays();
|
||||||
let selectedDates: SelectedDates = new SelectedDates();
|
const selectedDates: SelectedDates = new SelectedDates();
|
||||||
// Logic to populate selectedDates from now until the next year
|
// Logic to populate selectedDates from now until the next year
|
||||||
return selectedDates;
|
return selectedDates;
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ export class CalendarService {
|
|||||||
getSelectedDatesForYear(): SelectedDates {
|
getSelectedDatesForYear(): SelectedDates {
|
||||||
const holidays = this.getHolidays();
|
const holidays = this.getHolidays();
|
||||||
const monthInAYear = this.monthsInAYearFromNow();
|
const monthInAYear = this.monthsInAYearFromNow();
|
||||||
let selectedDates: SelectedDates = new SelectedDates();
|
const selectedDates: SelectedDates = new SelectedDates();
|
||||||
|
|
||||||
// Use DateCacheService to get today's date components
|
// Use DateCacheService to get today's date components
|
||||||
const {
|
const {
|
||||||
@@ -519,7 +519,7 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
const daysInRange = selectedDate.getLengthInDays();
|
const daysInRange = selectedDate.getLengthInDays();
|
||||||
|
|
||||||
// For each day in the range, add it to the appropriate month
|
// For each day in the range, add it to the appropriate month
|
||||||
let currentDate = new Date(selectedDate.range.start);
|
const currentDate = new Date(selectedDate.range.start);
|
||||||
for (let i = 0; i < daysInRange; i++) {
|
for (let i = 0; i < daysInRange; i++) {
|
||||||
const monthKey = currentDate.getFullYear() * 12 + currentDate.getMonth();
|
const monthKey = currentDate.getFullYear() * 12 + currentDate.getMonth();
|
||||||
monthCounts.set(monthKey, (monthCounts.get(monthKey) ?? 0) + 1);
|
monthCounts.set(monthKey, (monthCounts.get(monthKey) ?? 0) + 1);
|
||||||
@@ -719,7 +719,10 @@ export class SelectedDates implements SelectedDateInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lookForVacation(vacType: DayType, calendarService: CalendarService): { apply: () => void; heuristic: number } {
|
lookForVacation(
|
||||||
|
vacType: DayType,
|
||||||
|
calendarService: CalendarService,
|
||||||
|
): { apply: () => void; heuristic: number } {
|
||||||
const daysOfYear = calendarService.monthsInAYearFromNow();
|
const daysOfYear = calendarService.monthsInAYearFromNow();
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setHours(0, 0, 0, 0);
|
now.setHours(0, 0, 0, 0);
|
||||||
|
|||||||
@@ -3,40 +3,40 @@ import { provideZonelessChangeDetection } from '@angular/core';
|
|||||||
import { ThemeService } from './theme-service';
|
import { ThemeService } from './theme-service';
|
||||||
|
|
||||||
describe('ThemeService', () => {
|
describe('ThemeService', () => {
|
||||||
let service: ThemeService;
|
let service: ThemeService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [provideZonelessChangeDetection()],
|
providers: [provideZonelessChangeDetection()],
|
||||||
});
|
});
|
||||||
service = TestBed.inject(ThemeService);
|
service = TestBed.inject(ThemeService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize with system preference', () => {
|
it('should initialize with system preference', () => {
|
||||||
expect(service.isLightMode()).toBeDefined();
|
expect(service.isLightMode()).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should toggle theme', () => {
|
it('should toggle theme', () => {
|
||||||
const initialMode = service.isLightMode();
|
const initialMode = service.isLightMode();
|
||||||
service.toggleTheme();
|
service.toggleTheme();
|
||||||
expect(service.isLightMode()).toBe(!initialMode);
|
expect(service.isLightMode()).toBe(!initialMode);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should force light mode when specified', () => {
|
it('should force light mode when specified', () => {
|
||||||
service.toggleTheme(true);
|
service.toggleTheme(true);
|
||||||
expect(service.isLightMode()).toBe(true);
|
expect(service.isLightMode()).toBe(true);
|
||||||
expect(document.body.classList.contains('light-mode')).toBe(true);
|
expect(document.body.classList.contains('light-mode')).toBe(true);
|
||||||
expect(document.body.classList.contains('dark-mode')).toBe(false);
|
expect(document.body.classList.contains('dark-mode')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should force dark mode when specified', () => {
|
it('should force dark mode when specified', () => {
|
||||||
service.toggleTheme(false);
|
service.toggleTheme(false);
|
||||||
expect(service.isLightMode()).toBe(false);
|
expect(service.isLightMode()).toBe(false);
|
||||||
expect(document.body.classList.contains('light-mode')).toBe(false);
|
expect(document.body.classList.contains('light-mode')).toBe(false);
|
||||||
expect(document.body.classList.contains('dark-mode')).toBe(true);
|
expect(document.body.classList.contains('dark-mode')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, signal, PLATFORM_ID, inject } from '@angular/core';
|
import { Injectable, signal } from '@angular/core';
|
||||||
import { VacationsNumber } from './calendar-service';
|
import { VacationsNumber } from './calendar-service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|||||||
+10
-9
@@ -1,18 +1,19 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<title>VacOptimizer</title>
|
<title>VacOptimizer</title>
|
||||||
<base href="/">
|
<base href="/" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
<link
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
|
</html>
|
||||||
</html>
|
|
||||||
+1
-2
@@ -2,7 +2,6 @@ import { BootstrapContext, bootstrapApplication } from '@angular/platform-browse
|
|||||||
import { App } from './app/app';
|
import { App } from './app/app';
|
||||||
import { config } from './app/app.config.server';
|
import { config } from './app/app.config.server';
|
||||||
|
|
||||||
const bootstrap = (context: BootstrapContext) =>
|
const bootstrap = (context: BootstrapContext) => bootstrapApplication(App, config, context);
|
||||||
bootstrapApplication(App, config, context);
|
|
||||||
|
|
||||||
export default bootstrap;
|
export default bootstrap;
|
||||||
+1
-2
@@ -2,5 +2,4 @@ import { bootstrapApplication } from '@angular/platform-browser';
|
|||||||
import { appConfig } from './app/app.config';
|
import { appConfig } from './app/app.config';
|
||||||
import { App } from './app/app';
|
import { App } from './app/app';
|
||||||
|
|
||||||
bootstrapApplication(App, appConfig)
|
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
|
||||||
.catch((err) => console.error(err));
|
|
||||||
+1
-3
@@ -41,9 +41,7 @@ app.use(
|
|||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
angularApp
|
angularApp
|
||||||
.handle(req)
|
.handle(req)
|
||||||
.then((response) =>
|
.then((response) => (response ? writeResponseToNodeResponse(response, res) : next()))
|
||||||
response ? writeResponseToNodeResponse(response, res) : next(),
|
|
||||||
)
|
|
||||||
.catch(next);
|
.catch(next);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user